Turbine

Database Compatibility

Turbine targets PostgreSQL, so it also runs on databases that speak the PostgreSQL wire protocol. They use the default PostgreSQL dialect, with at most a small adapter for migration locking and catalog quirks.

Looking for SQLite, MySQL or SQL Server? Those are separate engines. See Database Engines.

Compatibility matrix#

DatabaseAdapterStatusNotes
PostgreSQL 14+NoneFullCI runs the full suite on 14, 15, 16 and 17. Older servers may work but are untested.
AlloyDBalloydb (no-op)FullSame wire protocol, catalogs and SQL as PostgreSQL.
TimescaleDBtimescale (no-op)FullHypertables introspect as tables.
NeonNoneFullUse turbine-orm/serverless for its HTTP driver.
SupabaseNoneFullStandard PostgreSQL; connect directly.
YugabyteDByugabytedbExperimentalCI runs only a statement-timeout check, nightly.
CockroachDBcockroachdbExperimentalCI runs a statement-timeout and row-lock check on v24.1. Results below are from v26.2.5.

Connect to any of them with a standard connection string:

import { TurbineClient } from 'turbine-orm';
import { SCHEMA } from './generated/turbine/metadata.js';
 
const db = new TurbineClient(
  {
    connectionString: process.env.DATABASE_URL,
    ssl: { rejectUnauthorized: true }, // most managed services require TLS
  },
  SCHEMA,
);

TimescaleDB#

turbine generate reads hypertables like any other table. It reads tables only by default, so continuous aggregates are left out: pass --include-views to add views and materialized views as read-only entities, or query them with db.sql:

type DailyViews = { day: Date; views: number };
 
const rows = await db.sql<DailyViews>`
  SELECT day, views FROM daily_page_views
  WHERE site_id = ${siteId}
  ORDER BY day DESC
  LIMIT 30
`;

Migration locks on distributed databases#

turbine migrate serializes concurrent runs with pg_try_advisory_lock(). On YugabyteDB that lock is scoped to one node, and on CockroachDB v26.2.5 it returns true to every session that asks. Two concurrent runs can both apply the same migrations.

The yugabytedb and cockroachdb adapters replace it with a row in a _turbine_lock table, taken with SELECT ... FOR UPDATE NOWAIT:

// turbine.config.ts
import { cockroachdb } from 'turbine-orm/adapters';
 
export default {
  url: process.env.COCKROACH_URL,
  adapter: cockroachdb, // or yugabytedb
};

turbine migrate up, deploy and down take the lock through the configured adapter (since 0.92.0).

YugabyteDB#

YugabyteDB reuses the PostgreSQL query layer, so the query surface should behave the same. Apart from the nightly timeout check, nothing here has run against a live cluster. Check these on your own:

  • Whether pg_try_advisory_lock() serializes two sessions (see above).
  • NULL ordering in an ordered relation with a limit. CockroachDB inverts it (see below).
  • Hash indexes are not supported; B-tree, GIN and GiST are.
  • pg_class.reltuples may lag on new tables, which only affects Studio's row estimates.

CockroachDB#

Measured once against CockroachDB v26.2.5 (single node). Behavior changes between CockroachDB versions, so treat this as one data point.

These work through Turbine: nested relations with per-relation orderBy and limit (measured with jsonEncoding: 'object'), introspect(), findMany, count, groupBy, create with RETURNING, atomic increment, relation filters, $transaction with savepoints and sessionContext, statement timeouts, pgvector with an hnsw index, Studio, and migrate deploy and migrate status apart from the lock.

Not tried: the default positional JSON encoding, pipelines, streaming and the flatten strategy.

AreaBehavior on v26.2.5What to do
Advisory locksReturn true to every callerSerialize migration runs yourself (see above).
LISTEN / NOTIFYNot supported$listen and $notify fail with a database error, not UnsupportedFeatureError.
Default isolationSERIALIZABLEExpect more retryable errors under contention. Use withRetry.
SERIAL columnsLarge, non-sequential ids from unique_rowid()Type them as a 64-bit-safe number or string.
pg_class.reltuplesAlways NULLStudio's sidebar shows 0 rows; the Data tab counts correctly.
crdb_internalAccess denied, even to rootThe cockroachdb adapter's row-estimate override fails.
ivfflat indexUnsupportedUse hnsw.
json_build_objectReturns jsonb, which sorts keysWith jsonEncoding: 'object', join and batched return nested keys in different orders. Compare parsed objects, not strings.

CockroachDB runs on PostgreSQL's dialect, so Turbine's PostgreSQL capability flags apply as they are. plan_cache_mode, DISTINCT ON and the batched loader's per-parent limit all work on v26.2.5.

Other PostgreSQL-compatible databases#

Turbine will likely work if the database supports:

  1. json_agg, json_build_object and json_build_array
  2. Correlated subqueries
  3. $1, $2, ... parameters
  4. information_schema.tables and information_schema.columns

Run npx turbine generate to check that introspection works. If something breaks, open an issue.