You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python Motor创建TTL索引后文档未按过期时间自动删除的问题

Troubleshooting Your MongoDB TTL Index Test Case

Let's break down why your test might not be behaving as expected, and fix it step by step.

First, here's your test code formatted for clarity:

import asyncio
from datetime import datetime, timedelta
import motor.motor_asyncio

async def test_expire_at(aiohttp_client, mongo_collection):
    await mongo_collection.delete_many({})
    await mongo_collection.create_index([("expireAt", 1)], expireAfterSeconds=0)
    
    expire = datetime.utcnow() + timedelta(seconds=5)
    await mongo_collection.update_one(
        {'key': '123'},
        { "$set": { 'key': '123', 'data': {}, 'expireAt': expire } },
        upsert=True
    )
    
    await asyncio.sleep(10)
    count = await mongo_collection.count()
    assert count == 0

Common Issues & Fixes

1. MongoDB's TTL Index Scanning Delay

The most likely culprit here is MongoDB's background TTL monitor: it scans for expired documents every 60 seconds by default. Even though your document expires after 5 seconds, the background thread won't pick it up immediately—waiting 10 seconds isn't long enough for the first scan to run.

To fix this in a test environment, you can manually trigger a TTL scan right after your sleep:

# Add this line after await asyncio.sleep(10)
await mongo_collection.database.command({"delete": mongo_collection.name, "expire": True})

Alternatively, if you have admin access, you can shorten the monitor's interval at the start of your test suite to make scans run more frequently:

# Run once before your tests
await mongo_collection.database.admin_command({"setParameter": 1, "ttlMonitorSleepSecs": 1})

2. Deprecated count() Method

Motor's count() method is deprecated and may return approximate counts in some scenarios. Replace it with count_documents({}) for an accurate, up-to-date count:

count = await mongo_collection.count_documents({})

3. Verify Index & Field Type

Even though you're awaiting create_index, it's wise to confirm the index was created correctly, and that your expireAt field is stored as a BSON Date (not a string or other type):

# After creating the index
indexes = await mongo_collection.index_information()
assert "expireAt_1" in indexes
assert indexes["expireAt_1"]["expireAfterSeconds"] == 0

# After upserting the document
doc = await mongo_collection.find_one({"key": "123"})
assert isinstance(doc["expireAt"], datetime)  # Motor converts BSON Date to Python datetime

4. UTC Time Consistency

You're already using datetime.utcnow() which is correct (MongoDB uses UTC for TTL calculations), but double-check that your MongoDB server's system clock is synchronized to UTC to avoid time mismatches.

Modified Test Case

Here's the updated test incorporating all these fixes:

import asyncio
from datetime import datetime, timedelta
import motor.motor_asyncio

async def test_expire_at(aiohttp_client, mongo_collection):
    await mongo_collection.delete_many({})
    
    # Create and verify TTL index
    await mongo_collection.create_index([("expireAt", 1)], expireAfterSeconds=0)
    indexes = await mongo_collection.index_information()
    assert "expireAt_1" in indexes
    assert indexes["expireAt_1"]["expireAfterSeconds"] == 0
    
    expire = datetime.utcnow() + timedelta(seconds=5)
    await mongo_collection.update_one(
        {'key': '123'},
        { "$set": { 'key': '123', 'data': {}, 'expireAt': expire } },
        upsert=True
    )
    
    # Confirm the document has a valid Date field
    doc = await mongo_collection.find_one({"key": "123"})
    assert isinstance(doc["expireAt"], datetime)
    
    await asyncio.sleep(10)
    
    # Force immediate TTL cleanup
    await mongo_collection.database.command({"delete": mongo_collection.name, "expire": True})
    
    # Get accurate document count
    count = await mongo_collection.count_documents({})
    assert count == 0

Final Notes

The delete command with expire: True works for MongoDB 4.0+. If you're using an older version, stick to shortening the TTL monitor interval instead.

内容的提问来源于stack exchange,提问作者Alexander

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:28:44