【问题标题】:App Engine Memcache Key Prefix Across Versions跨版本的 App Engine Memcache 密钥前缀
【发布时间】:2011-02-21 12:56:41
【问题描述】:

您好!

我有一个 Google App Engine 设置,其中 memcached 键以 os.environ['CURRENT_VERSION_ID'] 为前缀,以便在部署时生成新缓存,而无需手动刷新缓存。

这一直很好,直到开发需要同时运行两个版本的应用程序。当然,这会导致缓存不一致。

我现在正在寻找有关如何为键添加前缀的建议。本质上,当部署任何版本时,都需要有一个跨版本变化的变量。 (嗯,这不太理想,因为缓存被完全耗尽了。)

我在考虑以下几种可能性:

  • 创建一个RuntimeEnvironment 实体来存储最新的缓存前缀。缺点:即使被缓存,也会减慢每个请求。不能缓存在内存中,只能在memcached中,因为部署其他版本可能会改变。

  • 使用每个实体的版本号。这会产生非常好的粒度,因为缓存可以为未修改的实体保持温暖。缺点是我们需要在模型更改时推送到所有版本,我想避免这种情况,以便在部署到生产之前测试模型更改。

  • 忘记密钥前缀。键的全局命名空间。编写脚本以在每次部署时刷新缓存。这实际上似乎与第一个想法一样好,如果不是更好的话:缓存在两种情况下都完全被破坏了,而且这个避免了运行时实体的开销。

任何想法,不同的想法都非常感谢!

【问题讨论】:

  • 我不明白;由于您使用os.environ['CURRENT_VERSION_ID'] 为缓存键添加前缀,因此两个不同的版本如何在缓存中存在不一致?你能解释一下这一点吗?
  • 所以你想在两个不同的主要版本之间共享缓存,你不想处理向后兼容性,但你也不想把更新的副本推送到两个版本?在我看来,你们的目标是互不相容的。
  • @systempuntoout:两个不同的应用程序版本产生两个不同的os.environ['CURRENT_VERSION_ID'],因此两个版本的内存缓存对象不同。
  • @nick:啊对,你的意思是,给定一个模型的两个版本,protobuf 表示会不兼容,对吗?
  • 一点也不。但是您似乎在寻求一种可以在版本之间共享缓存的方式,但也不必在更改模型时更新代码,也不必明确地满足兼容性。不可能同时拥有所有三个:要么您需要考虑向后兼容性进行升级,要么您需要确保可以访问给定缓存实体的所有版本都有最新的代码。跨度>

标签: google-app-engine memcached versions


【解决方案1】:

os.environ['CURRENT_VERSION_ID'] 值将与您的两个版本不同,因此您将为每个版本(实时版本和开发/测试版本)拥有单独的缓存。

所以,我假设您的问题是,当您“部署”一个版本时,您不希望使用来自开发/测试的缓存? (否则,像 Nick 和 systempuntoout 一样,我很困惑)。

实现此目的的一种方法是使用缓存中的域/主机标头 - 因为这对于您的开发/实时版本是不同的。您可以通过执行以下操作来提取主机:

scheme, netloc, path, query, fragment = urlparse.urlsplit(self.request.url)

# Discard any port number from the hostname
domain = netloc.split(':', 1)[0]

这不会给出特别好的键,但它可能会做你想要的(假设我理解正确)。

【讨论】:

  • 感谢您的回复,我最终选择了不同的东西。我喜欢使用 url 在版本之间进行拆分的想法,但这不是我想要的。我会发布我所做的答案。
【解决方案2】:

我对问题的措辞有点困惑。

我最终选择了每个类的属性散列。以这个类为例:

class CachedModel(db.Model):

  @classmethod
  def cacheVersion(cls):
    if not hasattr(cls, '__cacheVersion'):
      props = cls.properties()
      prop_keys = sorted(props.keys())
      fn = lambda p: '%s:%s' % (p, str(props[p].model_class))
      string = ','.join(map(fn, prop_keys))
      cls.__cacheVersion = hashlib.md5(string).hexdigest()[0:10]
    return cls.__cacheVersion

  @classmethod
  def cacheKey(cls, key):
    return '%s-%s' % (cls.cacheVersion(), str(key))

这样,当实体使用它们的cacheKey(...) 保存到 memcached 时,它们将仅在实际类相同时共享缓存。

这还有一个额外的好处,即推送不修改模型的更新,使该模型的所有缓存条目保持不变。换句话说,推送更新不再充当刷新缓存。

这样做的缺点是每个 webapp 实例对类进行一次哈希处理。

2011 年 3 月 9 日更新:我改用一种更复杂但更准确的方式来获取版本。结果使用__dict__ 产生了不正确的结果,因为它的str 表示包括指针地址。这种新方法只考虑数据存储属性。

2011-3-14 更新:所以 python 的 hash(...) 显然不能保证在解释器的运行之间是相等的。出现奇怪的情况,即不同的应用程序引擎实例看到不同的哈希值。现在使用 md5(比 sha1 比 sha256 快)。没有真正需要它是加密安全的。只需要一个好的hashfn。可能会改用更快的东西,但现在我宁愿没有错误。还确保对键进行排序,而不是对属性对象进行排序。

【讨论】:

  • 我很困惑 - 这是否意味着您已经需要模型的副本才能找出缓存键?这不会使缓存有点多余吗?
  • 不,注意 @classmethod 装饰器。这是模型本身的静态方法,不需要实例。
猜你喜欢
  • 1970-01-01
  • 2017-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多