From dc92064b9539ca95f75e3b99eed23c97b560ab82 Mon Sep 17 00:00:00 2001 From: Aleksey Shakhmatov Date: Sun, 2 Aug 2026 14:39:26 +0300 Subject: [PATCH] docs: TTL limits and error codes in the README MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TTL feature commit documented what the option does but left the limits sections stale. v1 limits now names the sweep cost (a full walk of every TTL index entry, under the write lock for the whole pass), so the interval reads as the tuning knob it is; "Not (yet) implemented" gains collMod, with the consequence — drop and re-create to change an expiry. The TTL bullet trades its collMod sentence for the two codes a user actually hits (IndexOptionsConflict 85 on a changed expiry, InvalidIndexSpecificationOption 197 on {_id: 1}), the features bullet mentions the sweeper, and quick start shows a createIndex with expireAfterSeconds so the feature is visible without reading down. --- README.md | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 36d79eb..2f20fb4 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ mongosh --port 27017 > db.users.find({age: {$gt: 25}}).toArray() > db.users.updateOne({name: "alice"}, {$set: {vip: true}}) > db.users.deleteOne({name: "bob"}) +> db.sessions.createIndex({expireAt: 1}, {expireAfterSeconds: 3600}) ``` ## Features @@ -41,8 +42,9 @@ mongosh --port 27017 - **Secondary indexes**: `createIndex`/`listIndexes`/`dropIndex` via the three driver commands, single-field and compound, with `unique`, `sparse` and `expireAfterSeconds` (TTL) options, persisted in the log - and rebuilt on open (compaction - re-emits them). The query planner turns equality / `$in` / range + and rebuilt on open (compaction re-emits them). A background sweeper + expires TTL-indexed documents through the ordinary logged write path. + The query planner turns equality / `$in` / range predicates into index lookups across `find`, `count`, `update`, `delete`, `findAndModify`, and a leading `$match` in `aggregate`; every candidate is re-checked against the full filter, so an index that @@ -101,9 +103,10 @@ the query planner to narrow scans. `expireAfterSeconds` must be a whole number in `[0, 2147483647]` (`0` means "expire at the stored instant"), a non-date value at the path never expires, an array of dates expires on its earliest member, and expiry is - coarse: a document stays visible until the next sweep. Changing the - expiry of an existing index is `IndexOptionsConflict` — `collMod` is not - implemented. + coarse: a document stays visible until the next sweep. Re-creating an + index with a different expiry is `IndexOptionsConflict` (85) and an + expiry on `{_id: 1}` is `InvalidIndexSpecificationOption` (197), both as + MongoDB has them. - **Multikey**: an array at an indexed path is indexed as a whole *and* element-wise, mirroring the query matcher exactly, so both `{tags: "a"}` and `{tags: ["a","b"]}` hit the index. A compound index @@ -121,7 +124,11 @@ the query planner to narrow scans. v1 limits: no index-accelerated sort, no hashed/text/geo/partial indexes, and entry insert/removal is O(n) (a sorted array) — fine for a light -database, with a B-tree or id→entry map as the follow-up. +database, with a B-tree or id→entry map as the follow-up. A TTL sweep +walks every entry of every TTL index and holds the write lock for the +whole pass, so the interval is the tuning knob: expiry is never more +precise than `--ttl-sweep-secs`, and a very large TTL index wants a +longer one. ## Not (yet) implemented @@ -129,6 +136,8 @@ database, with a B-tree or id→entry map as the follow-up. - Real cursors (all results are returned in one batch, cursor id 0) - Transactions, change streams, replicasets - Compression (OP_COMPRESSED) +- `collMod`, so an index's `expireAfterSeconds` cannot be changed in + place — drop the index and re-create it with the new expiry - `dropCollection`/`dropDatabase` write no log record, so a dropped collection (and its index definitions) resurrect on restart; and compaction never resets `log_bytes`, so every write after the first