Commit Graph

21 Commits

Author SHA1 Message Date
joshua-spacetime 315afc938f Parameterized query plans (#5287)
# Description of Changes

Makes runtime parameters explicit in query plans (prerequisite for
parameterized views).

As part of this change, `sender` is no longer baked directly into query
plans as a literal value. Instead it is represented as a parameter in
the query plan. Values are supplied at runtime via a variable
environment called `ExecutionParams`.

Note, parameterized plans are still not shared across subscriptions yet.
That will be done in a follow up.

This is mostly a mechanical change. The majority of the diff is just
threading runtime params/variables through various call sites.

# API and ABI breaking changes

N/A

# Expected complexity level and risk

...

# Testing

Existing coverage
2026-06-15 21:46:00 +00:00
joshua-spacetime 7b7e6a393f Stop tracking multiple plan types in the subscription cache (#5263)
# Description of Changes

Required before we add formal parameters to query plans which in turn is
required to support parameterized views.

Before this change, we cached two types of query representations for a
subscription - a physical plan and what we call a pipelined executor.

This was mainly an artifact of built up technical debt, so this change
removes the physical plan and only caches the actual executor for the
subscription, in preparation for adding bind variables that are
substituted by the executor at runtime.

# API and ABI breaking changes

N/A

# Expected complexity level and risk

...

# Testing

Existing coverage
2026-06-12 13:01:02 +00:00
joshua-spacetime 5d1b1360e3 Unify index key representation in query plan (#5275)
# Description of Changes

Refactors index probes so that multi-column index keys are represented
as a single product value expression `PhysicalExpr::Product` instead of
being split across various fields and structs. As a result, this patch
also simplifies the index-scan and index-join query executor variants
now that index scans/probes share one physical shape.

This change is in preparation for adding formal parameters to query
plans. Since index probe values now have single unified representation
as a `PhysicalExpr`, a future parameterized plan can represent an index
scan or index join as follows:

```rust
IndexProbe::Point(PhysicalExpr::Product(vec![
    PhysicalExpr::Param(sender_slot),
    PhysicalExpr::Value(other_const),
]))

PhysicalExpr::Product(vec![
    PhysicalExpr::Param(sender_slot),
    PhysicalExpr::Field(lhs_join_field),
])
```

This will avoid having to duplicate parameter handling in a bunch of
different places or rules.

Note, a very nice consequence of this refactor is that it
removes/consolidates a significant amount of code as the diff shows.

# API and ABI breaking changes

N/A

# Expected complexity level and risk

...

# Testing

Existing coverage
2026-06-11 19:06:53 +00:00
Noa e3582131fe Migrate to Rust 2024 (#3802)
# Description of Changes

It'd be best to review this commit-by-commit, and using
[difftastic](https://difftastic.wilfred.me.uk) to easily tell when
changes are minor in terms of syntax but a line based diff doesn't show
that.

# Expected complexity level and risk

3 - edition2024 does bring changes to drop order, which could cause
issues with locks, but I looked through [all of the warnings that
weren't fixed
automatically](https://gistcdn.githack.com/coolreader18/80485ae5c5f82de1784229cce2febb26/raw/ba80f3fecda66ceb34f4f7ad73b98ea02d4893a2/warnings.html)
and couldn't find any issues.

# Testing

n/a; internal code change
2026-03-03 11:06:52 +00:00
Tyler Cloutier 3f58b5951b Implement event tables (server, Rust/TS/C# codegen + client SDKs) (#4217)
## Summary

Implements event tables end-to-end: server datastore, module bindings
(Rust/TypeScript/C#), client codegen (Rust/TypeScript/C#), client SDKs
(Rust/TypeScript/C#), and integration tests.

Event tables are tables whose rows are ephemeral — they persist to the
commitlog and are delivered to V2 subscribers, but are NOT merged into
committed state. Rows are only visible within the transaction that
inserted them. This is the mechanism that replaces reducer event
callbacks in 2.0.

## What's included

### Server
- `is_event` flag on `RawTableDefV10`, `TableDef`, `TableSchema`
- Event table rows recorded in TxData but skipped during committed state
merge
- Commitlog replay treats event table inserts as no-ops
- Migration validation rejects changing `is_event` between module
versions
- `SELECT * FROM *` excludes event tables
- V1 WebSocket subscriptions to event tables rejected with upgrade
message
- V2 subscription path delivers event table rows correctly
- `CanBeLookupTable` trait — event tables cannot be lookup tables in
semijoins
- Runtime view validation rejects event tables

### Module bindings
- **Rust**: `#[spacetimedb::table(name = my_events, public, event)]`
- **TypeScript**: `table({ event: true }, ...)`
- **C#**: `[Table(Event = true)]`

### Client codegen (`crates/codegen/`)
- **Rust**: Generates `EventTable` impl (insert-only) for event tables,
`Table` impl for normal tables. `CanBeLookupTable` emitted for non-event
tables.
- **TypeScript**: Emits `event: true` in generated table schemas.
`ClientTableCore` type excludes `onDelete`/`onUpdate` for event tables
via conditional types.
- **C#**: Generates classes inheriting from `RemoteEventTableHandle`
(which hides `OnDelete`/`OnBeforeDelete`/`OnUpdate`) for event tables.

### Client SDKs
- **Rust**: `EventTable` trait with insert-only callbacks, client cache
bypass, `count()` returns 0, `iter()` returns empty
- **TypeScript**: Event table cache bypass in `table_cache.ts` — fires
`onInsert` callbacks but doesn't store rows. Type-level narrowing
excludes delete/update methods.
- **C#**: `RemoteEventTableHandle` base class hides delete/update
events. Parse/Apply/PostApply handle `EventTableRows` wire format, skip
cache storage, fire only `OnInsert`.

### Tests
- 9 datastore unit tests (insert/delete/update semantics, replay,
constraints, indexes, auto-inc, cross-tx reset)
- 3 Rust SDK integration tests (basic events, multiple events per
reducer, no persistence across transactions)
- Codegen snapshot tests (Rust, TypeScript, C#)
- Trybuild compile tests (event tables rejected as semijoin lookup
tables)

## Deferred
- `on_delete` codegen for event tables (server only sends inserts;
client synthesis deferred)
- Event tables in subscription joins / views (well-defined but
restricted for now)
- C++ SDK support
- RLS integration test

## API and ABI breaking changes

- `is_event: bool` added to `RawTableDefV10` (appended, defaults to
`false` — existing modules unaffected)
- `CanBeLookupTable` trait bound on semijoin methods in query builder
(all non-event tables implement it, so existing code compiles unchanged)
- `RemoteEventTableHandle` added to C# SDK (new base class for generated
event table handles)

## Expected complexity level and risk

3 — Changes touch the schema pipeline end-to-end and all three client
SDKs, but each individual change is straightforward. The core risk area
is the committed state merge skip in `committed_state.rs`. Client SDK
changes are additive (new code paths for event tables, existing paths
unchanged).

## Testing

- [x] `cargo clippy --workspace --tests --benches` passes
- [x] `cargo test -p spacetimedb-codegen` (snapshot tests)
- [x] `cargo test -p spacetimedb-datastore --features
spacetimedb-schema/test -- event_table` (9 unit tests)
- [x] `pnpm format` passes
- [x] Rust SDK integration tests pass (`event_table_tests` module)

---------

Signed-off-by: Tyler Cloutier <cloutiertyler@users.noreply.github.com>
Co-authored-by: Phoebe Goldman <phoebe@goldman-tribe.org>
Co-authored-by: Jason Larabie <jason@clockworklabs.io>
Co-authored-by: joshua-spacetime <josh@clockworklabs.io>
2026-02-15 22:56:20 +00:00
Mazdak Farrokhzad 3d3c99f8db Shrink JsWorkerRequest & use the right HashMap/Set (#4150)
# Description of Changes

This PR shrinks `JsWorkerRequest` so that it is (almost) as small as the
call reducer request.
To do that, a bunch of trivial changes had to be done to auth code, that
mostly revolves around `String` -> `Box<str>`.
This should help the auth code, but that is incidental.
The main goal was to improve throughput through the request tx/rx
channel for V8, which is taking quite a bit of time in flamegraphs.

I also noticed while making this change that the wrong hash map was
being used in a bunch of places, so I fixed all of those.

A follow up PR will shrink the reply side to fit within a cache line.
Yet another follow up PR will change the channel to replace flume with
`fibre::spsc`.

# API and ABI breaking changes

None

# Expected complexity level and risk

2, fairly trivial changes.

# Testing

Covered by existing tests.
2026-01-29 08:46:09 +00:00
Mazdak Farrokhzad d6bc325244 Define TableName and ReducerName backed by EcoString (#4137)
# Description of Changes

The first commit defines a type `TableName` that is used in e.g.,
`TxData` and where determined profitable and necessary to do this
change.
`TableName` is backed by
[`ecow::EcoString`](https://docs.rs/ecow/0.2.6/ecow/string/struct.EcoString.html)
which affords O(1) clones and 15 bytes of inline storage and
`mem::size_of::<EcoString>() == 16`.

The second commit does the same for `ReducerName`. This is also used in
reducer execution.

Together, these commits increase TPS by around 5-7k TPS.

# API and ABI breaking changes

None

# Expected complexity level and risk

1

# Testing

Covered by existing tests.
2026-01-27 23:20:30 +00:00
Mazdak Farrokhzad 14b346c79c Add error handling for ranged seeks on non-range compat indices (#3974)
# Description of Changes

When doing a ranged seek on a non-ranged index (none such exist yet, but
will be added in a follow up), return an (ABI) error.

Also:

- Use point scans in query execution (`IxScan(Delta)Eq`).
- Refactor table index code with macro `same_for_all_types`.

# API and ABI breaking changes

None

# Expected complexity level and risk

2?

# Testing

Testing the error handling will be possible once hash indices are added
(follow up PR).
2026-01-13 21:53:03 +00:00
joshua-spacetime 75d35d324b Fix realtime update for views (#3747)
# Description of Changes

View tables have private metadata columns that need to be dropped before
sending results to clients. Before this patch we dropped these columns
for sql queries and initial subscriptions, but we didn't drop them after
incremental update which is what this patch does.

# API and ABI breaking changes

None

# Expected complexity level and risk

1

# Testing

- [x] Smoketest
2025-11-25 06:26:44 +00:00
joshua-spacetime 0de8910e2a Atomic view update (#3624)
# Description of Changes

Updates views atomically on commit, but before downgrading to a
read-only transaction for subscription evaluation.

What this patch does:
1. Renames `ViewId` to `ViewFnPtr`
2. Renames `ViewDatabaseId` to `ViewId`
3. Removes the `module_rx` module watcher from the subscription manager
4. Refactors read sets to only track table scans (index key tracking
will be added later)
5. Drops read sets and removes rows from `st_view_sub` when dropping a
view in an auto-migrate
6. Re-evaluates and updates views (`call_views_with_tx`) from
`call_reducer_with_tx` for any view whose read set overlaps with the
reducer's write set
7. Does the same for sql dml

# API and ABI breaking changes

None

# Expected complexity level and risk

3

It's a bit of a messy diff.

# Testing

- [x] Integrate with
https://github.com/clockworklabs/SpacetimeDB/pull/3616

---------

Signed-off-by: joshua-spacetime <josh@clockworklabs.io>
Co-authored-by: Shubham Mishra <shivam828787@gmail.com>
2025-11-11 04:26:52 +00:00
joshua-spacetime edac806697 Materialize views on subscribe (#3599)
# Description of Changes

This patch:

1. Materializes views on subscribe and sql calls by invoking `call_view`
on the `ModuleHost`.
2. Downgrades to a read-only transaction after view materialization but
before query execution.
3. Updates the `st_view_sub` system table on both subscribe and
unsubscribe.
4. Makes subscribe methods on the SubscriptionManager async.

# API and ABI breaking changes

None

# Expected complexity level and risk

2

# Testing

End-to-end tests to be added with atomic view updates
2025-11-08 22:47:08 +00:00
joshua-spacetime f5d3bcd1be Add view handling to query engine and planner (#3578)
# Description of Changes

This patch does the following:

1. Expands views as part of query planning. Views are always assumed to
be materialized by the query planner, however a view's backing table may
have private columns such as the `sender` column. The query planner
needs to filter by this column in order to select the rows pertaining to
a particular caller.
2. Plumbs `AuthCtx` through the query optimizer. This is needed in order
to implement (1).
3. Adds a new operator for views to the query engine that drops a view's
private columns

# API and ABI breaking changes

None

# Expected complexity level and risk

2.5

# Testing

- [x] SQL http tests
- [ ] Subscription tests
- [ ] One off query tests
2025-11-05 19:19:26 +00:00
joshua-spacetime 4f493e4d2f Relax conditions for query pruning (#2873) 2025-06-16 19:03:58 +00:00
joshua-spacetime c522c0f950 Prune more queries when evaluating subscription updates (#2855)
Signed-off-by: joshua-spacetime <josh@clockworklabs.io>
Co-authored-by: Mario Montoya <mamcx@elmalabarista.com>
2025-06-14 00:45:20 +00:00
joshua-spacetime 8a16a12304 Build indexes over TxData for subscription eval (#2768) 2025-05-28 20:53:14 +00:00
Phoebe Goldman 1e50c7d8ad Aggregate and broadcast DbUpdates off the main thread (#2793)
Co-authored-by: joshua-spacetime <josh@clockworklabs.io>
2025-05-28 17:10:48 +00:00
joshua-spacetime 59faab8a1f Remove redundant rows from subscription updates (#2654) 2025-04-24 00:54:07 +00:00
joshua-spacetime ec30c0b048 Add rls to the subscription api (#2546) 2025-04-04 21:12:39 +00:00
joshua-spacetime 01c391f8a9 Add sql support for :sender parameter (#2483) 2025-03-21 17:53:34 +00:00
joshua-spacetime 6abfe8ddba Add support for LIMIT (#2279) 2025-02-19 20:18:44 +00:00
joshua-spacetime 75ab91d36d Handle optimization and execution errors on initial subscription (#2213) 2025-02-06 19:14:44 +00:00