-
Notifications
You must be signed in to change notification settings - Fork 0
Production Readiness
Status of A3SQL for live use by units, multiplayer servers, and large mods, and the concrete gaps to close before you rely on it in production.
Everything below is exercised by the CI smoke test, which loads the real
release extension binary and runs the mod's own production SQL through the
exact C ABI Arma uses ("a3sql" callExtension stmt):
python3 tools/sql_smoke_test.py --bin a3sql_x64.so tools/smoke_test.sql| Area | Status |
|---|---|
| All 4 extension binaries (x64/i686 × Linux/Windows) | Verified loading + executing via ctypes/C harnesses, incl. under wine for the DLLs |
| STRING + ARRAY callExtension forms | Both work; every SQF call site routes correctly |
| The mod's own schema (patch_rules, patch_presets, server_commands) | Create/CRUD/cursor/prepare/save/load round-trip green |
| SQL injection surface |
$1 param substitution + escaping; call compile removed (RCE-class) |
| TCP listener | Loopback-only, optional LOGIN auth, per-statement panic barrier |
| Every SQL-Dialect.md documented feature | 94/94 pass against the real binary (dialect sweep) |
| 527 Rust tests + miri + clippy -D warnings + hemtt check | All green in CI |
These were real, confirmed limitations. Each is now fixed; the list is kept for the record and for anyone upgrading from an older build.
The parser is sqlparser (mature), but execution is hand-rolled. Real bugs
were found and fixed in v0.2.0: UNIQUE treated as PK, datetime('now') in
VALUES rejected, no numeric→TEXT affinity, SELECT on empty views failing,
STRINGS[]/FLOATS[] columns downgraded to scalars, plus the FFI layer
(STRING form empty, ARRAY form routing dead).
Mitigation (not a rewrite): every mod must run its own SQL through the
smoke test before shipping — python3 tools/sql_smoke_test.py <mod.sql>.
A 94-case dialect sweep now gates every documented feature against the real
binary in CI.
OUTPUT_BUF_SIZE = 30720 — matches Arma 3 v2.20's callExtension ceiling.
SELECTs exceeding ~30 KB still fail with ERR_INTERNAL and need
LIMIT/OFFSET pagination, but the headroom is now the full platform cap.
SAVE writes to a temp file then renames (atomic on POSIX/NTFS); a crash
mid-write leaves the last good save untouched. The previous good save is
kept as *.bak; LOAD falls back to it automatically if the main save is
corrupt, with a clear message. Binary format carries an FNV-1a checksum
trailer verified on LOAD.
All queries serialize on one Mutex<Database>. Fine for Arma's
single-threaded server and unit-scale data (thousands of rows). If a mod
hammers the TCP listener from many clients concurrently, expect contention —
the RwLock read/write split is the upgrade if profiling ever shows it
matters. Not worth the complexity at current scale.
listener_require_auth = true in a3sql.toml forces LOGIN on every TCP
connection even with no CBA credentials set — for shared hosts where any
local process could otherwise connect. The empty-credentials LOGIN bypass
(LOGIN with blank tokens) is closed, and the error message tells the
operator exactly what to configure. CBA credentials still work as before.
Older save formats are rejected with an actionable message: "Save file uses format v1, this build uses v2 — migrate via export_sql and re-import." Saves remain NOT forward/backward compatible across versions (pre-1.0, by design).
| Consumer | Can ship today? | Notes |
|---|---|---|
| Small unit (single server, <10k rows) | Yes | No blockers |
| Multiplayer server (public, dedicated host) | Yes | Set listener credentials; back up a3sql_data/
|
| Big mod (heavy SQL, big datasets) | Yes with SQL pass | Run own SQL through the smoke test; paginate >30 KB results |
A3SQL is an in-memory engine: the whole database lives in RAM, and every
SAVE writes a full snapshot of it. That is the right shape for a mod data
store (settings, loadouts, leaderboards, kv state, small-moderate tables) and
the wrong shape for a telemetry sink. Know the envelope:
| Volume | Works? | Notes |
|---|---|---|
| < 10k rows | Yes, trivially | Save is sub-millisecond; memory negligible |
| 10k–100k rows | Yes | ~1 MB RAM per 100k simple rows; save ~ms |
| 100k–500k rows | Yes with care | Watch save size: a 500k-row table saves a multi-MB snapshot every autosave (30s) |
| > 500k rows / high-write | No — not this engine | Memory grows unbounded and every save rewrites the whole DB; use a real embedded DB for telemetry-scale data |
Practical guidance for mods:
- Keep tables < ~100k rows. Everything up to that is snappy; beyond it, save time and RAM start to matter.
-
Bulk-export large data out of the engine, not through it. >30 KB result
sets now fail loudly with a cursor hint — page with
cursor create+cursor fetch <name> [limit], or export to CSV/JSON files viaexport_to_file. -
The 30 KB contract (unchanged, now explicit): any single response larger
than
OUTPUT_BUF_SIZE = 30720(Arma'scallExtensionceiling) returnsERR_EXEC "Result exceeds output buffer … use cursor"— never silent truncation. Exact-fit and smaller responses are byte-identical. - Autosave cadence: the 30s autosave thread holds the same DB mutex as queries — a large save briefly blocks new statements. At <100k rows this is sub-millisecond; at hundreds of thousands of rows it becomes a visible hitch. Keep data small or disable autosave and save on mission end.
-
python3 tools/sql_smoke_test.py tools/smoke_test.sql→ all PASS - Run your mod's own SQL through the smoke test → all PASS
- Set listener credentials via CBA settings (or
listener_require_auth = trueina3sql.toml) - Point
a3sql_database_auto_save_pathsomewhere backed up; test a restore (corrupt the save, confirm the.bakfallback) - Verify a >30 KB SELECT errors cleanly, then paginate or use cursors
- Size-check your tables against the data-volume envelope above; keep <100k rows