AppEngine:具有管道的实体模式迁移

时间:2013-11-29 14:52:36

标签: google-app-engine app-engine-ndb

我很想知道在Google App Engine中迁移实体架构的最佳做法是什么。我们经常使用管道,我倾向于构建一个管道任务来处理这种迁移。所以这就是我提出的(在这个例子中,如果用户的年龄是素数,我会存储):

class MigrateUsers(pipeline.Pipeline):
  def run(self, keys):
    futures = []
    users = ndb.get_multi(keys)
    for user in users:
      user.age_is_prime = is_prime(user.age)
      futures.append(user.put_async())
    ndb.Future.wait_all(futures)

class Migration(pipeline.Pipeline):
  def run(self):
    all_results = []

    q = ds.User.query().filter()
    more = True
    next_cursor = None

    # Fetch user keys in batch and create MigrateUsers jobs
    while more:
      user_keys, next_cursor, more = \
        q.fetch_page(500, keys_only=True, start_cursor=next_cursor)
      all_results.append((yield MigrateUsers(keys=user_keys)))

    # Wait for them all to finish
    pipeline.After(*all_results)

我的问题是,我做得对吗?我的“迁移”任务遍历所有用户以创建分段任务,这感觉有些麻烦。我确实看了一个mapreduce,但我没有感觉它是合适的。我很感激任何建议,如果你使用mapreduce并且不介意改变我的例子,我真的很感激。

2 个答案:

答案 0 :(得分:3)

MapReduce非常适合迁移。根据我自己的经验,迁移通常意味着我需要遍历所有实体,更新它们,然后将它们写回数据存储区。在这种情况下,我只需要“map”部分,而且我不需要mapreduce的“reduce”部分。

使用mapreduce的好处是它会自动在不同的实例上并行地批量处理您的实体,因此您的操作比在管道示例中以串行方式运行要快得多。 MR SDK有一个DatastoreInputReader(),它将获取给定类型的每个实体,并在每个实体上调用一个map函数,你只需提供该map函数:

from mapreduce import operation as op
def prime_age_map(user_entity):
    user_entity.age_is_prime = is_prime(user.age)
    if user_entity.age_is_prime:
        yield op.db.Put(user_entity)

我们有一些样板代码我不包括在内,因为我没有切换到最新的SDK,而且我可能不正确,但它应该非常简单,因为你只使用了一半的管道。 / p>

我不确定你的例子是多么逼真,但如果它是真实的并且你有很多实体,那么预先计算素数值会更好(http://primes.utm.edu/lists/small/1000.txt - 只有前30个左右是合理的年龄值),并对这些年龄值执行特定查询并更新这些实体,而不是迭代整个Kind。您可以使用MapReduce管道执行此操作,但您必须修改给定的DatastoreInputReader以发出比获取整个Kind更具体的查询。

答案 1 :(得分:0)

我强烈建议您查看应用引擎TaskQueues以进行架构迁移。设置和操作比后端或MapReduce,IMO容易得多。您可以在此处找到一些信息:blog entry