MongoDB 唯一复合索引允许重复

MongoDB Unique Compound Index is Allowing Duplicates

我正在使用 PyMongo 和 PyMODM 处理相当简单的文档结构。

我的模型是这样的:

class User(MongoModel):
    subscriber_uid: fields.CharField = fields.CharField(required=True)
    interface: fields.CharField = fields.CharField(required=True)
    ...other fields...

    class Meta:
        """Defines MongoDB options for this model"""
        cascade = True  # currently nothing to cascade deletes against
        indexes = [
            IndexModel(
                keys=[('subscriber_uid', pymongo.ASCENDING),
                      ('interface', pymongo.ASCENDING)],
                unique=True,
            ),
        ]

我的单元测试看起来像这样,请注意 DB 是一个 API,它包装了实际的 PyMongo 命令。例如:put_user 在一些异常处理逻辑中包装了 user.save(),而 read_user 包装了 User.objects.get().

...
    user = User(**TEST_USER)
    user.interface = '2/2/2/2'
    user.subscriber_uid = 'some other suid'
    DB.put_user(user)
    user_from_db = DB.read_user(subscriber_uid=user.subscriber_uid,
                                interface=user.interface)
    assert user_from_db.interface == user.interface
    assert user_from_db.subscriber_uid == user.subscriber_uid

    # attempt to create a new record with a non-unique suid+iface pair
    # ie: ensure this updates instead of creates
    user = User(**TEST_USER)
    user.someotherattr = 1023
    DB.put_user(user)
    user_from_db: User = DB.read_user(subscriber_uid=user.subscriber_uid,
                                      interface=user.interface)
    assert user_from_db.seed_index == user.seed_index
...

当我 运行 这个测试时, read_user()/User.objects.get() 调用失败并返回一个 MultipleObjectsReturned 对象错误,我可以确认有两条相同的记录interfacesubscriber_uid 值。

根据我在其他 Whosebug and comments 中看到的内容,上面的复合唯一索引应该可以防止这种情况发生。

每次测试都会删除数据库 运行,所以问题不是陈旧的数据挥之不去。我对复合索引有误解吗?

明显缺乏索引是一个转移注意力的问题。

PyMODM docs state:

Note that connect() has to be called (defining the respective connection alias, if any) before any MongoModel can be used with that alias. If indexes is defined on Meta, then this has to be before the MongoModel class is evaluated.

我正在使用一个函数来启动与 MongoDB 的连接,这当然是在我导入模型 类 之后发生的。对于其他数据库客户端,如果您不确定导入模块时您的数据库是否可用(常见于容器化工作负载,应用程序启动通常比数据库启动花费的时间更少),这通常是可行的方法。 PyMongo client is special though:

Starting with version 3.0 the MongoClient constructor no longer blocks while connecting to the server or servers, and it no longer raises ConnectionFailure if they are unavailable, nor ConfigurationError if the user’s credentials are wrong. Instead, the constructor returns immediately and launches the connection process on background threads.

connect() 移动到我的模型模块的顶部解决了这个问题。即使调用 connect() 时数据库不可用。一旦数据库可用,就会创建索引,一切都变得正确。