这是我们目前的情况:
我们试图获得缓存的django模型实例,缓存键包括模型名称和实例ID.使用Django的标准memcached后端.该程序是非常广泛使用的常用程序的一部分,不仅在芹菜中.
有时(随机和/或很少)cache.get(key)返回错误的对象:int或不同的模型实例,甚至出现了same-model-different-id情况.通过检查型号名称和id以及缓存键的对应关系来捕捉这个.
bug只出现在我们的三个celery任务的上下文中,永远不会在python shell或其他芹菜任务中重现.UPD:仅在长时间运行的CPU-RAM密集型任务下出现
缓存存储正确的值(我们在错误出现时手动检查)
使用相同的参数再次调用相同的任务可能不会重现该问题,尽管概率要高得多,因此错误出现倾向于在同一时间段内"分组"
重启芹菜解决了随机时间段(分钟 - 周)的问题
*NEW*这与内存溢出无关.发生这种情况时,我们总是至少有2Gb可用内存.
*新*我们有cache_instance = cache.get_cache("cache_entry")
静态代码.在调查过程中,我发现错误发生的那一刻会cache_instance.get(key)
返回错误的值,尽管get_cache("cache_entry").get(key)
在下一行返回正确的值.这意味着错误消失太快或由于某种原因cache_instance对象被破坏.django的缓存线程返回的缓存实例对象不安全吗?
*NEW*我们记录了非常奇怪的情况:作为缓存中的另一个错误对象,我们得到了模型实例w/o id set.这意味着,实例从未保存到DB,因此无法缓存.(我希望)
*新*MemoryError
这些天至少记录了一个
我知道,所有这些听起来都像是某种魔法......而且,真的,任何想法如何可能或如何调试这将是非常感激的.
PS:我目前的假设是这与多处理有关:只要在静态代码中创建缓存实例,并且在工作进程分叉之前,这将导致所有工作者共享相同的套接字(这听起来似乎合理吗?)
终于解决了:
Celery具有动态扩展功能 - 它能够根据负载添加/杀死工作人员
它是通过分叉现有的来实现的
打开的套接字和文件被复制到分叉进程,因此当一个进程读取另一个进程的响应时,两个进程共享它们,这会导致竞争条件.简单地说,一个进程可能会读取针对第二个进程的响应,反之亦然.
from django.core.cache import cache
此对象存储预连接的memcached套接字.当您的进程可以动态分叉时,请不要使用它.并且不要使用存储的连接,池和其他.
或者将它们存储在当前PID下,并在每次访问缓存时进行检查