【问题标题】:App Engine multiple namespacesApp Engine 多个命名空间
【发布时间】:2012-02-15 15:37:10
【问题描述】:

最近我们的应用发生了一些数据结构变化,我们决定使用命名空间来分隔不同版本的数据,并使用 mapreduce 任务将旧实体转换为新格式。

现在这一切都很好,但我们不想总是隔离我们拥有的整个数据集。我们数据的最大部分以一种非常简单且不需要经常更改的方式存储。所以我们决定使用 per-kind 命名空间。

类似:

class Author(ndb.model.Model):
    ns = '2'

class Book(ndb.model.Model):
    ns = '1'

因此,当迁移到版本 2 时,我们不需要转换所有数据(并将所有“Book”种类复制到其他命名空间),只需转换“Author”种类的实体即可。然后,我们不再定义appengine_config.namespace_manager_default_namespace_for_request,而是只为查询添加“命名空间”关键字参数:

Author.query(namespace=Author.ns).get()

问题:如何使用这些不同的命名空间来存储(即put())不同的种类?比如:

# Not an API
Author().put(namespace=Author.ns)

当然,以上行不通。 (是的,我可以向数据存储区询问该命名空间中的可用密钥,然后使用该密钥来存储实例,但这是我想避免的额外 API 调用。)

【问题讨论】:

    标签: google-app-engine google-cloud-datastore


    【解决方案1】:

    为了解决这样的问题,我写了一个装饰器如下:

    MY_NS = 'abc'
    
    def in_my_namespace(fn):
        """Decorator: Run the given function in the MY_NS namespace"""
        from google.appengine.api import namespace_manager
    
        @functools.wraps(fn)
        def wrapper(*args, **kwargs):
            orig_ns = namespace_manager.get_namespace()
            namespace_manager.set_namespace(MY_NS)
    
            try:
                res = fn(*args, **kwargs)
            finally: # always drop out of the NS on the way up.
                namespace_manager.set_namespace(orig_ns)
    
            return res
    
        return wrapper
    

    所以我可以简单地为应该出现在单独命名空间中的函数编写:

    @in_my_namespace
    def foo():
       Author().put() # put into `my` namespace
    

    当然,将其应用于系统以获得您想要的结果有点超出了本文的范围,但我认为它可能会有所帮助。

    编辑:使用with 上下文

    以下是使用with 上下文完成上述操作的方法:

    class namespace_of(object):
        def __init__(self, namespace):
            self.ns = namespace
    
        def __enter__(self):
            self.orig_ns = namespace_manager.get_namespace()
            namespace_manager.set_namespace(self.ns)
    
        def __exit__(self, type, value, traceback):
            namespace_manager.set_namespace(self.orig_ns)
    

    然后在别处:

    with namespace_of("Hello World"):
        Author().put() # put into the `Hello World` namespace
    

    【讨论】:

    • 确实很有帮助。我所做的类似,我将ndb.model.Model 子类化并在sublcas 的put_async() 方法中做了同样的事情:更改命名空间,调用super().put_async(),然后finally 将命名空间改回原来的样子。但是装饰器方法看起来也不错。
    • 谢谢。很高兴它有帮助。一个有趣的替代方法是编写上下文管理器并使用 with 语句。
    【解决方案2】:

    Model 实例将使用您通过 namespace_manager[1] 设置的命名空间,您可以在此处看到:python/google/appengine/ext/db/init.py

    你可以做的是创建一个 Model 的子类,它期望定义一个类级别的“ns”属性。然后这个子类覆盖 put() 并在调用原始 put 之前设置命名空间并在之后重置命名空间。像这样的:

    '''
    class MyModel(db.Model):
    
        ns = None
    
        def put(*args, **kwargs):
            if self.ns == None:
                raise ValueError('"ns" is not defined for this class.')
            original_namespace = namespace_manager.get_namespace()
            try:
                super(MyModelClass, self).put(*args, **kwargs)
            finally:
                namespace_manager.set_namespace(original_namespace)
    '''
    

    [1]http://code.google.com/appengine/docs/python/multitenancy/multitenancy.html

    【讨论】:

    • 有趣的是,这几乎正是我想出的,只是我做了异步版本(put_async)。谢谢!
    【解决方案3】:

    我认为避免额外的 API 调用是不可能的。命名空间被编码到实体的 Key 中,因此为了更改存储实体的命名空间,您需要创建一个新实体(具有带有新命名空间的 Key)并将旧实体的数据复制到其中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-09
      • 1970-01-01
      • 1970-01-01
      • 2011-03-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多