woshidan's blog

そんなことよりコードにダイブ。

ERBとCSVを使って簡単な自分用ドリルを作る

最近シェルの勉強をしたいんですが、以前SQLを勉強した時のように基本的なシェルを反復して書くのが一番では? と思って、ドリルのようなものが欲しくなりました。

そこで、CSVにお題とお題の答えを書き連ねたらアコーディオンで答えを出したり隠したりしてくれるHTMLを出すコードと、そのHTMLを見るアプリ*1を書いたのでポスト。

github.com

使い方は、

$ ruby generator/generator.rb input.csv > drill.html

を実行して drill.html をブラウザで開く、です。アプリで見る場合はさらに

$ cp drill.html cp drill.html CSDriller/app/assets/drill.html

とした後、一緒についているAndroidプロジェクトをビルドしてください。

実際動かしてみると下記のような感じです。

参考

qiita.com

特に上記記事のアコーディオンがよすぎて作りたくなったので多謝。

*1:といってもAndroidのWebViewに突っ込むだけ

Cocos2d-x 2.2.6をMac OS Sierraで動くようにするまでのメモ

仕事でやってて面白かったので許可を取って公開。

環境

スクリーンショット 2017-10-19 13.30.52.png (44.2 kB)

メモ

プロジェクトの作成

python create_project.py -project MyGame -package com.MyCompany.AwesomeGame -language cpp

Eclipse インストーラからJavaIDEをインストールして環境変数を設定

  • Help > Install New SoftWare
    • Work with の欄に https://dl-ssl.google.com/android/eclipse を入力
      • Developer Tools をダウンロード
      • Eclipseを再起動
      • DLしたらどこのAndroid SDK を使うか聞かれるので Andorid Studioで使っているパス /Users/woshidan/Library/Android/sdk あたりでも答えておく (設定した値は Preferences > Android からでも確認可能. Android SDKのバージョンが v26 以上なら後の方に書くようにEclipse用に別途古いAndroid SDKを用意した方が良い)

cocos2dxのプロジェクトをインポートとプロジェクト間の依存関係の設定

  • Import > Andorid > Existing Android Code Into Workspacecocos2d-x-2.2.6/cocos2dxplatform/andorid/java をインポート
    • Copy projects into workspace にチェックしておく
  • Import > Andorid > Existing Android Code Into Workspacecocos2d-x-2.2.6/cocos2dx/ProjectName をインポート
    • Copy projects into workspace にチェックしない
    • ProjectName のAndroidManifest.xml 中の minSDKVersion が小さすぎるので14くらいにあげておく
  • PackageManager 上で ProjectName の上を右クリックして Properties > Android を開き、Libraryのところで、libcocos2dxを指すよう設定する
    • isLibrary のチェックはしない(アプリプロジェクトにisLibraryのチェックをするとapkが生成されない)

アプリ側のプロジェクトのLinked Resourcesの修正

  • プロジェクトのところで右クリックして Properteis > Resources > Linked ResourcesLinked Resources のタブを確認。Classes, cocos2dx, extensions, scropting の壊れているパスを修正

古めのNDKを用意してNDK_ROOTの環境変数を設定

  • 古めのNDK(Android NDK, Revision 10e)を https://developer.android.com/ndk/downloads/older_releases.html からDL
    • EclipsePreferences > Android > NDK の項目の NDK LocationにDLしてきたNDKを配置したパスを書く
  • Preferences > Android > NDK に設定したパスが Eclipse に見えてないことがあるので C/C++ > Build > Environment にも NDK_ROOT として追加
  • Bulid Project した結果、 build_native.sh は走ってくれたが。。

トラブルシューティング

Could not find BuildTest.apk! ...

Unknown error: Unable to build: the file dx.jar was not loaded from the SDK folder!

Eclipse起動時に Failed to get the required ADT version number from the SDK

エミュレータを起動しようとしたら[Start]が押せず詳細を確認したら Google Nexus 5X No longer exists as a device

Description Resource Path Location TypeThe container 'Android Dependencies' references non existing library'cocos2d-x-2.2.6/cocos2dx/platform/android/java/bin/libcocos2dx.jar' ...

  • http://renkaze.seesaa.net/article/405756568.html
  • libcocos2xd のプロジェクトで jar を吐き出すパスが別のパスに設定されている
  • libcocos2dx のプロジェクトで右クリックして Properties > Java Build Path < Source の下側の Default output folderlibcocos2dx/bin/classes に設定

java.lang.IllegalArgumentException: No configs match configSpec というエラーでエミュレータで起動するとクラッシュする

Eclipseエミュレータを起動しようとしたら PANIC: Missing emulator engine program for 'arm' CPUS... と出る

Google Nexus 5X no longer exists as a device とでて Nexus 5 のスキンのエミュレータがつけない

まとめ

そろそろEclipseAndroidを開発するのをやめたほうがいいのでは。

現場からは以上です。

ウェブオペレーション サイト運用管理の実践テクニックを読んだ

ウェブオペレーション ―サイト運用管理の実践テクニック (THEORY/IN/PRACTICE)

ウェブオペレーション ―サイト運用管理の実践テクニック (THEORY/IN/PRACTICE)

前職で研修に使われていた記憶があり、いい本なのかなーと思って読みました。 運用系の仕事をしているわけではないので、一ヶ月強かけてなんとか読みおおせた感じです。

各章はかなり生き生きとした文章で、場合によっては「これはもしかして飲み会の書き起こしではないだろうか」「これ開発者の前に出したらグーパンされても文句言えないのでは」と感じた章もいくつかあり、いんたーねっつの進歩を体感することができます。

なんとか読みおおせた印象としては

  • ウェブサービスのサーバ構成について、それぞれのサーバの大まかなイメージが頭に描けるようになった
    • リバースプロキシで静的ファイルを返したりロードバランサの役目もさせて負荷分散をする、一回取得したデータをキャッシュにつっこんで奥のDBにリクエストがいかないようにする、リクエストが大量に来てキャッシュが切れた時DBにリクエストが殺到してダウンする、みたいな
  • 結構前の章でいいよって言われていることが次の章でそうでもないよって言われてたりして、道具を使ったり人の意見を採用する前に要件を確認するのは大事だな、と思えた
  • もともとあまりシェルになじみのない人でなんでもLL言語+RDBMSで考えがちだったのですが、監視のシステムの実装例でawkなどが出てきたり、道具の得意分野に合わせていろんな道具を操ることについて考えさせられた
  • Dynamoの論文の特徴が面白かった
    • Consistent hash, read repair, hinted handoffが書いてあったよと紹介されていて、特に気になったのは read repair.
  • 高可用性(リクエストがあったらいつでも応答できる)と応答速度/パフォーマンス(速くレスポンスが返ってくる)と一貫性(データに矛盾が生じない)が違う、相反しない、という例がくどくどと書かれていて身にしみた
    • 分割耐性はまだ自信がない
  • みんな結構データセンタや社内システムの外からファイアウォールping弾かれてて面白かった
  • サーバの監視システムについて、死活監視・リソース監視(Nagiosなど)・それらの結果の表示(Cacti, Muninなど)、みたいなソフトごとの得意領域があるんだなみたいなイメージが湧いた
  • レスポンスの配信の節で、サーバとクライアントの間に一度に取得できるオブジェクト数の上限があるということを知らなかったため、デバッグコンソールを眺めてみたり

でした。

個人的に特に面白かったのは、6章、9章、12章、15章。

6章の学生時代のサイト運営経験から右往左往しながらシステムを運用していく様子に共感を覚え、9章の急増したトラフィックへの障害対応の文章を読んでリクエストがどのサーバに順に到達するかのイメージを描き、12章でこの人本当にMySQL好きなんだなという気持ちになり*1、15章で高分散型*2NoSQLの監視ソフトウェアの違いや比較的小さい規模で動かすものと大きい規模で動かすものの思想の違いに思いを馳せたり。。

運用経験のない人が一人で読むとどうなのかな、と思いつつ、ここで紹介されている技術は今の時代ではそれなりに枯れていたり廃れていたり流行っていたりするためインターネットにまとめられた記事を適宜参照しながら進めました。しかしまあ、早口でまくしたてるクセがある他部署の先輩の運用談義を聞かされている風味もあり、本開いてる間はなんだか忙しかったのでした...。

前述の文体が気になる点や2011年出版でやや古いところもあるので、この本のポジションに収まる新しい本とかあったらそちらも読んでみたいです。

現場からは以上です。

*1:MySQLコンサルタントの方が書いてるのでそりゃそーだって話なんだけど、これだけMySQL好きな人になら一回コンサルされたいなという感じでした

*2:いまの言い方だとビッグデータ用?

Athenaでクエリの実行結果を使い回すために最近投げたクエリとそのquery_execution_idを記録する

Amazon Athena では

S3上にあるファイルをスキャンした量に応じた料金 + S3をスキャンするためにGETしたリクエスト数(+ファイル容量)

に応じて課金されます。

また、Athenaでは一回SQLを実行した時に query_execution_id が発行され、同じ query_execution_id を使って GetQueryResults に複数回リクエストを送ると二回目以降は一回目の実行結果が再利用され、新しいクエリは実行されません*1

このため、一度検索した結果が使い回せるのであれば、 query_execution_id を控えておいて結果を再利用した方が良いです。

というわけで、雑に CSV で直近で実行した クエリquery_execution_id の対応を控えておいて、実行しようとしたクエリがすでに実行済みであれば教えてくれるスクリプトを書いたのでポスト。

gem にしようかと思ったけどそれほど大きくなかったのでひとまずこれで。

# athena_query_keeper.rb
require "csv"

class AthenaQueryKeeper
  def initialize(csv_path, keepe_count=20)
    @csv_path = csv_path
    @keepe_count = keepe_count
    @keeped_queries = []

    begin
      CSV.foreach(@csv_path) do |row|
        @keeped_queries.push({ time: row[0], execution_id: row[1], query_string: row[2] })
      end
    rescue Exception => ex
      puts ex
      puts "query keeped csv(path=#{csv_path}) was not found or format was broken."
      puts "new csv will be created."
    end
  end

  def keep(excution_id, query_string)
    if @keeped_queries.count >= @keepe_count
      @keeped_queries.slice!(0, @keeped_queries.count + 1 - @keepe_count)
    end

    @keeped_queries.push({ time: Time.now.to_i, execution_id: excution_id, query_string: query_string })
    flush
  end

  def flush
    CSV.open(@csv_path, 'w') do |csv|
      @keeped_queries.each do |query|
        csv << [ query[:time], query[:execution_id], query[:query_string] ]
      end
    end
  end

  def keeped_execution_id_for(query_string)
    keeped_query = @keeped_queries.find { |query| query[:query_string] == query_string }

    if keeped_query.nil?
      puts "query: #{query_string.slice(0, 60)} ... has been not executed recently."
      ""
    else
      puts "query: \"#{query_string.slice(0, 60)} ... \" has been executed recently."
      puts "you should reuse that result unless the result update must be used."
      keeped_query[:execution_id]
    end
  end
end
# test.rb
require 'aws-sdk-athena'
require "#{File.dirname(__FILE__)}/athena_query_keeper.rb"

query_string = ARGV[0]
client = Aws::Athena::Client.new

begin
  query_execution_id = nil
  athena_query_keeper = AthenaQueryKeeper.new("./sample.csv", 20)
  keeped_query_id = athena_query_keeper.keeped_execution_id_for query_string
  if keeped_query_id.empty?
    start_query_response = client.start_query_execution({
        query_string: query_string,
        query_execution_context: {
           database: "mydatabase",
        },
        result_configuration: {
          output_location: "s3://example-woshidan-test/athena_query_result"
        },
    })

    sleep(3)

    query_execution_id = start_query_response.query_execution_id
    athena_query_keeper.keep(query_execution_id, query_string)
  else
    puts "reuse query result for id=#{keeped_query_id}."
    query_execution_id = keeped_query_id
  end

  puts "query_execution_id: #{query_execution_id}"

  get_query_response = client.get_query_results({
    query_execution_id: query_execution_id
  })

  get_query_response.inspect
rescue Aws::Athena::Errors::InvalidRequestException => ex
  puts ex.inspect
ensure
  puts "query request ended."
end
$ ruby test.rb 'SELECT * FROM mydatabase."athena_logs_20171003_app" WHERE user_id = "tester" LIMIT 10;'
query: SELECT * FROM mydatabase."athena_logs_20171003_app" WHERE us ... has been not executed recently.
result_csv_file_key: 32e86285-e0c5-4329-973d-38bd039945e4
#<Aws::Athena::Errors::InvalidRequestException: Query did not finish successfully. Final query state: FAILED>
query request ended.

$ ruby test.rb 'SELECT * FROM mydatabase."athena_logs_20171003_app" WHERE user_id = "tester" LIMIT 10;'
query: "SELECT * FROM mydatabase."athena_logs_20171003_app" WHERE us ... " has been executed recently.
you should reuse that result unless the result update must be used.
reuse query result for id=32e86285-e0c5-4329-973d-38bd039945e4
result_csv_file_key: 32e86285-e0c5-4329-973d-38bd039945e4
#<Aws::Athena::Errors::InvalidRequestException: Query did not finish successfully. Final query state: FAILED>
query request ended. 

参考

*1:管理画面のHistoryなどで確認できます

AWS CLIでAWS Athenaのクエリがデータベースが見つからないためになんども失敗する場合は結果出力先のS3のバケットのregionを確認する

俺の屍を越えていけ、的なメモ。

require 'aws-sdk-athena'
client = Aws::Athena::Client.new

begin 
    start_response = client.start_query_execution({
      query_string: "SELECT * FROM samples limit 10",
      query_execution_context: {
        database: "mydatabase",
      },
      result_configuration: {
        output_location: "s3://sample-woshidan-test-tokyo/athena_query_result"
      },
    })

  sleep(5)

    result_response = client.get_query_results({
        query_execution_id: start_response.query_execution_id
    })
rescue Aws::Athena::Errors::InvalidRequestException => ex
  puts ex.inspect
ensure
  puts "Ensure"
end

上記のコードで aws-sdk-athena でAthena のクエリを飛ばそうとしたら

Query did not finish successfully. Final query state: FAILED (Aws::Athena::Errors::InvalidRequestException)

というエラーが出て失敗する。なんで失敗したのかさっぱりわからないので、CLIで実行し直すと、

$ aws athena start-query-execution \
>  --query-string "SELECT * FROM mydatabase.samples limit 10;" \
>  --result-configuration OutputLocation=s3://sample-woshidan-test/athena_query_result
{
    "QueryExecutionId": "f8e45456-238a-44ba-955a-40f048e5c3b2"
}

$ aws athena get-query-execution --query-execution-id f8e45456-238a-44ba-955a-40f048e5c3b2
{
    "QueryExecution": {
        "Status": {
            "SubmissionDateTime": 1506675885.243, 
            "State": "FAILED", 
            "CompletionDateTime": 1506675885.387, 
            "StateChangeReason": "com.facebook.presto.hive.DataCatalogException: Namespace mydatabase not found. Please check your query."
        }, 
        "Query": "SELECT * FROM mydatabase.logs limit 10", 
        "Statistics": {
            "DataScannedInBytes": 0, 
            "EngineExecutionTimeInMillis": 53
        }, 
        "ResultConfiguration": {
            "OutputLocation": "s3://sample-woshidan-test/athena_query_result/f8e45456-238a-44ba-955a-40f048e5c3b2.csv"
        }, 
        "QueryExecutionId": "f8e45456-238a-44ba-955a-40f048e5c3b2"
    }
}

というメッセージが出て、どうも database が見つからないらしい。

もしかして、 AWS のアカウントの region を間違ったかな、とAWS CLIの設定で使う region を Athenaの管理画面で表示されている region に設定しなおしてみると

f:id:woshidan:20170929195730p:plain

- region = ap-northeast-1
+ region = us-east-2
The S3 location provided to save your query results is invalid. Please check your S3 location is correct and is in the same region and try again. If you continue to see the issue, contact customer support for further assistance.

戻すと

+ region = ap-northeast-1
- region = us-east-2
Query did not finish successfully. Final query state: FAILED (Aws::Athena::Errors::InvalidRequestException)

どっちに設定しても何かしらダメっぽい...

最終的に

自分がAthenaのクライアントとして選んだ aws-athena-client はその実装でAWS CLIを利用しています。

そして、AWS CLIで設定値として使うregion は ~/.aws/config で指定しているためか

  • クエリを投げるAthenaのデータベースが ~/.aws/config で指定したregionに存在すること
  • クエリ結果の出力先のS3のバケットが ~/.aws/config で指定したregionに存在すること

の2点がAWS CLIでAthenaにクエリを投げるためには必要みたいです。そして最終的に結果出力先のS3のバケットのregionをAthenaのデータベースのregionに揃えてことなきを得ました。

AthenaのクエリでスキャンするS3のバケットのregionは、AWS CLIで使うIAMアカウントがアクセス権を持ってさえいればAthenaのデータベースのregionと違ってもクエリが実行できるのもあっていまいち原因がわからず焦りました。

もしかしたらAthena - S3 間だけでなく、複数のAWSのサービスが協調して動く場合、CLIが一個しかregionの値を持たない、みたいなことが原因で似たようなトラブルはあるかもなと思いつつ、現場からは以上です。

iOSにてzlibのdeflate()コマンドでファイルをgzip形式で圧縮する方法について

zlibについてコピペで使うのはいやだなと思ったのでちょっと調べてメモ。

比較的小さいファイルの圧縮を行うなど、圧縮前後のデータを全てメモリ上に展開できる場合は、 compress() を使って一気に圧縮できますが、今回はストリームから少しずつデータを出し入れして圧縮していく deflate() コマンドを利用する場合についてメモします。

zlibとは

まず、zlibが何かをこちらのページから引用します。

zlibとは、圧縮アルゴリズムの一種である Deflate のライブラリであり,C#, Haskell, Java, Perl, Python, Ruby など,主要なプログラミング言語では,軒並み使えるように整備されています.圧縮・伸長が高速なこともあり,ディスク領域の有効利用や通信量の削減を目的として,zlib は気軽に利用できます.

XCodeでのiOSの開発環境への導入について

XCodeでのiOSの開発環境下では、iOS SDKに一部APIが利用できる状態でzlib.hが含まれているため umcompress() はリンクエラーで使えないようでしたが、compress() については zlib.h をimportすれば利用可能でした。

導入しようとしたプロジェクトによっては zlib.h のimportだけでは

Undefined symbols for architecture x86_64:
  "_deflate", referenced from:
      +[GzipSample compress:] in Sunaba(GzipSample.o)
  "_deflateInit2_", referenced from:
      +[GzipSample compress:] in Sunaba(GzipSample.o)
  "_deflateEnd", referenced from:
      +[GzipSample compress:] in Sunaba(GzipSample.o)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

のようなエラーメッセージでリンクに失敗することがあります。

その場合は、

f:id:woshidan:20170927212154p:plain

libzを利用するアプリのプロジェクトのターゲットの設定で、 Build Phases > Link Binary With Libraries から libz と入力して出てきたものの中から利用するバージョンの libz を追加してビルドしなおしてください *1

f:id:woshidan:20170927212226p:plain

今回は圧縮の方法についてだけひとまずメモしようと思っているのでこれで進みますが、iOS添付でない zlib.h の導入には以下が参考になりそうです。

https://gist.github.com/dulaccc/75f1f49f53e544cef549

zlibでファイルを圧縮する

z_stream構造体のオブジェクトを生成

zlibの基本的な使い方ですが、 z_stream 構造体のオブジェクトを生成して、 zalloc, zfree, opaque のメンバを設定します。

z_stream stream;
stream.zalloc = Z_NULL;
stream.zfree = Z_NULL;
stream.opaque = Z_NULL;

この時、設定する値はデフォルト値である Z_NULL で基本的に問題ありません。gzip形式で圧縮する場合は、 delateInit2() で stream の初期化処理を行います。

入力データの設定

z_stream 構造体の avail_in, next_in の設定をします。

stream.next_in = (Bytef *)[inputData bytes]; // 入力バッファへのポインタ
stream.avail_in = (uint)[inputData length];  // 入力データの量

出力データの初期設定

自分の環境のzlibではデフォルト値でもよかったんですが、ライブラリなどにする場合、どのzlibとリンクされるか固定ではないので、 z_stream 構造体の avail_out, total_out の初期値の設定をしておきます。

stream.total_out = 0; // これまでの出力されたデータの合計長
stream.avail_out = 0; // 出力データのバッファ上の残量

ストリームの初期化

gzip圧縮の場合、ストリームの初期化は下記の deflateInit2() コマンドで行います。

// int deflateInit2(z_stream *strm,
//                  int level,  .... 圧縮レベル(数値が小さいほど圧縮時間が小さく、大きいほど圧縮後のサイズが小さい)
//                                   デフォルト   Z_DEFAULT_COMPRESSION(=6)
//                                   圧縮速度最高 Z_BEST_SPEED(=1)
//                                   圧縮率最高   Z_BEST_COMPRESSION(=9)
//                                   圧縮無効     Z_NO_COMPRESSION(=0)
//                  int method, .... 圧縮方法     zlib-1.2.6 で指定できるのは Z_DEFLATED のみ
//                  int windowBits,  ウィンドウ・サイズ gzip 形式では  31 を指定する必要あり
//                  int memLevel, .. メモリの消費量を指定 大きくても特に嬉しいことはない. デフォルト8
//                  int strategy); . 圧縮の方式を指定。デフォルトは Z_DEFAULT_STRATEGY
deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY)

これによって、出力バッファの合計長 stream.total_out や、出力データのバッファ上の残量 stream.avail_out などの出力関連のパラメータが設定されます。

圧縮したデータをstreamに出力する

int result = deflate(&stream, Z_FINISH);
// 戻り値の意味
// Z_OK ... まだ出力できる範囲が残っているのでもう一回deflate()が呼び出せる
// Z_STREAM_END ... もう出力できる範囲がない
// Z_BUF_ERROR ... 入力データ、出力バッファの一時的な不足
// Z_STREAM_ERROR ... next_in, next_outがNULLの時や内部状態が破壊された

deflate を実行するたびに stream.next_out で指定されたポインタのバッファへ圧縮結果が書き込まれます。 この出力先のポインタは書き込むたびに書き込んだ分ずらす必要があります。

stream.next_out = (uint8_t *)[data mutableBytes] + stream.total_out;
// ... 出力先のバッファのポインタの位置をこれまで書き込んだ分 stream.total_out だけずらして設定
// [NSMutableData mutableBytes] はNSMutableDataで管理している生のバイト列へのポインタを返してくれる

また、出力を書き込むたびにバッファ残量の値が減少するので、不足するようであれば書き込み先のバイト列の長さを伸ばしたり、次に書き込む前に出力バッファ残量が不足しないよう設定し直してやる必要があります。

if (stream.total_out >= [data length])
{
    data.length += ChunkSize;
}               
stream.avail_out = (uInt)([data length] - stream.total_out);

圧縮処理に使ったメモリの解放

最後に後始末のため deflateEnd(&stream) を呼び出して、使ったメモリを開放します。

deflateEnd(&stream);

最終的に書いたみたコード

以上を踏まえつつ、

のコードを参考にして書くとこういうコードになりました。

#import <Foundation/Foundation.h>
#import <zlib.h>

static const NSUInteger CHUNK_SIZE = 1000; // バイト列を伸ばす処理を確認するために小さめにしている
static const int GZIP_WINDOW_BITS = 31;
static const int DEFAULT_MEMORY_USAGE = 8;

+ (NSData *)compress:(NSData *)sourceData
{
    if ([sourceData length])
    {
        z_stream stream;
        stream.zalloc = Z_NULL;
        stream.zfree = Z_NULL;
        stream.opaque = Z_NULL;
        stream.avail_in = (uint)[sourceData length];
        stream.next_in = (Bytef *)[sourceData bytes];
        stream.total_out = 0;
        stream.avail_out = 0;
        
        if (deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, GZIP_WINDOW_BITS, DEFAULT_MEMORY_USAGE, Z_DEFAULT_STRATEGY) == Z_OK)
        {
            NSMutableData *data = [NSMutableData dataWithLength:CHUNK_SIZE];
            int result = 0;
            while (result == Z_OK)
            {
                if (stream.total_out >= [data length])
                {
                    data.length += CHUNK_SIZE;
                }
                stream.next_out = (uint8_t *)[data mutableBytes] + stream.total_out;
                stream.avail_out = (uInt)([data length] - stream.total_out);
                result = deflate(&stream, Z_FINISH);
            }
            deflateEnd(&stream);
            if (result != Z_STREAM_END) {
                return nil;
            }
            data.length = stream.total_out;
            return data;
        }
    }
    return nil;
}

@end

参考

どー考えてもライブラリのリンク関連についてまた勉強する必要がありますが、それはおかわり案件にします。現場からは以上です。

*1:ライブラリ用のプロジェクトなので /path/to/libz.tbd is not an object file (not allowed in a library) とエラーメッセージが出てテスト用にビルドできなくて... という場合はテスト用のターゲットに追加する

Heartbeatとブロックデバイスについて

短いですが、一応何かしら書くぞということで、本を読んでいたら「Heartbeatがスタンバイしたサーバを有効にする」「ブロックデバイスレプリケーション」と出てきたのが意味がわからなかったのでメモ。 

Heartbeat関連

  • クラスタに関することでハートビートといえばHAクラスタを実現する上で利用される仕組み
    • HA(High Availability)クラスタクラスタリングの中の一種で、あるサービスを提供するサーバが落ちたら代替機がそのサービスを引き継ぐようにするようなクラスタ
    • サーバ間で一定の間隔でパケットを送りあってお互いが生きているか死活監視している
    • 一定以上他のノードからパケットが届かなくなったらフェイルオーバーを作動させる
  • クラスタの種類
  • ハートビート色々
    • http://www.atmarkit.co.jp/ait/articles/0711/13/news139_2.html の記事では
      • Heartbeat … オープンソースソフトウェア「Heartbeat」全体
      • heartbeat … 上記のHeartbeatで提供されるプログラム、もしくはそれを起動したときのプロセス
      • ハートビート … HAクラスタを構成するPCが相互に状態監視をする際のネットワーク構成、およびその通信を指すものとします
        • 数ページしか調べてない時点で上記とちょっと違う定義が出て来ていて、一番上の行にあるのがそれ。要注意単語。
  • ハートビートの通信をどう行うか
    • シリアル通信や、ハートビート用に確保したLANを利用したり。
    • 監視対象のサーバに外部からのリクエストがたくさん来て高負荷のときに、輻輳に巻き込まれてハートビートのパケットが失われないように、アプリケーション本体の動作に使われるネットワークを避けたほうがいいらしい

Linux系OSのブロックデバイスとキャラクタデバイス

  • Linux系OSでは、デバイスはコンピュータに接続された周辺機器のことをデバイスと呼び、 /dev 以下に作成されるデバイスファイルが周辺機器のハードウェアへのインタフェースとなっている
  • ブロックデバイスとは、ある一定の量(通常512~2048バイト)の読み書きをランダムアクセスで行えるデバイスで、ディスク装置全般が該当する
  • キャラクタデバイスはデータの入出力をバイト(1文字)単位で扱うデバイスで、データの読み出しや書き込みがシーケンシャルアクセスとなる
    • 一度読み書きしたデータを後戻りして再び読み書きすることはできない
    • キーボードやマウスなどが該当する

参考

ウェブオペレーションまとめ用に読み直してるんですが、真面目に読むと11章や12章がとても重くて少しいやですね。わかんないものはわかんないで仕方ないですが。

*1:ここのwebアプリケーションサーバをEC2で調達したりする話を聞いたことが多いので、なんとなくクラスタ=クラウド感がありました