LoginSignup
27
27

More than 5 years have passed since last update.

Google App EngineのDatastoreとSearch APIの連携

Last updated at Posted at 2014-08-20

Google App EngineのDatastoreとSearch APIの連携

Datastoreにデータを保存しつつ、Search APIのIndexに登録したいというシチュエーションはかなりあると思われる。しかし、トランザクションに注意しなければ、Datastoreに保存されているにも関わらず、Search APIのIndex(以下、IndexといえばSearch APIのIndexを指すこととする)に登録されていないということが起こりえてしまう。

そこで、DatastoreとIndexに(ほぼ)同時にデータを登録する方法について考える。

  1. _post_put_hookを使う方法(一貫性が保証されない)
  2. シンプルに@ndb.transacationalを使う方法(一貫性が保証)
  3. Task Queueを使う方法(一貫性が保証)
  4. 結論

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の方法を使う。シチュエーションに応じて使い分ける。

27
27
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
27