List<ImmutableDatastoreEntity> results = ofy().load()
.kind(ImmutableDatastoreEntity.class)
.filter("isTip", true)
.filter(/** apply other filters here */)
.list();


ニュースフィード クエリを実行


私たちの目標の 1 つは、論理エンティティが時間とともにどのように変わってきたかを示すことでした。そのためには、チェーン内のすべてのデータストア エンティティをクエリできなければなりません。

これはかなりシンプルなクエリです。consistentId でクエリし、結果をタイムスタンプで並べるだけです。そうすれば、論理エンティティのすべてのバージョンが得られます。各データストア エンティティについて、前のデータストア エンティティとの差分を取れば、ニュースフィードに必要なデータを生成できます。


Key ancestorKey = KeyFactory.createKey(ImmutableDatastoreEntity.class, consistentId);
List<ImmutableDatastoreEntity> versions = ofy().load()
.kind(ImmutableDatastoreEntity.class)
.filter("consistentId", consistentId)
.ancestor(ancestorKey)
.list();

課題

以上の設計により、私たちは、デバッグしやすく、ニュースフィード風の機能を開発しやすい、ほぼイミュータブルなエンティティを実装するという目標を達成できました。しかし、この方法にはいくつか課題もあります。

  1. エンティティを取得するには必ずクエリを実行する必要がある : 特定の論理エンティティを取得するには、前述したようにクエリを実行しなければなりません。Cloud Datastore では、これはキーによる従来の “取得” よりも時間のかかる操作です。さらに、Objectify はビルトイン キャッシング機能を提供しますが、イミュータブル エンティティの 1 つを取得しようとするときは、この機能は使えません(Objectify はクエリをキャッシュできないからです)。この課題の対策としては、パフォーマンス上の問題が発生したら独自のキャッシングを memcache で実装することです。
  2. エンティティの取得をバッチで実行する方法がない : 各クエリは、整合性を確保するために 1 つのエンティティ グループを対象にしなければならないので、複数の論理エンティティの最新のデータストア エンティティを、1 回のデータストア操作でフェッチすることはできません。この問題に対処するため、私たちは複数の非同期クエリを実行し、それらがすべて完了するのを待ちます。この方法は理想的でもクリーンでもありませんが、実用上はかなりうまくいきます。ただし、App Engine で RPC を同時に呼び出す場合、RPC の発行数は 30 までに制限されていることに留意する必要があります。そのため、この対処方法は暫定的なものと言わざるをえません。
  3. エンティティを実装する初期コストが高い : 私たちは、上で説明した設計の大部分を抽象化し、イミュータブル エンティティを今後は低コストで実装できるようにしました。しかし、エンティティを最初に実装するのは容易なことではありませんでした。さまざまな問題をすべて解決するにはかなりの時間を要しました。こうした手間のかかる実装を行う価値があるのは、イミュータビリティを切実に必要としている場合か、もしくは実装をさまざまなユースケースに活用し、その多くの受益者に実装コストを “広く薄く” 負担してもらう場合に限られます。
  4. エンティティが実際には削除されない : 設計上、私たちはイミュータブル エンティティを削除しません。しかし、ユーザーの側では、私たちのアプリで何かを削除したら、私たちがそのデータを実際に削除することを期待するかもしれません。一部の規制対象業種(ヘルスケアなど)でも、そうしたことが求められる可能性があります。私たちのユースケースではそうした配慮は不要でしたが、お客様によっては、データセットを監視し、論理エンティティが削除されたのを見つけたら、バッチ タスクで定期的に、それらを表すデータストア エンティティをすべて削除するシステムを開発したほうがよいかもしれません。


次のステップ

私たちは、イミュータブル エンティティを本番環境で短期間しか運用していません。そのため、これから未知の問題に直面することもあるでしょう。また、イミュータブル エンティティとして実装するデータセットがさらにいくつか増えれば、実装のコストや労力に見合った効果が出ているかどうかを明確に判断できるようになるはずです。

最新情報をお知りになりたい方は、ぜひ私たちのブログをご覧ください。

今回紹介したようなデータ インフラストラクチャに興味を持たれた方は、ぜひご連絡ください。なお、私たちはバックエンド チームのメンバーも募集しています。私たちの人材募集ページで詳しい情報をご覧ください。

ディスカッションは Hacker News でどうぞ。

1 これは MVCC(https://en.wikipedia.org/wiki/MultiVersion_Concurrency_Control)の考え方と非常に似ています。MVCC は、多くの現代的なデータベースでトランザクションやロールバックの実装に使用されています。


- Posted by Aleem Mawani, Co-Founder, Streak.com