Skip to content
What's new

Rent v0.8.6

Less database work, fewer copies, and correct JSON appends.

Rent v0.8.6 reduces repeated permission evaluation, unnecessary database round trips, payload copying and intermediate JSON conversions. The largest improvements benefit permission-heavy operations, JSON-heavy writes, pagination and relationship loading. Eligible paths use native decoding and ownership transfer while preserving authorization, hooks, codecs and transaction behavior.

Fewer permission checks and database round trips

ChangeRepresentative saving
Stop ordered permission evaluation once the answer is establishedOwner lookup: 45 → 13 SQL calls and 14.78 → 4.58 ms. Owner update: 112 → 35 calls and 35.83 → 10.42 ms.
Use one PostgreSQL statement for eligible finite counts, projections, grouping and aggregates4 → 1 SQL calls, with 49–64% lower latency in these workloads. A public count went from 1.01 → 0.48 ms.
Reuse prepared final-row authorization SQLEight field permissions allocated 868,048 → 608,723 bytes, about 30% less. No separate latency gain is claimed.
Preserve valid reference authorization when applying built-in PostgreSQL timestamp defaultsCreating 16 timestamped rows with a shared parent used 595 → 82 SQL calls and 204.09 → 18.71 ms, with about 93% less allocation.

Permission evaluation retains the rules for exclusions, unresolved decisions and cycles. Defaults that cannot safely reuse authorization retain their existing checks. Ordinary public point reads keep the same SQL call and allocation counts.

Fewer copies on common reads and writes

The following results isolate individual improvements or their incremental effect. They overlap and must not be added together. Large JSON values make unnecessary copies especially visible; small scalar requests generally change much less.

ChangeRepresentative saving
Avoid cloning a payload when no field codec appliesNested JSON update: 9.1% lower SQLite latency, 3.7% lower PostgreSQL latency and about 12% less allocation.
Look up codecs only for the current modelReading 100 rows with 256 unrelated codec registrations: 14.4% lower SQLite latency and 3.9% lower PostgreSQL latency.
Consume INSERT values when executing the builderLarge JSON create: 13.6% lower SQLite latency and 13.3% less allocation.
Consume UPDATE arguments when executing the builderLarge JSON update: 8.3% lower SQLite latency.
Omit an unused mutation snapshot when there are no hooks or restrictionsLarge JSON create/update: 11.8%/12.3% lower SQLite latency and about 13.3% less allocation.
Avoid unused foreign-key preparation during explicit upsertLarge explicit-conflict upsert: 20% lower SQLite latency, 8.4% lower PostgreSQL latency and about 21% less allocation. Required validation remains in place.
Count serialized JSON bytes without building a temporary stringA 16-row JSON bulk write: 12.1% lower latency and 8.4% less allocation.
Move raw JSON into generated settersJSON bulk writes: 25.8% lower latency and 41.4% less allocation. Custom and list serialization retain their existing behavior.
Move selected values out of rows that are being discardedLarge JSON projection: 21.1% lower SQLite latency and 30.3% less allocation.
Consume joined JSON row objectsLoading 16 large joined rows: 6.6% lower SQLite latency and 33.2% less allocation.
Move a select-in relationship value to its final consumerLarge relationship values: 48.4% less allocation, with roughly unchanged latency.
Borrow conflict assignments instead of cloning a temporary listExplicit-conflict upsert: 8.5% lower SQLite latency, 5.3% lower PostgreSQL latency and 13.3% less allocation.
Encode unrestricted codec-only bulk writes directlyLarge codec bulk writes: 46.5% lower SQLite latency, 13.6% lower PostgreSQL latency and 62.2% less allocation. Restricted writes keep their preparation.

Taken together, this copy-and-codec bundle produced the following incremental results:

WorkloadSQLite latencyPostgreSQL latencySQLite allocation reduction
Large JSON create931 → 578 µs2,995 → 2,807 µs47.0%
Large JSON update with returned data953 → 596 µs3,944 → 3,794 µs46.9%
Explicit JSON conflict1,099 → 741 µs3,448 → 2,878 µs38.0%
Ordinary JSON bulk write1,427 → 838 µs6,805 → 6,014 µs49.6%
Codec JSON bulk write2,543 → 839 µs8,420 → 6,098 µs77.5%
Large projection406 → 322 µs1,413 → 1,278 µs30.3%
16 large joined rows485 → 456 µs4,456 → 4,392 µs33.2%
16 large select-in relationships163 → 150 µs4,862 → 4,790 µs48.4%

This comparison starts after the permission and round-trip improvements above. A small select-in workload with distinct targets allocated 1,140 extra bytes per operation, about 1.7%, for relationship bookkeeping. Not every operation allocates less.

Less intermediate JSON on reads

ChangeRepresentative saving
Native decoding for eligible UUID, timestamp and JSON fieldsPostgreSQL reads of 100 UUIDs allocated 61.5% less; 100 timestamps allocated 64.7% less. Large SQLite JSON reads used about 43% less time and 49.5% less allocation.
Typed many-to-many loadingPostgreSQL allocation fell 24–47% in the evaluated workloads, with modest latency changes.
Move many-to-many values to their final consumerLarge distinct targets allocated 32.5% less on SQLite and about 27% less on PostgreSQL. These savings overlap typed loading.
Extract only the keys needed for dynamic pagination cursorsA one-record JSON page went from 1.064 → 0.887 ms on SQLite and 2.827 → 2.670 ms on PostgreSQL, with 18.6% less SQLite allocation.
Remove redundant JSON helper serializationConstructing a large PostgreSQL JSON containment expression went from 28.0 → 2.834 µs and allocated 71.2% less. Whole-request latency was essentially unchanged.
Reuse the parent serialization buffer during joined-row groupingRepeated large parents allocated 40,685,000 → 5,460,394 bytes, 86.6% less in this component. Tiny unique rows retain the original allocation behavior.

Native decoding is selected only where the generated model can use it safely. SQLite UUID-only generated queries and streams retain their existing document decoding path because native decoding was slower there. Custom representations and unsupported combinations keep their compatible fallback paths.

JSON append correctness

Appending objects, arrays and booleans now preserves their JSON types on MySQL and MariaDB, both when initializing a missing array and when appending to an existing one. SQLite appends preserve JSON booleans rather than storing them as integers. The correction also applies to nested arrays and nullable paths. Existing scalar string, number and null behavior is preserved.

What the combined results mean

A representative 13-operation mix compared v0.8.5 with the combined candidate:

DatabaseTotal latencyLatency reductionAllocation reduction
SQLite3.515 → 2.293 ms34.8%About 45%
PostgreSQL22.400 → 20.291 ms9.4%About 43%
MySQL26.249 → 21.866 ms16.7%About 31%
MariaDB22.990 → 20.991 ms8.7%About 30%

This is a historical candidate comparison, not a benchmark of the exact final release. It preceded the SQLite UUID fallback, small-row grouping correction and JSON append fix. Both versions issued 18 SQL statements and returned 247 rows across that mix; the permission-heavy workloads above demonstrate separate SQL reductions.

Compare each database with its own baseline, not with the other databases. The mix uses a plain JSON read in place of containment on SQLite, and string appends on MySQL/MariaDB because structured appends exposed the pre-existing defect.

All figures describe specific optimized-build workloads on a shared development machine. Allocation figures describe cumulative heap traffic captured separately from timing, not process memory or peak resident size. Peak live memory in the mixed workload was roughly unchanged. The improvements are workload-dependent and do not constitute a single universal Rent speedup.

Upgrade

Use v0.8.6 for the CLI and every Rent crate, regenerate configured clients, and commit the application's lockfile and generated changes. Regeneration enables the generated setter, decoder, cursor and relationship improvements.

This release introduces no database schema migration. Review migration diffs for other application changes before applying them. Detailed database instrumentation remains opt-in: ordinary builds should leave the instrumentation feature disabled.