All notable changes to OxiDB, organized by version.
OXIDB_PG_PORT, requires OXIDB_SQL=1). Verified with real drivers, not against the spec: psql 18, psycopg 3.3, Npgsql 8 in its default mode, pgjdbc 42.7 (including DatabaseMetaData introspection), and DBeaver 25.3 connects and browses with its native PostgreSQL driver. TLS works (sslmode=require); authentication is the same SCRAM-SHA-256 as the native port, so the same accounts work on both.EnsureCreated, generated keys via RETURNING, joins, transactions, LATERAL — end to end. Getting there added binary timestamp parameters and results, AT TIME ZONE 'UTC', PostgreSQL 14's 3-argument date_trunc, and calendar INTERVAL arithmetic desugared onto the calendar-correct add_months.UseOxiDb("Path=./mydata") runs the whole EF Core stack in-process — no server, SQLite-style: the database is a directory next to your application. The same DbContext points at a server by changing one connection string. One engine per directory is shared process-wide while each connection keeps its own interactive transaction, so concurrent transactions from multiple contexts work exactly as they do over TCP. Minimal example at examples/dotnet/EmbeddedEfCore/.OXIDB_SQL_DISK_FIRST=0 restores all-resident rows): a warm 1.2M-row, index-heavy database costs ~39 MB of process memory instead of hundreds, and rows, primary keys, and every index live in mapped files bounded by the checkpoint interval — not by row count. The sparse row index costs 0.69 bytes per row, so 100M rows need ~69 MB resident where the previous layout needed 3.1 GB.docs/query-benchmark.md.OXIDB_DOC=0 runs the server without the document engine entirely — 9.8 MB idle RSS for a SQL/TSDB-only deployment.CONSTRAINT pk PRIMARY KEY (a, b)), enforced on every write path including transactions.SMALLINT/INT/TINYINT are range-checked constraints (PostgreSQL error code 22003) rather than silently widened; storage stays i64, and existing catalogs keep their old semantics.CREATE UNIQUE INDEX is enforced — it validates existing rows, then rides the same uniqueness machinery as declared UNIQUE columns, on the live path, WAL replay, and after checkpoints alike. Shapes that cannot be enforced (multi-column, inside a transaction) are refused by name; before this, EF's IsUnique() quietly produced a plain index and duplicates sailed through a constraint the application believed in.DateTime parameter. The index found the entries and the candidate verification then rejected all of them, because index-key equality disagreed with index-key ordering about cross-type numerics. Equality now agrees with ordering.HAVING on a group key mis-read the projection on the streamed aggregation path (comparing the count where the key should be), and HAVING count(*) without an ORDER BY was rejected outright. Both answer correctly now.cargo test -- --ignored), which the normal run skips: three tests failed on one bug — the Jepsen-style bank that kills the process mid-commit, the multi-collection atomicity drill, and exactly-once retry.Sec-WebSocket-Accept with the wrong RFC 6455 GUID, so every client that validates the accept hash — browsers, ws, undici — refused the connection, and only clients that skipped the check could connect. The GUID is now the RFC value, so native WebSocket works everywhere; the JavaScript client's hand-rolled Node WebSocket workaround is gone in favour of the platform one (oxidb npm 0.26.0, Node 22+).{"cmd":"auth","token":…,"db":"<ref>"} verifies against that project's ES256 key and pins the connection to that database.find/count filter per row, writes check per document, and a subscribe delivers an RLS-filtered event stream — an event whose document the caller may not see is dropped rather than leaking its id. Engine fix: insert_many emitted change events with no document body (the path every REST insert takes), so subscribers now receive the inserted document./api/storage — list buckets and objects, upload, download (original content type + ETag), delete, HEAD metadata. Isolation is per tenant database, a per-project storage quota is enforced at upload time, anonymous keys are read-only, and a non-empty bucket refuses to delete.POST /api/backup?db=<ref> (admin) streams a tar.gz of that database as an attachment. Stateless — nothing is retained server-side to expire or leak.ALTER TABLE … ALTER COLUMN … TYPEALTER COLUMN c [SET DATA] TYPE t and MySQL MODIFY COLUMN. Every row is dry-run cast first — an uncastable value or an over-length VARCHAR(n) aborts before anything reaches the WAL — then the column is rewritten in place, indexes rebuilt, and a checkpoint taken. Columns bound by PRIMARY KEY / AUTO_INCREMENT / UNIQUE / FOREIGN KEY are refused, since a cast can collide previously distinct keys.<host>/<project>/rest/v1/…) so no wildcard cert is needed, and a static dashboard. Developer sign-in is Google-only.doc.<field> is enforced per returned row — an unfiltered select returns only the caller's own rows. Security-rule expressions are validated before they are saved, so a typo can no longer become a silent fail-closed “deny all”.OXIDB_DOC_CACHE_SIZE / OXIDB_DOC_BYTES_CACHE_SIZE.OXIDB_DISK_FIRST=0 restores the always-resident mode; existing collections keep the format they were created with, so upgrading never reinterprets data. The entire test fleet now runs against disk-first, including the SIGKILL crash suites, and encryption at rest was re-verified against every file the engine writes.SELECT ... FOR UPDATE takes real row locks. It used to parse and silently not lock. Matched rows are now pessimistically locked until commit/rollback; concurrent UPDATE/DELETE/FOR UPDATE on them block (up to OXIDB_SQL_LOCK_TIMEOUT_MS, default 5000 — also how a deadlock resolves). Plain UPDATE/DELETE lock their rows too, closing the engine's lost-update window: two concurrent read-modify-write transactions on one row now serialize. Shapes that cannot lock base rows (joins, aggregates, DISTINCT, set ops, views, derived tables, FOR SHARE) are refused with a clear error, never accepted without the lock — and FOR UPDATE classifies as a write, so oxipool never routes it to a replica. Details.{"cmd": "disk_usage"} — per-engine on-disk footprint of the data directory in one call (documents incl. the mmap'd share, SQL, time-series, blobs, OxiMem, MQTT/AMQP substrates, full-text, PITR archive, system)..0 always exists, but the first online checkpoint deleted it — after which later segments were never retired (unbounded disk growth) and, far worse, never replayed at recovery: a crash between a seal and its persist lost the acknowledged writes in that segment. .0 is now a permanent empty sentinel, every checkpoint retires all covered segments (data dirs the old bug left behind self-heal), and three regression tests pin it — the crash-replay test was red before the fix.OXIDB_AMQP_PORT, off by default) — RabbitMQ client code works unmodified, verified end-to-end with pika (Python), RabbitMQ.Client (.NET) and amqp091-go (Go). Work queues with competing consumers (the semantic MQTT cannot express), default + direct/fanout/topic exchanges, Basic.Qos prefetch, publisher confirms, mandatory Basic.Return, nack/reject. Anything outside the subset is refused with a clear channel error, never silently accepted.delivery_mode=2 messages is written through the document engine's WAL — the confirm is only sent after the fsync, and messages survive a SIGKILL (crash-tested; acknowledged messages stay consumed).amq.topic exchange, the same mapping RabbitMQ's MQTT plugin uses (/ ↔ ., QoS ≥1 ↔ persistent): a sensor publishes MQTT, a worker pool consumes AMQP, one binary.insert_many per pipeline burst; 264 → 53k msg/s), a cross-connection group committer (concurrent bursts share fsync rounds), and a cross-thread wake pipe in each connection's poll(2) set (delivery latency 51 ms → 0.02 ms). The one loss — single-connection durable — is the price of a real F_FULLFSYNC behind every confirm, which RabbitMQ's lazy interval flush does not pay.OXIDB_WAL_CHECKPOINT_BYTES (default 64 MiB, 0 restores the old behaviour); crash-tested against SIGKILL mid-checkpoint.OxiDb.Client.Tcp (.NET) — TsdbWriteAsync, TsdbWriteLineProtocolAsync and TsdbQueryAsync with typed points, aggregations (TsdbAgg.Mean … Percentile(p)) and epoch-ms helpers, replacing hand-rolled raw commands.aws-cli, boto3, MinIO SDKs and the AWS .NET SDK now all verify uploads cleanly, with no workaround flags.TransactionId (a u64) lived in the native-only tx_log module, so five portable modules each duplicated it behind a cfg and the sixth broke the build; it now has one portable home. Also fixed: explain's Instant (wasm32 has no monotonic clock) and a shutdown() that assumed background threads.raft_init registered the initial member with no address while every learner got a real one. Invisible for as long as that node leads — nobody dials the leader — but once it loses leadership no new leader can ever reach it, and it silently freezes at its old log while the cluster commits without it. Bootstrapping with no address is now refused outright.AND / OR now short-circuit. Both sides were always evaluated — x > 995 AND TRUE cost 52% more than x > 995 alone. Every compound WHERE in the engine gains.STARTS_WITH / ENDS_WITH — exact, literal, case-sensitive affix tests that compare borrowed bytes in place. Ordinal StartsWith/EndsWith previously rendered as per-row SUBSTRING+LENGTH, because LIKE is case-insensitive and a needle containing % would become a wildcard. Faster than LIKE without giving up the semantics that ruled it out.any_compound 0.79x→1.21x, string_multi 0.67x→2.07x.oxidb-server --version / --help — probing the binary with --version used to start a server on the default port. --help is also the only in-binary documentation of the env-var configuration.ALTER TABLE ADD COLUMN / DROP COLUMN are O(1) — metadata-only, no row rewrite, no checkpoint. Add or drop a column on a 500M-row live table with zero downtime. ADD pads old rows with the default on read; DROP tombstones the column in place and projects it out.gen.<N>/ and promotes it with a single atomic MANIFEST rename. A crash before it leaves the previous generation whole; catalog and snapshot arities can never disagree after a crash. Recovery replays only WAL records past a watermark.Value, and an OxiWire binary wire format.WITH / WITH RECURSIVE CTEs, set operations (UNION/EXCEPT/INTERSECT), LATERAL joins, DISTINCT ON, mode() WITHIN GROUP, CREATE SEQUENCE / NEXT VALUE FOR, multi-level correlation, case-insensitive LIKE + COLLATE.oxidb-tsdb engine (mounted like SQL, engine: "tsdb") — Gorilla-compressed columnar streams (~0.3 bytes/point), typed fields, InfluxDB line-protocol ingest, rate()/percentile, continuous-aggregate rollups, and MANIFEST-atomic persistence. Go client included.std::fs in the /proc stats readers.sequences.json so a NEXT VALUE FOR can never desync a generation's catalog from its snapshots.+ (one level) and # (subtree) filters, backed by the OxiMem pattern-subscriber layer.OXIDB_MQTT_USER/OXIDB_MQTT_PASSWORD require matching CONNECT credentials.aws s3 ls pages correctly over large buckets.?lifecycle Days rules per bucket with a background sweeper.cargo test: signed roundtrip, corrupted-signature 403, multipart assembly, batch delete.redis.call, cjson, redis.sha1hex; atomic, busy-script time limit, SCRIPT KILL.expired events.ZREVRANGE key 0 0 returned the lowest member; ranks now index the descending view.find_for_update pessimistic document locks: contenders queue instead of conflict-storming.$ohlcv (tick→candle), range/time window frames, $densify, $fill.GET /metrics on the REST listener; zero dependencies.OXIDB_SLOW_QUERY_MS capture.cmd: "hello" returns server version, supported wire versions, stable-surface feature set, experimental feature set, and auth methods. Pre-auth, idempotent, backward-compatible (clients without HELLO default to wire v1). See oxidb-server/src/hello.rs and ADR-0003 Phase 2.
/v1/ URL prefix — GET /v1/hello returns server info; /v1/api/... is the 1.0 stable surface entry point. Legacy bare /api/... still routes during the deprecation window.
oxidb.v1 via Sec-WebSocket-Protocol. Clients without the header still connect.
oxidb migrate CLI — new subcommand on oxidb-cli: migrate inspect --data <PATH> walks a data directory and reports each file's on-disk format version (OXWA / OXTX / OXBT / OXIX / blob format_version). migrate run validates versions and is the scaffold for future v2 migrations (ADR-0003 Phase 4).
jsonb_oxiwire module converts JSONB to OxiWire bytes via a custom serde Visitor, skipping the serde_json::Value tree intermediate (~20 µs/doc saving on cache miss). A new doc_bytes_cache (env-tunable via OXIDB_DOC_BYTES_CACHE_SIZE, default 1M) keeps pre-encoded bytes around.
find queries that are exactly covered by a composite index's fields now route through find_prefix directly, skipping post-filter and Value materialisation.
$eq/$ne/$gt/$gte/$lt/$lte/$in conditions plus $and / $or / dot-paths directly against JSONB bytes using codec::extract_field. Wired into the aggregation pipeline's $match step AND the find full-scan rayon path; reserves the full JSONB→Value decode for queries with predicates the partial matcher can't evaluate.
OXIDB_DOC_CACHE_SIZE overrides the 100K default. Production hardware with more RAM can hold the full working set.
tests/comparison-mongodb bench at 1M-document scale (in-network Docker harness, no port-forward artifact) goes OxiDB 24 – MongoDB 0 across 24 measured workloads. Largest wins: count-all 2189×, Top-5 cities aggregation 1262×, composite-indexed compound 4.1×. Smallest wins: bulk insert 1.1×, range-10K-rows-each 1.2×. Resource footprint at peak: OxiDB 1.71 GiB RSS / 741 MB disk vs MongoDB 1.00 GiB / 626 MB.
OxiDb.Client.Tcp, OxiDb.Client.Embedded, OxiDb.EntityFrameworkCore, and NEW: OxiDb.Linq (LINQ provider, previously source-only).
OxiDbException base + OxiDbDuplicateKeyException, OxiDbTransactionConflictException, OxiDbAuthenticationException, OxiDbNotFoundException, OxiDbImmutableException (WORM), OxiDbConnectionException, OxiDbProtocolException. Server error strings routed to the right subclass via FromServerMessage. Legacy OxiDbTcpException retained as [Obsolete] alias.
HelloAsync + HelloResponse record — wire-protocol handshake returning server version, supported wire versions, stable + experimental feature sets, auth methods.
FindAsync<T>, FindOneAsync<T>, InsertReturningIdAsync (returns long), InsertManyReturningIdsAsync (returns long[]). Eliminate the JsonElement→parse dance.
StreamAsync<T> — IAsyncEnumerable<T> over paginated LIMIT/SKIP batches for million-row result sets.
services.AddOxiDbTcp(opts => opts.Host(…)) registers IOxiDbClient as a singleton.
Query.Eq, Query.Gte, Query.In, Query.And, Query.Or, Query.Range … for runtime-constructed queries that don't fit LINQ.
docs/SEMVER.md, docs/STABILITY.md, docs/DEPRECATION.md, docs/SECURITY.md — Phase 5 of ADR-0003. Translate the ADR-0004 release-policy decisions into operational docs (24-month LTS, additive-only minor releases, GitHub Security Advisories channel, etc.).
docs/PHASE3-SDK-FREEZE.md + Python client api/v1.json snapshot + CI gate script (template for the other 9 Tier-A clients).
docs/format/compat-matrix.md — Phase 2 cross-version compat matrix (OxiWire / REST / WebSocket).
RotationPolicy in oxidb-server/src/audit.rs supports size-based (OXIDB_AUDIT_MAX_BYTES), age-based (OXIDB_AUDIT_MAX_AGE_SECS), and calendar-aligned UTC rotation (OXIDB_AUDIT_CALENDAR=hourly|daily), with optional gzip compression of rotated files (OXIDB_AUDIT_COMPRESS=true). Wired into both standalone and cluster modes.
_tx_commit_log, OXWA for .wal, OXBT for .btree; explicit format_version in blob .meta JSON. Establishes the 1.0 on-disk-format contract.
find / aggregate now return a Tables.jl-compatible row collection (DataFrames, CSV, MLJ, GLM accept it directly). SQL exports removed from Julia clients — OxiDB is a document database; Tables.jl covers the data-frame integration story.
oxidb-server/src/raft/log_store.rs to split state into a small raft_meta.json (vote / committed / sm_data) and an append-only raft_log.jsonl (one entry per line).
append_to_log is now a single line append per entry instead of rewriting the entire log; delete_conflict_logs_since and purge_logs_upto rewrite only on those rare events.
raft_state.json on first boot.
OxiDbStore in oxidb-server/src/raft/log_store.rs was previously in-memory only; nodes that restarted came back as Learner term=0 and lost cluster membership, breaking failover scenarios.
OxiDbStore::open(db, &data_dir) constructor — loads existing Raft state on startup; OxiDbStore::new(db) retained as in-memory variant for tests.
save_vote, save_committed, append_to_log, delete_conflict_logs_since, purge_logs_upto, apply_to_state_machine, install_snapshot.
$not, $nor, $all, $size, $type, $mod, $expr, $elemMatch.
$not field operator — negate any field condition; missing fields evaluate to true (MongoDB-compatible).
$nor top-level operator — match documents where none of the listed conditions are true.
$all array operator — array must contain all specified values.
$size operator — match arrays with an exact length.
$type operator — match by JSON type (string, number, bool, array, object, null, int).
$mod operator — modulo arithmetic on numeric fields ([divisor, remainder]).
$expr top-level operator — cross-field comparisons, e.g. {"$expr": {"$gt": ["$sold", "$stock"]}}.
$elemMatch operator — match array elements against sub-queries with AND semantics.
CreateProcedure, CallProcedure, ListProcedures...), CreateTTLIndex, retention policies, alerting methods, ExtractText, Backup/Restore, SetDialect.
$set / $inc / $unset — variants.0.stock no longer corrupts arrays.
matches_doc and matches_value into a shared eval_field_op helper.
oxidb-wasm crate compiles OxiDB to wasm32 and runs entirely in the browser.
init, insert, find, update, delete, count, sql, aggregate.
src/locks.rs) -- Uses parking_lot on native targets, spin locks on wasm32.
rayon, memmap2, zstd, and other native-only crates moved to target-specific dependencies to enable WASM compilation.
#[cfg(not(target_arch = "wasm32"))] guards throughout core engine -- Platform-incompatible code paths conditionally compiled out for the WASM target.
0xDB. Replaces MsgPack for all request/response paths.
Encoder + decoder in Rust and Go
OxiDb.Client.Tcp, OxiDb.Client.Embedded, OxiDb.EntityFrameworkCore.
d7d5a05
github.com/vmihailenco/msgpack/v5 -- Removed from Go module dependencies.
Arc-refcounted.
0997d8e
pread for concurrent reads without locking the write path.
63093a3
_id and the indexed field from raw JSONB during index build.
beb3f49
$group with $sum: 1 on indexed fields returns set size without touching documents.
b339e5a
Value clones, use DocIdSet instead of BTreeSet for single-entry indexes.
4897f86
cluster feature flag).oxidb-client-ffi (cdylib) and oxidb-embedded-ffi (staticlib + cdylib) for language bindings.