2012/05/02

Javaプログラマ的なDynamoDBの捉え方

予測可能なパフォーマンスを、メンテナンスフリーで手に入れられるAmazon DynamoDB。非常に魅力的ではありますが、DynamoDBは徹底的にKey-Valueな考えが必要になります。というか、主キー以外でのルックアップは基本的に実行できないと考えた方が良いです。特にPrimary Keyの扱いが重要なので、ちょっとまとめみました。

DynamoDBのPrimary Keyには「Hash Type Primary Key」と、「Hash and Range Type Primary Key」の二種類があります。詳しい解説は他のサイトにまかせるとして、これらをJavaプログラマ的に捉えると下記のようになります。

Hash Type Primary Key

Map<HashKey,Object>
Hash Type Primary Keyを使用する場合には、本当に純粋なMapとして捉えられます。HashKeyが適切に分散さえしていれば、DynamoDBのすばらしい性能の恩恵を受けることができます。

Hash and Range Type Primary Key

Map<HashKey, SortedMap<RangeKey,Object>>
Hash and Range Type Primary Keyを使用する場合にも、Hash Keyの役割は変わりません。ただ、これに加えて、ソートされたRange Keyを用いることができます。Range Keyはソートされているだけでなく、Queryという機能を用いて柔軟にアクセスできます。ただし、Range Keyだけでは十分な分散は実現できません。

Primary Keyを用いないアクセス

Primary Keyを用いない場合には、Scanという機能で総当たり処理が可能です。ただ、Scanは主キーを無視した完全な総当たり処理であり、原則として実運用に用いるべきものではないと考えます。Primary Key以外で検索を行う必要がある場合には、別のTableをインデックスとして作成するか、DynamoDB以外と組み合わせることを検討した方が良いと思います。

Use it well

DynamoDBは強い癖がありますが、うまく使えば非常に強力な道具になります。エンジニアの腕の見せ所ですね。

2012/04/28

AWS Java SDK用のGuice Provider

AWS Java SDKに含まれるクライアントはすべてThread-Safeであり、クライアントを再利用すべきです。もちろん自前でClientを保持するクラスを書いても良いのですが、GuiceによるDIを使っているのであればProviderを定義するのがオススメです。

AWS SDKはもともとインタフェイスが定義されているので、そのインタフェイスに対してGuiceのProviderを定義し、共有クライアントをProvider内で管理します。クライアントを使う部分では、下記のように注入するだけなので、コードの見通しが非常に良くなります。

@Inject
AmazonDynamoDBAsync client;

もしくは

@Inject
Provider<AmazonDynamoDBAsync> clientProvider;

Provider自体はすぐに書けるコードなのですが、再利用性が高そうなのでgithubで公開しました。

参考URL

2012/04/27

Snowflake的なID生成方法

作っているプログラムでランダムなIDが必要となったため、ちょっとリサーチしました。久しぶりにJavaでコードを書いています。

UUID

完全に分散した環境で使用するのには、UUID(Universally Unique Identifier)が最適です。1IDあたり16バイトの容量が必要となる点をのぞけば、理想的なIDと言えると思います。ただIDは大量に使用されるので、16バイトというサイズはちょっと気になります。

Snowflake

SnowflakeはTwitterが使用しており、Apache Licenseで公開しているID生成方法です。ある程度分散した環境でも、1IDあたり8バイトの容量で利用できます。また非常に重要な特徴としてIDの先頭部分にタイムスタンプ利用しており、生成されたIDはある程度時系列に並びます。8バイト、つまり64bitを下記のように振り分けて使用します。

  • 先頭42bitにタイムスタンプ(ms)を割り当て、時刻を分散
  • 10bitのマシンIDを割り当て、ID生成を行うマシンを分散
  • 12bitのシーケンスIDを割り当て、同一時刻(ms)、同一マシンでのIDを分散
タイムスタンプに42bitしか割り当てないのは心もとないですが、基準時刻をシフトさせることで利用できる期間をのばしています。

参考

Snowflake的なID生成方法

Snowflakeは魅力的なのですが、テスト環境等も含めてマシンIDを管理するのは意外に面倒です。ただ、この体系の本質的な特徴は「先頭にタイムスタンプを割り当てた分散ID生成」であり、このテクニックはいろいろ応用できそうです。

今回使用する構成ではAmazon DynamoDBでDHCPのようにworkerを管理することにしました。DynamoDBをアトミックカウンターとして使用するという超贅沢仕様で、ついでにidの取得時刻、開放時刻等も記録してみました。worker idが枯渇するリスクを下げるため、worker idは24時間に一度更新しています。

2012/04/25

@propertyとAutomatic Reference Counting (ARC)

Objective-Cは段階的に進化してきたため、ネットで情報を検索する際には対象としている世代に注意する必要があります。特に、ARC導入前後はかなり考え方が変わっているので、簡単にまとめてみました。

共通の考え方

オブジェクトには参照カウンタが存在し、参照カウンタが0になった時点でオブジェクトは解放されます。JavaのGCのように定期的にメモリ解放が行われるのではなく、カウントが0になった時点ですぐにメモリが解放されます。

@property導入前

Next STEP時代から続くメモリ管理手法で、参照カウンタはすべてプログラマが手動で管理します。retainで参照カウンタを増やし、releaseもしくはautoreleaseで参照カウンタを減らします。setterもプログラマが明示的に記述し、必要に応じてretain/releaseを行います。

ARC導入前の@property (retain,assign世代)

@propertyに「retain」を指定した状態で@synthesizeを利用してメソッドを生成すると、「setするオブジェクトをretainし、以前setされていたオブジェクトをreleaseする」という処理を自動的に生成してくれます。あくまでも「プログラマの代わりにsetterを書いてくれる」だけなので、@synthesizeされたsetter以外では効果がありません。

ARC導入後の@property (strong,weak世代)

Automatic Reference Countingの名が示す通り、retain/releaseはすべて自動的にコンパイラが生成します。逆に、プログラマは明示的にretain/releaseを行うことはできません。簡単に書くとポインタを強参照(strong)と弱参照(weak)で区別し、強参照が増える場合にはretain、減る場合にはreleaseがコンパイラによって挿入されます。@propetyのstrong,weakで、その変数を強参照にするか弱参照にするかを定義します。当然、@synthesizeされたsetter以外でも効果があります。

ARCを使用するべきか?

使わない理由はないと思います。
ARCはコンパイル時のオプションであり、プロジェクトの一部分だけにARCを使用することもできます。また、ターゲットがiOS4の場合でも使用可能です。(ただしiOS4ではweakポインタが使えないので、zeroingのない__unsafe_unretainedポインタを使用します。)

XCode4.3環境でCocoaPodsをインストール

CocoaPods ではじめる Objective-C ライブラリ管理を参考にCocoaPodsをインストールしようとしたら、最初からハマりました...

sudo gem install cocoapods
Building native extensions.  This could take a while...
ERROR:  Error installing cocoapods:
 ERROR: Failed to build gem native extension.

/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby extconf.rb
mkmf.rb can't find header files for ruby at /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/ruby.h


Gem files will remain installed in /Library/Ruby/Gems/1.8/gems/xcodeproj-0.1.0 for inspection.
Results logged to /Library/Ruby/Gems/1.8/gems/xcodeproj-0.1.0/ext/xcodeproj/gem_make.out

最初はmacrubyをインストールしてmacjemでも試したのですが、同じようなエラーでした。調べてみると、まずXCode4.3環境ではmacrubyは必要ないようです。さらに、XCode Command Line Toolを追加でインストールする必要があります。さっそくインストールして再度トライ!

sudo gem install cocoapods
Password:
Building native extensions.  This could take a while...
[!] If this is your first time install of CocoaPods, or if you are upgrading, first run: $ pod setup
Successfully installed xcodeproj-0.1.0
Successfully installed cocoapods-0.5.1
2 gems installed
Installing ri documentation for xcodeproj-0.1.0...

No definition for generate_uuid

No definition for read_plist

No definition for write_plist
Installing ri documentation for cocoapods-0.5.1...
Installing RDoc documentation for xcodeproj-0.1.0...

No definition for generate_uuid

No definition for read_plist

No definition for write_plist
Installing RDoc documentation for cocoapods-0.5.1...

今度は無事にインストールできました。XCode4.3環境の人は、「XCode上でCommand Line Tools」をインストールしてから「sudo gem install cocoapods」するだけなのでとってもお手軽です。

参考リンク