跨版本的App Engine Memcache密钥前缀

时间:2011-02-21 12:56:42

标签: google-app-engine memcached versions

问候!

我有一个Google App Engine安装程序,memcached个密钥以os.environ['CURRENT_VERSION_ID']为前缀,以便在部署时生成新的缓存,而无需手动刷新缓存。

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

我正在寻找关于如何为键添加前缀的建议。从本质上讲,在部署任何版本时,需要有一个随版本更改的变量。 (嗯,这不太理想,因为缓存完全被吹灭了。)

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

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

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

  • 忘记密钥前缀。密钥的全局命名空间。编写脚本以在每次部署时刷新缓存。这实际上看起来和第一个想法一样好,如果不是更好:缓存在两个场景中都是完全爆炸的,这个避免了运行时实体的开销。

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

2 个答案:

答案 0 :(得分:1)

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

所以,我认为你的问题是当你“部署”一个版本时,你不希望使用开发/测试的缓存吗? (否则,就像Nick和systempuntoout,我很困惑)。

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

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

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

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

答案 1 :(得分:0)

我对这个问题的处理方式有点混淆。

我最终选择了属性的每类哈希。以这门课为例:

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(比sha256更快比sha1快)。没有真正需要它是加密安全的。只需要一个ok hashfn。可能会转而使用更快的东西,但现在我宁愿无bug。还确保键被排序,而不是属性对象。