Commit Graph

115 Commits

Author SHA1 Message Date
7482042f34 index/db/commands: fold duplicated index logic into single definitions
Cleanup pass over the secondary-index feature.

add_doc is now the one entry-commit path. create_index and
build_all_indexes each hand-rolled build -> check_unique -> reserve ->
insert, and had already drifted on whether multikey is set before or
after the unique check; add_doc gained an enforce_unique flag so the
rebuild path keeps its tolerate-and-warn behavior. reserve_for and
insert_entries are now the only way the engine touches Index.entries.

One definition each for: prefix comparison and the prefix binary
searches (prefix_order + std.sort), the cartesian-product odometer
(advance_choice), the spec pair list (write_spec builds on spec_pairs,
so the log format and the listIndexes reply share one schema), the _id
clause parser (plan_id reuses analyze_clause), key-pattern direction
(index.descending, which desc_dir already disagreed with on non-numeric
values), option truthiness (query.truthy), the E11000 message, and
index-removal-by-name (Collection.find_index/remove_index). Key-pattern
matching moved out of the dispatcher into index.find_by_key_pattern.

Dead or redundant: ParallelArraysError, the unread `dropped` counter,
insert_entries' discarded gpa, a third pass computing multikey, the
has_id/is_id_index flag pair, Plan.key_len (always lookup_keys[0].len,
now a method), first_match_consumed (now stages = stages[1..]).

Cheaper hot paths: remove_id compacts in one pass instead of an
orderedRemove per hit; Plan.search skips the sort/dedupe when neither
multikey nor multiple lookup keys can produce a repeat; the _id fast
path reuses one scratch key buffer (bson.write_serialized_value); the
plan loop uses the bound collection instead of re-resolving it through
two hash lookups per candidate.

Behavior is unchanged except that dropping plan_id's fixed 16-clause
buffer enables the _id fast path on filters that previously exceeded it.
2026-08-02 13:40:42 +03:00
0d264c6c57 index: guard the $in cartesian cap against u64 overflow; drop debug print 2026-08-02 12:41:28 +03:00
adcf0014a9 docs/e2e: indexes out of 'not implemented'; e2e3 covers driver index APIs
README documents supported key patterns, unique/sparse/multikey behavior,
planner rules (multikey two-bound range fallback, sparse/null bail, the
_id fast-path guards), and v1 limits plus the two pre-existing issues the
work surfaces (drop-collection resurrection, compact log_bytes).
e2e3.js exercises createIndex/getIndexes/dropIndex/dropIndexes, the
unique-constraint 11000 path, compound, sparse, and descending indexes
through the official Node driver.
2026-08-02 12:40:30 +03:00
7f2f7c6977 commands: createIndexes/listIndexes/dropIndexes + planner wiring
Adds the three driver commands, parameterized E11000 messages (engine
dup_index carries the index name into writeErrors), the scan_matching
planner wiring (_id fast path → index plan → scan) and the first-$match
aggregate pushdown. The equivalence test (mixed-type corpus x 27 filters,
non-sparse and sparse indexes) drove out three real bugs: the two-bound
range under-approximation on multikey indexes (fall back to scan), a
dangling single-value option array in the planner, and update-time
unique violations now reporting writeErrors instead of corrupting state.
2026-08-02 12:38:25 +03:00
a733fc1993 db: engine maintenance for secondary indexes
Collection gains an indexes list; evict_doc removes entries at the single
document-death chokepoint; upsert does build → check → reserve → log →
evict → publish so entry insertion after the append is infallible and a
rejected unique write never reaches the log. Replay registers empty
indexes from create/drop records (types 3/4) and Engine.open rebuilds them
from live docs. compact re-emits index-create records. Engine gains
create_index/drop_index and dup_index for E11000 naming.
2026-08-02 12:25:03 +03:00
a38ddc2f50 index: secondary index core — entries, search, planner, _id fast path
Adds src/index.zig with the full secondary-index machinery: entry
generation mirroring field_matches (array value + elements), BSON-order
sorted entries with binary search, compound prefix and range lookups,
unique/sparse options, the query planner (longest equality/$in run +
optional range, $in cartesian cap, sparse/null bail), and the _id_ fast
path guarded against serialization-ambiguous values (numbers, strings,
symbols, codes, opaque payloads).

query.collect_values is now pub so entry generation can mirror it exactly.
storage.zig gains record_type_index_create/drop; lib.zig exports index.
2026-08-02 12:21:22 +03:00
mongo-light
662df9b121 tests/e2e: pin driver deps (package.json/package-lock.json); ignore node_modules 2026-08-02 10:57:02 +03:00
mongo-light
c29c09d6e8 query: support bare regex filter values and array-index dot paths
The Node driver sends {field: /re/} as a BSON regex element (type 0x0B),
which the matcher previously only handled via the $regex operator form;
and dot paths with numeric segments (tags.0) were ignored because array
descent only recursed into embedded docs. Both are part of standard
MongoDB query semantics and were caught by the driver e2e suite.

server: unbounded Io async limit so the accept loop never wedges, and
treat header-read failures (client RST on pool teardown) as clean
disconnects. With the default cpu_count-1 limit, groupAsync's eager
fallback ran connection handlers inline on the accept-loop fiber once
that many connections were alive, stalling accept() and timing out
handshakes for further clients.

Add tests/e2e/: official driver CRUD, concurrency, and kill -9 recovery
suites (29 + 2 + 3 checks), plus unit tests for the query fixes.
2026-08-02 10:56:43 +03:00
mongo-light
4b975d1c93 README: document read/write lock concurrency model 2026-08-02 10:42:39 +03:00
mongo-light
e14cb8cef9 commands: add concurrent insert/find stress test; fix lock defer scoping in dispatch
The original dispatch restructure placed the unlock defers inside switch
prongs, where Zig runs them when the prong block exits — immediately after
acquisition. All commands therefore ran with no lock at all, which the new
stress test caught deterministically (two writers inside Engine.insert at
once). Lock acquisition now happens in the prongs and the command body runs
via dispatch_impl, so the defers (still prong-scoped) release the lock only
after the command finishes.
2026-08-02 10:39:55 +03:00
mongo-light
c0550291e2 db: add concurrent readers/writers stress test on threaded Io 2026-08-02 10:32:02 +03:00
mongo-light
f705bcf458 commands: classify commands into none/read/write lock scopes 2026-08-02 10:30:06 +03:00
mongo-light
b71b97824e db: replace engine mutex with writer-preferring RwLock, add lock_read/unlock_read 2026-08-02 10:29:33 +03:00
mongo-light
62a4c4244c bson: make ObjectIdGen counter atomic for concurrent connections 2026-08-02 10:29:16 +03:00
mongo-light
4de42091a4 baseline: mongo-light working tree before concurrency refactor 2026-08-02 10:29:01 +03:00