我正在实现一个处理针对邮件服务器的身份验证的PAS插件。实际上只实现了DBMail。
我意识到,每个请求会多次调用PAS插件中的enumerateUsers
函数,并且需要我的插件为每个(后续)请求打开/关闭SQL连接。当然,这非常昂贵。
连接本身在plone工具中处理,该工具能够处理多个不同的邮件服务器,并删除对代表已注册服务器的包装器对象的enumerateUsers
调用。
现在我的问题是,我应该使用什么样的缓存(OOBTree,Session?)来提供重复枚举的临时本地存储并避免后续的SQL连接?
另一个想法是,要挂钩第一次登录时发生的用户创建过程,外部用户会发布并完全“本地化”用户。
如果可能的话,第三个想法是将所需数据存储在特定成员中。
这里最好的做法是什么?
答案 0 :(得分:4)
我确实会缓存查询结果。您需要对缓存结果的时间做出决定,如果长期存储,则需要如何使该缓存无效或检查更改。
这些决策没有最佳实践,因为它们完全取决于存储的数据类型和后端的API。例如,如果它们支持某种新鲜度查询,那么您将永久存储所有内容并轮询后端以查看缓存是否需要更新。
您可以从简单的请求缓存开始;每个请求查询一次,将存储在请求对象上。当请求对象被清理时,您的缓存将在请求结束时自动失效,下一个请求将是一个干净的平板。
如果您的后端用户很少更改,您可以在本地缓存中缓存信息更长时间。我在插件上使用了volatile属性。持久性机制会忽略以_v_
开头的任何属性。因此,存储在_v_
volatile属性中的任何内容都是线程本地的,并且仅在进程的生命周期内存在,重新启动服务器会自动清除它们。
至少应该使用_v_
volatile属性来存储后端SQL连接。这样他们可以在请求之间保持打开状态,并且可以重复使用。像下面这样的方法可以做得很好:
def _connection(self):
# Return a backend connection
if getattr(self, '_v_connection', None) is None:
# Create connection here
self._v_connection = yourdatabaseconnection
return self._v_connection
您还可以在插件上使用持久属性来存储缓存。此缓存将提交给ZODB并在重新启动时保持不变。然后你真的需要弄清楚如何使内容无效;存储时间戳并将数据逐出旧时等等。
您的缓存数据结构完全取决于您的应用程序需求。如果您不保留信息,则字典(用户名 - >信息)可能绰绰有余。持久化缓存可以从使用OOBTree
而不是字典中受益,因为它们可以减少不同线程之间冲突的可能性,并且在涉及大量数据时更有效。
无论你做什么,不都需要使用会话。会话容易发生冲突,不能很好地扩展,并且在任何情况下都不是存储此类缓存的地方。