---
title: "Release 3.3 | SurrealDB"
description: "The third 3.3 beta moves the permission and COMPUTED write rules to runtime enforcement, and fixes the embedded JavaScript engine's storage lifetime and…"
url: https://surrealdb.com/releases/3.3
---

Back to top

[Version 3.3.0-beta.3](https://surrealdb.com/releases/3.3#release-3-3-0-beta-3)

[Version 3.3.0-beta.2](https://surrealdb.com/releases/3.3#release-3-3-0-beta-2)

[Version 3.3.0-beta.1](https://surrealdb.com/releases/3.3#release-3-3-0-beta-1)

[Home](https://surrealdb.com/)

[Releases](https://surrealdb.com/releases)

3.3

# Release 3.3

3 patch releases · Latest 3.3.0-beta.3 on Aug 20, 2026

The third 3.3 beta moves the permission and COMPUTED write rules to runtime enforcement, and fixes the embedded JavaScript engine's storage lifetime and…

## 3.3.0-beta.3

PRE-RELEASE

Released on Aug 20, 2026

The third 3.3 beta moves the permission and `COMPUTED` write rules to runtime enforcement, and fixes the embedded JavaScript engine's storage lifetime and packaging found while wiring the SDK against the published `v3.3.0-beta.2` packages. Everything new in 3.3 from `v3.3.0-beta.1` and `v3.3.0-beta.2` is included and not repeated here.

Items tagged `[Enterprise]` on a bullet refer to the SurrealDB Enterprise product. Everything else applies to community.

## Improvements

- `[Query]` **Permission and `COMPUTED` write rules move to runtime enforcement.** `v3.3.0-beta.1` added a definition-time check that tried to decide, when a schema object was defined, whether a `PERMISSIONS` guard or `COMPUTED` body could ever reach a data-modifying statement. Seeing through a function call means resolving the callee's stored body against the catalog, and that answer is not sound: whether a body reaches its write depends on which branch its arguments select, so a function that conditionally calls `CREATE` / `UPDATE` was refused for every caller, including the guards and `COMPUTED` fields that never take that branch. That check is dropped: a `PERMISSIONS` clause loses its definition-time write refusal entirely, and a `COMPUTED` body loses it for writes reached through a function call. A write written directly in a `COMPUTED` body is still refused at `DEFINE` / `ALTER` time. A `DEFINE` / `ALTER` that stores a writing `SELECT` guard, or a `COMPUTED` body that reaches a write through a function call, is now accepted, and fails at the point a read actually reaches the write, with the existing `A PERMISSIONS clause cannot contain a statement that modifies data` / `A COMPUTED clause cannot contain a statement that modifies data`. The runtime frame is exact regardless of call depth or indirection (`eval`, scripts, closures arriving as data), which the definition-time check could never be. `create` / `update` / `delete` permission clauses may carry side effects, since they are reached only from a statement that is already writing. This reverses the definition-time behaviour described under *Writing computed fields and functions are refused at definition time* in `v3.3.0-beta.1`.
- `[Embedded]` **Per-platform binary packages for `@surrealdb/node-native`.** The eight native binaries are no longer bundled inside the root package. Each is published as `@surrealdb/node-native-<platform>` carrying its own `os` / `cpu` / `libc`, and the root package lists them all under `optionalDependencies`, so an install downloads only the one binary its host can use rather than all eight, keeping a `@surrealdb/node` install under AWS Lambda's 250 MB unzipped limit.

## Bug fixes

- `[Embedded]` `free()` on `@surrealdb/node-native` now releases the datastore's storage before it resolves, so a file-backed datastore can be reopened on the same path after `close()` within one process. surrealkv releases its lockfile late in shutdown and `Drop` only spawned a detached close, so `free()` previously returned with the data directory still locked. The wasm engine has no file backends and is unaffected.
- `[Embedded]` The `surrealkv+versioned://` scheme, which has named no backend since versioning moved to the `?versioned=true` parameter, now reports the supported spelling instead of the generic "unable to load the specified datastore", which read like a build compiled without the backend.
- `[SDK]` Query-stream `begin` frames now carry the protocol revision, and the structural contract a client can rely on when decoding is written down: a `stream` tag is never repurposed, unknown tags and fields are ignorable, and `single` is always present on a `finished` frame (it was already on the wire; only the SDK type made it optional).

## Upgrade notes

- The `mutable_permissions` experimental capability is removed. It existed only to reopen the `create` / `update` / `delete` guards that the definition-time rule had shut, and was on for every server already. Remove any `--allow-experimental mutable_permissions` / `--deny-experimental mutable_permissions` flag and the SDK's `ExperimentalFeature::MutablePermissions` builder entry; there is no replacement, since those guards now permit side effects unconditionally.
- The `surrealdb_statement_mutable_permission_writes_total` metric is removed along with the capability. Drop any dashboard panel or alert that referenced it.

Upgrade or install

## Get SurrealDB v3.3.0-beta.3

Pick how you want to install or upgrade. SurrealDB Studio can update connected instances in place, or choose a platform below to copy a CLI command for v3.3.0-beta.3.

SurrealDB Cloud

macOS

Linux

Windows

Docker

Already installed

You can upgrade your SurrealDB Cloud instance to v3.3.0-beta.3 effortlessly through SurrealDB Studio.

1. Open the [SurrealDB Studio](https://studio.surrealdb.com/current/instances)
2. Select your organisation and instance
3. On the dashboard, click on the "Upgrade" button
4. Your instance will be updated and restarted automatically

[Full install guide](https://surrealdb.com/surrealdb/install)

## 3.3.0-beta.2

PRE-RELEASE

Released on Aug 18, 2026

The second 3.3 beta is a focused round of startup, shutdown, and readiness hardening for clustered deployments, driven by incidents observed on production multi-node clusters. Everything new in 3.3 from `v3.3.0-beta.1` is included and not repeated here.

Items tagged `[Enterprise]` on a bullet refer to the SurrealDB Enterprise product. Everything else applies to community.

## Improvements

- `[Server]` **Configurable readiness heartbeat window.** The `/ready` probe treats a node as unhealthy once its cluster heartbeat goes stale, and that window can now be set directly with `--readiness-heartbeat-max-age` / `SURREAL_READINESS_HEARTBEAT_MAX_AGE`. Previously it was always derived as three times the node-membership refresh interval (9s by default), which welds the probe to the very write path it measures. That is fine for a storage engine whose node-row write is local and sub-millisecond, but under a distributed engine the same write is a consensus transaction whose latency moves with cluster health, so a slow-but-healthy write path made every replica flip `NotReady` at once and presented partial degradation as total unavailability. The default is unchanged, so behaviour is identical unless you set the flag. Startup now also warns if the configured window reaches the interval at which peers archive an unresponsive node (30s), since a node reported ready after its peers have written it off keeps taking traffic while its cluster registration and live queries are collected underneath it.
- `[Server]` **Graceful shutdown of startup background work.** Deferred startup tasks - a startup import, root credential creation, the Surrealism eager module load, and the first node-maintenance pass - now register with the datastore, so `shutdown` waits for them (bounded by the existing 30-second maintenance shutdown timeout) instead of closing the storage engine while they are still writing. Cancellation is prompt: each task selects on the shutdown token, an aborted import deliberately leaves the node not ready rather than flipping a dying pod healthy, and unreached Surrealism modules simply load on first use.

## Bug fixes

- `[Server]` Archived-node cleanup no longer gates the HTTP listener. On a cluster recovering from a restart storm, the listener could take minutes to become reachable after the storage engine was ready: the bind waited behind sequential startup operations, and `remove_nodes` opened one write transaction per archived node with no bound on how much dead-member residue a restart storm left behind. The first expiry and cleanup pass is now spawned after this node registers itself, not awaited, so it cannot hold up the bind. The periodic maintenance scheduler is unchanged.
- `[Server]` The cluster heartbeat now survives a slow write. It previously made a single attempt per tick under a fixed 60s budget, so one slow write could lose readiness roughly 51s before the attempt itself failed. Each tick now spends its whole budget - bounded relative to the readiness window - on the write and retries a fast failure within the tick, so a write that is slow but working still lands in time to keep the node ready.
- `[Enterprise]` The read-catch-up completeness watermark is now persisted, so a restart no longer re-drains the committed log from zero. The floor was held in memory only, so every process start walked the entire committed history - one quorum round trip per 100 records - under a begin whose fixed 300s request timeout it eventually exceeded; the startup gate treated that as fatal and exited, and the next start walked from the same zero floor, crashlooping. The watermark now lives in the durable `meta` column family, is restored on open clamped to the durable committed frontier, and is established at startup under a begin bounded on progress rather than wall time. Measured on a 3-node cluster: a rollout that had been crashlooping against a 30-minute timeout completed in 5m04s, the startup gate went from 300s-then-exit to single-digit milliseconds, and watermark-caused readiness 503s went to zero.
- `[Enterprise]` A pending admission freeze no longer wedges startup. A guard-holding wait inside the read catch-up - the committed-manifest window pull, with 60s of patience per page in an unbounded page loop - could hold its guard across a `Normal`-leaving view transition, so the freeze never drained and the startup warm-up failed. Because that path exits the process on any error, the node killed itself and restarted into the same state. Those waits are now raced against the admission-freeze marker and bail retriably, and the warm-up absorbs the transients under a single request budget instead of surfacing them. Store replays and offloaded reads are deliberately not preempted, since dropping an offloaded closure would strand a durable write ahead of its post-apply bookkeeping.
- `[Enterprise]` A begin or read establishment refused because a view transition is pending is now reported as a retryable `TransactionConflict` rather than a hard datastore error. The commit path deliberately keeps its unknown-outcome classification, since a commit interrupted mid-round cannot know whether other replicas still hold tentative reservations.

## Upgrade notes

- `[Enterprise]` If you applied `SURREAL_DS_STARTUP_NORMAL_TIMEOUT=0` as a break-glass on `v3.3.0-beta.1`, unset it on this build. The startup warm-up now retries through view-transition churn, so `=0` only removes protection - it disables both enterprise startup gates and leaves `check_version` as the only one.
- `[Enterprise]` Keep `SURREAL_STARTUP_OPERATION_TIMEOUT=180` for now. The default is still 60s, which is tight for a consensus engine on a cold cluster.

Newer patch available

## Upgrade to 3.3.0-beta.3

You are viewing the 3.3.0-beta.2 changelog. A newer patch in this release line is available - we recommend running 3.3.0-beta.3 for the latest fixes and improvements.

[View 3.3.0-beta.3 release notes](https://surrealdb.com/releases/3.3#release-3-3-0-beta-3)

## 3.3.0-beta.1

PRE-RELEASE

Released on Aug 14, 2026

This is the first beta in the 3.3 series. It opens the line with two new ways to connect - a Postgres wire-protocol listener that lets any Postgres client run SurrealQL or ISO GQL, and a gRPC engine with end-to-end result streaming - and makes ISO GQL available by default. File buckets gain S3, GCS, and Azure object-storage backends, the Node.js and browser embedded engines are rebuilt against the current engine, and a deep round of index work lands: bitmap index fusion, pre-filtered vector search, and a much faster full-text write path. For SurrealDS (Enterprise), cold-start convergence is dramatically faster, recovery cost is bounded, and cluster storage can now be S3-backed.

The items below cover what is new in the 3.3 line. The fixes already released across the `v3.2.1`–`v3.2.4` patches are also included in this build but are not repeated here.

Items tagged `(Enterprise)` on a heading or `[Enterprise]` on a bullet refer to the SurrealDB Enterprise product. Everything else applies to community.

## Highlights

### 🐘 Postgres wire protocol

SurrealDB can now speak the Postgres wire protocol (v3.0), so any Postgres client - `psql`, JDBC, npgsql, tokio-postgres, and the rest - can connect and run SurrealQL or ISO GQL with properly typed results.

- Opt-in via `--postgres-bind <addr>` / `SURREAL_POSTGRES_BIND`, and capability-gated through a new `postgres` route target. TLS is available through the standard `SSLRequest` upgrade, reusing `--web-crt` / `--web-key`.
- Both the simple and extended query protocols are supported (Parse/Bind/Describe/Execute/Close/Sync/Flush), with text and binary codecs for every mapped type including `numeric`. The namespace and database come from the startup `database` parameter as `ns/db`, with `USE` and `LET` persisting across queries; interactive `BEGIN`/`COMMIT`/`ROLLBACK` behaves like Postgres, including `25P02` aborted-transaction poisoning, and `CancelRequest` is supported.
- The query dialect is selectable at connect time (`options=-c dialect=gql`) or in-session (`SET dialect`), so a Postgres client can run ISO GQL directly.
- Authentication supports cleartext passwords over TLS and **SCRAM-SHA-256** (RFC 5802 / RFC 7677). `DEFINE USER ... PASSWORD` now derives SCRAM verifier material alongside the existing Argon2 hash, and a new additive `PASSSCRAM '<verifier>'` clause on `DEFINE USER` / `ALTER USER` imports a precomputed PostgreSQL-format verifier so exports round-trip losslessly. Existing users without SCRAM material keep working.
- Postgres positional parameters `$1..$n` are rewritten to `$_1..$_n`, and SurrealDB auto-commits each top-level statement - use an explicit `BEGIN ... COMMIT` for all-or-nothing behaviour. ANSI-SQL translation, `pg_catalog` emulation, MD5/COPY, and `LIVE` queries are out of scope for now.

### 🕸 ISO GQL enabled by default

ISO GQL querying - introduced experimentally in 3.2 - is now available by default, matching GraphQL. The `--allow-experimental gql` flag, the SDK experimental-feature builder, and the `gql` cargo feature all remain valid as harmless no-ops, so existing configurations keep working. GQL is reachable over the `/gql` HTTP route, the `gql` RPC method, MCP, and the new Postgres listener.

### 📡 A gRPC engine and end-to-end result streaming

The SDK's engines now sit behind one typed `SurrealEngine` interface, and a new native gRPC engine joins the family alongside major streaming work across every transport.

- New `grpc://` / `grpcs://` connection schemes, shipped enabled in the released `surreal` binary, so `surreal sql --endpoint grpc://host:port` works out of the box.
- Query results stream end to end over gRPC via `db.query(..).stream_items()` - the client no longer waits for the whole result set to be produced. Measured on a 10,000-row `SELECT`, the first rows reach the client 65 µs after execution begins, against ~20 ms to produce them all. Rows are provisional until their statement's terminal frame arrives, which is withheld until the outcome is final.
- The WebSocket RPC protocol gains a `query_stream` request answered by a sequence of frames (`begin`, per-statement `rows`/`value`/`finished`, then one `end`), carried in JSON, CBOR, and flatbuffers alike. This brings streaming to the places gRPC cannot reach: browsers/WASM and Cloudflare Workers.
- The embedded JavaScript engines stream now too: a `QueryStream` with `next()` in `@surrealdb/node-native` and a `ReadableStream` in `@surrealdb/wasm-native`, with bounded buffering so nothing runs ahead of the reader, and abandoned streams stopping their execution instead of orphaning a transaction.

### 🪣 Cloud object storage for buckets

File buckets (`DEFINE BUCKET` and the `file::*` functions) gain cloud object-storage backends in the open-source engine, built on the `object_store` crate.

- AWS S3 and S3-compatible stores (MinIO, Backblaze B2, Wasabi, Cloudflare R2) via `s3://`, `s3+http://`, `s3+https://`; Google Cloud Storage via `gs://` / `gcs://`; Azure Blob Storage via `az://` / `azure://`. The existing S3 URL format is preserved exactly, and a latent bug that dropped a custom endpoint's port is fixed.
- Bucket traffic is now metered: three authenticated-only OpenTelemetry instruments under `surrealdb.bucket` - `sent_bytes`, `received_bytes`, and `operations`.
- Embedders can install a custom object store behind buckets with `Builder::with_bucket_store_provider(...)` - the seam needed to back buckets with a platform store such as a Cloudflare Workers R2 binding.

### 🎯 Bitmap index fusion and pre-filtered vector search

The streaming planner can now compose index results as bitmaps over a shared per-table document-ID space, and vector search can use those bitmaps to filter before traversal.

- Index-backed predicates compose with AND/OR/NOT as compressed bitmaps, and a `COUNT` answerable from indexes alone never touches records. New plan nodes (`BitmapIndexScan`, `BitmapFullTextScan`, `BitmapAnd`, `BitmapOr`, `BitmapAndNot`) are visible in `EXPLAIN`, with per-node candidate cardinalities in `EXPLAIN ANALYZE`. Existing b-tree indexes keep working with no rebuild. Non-anchor range branches are capped by `SURREAL_BITMAP_BRANCH_BUDGET` (default 250,000 entries; `0` disables), and the full `WHERE` clause is always retained as a residual filter.
- **Pre-filtered vector search.** When a KNN plan is chosen, `WHERE` conjuncts that provably match an index bitmap are evaluated into an allow-list *before* HNSW/DiskANN traversal, replacing one record fetch per visited graph node. An adaptive triage picks a tier, reported in `EXPLAIN ANALYZE` as `prefilter_tier`: `exact` (small allow-lists skip the graph entirely for guaranteed-correct top-K, threshold `SURREAL_KNN_PREFILTER_EXACT_THRESHOLD`, default 2,000), `graph` (gated traversal with a boosted search width, up to `SURREAL_KNN_PREFILTER_EF_BOOST_THRESHOLD`, default 100,000), and `graph_unboosted` above that, with a `fallback` tier reported when the allow-list cannot be built within the bitmap branch budget. Enabled by default; `SURREAL_KNN_PREFILTER_ENABLED=false` turns it off.

### ⚡ Faster full-text and count indexes

The write path of the concurrent full-text index has been overhauled end to end.

- Full-text delta-log entries are batched per transaction instead of one key per (term, document), and postings are now keyed by document rather than by term which significantly reduces write amplification. Older indexes are read through a legacy fallback, so no migration is needed.
- COUNT-index deltas are aggregated per transaction, and the compactor is woken by the commit that queues its work rather than only by its timer.
- Exports now size each emitted `INSERT` statement by the KV keys a record will write given the table's index set, so restores of heavily indexed tables use appropriately sized transactions.

### ❄️ SurrealDS cold starts, recovery, and admission (Enterprise)

A major round of work on SurrealDS cluster formation, recovery cost, and overload behaviour.

- **Faster cold-start convergence.** Replicas leaving recovery at different times could stall cluster formation on retry timers. View-change retransmission, a formation-aware backoff, and concurrent outcome-donor drains cut measured convergence tails from 4.44 s to 0.02 s (n=3) and 14.75 s to 4.61 s (n=5), and the default-config worst case from 86.9 s to 13.7 s.
- **Bounded recovery drains.** The Phase-1 outcome drain previously re-paged a donor's entire outcome history on every entry into recovery - O(all transactions ever committed). Each replica now keeps a bounded in-memory write-order journal and a recovering replica asks each donor only for what it wrote since its last position, falling back to the full stream when needed. Configured via `SURREAL_DS_RECOVERY_OUTCOME_JOURNAL_ENTRIES` (default 262,144; `0` disables). Drain behaviour is observable on new `surrealdb.ds.recovery_outcome_drain*` counters.
- **Bounded transaction write sets.** A new operation-count bound on transaction write sets, `SURREAL_DS_TRANSACTION_WRITE_SET_LIMIT_OPS` (default 100,000; `0` disables), sits beside the existing 32 MiB byte cap: an over-bound transaction fails fast at the coordinator with `TransactionTooManyOperations` before any network traffic, with a warning fired once at half the bound. Keep the value uniform across the fleet.
- **Membership-epoch correctness.** A membership-changing leader now withholds serving until a quorum of the new voter set is confirmed to hold the decided configuration; a coordinator whose epoch was superseded mid-transaction abandons the round promptly instead of resending into a silent fence; and a voter fenced for being *ahead* of its own cluster now escalates to a view change instead of starving indefinitely. The fence itself is now observable via the `surrealdb.ds.epoch_fence_drops` counter with a `direction` label.

## Improvements

- **Mutable permission clauses, behind a capability.** The 3.1-era security block on writes in *all* permission clauses over-reached for `create`/`update`/`delete` clauses, where audit-logging side effects are a relied-upon pattern. Those are re-permitted behind a new transitional `mutable_permissions` experimental capability - allowed by default on the server (deny it with `--deny-experimental mutable_permissions`), off by default for embedders. `SELECT` permission clauses stay read-only. Usage is measurable via the new `surrealdb_statement_mutable_permission_writes_total` counter. Alongside this, function mutability is now resolved against the stored call graph, so a write hidden behind a user-defined function call can no longer slip past definition-time checks on `COMPUTED` fields and permission clauses.
- **Datastore versioning and startup migrations.** The datastore now records its semantic version and runs registered, ledger-tracked data migrations on startup, resuming interrupted runs, with a per-node version history. The first migration fixes a keyspace collision present since 3.0.0, where `DEFINE SEQUENCE` keys sorted inside the table-name band and any table whose name began with `sq` could break `INFO FOR DB`, sequence exports, and `REMOVE NAMESPACE` / `REMOVE DATABASE`. Note for rolling upgrades: a sequence created after the rollout begins is not visible to nodes still on the previous release until they upgrade.
- **Wall-clock query timeouts on the transports.** The HTTP (`/sql`, `/gql`, `/graphql`) and RPC (WebSocket and HTTP) surfaces now apply a hard wall-clock timeout reusing the configured `--query-timeout` value, on top of the existing cooperative deadline. Timed-out HTTP requests return `504 Gateway Timeout` with a proper error envelope. Off unless `--query-timeout` is set; transaction-control methods are exempt, while `signin`/`signup`/`authenticate` (which can run user-defined SurrealQL) are guarded.
- **Runtime-swappable capabilities for embedders.** `Datastore::set_capabilities(...)` atomically swaps the capability set with no datastore rebuild, aimed at embedders that cannot restart per instance (such as a wasm Cloudflare Durable Object serving many tenants). In-flight queries keep the snapshot they started with, and the outbound HTTP client is rebuilt in the same swap so a runtime network tightening is enforced on redirect hops and DNS resolution too.
- **Stateless MCP.** The first-party MCP server now serves the stateless `2026-07-28` protocol revision alongside the handshake-based revisions on the same `/mcp` endpoint. Every tool except `use` gains optional `namespace` and `database` arguments, with scope resolving from call arguments, then `surreal-ns` / `surreal-db` request headers, then session state, then server defaults. The advertised protocol revision is now pinned explicitly (upgrading the underlying SDK can no longer silently change it), and the `gql` tool's annotations no longer declare it read-only - those hints drive client-side auto-approval, and the GQL dialect can write.
- **`vector::sum`.** A new function, in both forms: as a grouped aggregate it folds a vector-valued expression across a group's rows in O(dimension) state regardless of row count, and as a scalar it sums an `array<array<number>>` directly - making an in-database weighted centroid composable from existing functions.
- **New vector functions, stricter numeric edge cases.** `vector::distance::mahalanobis(a, b, cov)` and `vector::similarity::spearman(a, b)` are implemented. A family of silent-NaN or sentinel results now error instead: `vector::divide` with a zero divisor element, `normalize`/`angle`/`project` at zero magnitude, and `pearson`/`spearman` on degenerate inputs; `vector::similarity::jaccard` now uses true set semantics, and `time::nano` errors outside the i64-nanosecond range instead of returning 0.
- **`ORDER BY` outside the projection.** `SELECT event, subject FROM audit_log ORDER BY at` now works - the sort field no longer has to appear in the projection.
- **Brute-force KNN with computed query vectors.** The streaming executor now supports `<|k, DIST|>` with a non-literal query vector (bind parameters, function calls, computed arrays), closing the last unimplemented case in the read-only planner.
- **Multi-part `MATCHES`.** A full-text `@@` reached through a record link to an index on another table (`t.name @@ 'x'`) now executes on the streaming engine.
- **Index matching through record traversals.** A `WHERE` clause matching a compound index through record-idiom traversals of a parameter (for example `WHERE createdAt <= $scan.task.finishedAt`) previously always full-table-scanned; row-independent traversals are now resolved at plan time so index analysis can match them, with strict provable-equivalence conditions.
- **Silo package resolution.** Surrealism silo packages now resolve over HTTPS from a configurable endpoint (default `https://silo.surrealdb.com`), with a 256 MiB cap and organisation- and package-name restrictions that block path traversal. The fetch needs no `allow_net` grant since the host comes from server configuration.
- **Import and scan performance.** Collection literals whose elements are already values now convert without the async evaluator (a 200K-record import profile had 52.9% of CPU under literal evaluation); mock targets (`|table:N|`) are drained id-by-id instead of pre-expanding a million-entry list; nine scan loops stopped copying whole cursor batches; and a delete's reference batch is decoded once instead of per entry.
- **Quieter, correct system metrics.** One system-metrics refresher now runs per process regardless of datastore count (embedders with several datastores got CPU figures from arbitrary sub-intervals), and the metrics cache is populated before the first query so `INFO FOR ROOT` can no longer report an all-zero `system` block.
- **Storage engine updates.** The memory backend adopts surrealmx 0.24's native savepoint release, removing an emulated savepoint stack; `affinitypool` 0.8 speeds up the blocking-work pools and fixes a pool deadlock; TiKV gRPC status failures are now logged with their code and message at debug level instead of being collapsed.
- `[Enterprise]` **Recovery and starvation observability.** The Phase-1 outcome drain's certification behaviour is surfaced on three new counters; the starved-begin metric is split by starvation class (`below_quorum`, `no_local_reply` - dashboards summing only `anterior_prepared` will now under-report); and the log volume of a membership reconfiguration is bounded and rate-paced.

## Bug fixes

- `[Server]` `USE NS <expr>` / `USE DB <expr>` with a subquery argument hit an internal panic; such statements now return an ordinary query error with the transaction rolled back.
- `[Query]` A statement's read-only classification had three holes (`SELECT` clauses like `LIMIT (CREATE ...)`, `RETURN ... FETCH`, and several idiom parts), so writing statements could run on read transactions and fail partway through.
- `[Query]` `rand::*` calls in a `WHERE` clause were folded into a single plan-time constant on the streaming engine, making the predicate all-or-nothing across rows; they now evaluate per row, as SQL engines do for volatile functions.
- `[Query]` Numeric hash/equality mismatches on the streaming engine: `GROUP BY` could split one value into two groups, and `IN` / `INSIDE` / `CONTAINSANY` / `CONTAINSALL` could silently drop rows when a column mixed `float` and `decimal` representations of the same number. Grouping and set-membership now key on ordering, matching `=`.
- `[Query]` `CONTAINSALL` with mixed-representation literals dropped rows on the index-overlap path; literal equivalence classes now each get exactly one bitmap bit. Separately, a `UnionIndexScan` could silently drop a `CONTAINSANY` / `ANYINSIDE` conjunct from its residual filter and return rows that did not match the original `WHERE` clause.
- `[Query]` `RETURN DISTINCT` emitted one value as two rows when `float` and `decimal` forms of the same number hashed differently; dedup and hash-joins now key on ordering, which also halves peak join build memory.
- `[Query]` `[WHERE ...]` applied to a non-collection returned a one-element array instead of `NONE`, and sets were not filtered element-wise. Separately, `ORDER BY` ties are now stable (input order as the final key), so a `LIMIT` cutting inside a tie group can no longer repeat or drop rows across pages.
- `[Query]` The streaming executor's `MATCHES` fallback returned `true` unconditionally (`WHERE 'abc' @@ 'zzz'` matched every row); and the legacy engine's cross-table `MATCHES` evaluated a tautology that matched every row. Roughly 490 unit tests were backfilled across the executor at the same time.
- `[Query]` `BREAK` / `CONTINUE` raised inside a `LET` binding's value were swallowed by the streaming executor instead of propagating to the enclosing loop.
- `[Query]` `PERMISSIONS` predicates reaching the row via `$parent` denied every row on the streaming engine, and `math::variance` / `math::stddev` returned population figures where sample figures were correct, in both ad-hoc `GROUP BY` and materialized views. The field-permission pass could also cut whole array elements out of results, causing silent data loss including an empty `RETURN DIFF` for non-owner sessions.
- `[Query]` A closure body's writes are now reflected in its access mode, so `array::map` with a writing closure can no longer be scheduled on read-only execution paths; likewise an unresolvable `mod::` function's write flag now defaults to writeable.
- `[Query]` 85 signature divergences between the streaming function registry and the legacy layer were aligned; six `api::*` middleware functions were missing from the streaming registry entirely, and several async builtins silently ignored extra arguments instead of erroring. `object::matches` - advertised by the parser but implemented in neither registry, so every call failed at runtime - is now rejected cleanly at parse time.
- `[Query]` Values now coerce into `set` types, so `SET field += value` on a `set<...>` field works, and deduplication is applied after a field's `VALUE` clause runs rather than before.
- `[Index]` Any index rebuild could publish a durably `Online` but empty index (the compactor's generation guard was wiped with the build subspace), after which every query against it silently returned nothing. Covers `DEFINE INDEX`, `DEFINE INDEX OVERWRITE`, and `REBUILD INDEX`.
- `[Index]` Rolling back a `DEFINE INDEX ... CONCURRENTLY` could leave durable build state behind, because cleanup deleted the build keys while the builder task was still writing.
- `[Index]` A full-text term's compacted document set could depend on how compaction rounds grouped its deltas, so a document could keep a term it had lost (a false `MATCHES` hit) with nothing to correct it later; compaction now carries a per-term residual. Full-text compaction rounds are also now bounded by a document budget rather than a key count.
- `[Index]` DiskANN KNN memory and latency grew without bound under sustained write load (OOM-cycling on capped pods); the pending-update set is now sharded so per-query work tracks the active backlog.
- `[RPC]` A single client could permanently deadlock its own session across 15 RPC methods over WebSocket, HTTP, or gRPC, when a buffered query overlapped a session-mutating call (`set`, `signin`, `use`, ...).
- `[RPC]` Three paths dropped writeable transactions without committing or cancelling them - worst was async event processing, where an undecodable queue entry leaked a transaction on every batch indefinitely. Present since 3.1.
- `[RPC]` Live-query registry leaks: an ended live query kept its registry entry and its `surrealdb.live_query.active` gauge increment for the life of the connection; `REMOVE TABLE`/`DATABASE`/`NAMESPACE` and principal revocation are now covered, and gRPC streams delete subscriptions the client never claimed.
- `[RPC]` Dropping a client-side item stream mid-query leaked its transaction and driving tasks for the life of the process; the execution now stops when the stream is dropped. The per-connection WebSocket stream cap is also now claimed atomically.
- `[SDK]` Cloning a `Surreal` handle on the WebSocket engine replayed session state fire-and-forget, so the clone's first request could execute before its own `signin` - reproduced at roughly 1% of iterations. Requests now park until the replayed setup is acknowledged.
- `[Embedded]` `db.use_defaults()` cleared the session instead of applying `DEFINE CONFIG DEFAULT`, so embedded consumers silently ignored configured default namespaces/databases.
- `[Embedded]` A query abandoned mid-batch left a half-written transaction in the session that a later `commit` would commit; abandoned queries now take their transaction with them. The session registry no longer auto-creates entries for already-ended sessions.
- `[Export]` A database with an `ENFORCED` relation table could not restore its own export - every edge was silently dropped while the restore reported success. The endpoint check is now deferred under `OPTION IMPORT`, fixing existing export files too.
- `[HTTP]` `/signin` and `/signup` each applied the other's body-size limit; each route now uses its own. On wasm, `http::*` calls failed capability checks outright and carried no `User-Agent`; both fixed. `fetch()` with `redirect: "error"` / `"manual"` could not fetch any URL because its client was built from the wrong rule set.
- `[KV]` Releasing a savepoint discarded the wrong scope's undo entries on every backend, so a record created by a statement that later failed could survive along with its index writes (reachable via synchronous `DEFINE EVENT`). Also fixed a TiKV regression that roughly doubled write-path RPCs for multi-record `INSERT`/`UPSERT` after any savepoint release.
- `[KV]` surrealkv is updated to 0.21.3, carrying two durability fixes - a missing fsync after compaction that could leave a SIGKILL'd pod unable to start, and a memtable rotation bug.
- `[KV]` The `DEFINE API` handler's transaction now applies the statement write-cardinality guard (`SURREAL_TRANSACTION_MAX_WRITE_KEYS`), which previously did not cover custom API handlers.
- `[Parser]` More legacy SurrealQL accepted by the new parser (`SET idiom` forms, function-call `sleep`, keyword record ids in `RELATE` targets), and `RecordId::parse_simple` no longer adds a layer of backticks on every round trip.
- `[Enterprise]` Recovery could abort a committed, durably applied transaction when a replica's last transaction-id-keyed evidence was reaped exactly as its applied gate was set; votes are now retained until a real id-keyed decision exists.
- `[Enterprise]` OCC read sets are now validated against the writes that can actually invalidate them, tightening isolation correctness.
- `[Enterprise]` An indeterminate commit is now reported as `CommitOutcomeUnknown` - telling the client to read back rather than replay - instead of a definite "nothing was written" error.
- `[Enterprise]` Both `Transactable` implementations discarded a released savepoint's undo state, mirroring the community savepoint fix.

## Breaking changes

### ISO GQL is enabled by default

The `gql` experimental capability gate is removed and ISO GQL querying is available by default. Deployments that relied on it being off should control access via the `gql` route target and the standard capability configuration. The old `--allow-experimental gql` spelling remains accepted as a no-op.

#### `UPDATE` / `UPSERT` evaluation semantics

`UPDATE` and `UPSERT` now evaluate the `WHERE` condition *before* the data clause, so a side-effecting data clause (for example `SET spawn = (CREATE log).id`) no longer runs for records the condition rejects - previously the side-effect count could depend on whether the planner picked an index. The condition and data clause now share a single pre-mutation snapshot, so reads never observe the statement's own writes, and field clauses evaluate in dependency order rather than alphabetically. Review statements that relied on assignment-visibility or alphabetical field ordering.

#### `DEFINE MODULE` syntax (Surrealism, experimental)

Three syntax changes to the experimental Surrealism module surface: the executable form now uses `FROM` instead of `AS` (`DEFINE MODULE mod::color FROM f"modules:/color.surli" UNSIGNED;`), a trailing `UNSIGNED` keyword is now required on both executable forms, and the silo version segment is now written `silo::{org}::{pkg}::<1.0.0>` (with `::` before the version). Stored definitions are unaffected and re-render in the new syntax.

#### Memory backend no longer supports time-travel

The memory backend's versioned-read support is removed: `VERSION` clauses return an unsupported-versioned-queries error, matching `indxdb`, and the backend now rejects `versioned` / `retention` connection parameters at startup instead of accepting them and failing later. Version coverage lives in the `rocksdb` and `surrealkv` backends, which retain native versioning.

#### `duration::set_*` functions removed

All seven `duration::set_*` functions were parser-advertised but never implemented - every call failed at runtime. They now produce a clean parse-time error.

#### Writing computed fields and functions are refused at definition time

A `COMPUTED` field or permission clause whose body writes through a user-defined function call was previously accepted (and wrote on every read); function mutability is now resolved against the stored call graph and such definitions are refused at `DEFINE`/`ALTER` time.

#### Rust SDK and embedder API changes

The SDK's engine layer is now the typed `SurrealEngine` trait (one method per operation) rather than a command enum over a channel; SDK futures remain `Send` but are no longer `Sync`. `surrealdb-core` no longer re-exports `surrealdb-rpc`, so Rust consumers must depend on it directly. `Datastore::transaction(..)` loses its ignored lock-type argument, and `Datastore::get_capabilities()` returns an owned `Arc<Capabilities>`.

#### JavaScript package split

`@surrealdb/node` is split: the native addon is published as `@surrealdb/node-native` and the SDK engine implementation moves to surrealdb.js. The `strict` connection option - advertised in the typings but silently ignored - is removed and is now a type error. The wasm module is renamed `surrealdb_wasm` and published as `@surrealdb/wasm-native`.

Newer patch available

## Upgrade to 3.3.0-beta.3

You are viewing the 3.3.0-beta.1 changelog. A newer patch in this release line is available - we recommend running 3.3.0-beta.3 for the latest fixes and improvements.

[View 3.3.0-beta.3 release notes](https://surrealdb.com/releases/3.3#release-3-3-0-beta-3)

Our newsletter

Get tutorials, AI agent recipes, webinars, and early product updates in your inbox every two weeks

No newer release line.

Older release line

3.2

Updated Aug 3, 2026

[Older release line](https://surrealdb.com/releases/3.2)

[All releases](https://surrealdb.com/releases)

GET STARTED

## Start building with SurrealDB

The unified data layer for AI. Simplify your stack. Reduce complexity. Build faster.

![Samsung](https://surrealdb.com/assets/static/4c58b81e7b3c9466.C_Hv0eml.svg)![NVIDIA](https://surrealdb.com/assets/static/nvidia.DaIEuMil.svg)![Apple](https://surrealdb.com/assets/static/f7dc2519e0d212bc.Cn8MYAK7.svg)![Verizon](https://surrealdb.com/assets/static/18b99996c689000f.B5PQ-nI9.svg)![Tencent](https://surrealdb.com/assets/static/401d8346058682c8.DqM87mst.svg)

SOC 2 Type 2

GDPR

Cyber Essentials Plus

ISO 27001

[Start free with SurrealDB](https://studio.surrealdb.com/current/instances/deploy) [Learn more](https://surrealdb.com/docs/what-is-surrealdb)

```json
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://surrealdb.com"},{"@type":"ListItem","position":2,"name":"Releases","item":"https://surrealdb.com/releases"},{"@type":"ListItem","position":3,"name":"3.3","item":"https://surrealdb.com/releases/3.3"}]}
```

```json
{"@context":"https://schema.org","@type":"TechArticle","headline":"SurrealDB 3.3 release notes","description":"The third 3.3 beta moves the permission and COMPUTED write rules to runtime enforcement, and fixes the embedded JavaScript engine's storage lifetime and…","datePublished":"2026-08-20T00:00:00.000Z","dateModified":"2026-08-20T00:00:00.000Z","url":"https://surrealdb.com/releases/3.3","mainEntityOfPage":{"@type":"WebPage","@id":"https://surrealdb.com/releases/3.3"},"author":{"@type":"Organization","name":"SurrealDB","url":"https://surrealdb.com"},"publisher":{"@type":"Organization","name":"SurrealDB","logo":{"@type":"ImageObject","url":"https://surrealdb.com/assets/static/logo.BG7_TG2b.svg"}},"about":{"@type":"SoftwareApplication","name":"SurrealDB","applicationCategory":"DeveloperApplication","operatingSystem":"Linux, macOS, Windows","softwareVersion":"3.3"}}
```

```json
{"@context":"https://schema.org","@type":"Organization","name":"SurrealDB","url":"https://surrealdb.com","logo":"https://surrealdb.com/assets/static/logo.BG7_TG2b.svg","description":"SurrealDB is the unified data layer for AI. A multi-model database for documents, graphs, vectors, and time-series.","foundingDate":"2022","legalName":"SurrealDB Ltd","identifier":{"@type":"PropertyValue","propertyID":"GB-COH","value":"13615201"},"address":{"@type":"PostalAddress","streetAddress":"3rd Floor, 1 Ashley Road","addressLocality":"Altrincham","addressRegion":"Cheshire","postalCode":"WA14 2DT","addressCountry":"GB"},"contactPoint":[{"@type":"ContactPoint","contactType":"customer support","email":"support@surrealdb.com","url":"https://surrealdb.com/contact","availableLanguage":"English"},{"@type":"ContactPoint","contactType":"sales","email":"info@surrealdb.com","url":"https://surrealdb.com/contact","availableLanguage":"English"},{"@type":"ContactPoint","contactType":"security","email":"security@surrealdb.com","url":"https://surrealdb.com/.well-known/security.txt","availableLanguage":"English"},{"@type":"ContactPoint","contactType":"legal","email":"legal@surrealdb.com","url":"https://surrealdb.com/legal","availableLanguage":"English"}],"hasCertification":[{"@type":"Certification","name":"SOC 2 Type 2"},{"@type":"Certification","name":"GDPR"},{"@type":"Certification","name":"Cyber Essentials Plus"},{"@type":"Certification","name":"ISO 27001"}],"owns":[{"@type":"SoftwareApplication","name":"SurrealDB","url":"https://surrealdb.com/surrealdb"},{"@type":"SoftwareApplication","name":"Agent Memory","url":"https://surrealdb.com/agent-memory"}],"knowsAbout":["multi-model databases","document databases","graph databases","vector search","time-series databases","SurrealQL","Agent Memory","real-time databases","embedded databases","context layer","graph ontology","distributed database","knowledge graphs","distributed transaction protocols","highly-scalable databases"],"sameAs":["https://www.wikidata.org/wiki/Q124316308","https://github.com/surrealdb/surrealdb","https://twitter.com/surrealdb","https://www.youtube.com/@surrealdb","https://www.linkedin.com/company/surrealdb","https://discord.gg/surrealdb","https://www.reddit.com/r/surrealdb","https://www.instagram.com/surrealdb","https://medium.com/surrealdb","https://dev.to/surrealdb"]}
```
