【问题标题】:Does App Engine Search API support transactions?App Engine Search API 是否支持事务?
【发布时间】:2014-02-11 08:33:56
【问题描述】:

搜索 API 操作是否可以成为数据存储事务的一部分,从文档中并不太清楚。因此,我们能否期望事务中的数据存储操作具有相同的 ACID 属性。在这方面,文档是否类似于实体?

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

如果不是,我们如何在大规模应用中保持一致性?

【问题讨论】:

    标签: google-app-engine transactions google-cloud-datastore google-search-api


    【解决方案1】:

    为您的搜索文档编制索引不是事务性的,但事务性的是将任务推迟到以后运行。

    您可以检查您是否与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())
    

    【讨论】:

    • 那篇文章非常有用。
    【解决方案2】:

    嗯,我不知道这是否曾经可用,但他们在 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
    

    【讨论】:

    • 谢谢布莱恩。感谢您实现“post_put_hook”,但我没有在您的代码中看到任何事务或 ACID 属性的示例。了解您如何处理您在文档创建过程中提到的错误会更有趣?
    猜你喜欢
    • 1970-01-01
    • 2012-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-27
    • 2014-08-05
    • 2020-02-21
    • 2016-05-23
    相关资源
    最近更新 更多