index: ordered _id index (roadmap item 2)

Give every Collection an implicit _id_ index (a normal Index with keys
[_id: 1]) so _id equality, $in, ranges and sorts stop depending on the
docs-map hash or a full scan. Kept out of the secondary indexes list, so
listIndexes/dropIndexes/createIndex and the log format are unchanged (no
index_create record, no double listing) and e2e3.js passes unmodified.

Maintained in upsert through the same reserve-then-insert protocol as
the secondaries, removed in evict_doc, and rebuilt after replay by
build_all_indexes alongside them (never maintained mid-replay, so a
failed add can't leave the index under-approximating). index.plan now
takes it as a separate argument. Its keys are canonical
(bson.encode_key gives int32 1, int64 1 and double 1.0 identical bytes),
so the serialization-guarded docs-map fast path (plan_id,
value_fast_path_safe and friends) is deleted.

Measured (tests/e2e/results/phase3.txt): sort({_id:-1}).limit(20) 6.2 ->
2.4 ms (2.3x slower than MongoDB -> parity); integer/string _id point
lookups, $in and ranges verified against the tree. Unit suite in all
three optimize modes, the crash pair, e2e3/e2e4/e2e6.
This commit is contained in:
2026-08-02 21:18:40 +03:00
parent 61fe952125
commit 58914a69c3
6 changed files with 267 additions and 245 deletions

View File

@@ -644,31 +644,10 @@ fn scan_sorted(
const filter_doc = bson.Document{ .arena = undefined, .pairs = filter };
var n: usize = 0;
// _id_ fast path: the docs map is the _id index. Skipped when the
// queried value's compare-equivalence class is serialization-ambiguous
// (see index.plan_id).
if (try index.plan_id(ctx.gpa, filter)) |id_plan| {
var plan = id_plan;
defer plan.deinit(ctx.gpa);
// One scratch key, rebuilt per value: a key is never needed past its
// own lookup.
var key: std.ArrayListUnmanaged(u8) = .empty;
defer key.deinit(ctx.gpa);
for (plan.values) |v| {
key.clearRetainingCapacity();
try bson.write_serialized_value(v, ctx.gpa, &key);
const doc = coll.docs.get(key.items) orelse continue;
if (!try query.matches(ctx.gpa, &filter_doc, doc)) continue;
if (out) |list| try list.append(ctx.gpa, doc);
n += 1;
if (lim != 0 and n >= lim) break;
}
return n;
}
// Secondary-index plan: candidates in index order, re-filtered. The
// returned ids alias the docs map keys, valid under the read lock.
if (try index.plan(ctx.gpa, coll.indexes.items, filter, sort)) |p| {
// Index plan (the implicit _id_ index first, then the secondaries):
// candidates in index order, re-filtered. The returned ids alias the
// docs map keys, valid under the read lock.
if (try index.plan(ctx.gpa, &coll.id_index, coll.indexes.items, filter, sort)) |p| {
var plan = p;
defer plan.deinit(ctx.gpa);
var ids: std.ArrayListUnmanaged([]const u8) = .empty;