Mongoengine文档在并发下的一致性

0 投票
1 回答
604 浏览
提问于 2025-04-17 04:11

我正在尝试创建一个虚拟的包装类(一个mongoengine文档),这个类要实现Redis哈希的接口。比如说:

class HashModel(mongoengine.Document):
    '''
    Represents a dictionary with a name.
    Interface similar to Redis Hashes
    '''
    name = mongoengine.StringField()
    adict = mongoengine.DictField()

    def safe_reload(self): # because it fails if no object present
        try: self.reload()
        except:pass

    def fix_key(self, key): # for mongoengine validation ( if you really read my code, please also answer why does mongoengine need that ) 
        return key.replace(".","").replace("$","") 

    def hset(self, key, value):
        self.safe_reload()
        self.adict["%s" % self.fix_key(key)] = value  
        self.save(safe=True)
        return True

    def hexists(self, key):
        self.safe_reload()
        key = "%s" % self.fix_key(key)
        return key in self.adict 

然后,我使用celery来执行一些任务。在这之前,我会初始化一个HashModel对象,任务会在这个对象上进行一些操作。但是因为使用了多进程,我发现了一些不一致的情况。不同的进程“获取”的对象“快照”是不一样的,这其实也是很正常的。为了绕过这个问题,我每次都重新初始化这个对象,这样每次都能得到一个“几乎”全新的快照。

问题是:有没有办法避免重新初始化?我可以在上面的类中添加一些代码吗?如果可以,应该添加什么代码呢?

编辑: 答案是:看起来mongoengine.Document.reload()函数可以做到这一点。我更新了我的代码,展示了与我下一个问题相关的所有内容:

回到celery任务中,出现了这样一个问题:当我执行hset(a_key, a_value)后,稍后再检查这个键是否存在时,有时hexists(a_key)会返回False。即使我强制执行hset,像这样:

    while True:
        self.handler.hset(a_key, a_value)
        if self.handler.hexists(a_key): 
            break

在某个时刻(并没有在其他地方删除a_key),有时hexists(a_key)仍然会返回False。这是怎么回事呢???

1 个回答

1

这是一个纯粹的竞争条件问题。我的celery工作进程可能会同时获取一个实例,并对它进行不同的操作,然后再保存,这样就没有一致性了。

撰写回答