2.2.0 is here, and this one is a meaningful step forward for SpacetimeDB's realtime performance, operational safety, and day-to-day developer workflow. There are plenty of smaller fixes in this release too, but these are the major changes worth calling out.
Features
Faster realtime transport and client throughput
We’ve introduced a new v3 WebSocket transport that batches multiple logical client messages into a single frame, cutting per-message overhead while keeping the existing message model intact (#4761). The TypeScript SDK now uses the new transport by default (#4784). Under the hood, this release also includes a substantial round of hot-path performance work across the TS client, JS module runtime, and durability pipeline to improve throughput and reduce scheduler overhead under load.
Safer production database operations
We added spacetime lock and spacetime unlock to protect databases from accidental deletion (#4502). On top of that, spacetime delete now asks for confirmation by default (#4770), spacetime list shows database names alongside identities (#4769), and spacetime publish --yes can now skip only the prompts you intend to skip instead of skipping all of them (#4885).
Better TypeScript app ergonomics
Web developers get two nice upgrades in 2.2.0. There’s now a first-party Astro + TypeScript template with SSR and a live React island for realtime updates (#4688), and the TypeScript React bindings now include a typed useProcedure hook so procedures fit the same ergonomic pattern as reducers (#4752).
Smoother schema evolution
Publishing schema changes is less brittle now. Empty tables can be dropped during auto-migration (#4593), and changing or removing a primary key no longer leaves stale schema state behind that breaks future publishes (#4666).
More powerful table and index APIs
Modules can now clear tables directly from Rust, C#, C++, and TypeScript (#4729), and the index layer gained bytes-key B-tree support for more capable multi-column range scans (#4733).
Bug Fixes
- Improved crash resistance for JavaScript modules by preventing V8 near-heap-limit failures from taking down the server, and by rotating isolates when heap growth or fragmentation gets out of hand (#4777, #4684).
- Fixed a schema migration bug where changing or removing a primary key could leave stale schema state behind and break a later publish (#4666).
- Fixed table migration sequence persistence so
autoincvalues no longer reset after restart when a table has been migrated (#4902). - Hardened local durability so snapshot files,
metadata.toml, and pid files are properly synced to disk instead of being vulnerable to loss on an untimely crash (#4891, #4892, #4890). - Fixed an Unreal SDK bug where overlapping subscriptions could fire duplicate
OnInsertevents for already-cached rows (#4903).
If you run into anything new with this release, file an issue on GitHub or drop into Discord and let us know.
What's Changed
- [C#] Add support for IEnumerable for Views' return types by @rekhoff in #4486
- Replace
JsInstancepool with single worker and FIFO queue by @joshua-spacetime in #4663 - Improve benchmark cli, make compatible with deno by @coolreader18 in #4647
- Improve
merge_apply_insertsby @Centril in #4310 - Add distributed typescript benchmark harness by @joshua-spacetime in #4698
- Rotate V8 isolate on heap growth or fragmentation by @joshua-spacetime in #4684
- fix(keynote-2): split demo and bench CLI parsing by @joshua-spacetime in #4703
- core: Bounded channel for durability worker by @kim in #4652
- Bypass
AlgebraicValuefor datastore updates and bsatn based index scans + BytesKey optimization by @Centril in #4311 - Add more context around some errors by @jsdt in #4702
- Add .gitignore files to quickstart templates by @clockwork-labs-bot in #4609
- docs: Add supported index key types to Index docs page by @clockwork-labs-bot in #4606
- fix: single-column BTree index Range filter/delete by @DexterKoelson in #4737
- Stop setting core affinity on macos by @joshua-spacetime in #4676
- Small typescript sdk optimizations by @joshua-spacetime in #4640
- Configure compression for keynote benchmark by @joshua-spacetime in #4743
- Update client defaults in keynote bench by @joshua-spacetime in #4745
- Reduce overhead in the typescript sdk by @joshua-spacetime in #4744
- Replace JS worker's rendezvous channel with unbounded queue by @joshua-spacetime in #4704
- fix(unreal): macOS build failure — Nil macro collision with Objective-C++ by @brougkr in #4712
- Run reducers in their own V8
HandleScopefor js modules by @joshua-spacetime in #4746 - Remove rust client from keynote bench by @joshua-spacetime in #4753
- Remove warmup from distributed keynote bench by @joshua-spacetime in #4757
- core: Enable instrumentation of multiple tokio runtimes by @kim in #4694
- Improve a test with new
TableIndex::iter& simplify index iterator defs by @Centril in #4759 - Bump the esm (gzip) package size again by @jdetter in #4760
- Add module ABI & API for
clearing tables by @Centril in #4729 - fix: Replace unwrap with proper error handling in WebSocket subscribe handler by @Shiven0504 in #4696
- [TS] Fix access before initialization in codegen by @coolreader18 in #4709
- feat: Allow dropping empty tables during auto-migration by @Ludv1gL in #4593
- Increase timeout for typescript query builder tests by @joshua-spacetime in #4783
- Merge durability actors to reduce per-tx scheduler handoffs by @joshua-spacetime in #4767
- Cache V8 heap stats instead of querying them on every call by @joshua-spacetime in #4778
- Record metrics periodically in batches by @joshua-spacetime in #4801
- Show database names in
spacetime listby @clockwork-labs-bot in #4769 - Require confirmation for
spacetime deleteby @clockwork-labs-bot in #4770 - v3 websocket transport protocol by @joshua-spacetime in #4761
- durability: Use
async-channelto allow blocking send by @kim in #4802 - Fix primary key migration causing stale schema (#3934) by @clockwork-labs-bot in #4666
- Refactored
SELECTdetection in PG SQL by @egormanga in #3771 - Add TESTING.md, which documents some of our testing by @gefjon in #2898
- CI - rust smoketests lib expansion by @bfops in #4269
- Extract replay stuff out of
CommittedState, part 1 by @Centril in #4804 - Unity tutorial part 2 CLI call fix by @T-Podgorski in #3665
- Add internal docs for C# bindings packages by @kazimuth in #2938
- Run client_connected hook for HTTP SQL requests by @clockwork-labs-bot in #4563
- Update typescript sdk to use v3 websocket api by @joshua-spacetime in #4784
- docs: Add commitlog reference document by @clockwork-labs-bot in #4668
- durability: Simplify shutdown by @kim in #4808
- fix(ts-bindings): populate response headers in fetch() by @philtrem in #4691
- Export AuthCtx and JwtClaims in stdb/server typescript sdk by @dusk125 in #4649
- Fix
spacetime devbug when running with a project path by @aasoni in #4809 - fix: reorder Vue component by @MichaHuhn in #4748
- Update axum by @coolreader18 in #2713
- Move field
replay_table_updatedtoReplayCommittedStateby @Centril in #4807 - Add path context to invalid TOML config errors by @clockwork-labs-bot in #4815
- Make Tyler a codeowner for CI and tools by @jdetter in #4836
- Windows VM runner test by @jdetter in #4278
- move 00300-spacetime-json.md into the right docs folder by @xDovos in #4779
- Clean up keynote-2 template README & DEVELOP by @bradleyshep in #4624
- docs(fix): add missing
ctx.dbby @MichaHuhn in #4768 - Add Astro TypeScript template by @leovoon in #4688
- Add a test for #1121 by @drogus in #1125
- Add
spacetime lock/unlockto prevent accidental database deletion by @clockwork-labs-bot in #4502 - Adds a non-repeating scheduled reducer test by @rekhoff in #3233
- Wait for database update to become durable by @kim in #4846
- Update the defaultValue check to allow for falsy and undefined values by @JasonAtClockwork in #4838
- Better module backtraces for panics and whatnot by @coolreader18 in #577
- Add
cargo lintas alias forcargo ci lintby @bfops in #4853 - CI - Move the
git diffcheck undercargo ci smoketestsby @bfops in #4854 - CI - move
pnpm buildintocargo ci testby @bfops in #4855 - Add
sdk-test-procedure-cstest module by @rekhoff in #4840 - Add useProcedure React hook and export procedures from codegen by @DexterKoelson in #4752
- Update NativeAOT-LLVM infrastructure to current ABI by @cloutiertyler in #4515
- Keep subscription fanout worker warm with adaptive linger policy by @joshua-spacetime in #4805
- Replay: some code motion & reuse
ReplayCommittedStateby @Centril in #4849 - CI - Fold typescript lint into
cargo ci lintby @bfops in #4856 - [TS] Implement near-heap-limit termination by @coolreader18 in #4777
- CI - Merge hooks by @bfops in #4224
cargo ci self-docsuses doc comments as well as explicit helptext by @bfops in #4860- CI - Move the DLL updating code into a function by @bfops in #4868
- CI - Merge workflow files by @bfops in #4869
- CI -
cargo ci update-flowruns on Windows by @bfops in #4874 - CI - Move simple jobs into
cargo ciby @bfops in #4871 - Smoketests - Fix another name collision by @bfops in #4873
- Fix SQL parser to support negative numbers in INSERT statements by @rekhoff in #4660
- Add support for bytes key btree indices by @Centril in #4733
- Indices: house keeping (privatize stuff + fix minor bug in
insert_index) by @Centril in #4782 - Finish refactoring out replay by @Centril in #4850
- bindings: reuse panic message for datastore_index_scan_point_bsatn by @Centril in #4876
- Revert breaking PRs by @bfops in #4881
- Add some tests and metadata to LockedFile by @jsdt in #4834
- @brougkr fix(unreal): Replace FTickableGameObject with FTSTicker by @JasonAtClockwork in #4835
- CI - Use new internal test inputs by @bfops in #4231
- Properly handle execution time<->energy conversion in v8 host by @coolreader18 in #4884
- fix: update seq table when migrating table. by @Shubham8287 in #4902
- Include location in nginx config for logs specific directives by @aceysmith in #3654
- CLI - Hide the
--server-issued-loginparam by @bfops in #4905 - align UUID reducer examples with allowed signatures by @GEFNENEN in #3999
- Add enabled option to useTable hook by @DexterKoelson in #4721
- Re-land Replay extraction PRs by @Centril in #4893
- Remove Python smoketests by @bfops in #4896
- Rekhoff/reapply update nativeaot llvm infrastructure by @rekhoff in #4897
- paths: fdatasync PidFile by @kim in #4890
- Update Unreal SDK tests to work on Mac by @JasonAtClockwork in #4861
- Fix listen_addr help default value by @clockwork-labs-bot in #4812
- WIP: Start SDK tests for delete_all_by_eq_bsatn by @kazimuth in #2164
- Make Timestamp a FilterableValue by @rafaelGuerreiro in #4693
- Confirmed database updates, take 3 by @kim in #4909
spacetime publish --yesnow takes optional arguments by @jdetter in #4885- consult, prioritize event.request_id over database_update.request_id (#2729) by @apron-manlike0o in #3368
- [TS] Allow brotli to be specified for compression and reorganize some websocket stuff by @coolreader18 in #4561
- snapshot: Ensure all snapshot files are durable by @kim in #4891
- Update the C++ smoketest to use
latestby @JasonAtClockwork in #4919 - Case conversion tests by @Shubham8287 in #4450
- fix(unreal): suppress spurious OnInsert on overlapping subscription refcount bump by @SamuelE-Arvika in #4903
- Windows Binary Signing through DigiCert by @rekhoff in #4906
- client-api: stop logging suspended-database errors as 500s by @jdetter in #4918
- paths: fsync after writing to a file by @kim in #4892
- Version bump 2.2.0 by @jdetter in #4916
New Contributors
- @DexterKoelson made their first contribution in #4737
- @T-Podgorski made their first contribution in #3665
- @philtrem made their first contribution in #4691
- @MichaHuhn made their first contribution in #4748
- @xDovos made their first contribution in #4779
- @leovoon made their first contribution in #4688
- @GEFNENEN made their first contribution in #3999
- @apron-manlike0o made their first contribution in #3368
Full Changelog: v2.1.0...v2.2.0