Google App EngineのDatastoreとSearch APIの連携
Datastoreにデータを保存しつつ、Search APIのIndexに登録したいというシチュエーションはかなりあると思われる。しかし、トランザクションに注意しなければ、Datastoreに保存されているにも関わらず、Search APIのIndex(以下、IndexといえばSearch APIのIndexを指すこととする)に登録されていないということが起こりえてしまう。
そこで、DatastoreとIndexに(ほぼ)同時にデータを登録する方法について考える。
- _post_put_hookを使う方法(一貫性が保証されない)
- シンプルに@ndb.transacationalを使う方法(一貫性が保証)
- Task Queueを使う方法(一貫性が保証)
- 結論
1の方法はおすすめしない。ただ、1の方法を使っている例が見受けられる(Stack Overflowで回答として提出されていたのが1つ。そして、Ferris2.2が1の方法使っている。)ため、一応紹介している。
1. _post_put_hookを使う方法
特に理由がない限り、おすすめしない。
class User(ndb.Model):
name = ndb.StringProperty()
def _post_put_hook(self, future):
result = future.get_result()
# Search APIのIndexに登録
...
post_put_hookは、Userがputされた後に呼び出される。もし、putが失敗していたら、future.get_result()の部分でエラーが送出されるため、Indexに登録されることはない。しかし、Userの登録が成功したのに、Indexへの登録が失敗する可能性はある。
つまり、この方法ではDatastoreとIndexの一貫性が保証されていない。一貫性が保証されなくてもよいなら問題はないが、そうでないなら避けるべきだ。
2. シンプルに@ndb.transacationalを使う方法
user = User(name='Yohei')
@ndb.transactional
def put():
user.put()
doc = search.Document(
doc_id = person.key.urlsafe(),
fields=[
search.TextField(name='name', value=user.name),
],)
index.put(doc)
put()
これならば一貫性は保証される。しかし、次のような例では気をつけなければいけない。
user = User(name='Yohei')
@ndb.transactional
def put():
user.put()
doc = search.Document(
doc_id = person.key.urlsafe(),
fields=[
search.TextField(name='name', value=user.name),
],)
index.put(doc)
# do something
...
put()
ここでdo somethingの部分でエラーが起こると、Datastoreに登録されないが、Indexには登録されるということが起こりえる。
3. Task Queueを使う方法
Task Queueはtransactionalに処理することができる。1よってTask Queueに「Search APIのIndexへの登録」というタスクを積めば、一貫性が保証できる。
user = User(name='Yohei')
user2 = User(name='Yohei2')
@ndb.transactional(xg=True)
def put():
user.put()
user2.put()
taskqueue.add(
url='/put_to_index',
params={
'key': user.key.urlsafe(),
'name': user.name},
transactional=True,)
taskqueue.add(
url='/put_to_index',
params={
'key': user2.key.urlsafe(),
'name': user2.name},
transactional=True,)
# do something
...
put()
これならばたとえdo somethingでエラーが発生したとしても、一貫性が保証される。例のように、同時に2つのIndexへの登録も可能だ。注意するのはtransactionalなTask Queueは5つまでしか積めないということ。同時に書き込みが多数あるシチュエーションでは避けるべきだろう。
4. 結論
2か3の方法を使う。シチュエーションに応じて使い分ける。