如何用Mongoengine实现原子化的创建或修改文档操作?
问题描述
我用Mongoengine定义了如下MongoDB文档类:
class Archive( mongoengine.Document ): user_id = mongoengine.StringField() transaction_ids = mongoengine.ListField( default=list )
我希望该类能实现类似Python对象archive = collections.defaultdict(list)的功能,可轻松执行archive[a_user_id].append(a_transaction_id)操作,关键是要让这个操作在MongoDB中具备原子性。
我设想的伪代码如下:
def atomically_archive_transaction( a_user_id, a_transaction_id ): Archive.atomically_create_or_modify( query = { user_id=a_user_id }, create_if_does_not_exist = { user_id=a_user_id, transaction_ids=[a_transaction_id,] }, modify_if_does_exist = { push__transaction_id=a_transaction_id }, write_to_disk_afterwards = 'all in the same atomic transaction', )
是否有可行的实现方案?
另一种思路是尝试为指定a_user_id创建新文档,若文档已存在则创建失败,随后执行原子化push操作以避免竞态条件,但我不清楚如何确保save与push在同一原子操作中完成。
可行实现方案
直接使用Mongoengine的update_one()方法配合upsert=True参数即可实现需求,这是MongoDB原生支持的原子操作,完全符合预期:
def atomically_archive_transaction(a_user_id, a_transaction_id): # 注意字段名是transaction_ids,你伪代码里写错成transaction_id了 Archive.objects(user_id=a_user_id).update_one( push__transaction_ids=a_transaction_id, upsert=True )
原理说明
upsert=True参数会让MongoDB自动处理两种情况:- 如果不存在
user_id=a_user_id的文档,会自动创建一个新文档,其中user_id为指定值,transaction_ids初始化为包含a_transaction_id的列表; - 如果文档已存在,则原子性地将
a_transaction_id追加到transaction_ids列表中。
- 如果不存在
- 整个操作是MongoDB端的单原子操作,不会出现竞态条件,完全满足对原子性的要求,不需要分开执行创建和push操作。
内容的提问来源于stack exchange,提问作者IronPillow2
相关产品推荐
相关产品推荐

