Skip to content

v0.92.0

Choose a tag to compare

@github-actions github-actions released this 15 Oct 16:14
· 17 commits to main since this release
8724ade

Nested reads are faster, and four CLI fixes. A findMany with a with
decodes its relation rows in one pass, which takes the 50-users-with-posts
read from 1.86x to 1.33x of a hand-written query on the benchmark machine.
turbine migrate now takes its lock through the adapter in your config,
turbine observe reads --metrics-url, and two help screens state the
right numbers. No query changes its SQL or its result.

Fixed

  • turbine migrate uses the configured adapter. migrate up,
    deploy and down never passed the config file's adapter to the
    migration runner, so a CockroachDB or YugabyteDB project still took
    pg_try_advisory_lock(), which on those engines does not keep two
    concurrent runs apart. The lock now goes through the adapter's lock table.
  • turbine observe --metrics-url works. The flag was accepted and
    ignored, so observe refused to start unless TURBINE_OBSERVE_URL was set.
    It now reads --metrics-url first, then TURBINE_OBSERVE_URL.
  • turbine mcp --help says eleven tools, the number the server lists. A
    test now compares the two.
  • turbine doctor --help names both pooler ports it refuses, 6543 and
    6432. A test now checks every refused port appears.

Changed

  • Studio and the observe dashboard use the brand blue, #6f9bf7, in
    place of #60a5fa.
  • Bundle-size limits move by 1 kB: the main entry is held under 149 kB
    brotli and the edge entry under 117 kB. The growth is the one-pass decoder
    below.

Performance

  • Nested rows decode in one pass. Under the positional encoding (the
    PostgreSQL default), each relation row used to be built three times: as a
    keyed object, as a copy with its ::text columns decoded, and as the parsed
    row, which also cost an Object.keys per row to find its decode plan. The
    row parser now compiles each relation shape into a plan once per query and
    writes every nested row exactly once. The plan reads the same field names,
    cell kinds, wire rules and cell decodes the general path does, and a query
    whose shapes it cannot prove identical (two columns mapping to one field, a
    field named like a relation, an integer-like or Object.prototype key, a
    _count__ field) takes the general path unchanged. Rows are identical on
    every strategy and encoding; a differential test holds the plan to the
    general path over seeded random shapes and payloads, malformed ones
    included, on three client configurations, and against real rows with BC
    dates, five-digit years and infinity.
  • timestamptz values skip the regex work. A value ending in a
    ±HH:MM offset, which is how every timestamptz reaches a nested row, is
    now handed straight to new Date. The general path gave the same answer for
    that shape after one regex test and two replaces that could not match, so
    the result is unchanged; BC and five-digit years still take the general
    parser.

Measured

On the dedicated benchmark machine (benchmarks.yml: a 4-vCPU Firecracker VM
on an AMD Ryzen 9 7950X host, PostgreSQL 17 over a Unix socket, three runs),
against the same workflow on 0.91.0:

Scenario 0.91.0 0.92.0
findMany, 50 users + posts 1.470 ms (1.86x hand-written) 1.072 ms (1.33x)
findMany, 10 users + posts + comments 0.795 ms 0.636 ms
to-one, 500 posts + author 1.774 ms 1.511 ms
many-to-many, 200 posts + tags 1.642 ms 1.490 ms
overhead over hand-written SQL, geomean of 8 1.141x 1.100x

Paired and interleaved in one process (this build against 0.91.0,
plus a second copy of 0.91.0 as the negative control), 600 rounds, local
PostgreSQL 17, medians:

Scenario 0.91.0 This build Paired change
findMany, 50 users + posts 1.605 ms 1.376 ms -0.229 ms (-14%)
its row decode alone 0.441 ms 0.178 ms -0.263 ms (-60%)
findMany, 50 posts + to-one user 0.188 ms 0.172 ms -0.017 ms (-8%)
findMany, 50 users, no relations 0.071 ms 0.070 ms none

Against a hand-written query that decodes the same rows to objects and Dates,
the nested read went from 1.39x to 1.19x on this machine. The ::text casts on
int8 / numeric relation columns stay: they are what keeps a value past
2^53 exact.