Objectifyのv6変更点、及びSpringBootアプリから利用する場合のメモ
GoogleAppEngineのJavaアプリケーションからCloud Datastoreを利用するための
Objectifyというライブラリがあります。
2018年4月に、このライブラリのv6がリリースされたのですが、
過去のバージョンと大きく仕組みが変わっているので、その内容のメモを残しておきます。
objectify:
https://github.com/objectify/objectify
v6での最も大きな変更点
Objectify v6の最も大きな変更点は、利用しているSDKの切り替えです。
以下のように利用するSDKが変更になっています。
- v5まで: Google App Engine SDK
- v6から: Google Cloud SDK
この2つのSDKの違いですが。
「Google App Engine SDK」はAppEngine上で動作するアプリケーションでのみ利用可能なSDKです。
「Google Cloud SDK」は任意の環境からGCPの各サービスを利用するためのSDKです。
これによってv6からは、
(AppEngine以外の)任意の環境からDatastoreを利用出来るようになりました。
ですが、逆に出来なくなったこともあり。
v5まででは、AppEngine上のmemcacheに、Datastoreのアクセスをキャッシュしていたのですが、
v6では、このキャッシュが(標準では)出来なくなりました。
AppEngineのmemcacheはAppEngineからのみ利用可能で、
利用する為には「Google App Engine SDK」を利用する必要があるためです。
# 2つのSDKの見分け方ですが、
# パッケージ名が com.google.appengine で始まるものが Google App Engine SDK
# com.google.cloud で始まるものが Google Cloud SDK と見分ければ良いと思います。
v6でのローカル開発環境
v5までのObjectifyで開発を行う場合は、AppEngineのローカル開発用サーバを利用しますが、
(v5までの場合) Javaローカル開発用サーバの使用 | GCP
https://cloud.google.com/appengine/docs/standard/java/tools/using-local-server?hl=ja
v6で開発を行う場合は、以下のどちらかの方法をとることになります。
- Datastoreエミュレータを利用する
- 直接GCP環境のDatastoreにアクセスする
(v6の場合) Cloud Datastoreエミュレータの実行 | GCP
https://cloud.google.com/datastore/docs/tools/datastore-emulator?hl=ja
# 紛らわしいのですが、このCloud Datastoreエミュレータと
# AppEngineのローカル開発用サーバにあるdatastoreのエミュレータ機能は全く別のものです。
Cloud Datastoreエミュレータの起動と実行
Cloud Datastoreエミュレータの起動と実行は、以下のコマンドになります。
インストール
gcloud components install cloud-datastore-emulator
実行
gcloud beta emulators datastore start --host-port=localhost:8484
Objectifyから接続する場合は、以下のようなコードでObjectifyServiceを初期化します。
ObjectifyService.init(new ObjectifyFactory(
DatastoreOptions.newBuilder()
.setHost("http://localhost:8484")
.setProjectId("my-project")
.build()
.getService()
));
v6でのdatastoreのキャッシュ
v6でdatastoreのキャッシュを行う場合は2つの方法があります。
- 自前のmemcachedでキャッシュする
- AppEngineのmemcacheにキャッシュする(要実装)
後者のAppEngineのmemcacheにキャッシュがv5までと同様の挙動になりますが、
Objectifyに用意されているMemcacheSerivceのInterfaceを持った、
AppEngineのMemcacheに保存する為のクラスを自分で実装する必要があります。
※将来的にはObjectify標準のものが提供されるような気がしていますが。。
自前のmemcachedでキャッシュする
memcachedを立ち上げて、
ObjectifyServiceを初期化時に、memcachedの接続情報を指定します。
ObjectifyService.init(new ObjectifyFactory(
DatastoreOptions.newBuilder().setHost("http://localhost:8484")
.setProjectId("my-project")
.build().getService(),
new SpyMemcacheService(new MemcachedClient(new InetSocketAddress("localhost", 11211)))
));
AppEngineのmemcacheにキャッシュする
AppEngineのmemcacheを使いたい場合は、
以下のMemcacheServiceのInterfaceを実装したクラスを自分で実装し、
そのクラスを、memcachedを使う時に指定した「SpyMemcacheService」の代わりに指定します。
MemcacheService.java
https://github.com/objectify/objectify/blob/master/src/main/java/com/googlecode/objectify/cache/MemcacheService.java
AppEngineのmemcacheには「Google App Engine SDK」の
「com.google.appengine.api.memcache.MemcacheService」でアクセス出来ます。
com.google.appengine.api.memcache.MemcacheService | GCP
https://cloud.google.com/appengine/docs/standard/java/javadoc/com/google/appengine/api/memcache/MemcacheService?hl=ja
Memcacheの概要 | GCP
https://cloud.google.com/appengine/docs/standard/java/memcache/?hl=ja
私が実装してみたのが、以下のコードですが、
全く動作確認していないので、イメージつかむ程度で参考にしてください。
AppEngineMemcacheClientService.java
https://github.com/takemikami/spring-boot-objectify-sample/blob/master/src/main/java/objectifysample/AppEngineMemcacheClientService.java
SpringBootアプリケーションでの利用
以下のGitHubリポジトリに、
SpringBootのアプリケーションからObjectify v6を利用したサンプルを作ってみました。
spring-boot-objectify-sample | github
https://github.com/takemikami/spring-boot-objectify-sample
gradle bootRun
で起動するとキャッシュを利用しない、
gradle appengineRun
で起動するとAppEngineのローカル開発サーバのMemcacheでキャッシュします。
以下のObjectifyConfigクラスで切り替えています。
ObjectifyConfig.java
https://github.com/takemikami/spring-boot-objectify-sample/blob/master/src/main/java/objectifysample/ObjectifyConfig.java
JUnitでのテスト方法
ObjectifyのコードをJUnitでテストする場合は、
「LocalDatastoreHelper」を使ったdatastoreのエミュレーションで実行します。
以下にサンプルコードを作ったので、参考にしてください。
ここで1点、はまったポイントがあったのですが、
「LocalDatastoreHelper」はデフォルトでは、consistencyが0.9になります。
この設定では、
0.1の割合で、書き込みオペレーション実行直後の結果反映が行われず、
一定の割合で単体テストが失敗してしまいます。
なので、「LocalDatastoreHelper」の初期化時は、明示的にconsistencyに1.0を指定します。
private static LocalDatastoreHelper helper = LocalDatastoreHelper.create(1.0);;
これはDatastoreの特性によるものですが、 この辺りは、以下の結果整合性の説明が参考になります。
Google Cloud Datastore での強整合性と結果整合性のバランス | GCP https://cloud.google.com/datastore/docs/articles/balancing-strong-and-eventual-consistency-with-google-cloud-datastore/?hl=ja