1

如果搜索API操作可以成为数据存储事务的一部分,那么从文档中不太清楚。因此,我们可以期待我们期望从事务中的数据存储操作获得相同的ACID属性。在这方面是一个类似于实体的文档?App Engine搜索API是否支持事务?

从这段视频看来,他们是交易的一部分: http://www.youtube.com/watch?v=7B7FyU9wW8Y&list=FLcBSmKKUXoPd5yFneNFDv4A#t=1952

如果没有,我们如何保持大面积推广应用的一致性?

回答

2

为您的搜索文档建立索引不是事务性的,但是什么是事务性的推迟了以后要运行的任务。

您可以检查,如果你是在一个事务中与ndb.in_transaction(),并轻松defer这样说:

class UserModel(ndb.Model): 
    ... 
    def _post_put_hook(self, future): 
     deferred.defer(UserModel.put_search_document, 
         self.username, 
         self.version, 
         _transactional=ndb.in_transaction()) 

您还需要处理重试和故障。 This excellent article有一个完整的演练和解释,包括简单的版本控制以防止故障,重试和脏读。

以下是这篇文章的完整的示例代码:

import logging 
from google.appengine.api import search 
from google.appengine.ext import ndb 
from google.appengine.ext import deferred 

class UserModel(ndb.model): 

    username = ndb.StringProperty(required=True) 
    email = ndb.StringProperty(required=True) 
    version = ndb.IntegerProperty(default=0) 

    @classmethod 
    def put_search_document(cls, username, version): 
     model = ndb.Key(cls, username).get() 
     if model: 
      if version < model.version: 
       logging.warning('Attempting to write stale data. Ignore') 
       return 

      if version > model.version: 
       msg = 'Attempting to write future data. Retry to await consistency.' 
       logging.warning(msg) 
       raise Exception(msg) 

      # Versions match. Update the search document 
      document = search.Document(
       doc_id = username, 
       fields=[ 
        search.TextField(name='username', value=model.username), 
        search.TextField(name='email', value=model.email), 
        search.TextField(name='version', value=model.version), 
        ]) 
      index = search.Index(name="UserIndex") 
      index.put(document) 

    def _pre_put_hook(self): 
     self.version = self.version + 1 

    def _post_put_hook(self, future): 
     deferred.defer(UserModel.put_search_document, 
         self.username, 
         self.version, 
         _transactional=ndb.in_transaction()) 
+0

该文章非常有帮助。 – Micro

0

好吧,我不知道这是否一次可用,但他们在IO视频中显示的内容今天不可用,至少数据存储“集成”。该文档没有提到“searchType”参数或“query.matches”函数。

因此,就一致性而言,我所做的只是将post_put挂钩添加到我的数据存储模型,并在搜索API中将文档编入索引。我有一个索引给定实体的处理程序,并在post_put钩子中触发此处理程序的任务。每当put()在我的实体上完成时,我知道搜索索引中的文档将被更新。

当然,您必须自己处理您在文档创建过程中可能遇到的错误,但我还没有找到比这更好的方法。

class MyModel(ndb.Model): 
    fieldA = ndb.StringProperty() 
    fieldB = ndb.StringProperty() 

    def _post_put_hook(self, future): 
    # here create document 
+0

谢谢布赖恩。我感谢你实现了“post_put_hook”,但我没有看到任何代码中的事务或ACID属性的例子。知道如何处理在文档创建过程中提到的错误会更有趣? – Jens