使用MapReduce时,ndb模型不会保存在内存缓存中

时间:2014-10-06 19:08:19

标签: google-app-engine mapreduce memcached google-cloud-datastore app-engine-ndb

我创建了两个MapReduce管道,用于上传CSV文件以批量创建类别和产品。每个产品都通过KeyProperty绑定到Category。 Category和Product模型是基于ndb.Model构建的,因此根据文档,我认为从数据存储区检索时它们会自动缓存在Memcache中。

我在服务器上运行这些脚本来上传30个类别,然后再上传3000个产品。所有数据都按预期显示在数据存储区中。

但是,似乎产品上传不使用Memcache来获取类别。当我检查门户网站中的Memcache查看器时,它会显示点击计数大约为180并且未命中数约为60的内容。如果我每次上传3000个产品并检索该类别,我不应该有大约3000个从获取类别中击中+未命中(即Category.get_by_id(category_id))?在创建新产品之前尝试检索现有产品可能会有3000多次失败(算法处理实体创建和更新)。

以下是相关的产品映射功能,它从CSV文件中获取一行以创建或更新产品:

def product_bulk_import_map(data):
    """Product Bulk Import map function."""

    result = {"status" : "CREATED"}
    product_data = data

    try:
        # parse input parameter tuple
        byteoffset, line_data = data

        # parse base product data
        product_data = [x for x in csv.reader([line_data])][0]
        (p_id, c_id, p_type, p_description) = product_data

        # process category
        category = Category.get_by_id(c_id)
        if category is None:
            raise Exception(product_import_error_messages["category"] % c_id)

        # store in datastore
        product = Product.get_by_id(p_id)
        if product is not None:
            result["status"] = "UPDATED"
            product.category = category.key
            product.product_type = p_type
            product.description = p_description
        else:
            product = Product(
                id = p_id,
                category = category.key,
                product_type = p_type,
                description = p_description
            )
        product.put()
        result["entity"] = product.to_dict()
    except Exception as e:
        # catch any exceptions, and note failure in output
        result["status"] = "FAILED"
        result["entity"] = str(e)

    # return results
    yield (str(product_data), result)

2 个答案:

答案 0 :(得分:8)

MapReduce故意禁用NDB的memcache。

请参阅mapreduce/util.py ln 373,_set_ndb_cache_policy()(截至2015-05-01):

def _set_ndb_cache_policy():
  """Tell NDB to never cache anything in memcache or in-process.

  This ensures that entities fetched from Datastore input_readers via NDB
  will not bloat up the request memory size and Datastore Puts will avoid
  doing calls to memcache. Without this you get soft memory limit exits,
  which hurts overall throughput.
  """
  ndb_ctx = ndb.get_context()
  ndb_ctx.set_cache_policy(lambda key: False)
  ndb_ctx.set_memcache_policy(lambda key: False)

您可以强制get_by_id()put()使用内存缓存,例如:

product = Product.get_by_id(p_id, use_memcache=True)
...
product.put(use_memcache=True)

或者,如果要与mapreduce.operation一起批处理,则可以修改NDB上下文。但是,我不知道这是否还有其他不良影响:

ndb_ctx = ndb.get_context()
ndb_ctx.set_memcache_policy(lambda key: True)
...
yield operation.db.Put(product)

至于关于“软内存限制退出”的文档字符串,我不明白为什么只有启用 memcache 才会发生这种情况(即没有上下文缓存)。

实际上你似乎想要想要 memcache为put启用,否则你的应用程序会在你的映射器修改下面的数据之后从NDB的memcache中读取陈旧数据。

答案 1 :(得分:1)

正如Slawek Rewaj已经提到的,这是由上下文缓存引起的。检索实体时,NDB首先尝试上下文缓存,然后尝试内存缓存,最后如果在上下文缓存和内存缓存中都找不到实体,它会从数据存储中检索实体。上下文缓存只是一个Python字典,其生命周期和可见性仅限于当前请求,但MapReduce在单个请求中对product_bulk_import_map()进行多次调用。

您可以在此处找到有关上下文缓存的更多信息:https://cloud.google.com/appengine/docs/python/ndb/cache#incontext