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
| Change | Representative saving |
|---|---|
| Stop ordered permission evaluation once the answer is established | Owner 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 aggregates | 4 → 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 SQL | Eight 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 defaults | Creating 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.
| Change | Representative saving |
|---|---|
| Avoid cloning a payload when no field codec applies | Nested JSON update: 9.1% lower SQLite latency, 3.7% lower PostgreSQL latency and about 12% less allocation. |
| Look up codecs only for the current model | Reading 100 rows with 256 unrelated codec registrations: 14.4% lower SQLite latency and 3.9% lower PostgreSQL latency. |
| Consume INSERT values when executing the builder | Large JSON create: 13.6% lower SQLite latency and 13.3% less allocation. |
| Consume UPDATE arguments when executing the builder | Large JSON update: 8.3% lower SQLite latency. |
| Omit an unused mutation snapshot when there are no hooks or restrictions | Large JSON create/update: 11.8%/12.3% lower SQLite latency and about 13.3% less allocation. |
| Avoid unused foreign-key preparation during explicit upsert | Large 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 string | A 16-row JSON bulk write: 12.1% lower latency and 8.4% less allocation. |
| Move raw JSON into generated setters | JSON 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 discarded | Large JSON projection: 21.1% lower SQLite latency and 30.3% less allocation. |
| Consume joined JSON row objects | Loading 16 large joined rows: 6.6% lower SQLite latency and 33.2% less allocation. |
| Move a select-in relationship value to its final consumer | Large relationship values: 48.4% less allocation, with roughly unchanged latency. |
| Borrow conflict assignments instead of cloning a temporary list | Explicit-conflict upsert: 8.5% lower SQLite latency, 5.3% lower PostgreSQL latency and 13.3% less allocation. |
| Encode unrestricted codec-only bulk writes directly | Large 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:
| Workload | SQLite latency | PostgreSQL latency | SQLite allocation reduction |
|---|---|---|---|
| Large JSON create | 931 → 578 µs | 2,995 → 2,807 µs | 47.0% |
| Large JSON update with returned data | 953 → 596 µs | 3,944 → 3,794 µs | 46.9% |
| Explicit JSON conflict | 1,099 → 741 µs | 3,448 → 2,878 µs | 38.0% |
| Ordinary JSON bulk write | 1,427 → 838 µs | 6,805 → 6,014 µs | 49.6% |
| Codec JSON bulk write | 2,543 → 839 µs | 8,420 → 6,098 µs | 77.5% |
| Large projection | 406 → 322 µs | 1,413 → 1,278 µs | 30.3% |
| 16 large joined rows | 485 → 456 µs | 4,456 → 4,392 µs | 33.2% |
| 16 large select-in relationships | 163 → 150 µs | 4,862 → 4,790 µs | 48.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
| Change | Representative saving |
|---|---|
| Native decoding for eligible UUID, timestamp and JSON fields | PostgreSQL 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 loading | PostgreSQL allocation fell 24–47% in the evaluated workloads, with modest latency changes. |
| Move many-to-many values to their final consumer | Large 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 cursors | A 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 serialization | Constructing 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 grouping | Repeated 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:
| Database | Total latency | Latency reduction | Allocation reduction |
|---|---|---|---|
| SQLite | 3.515 → 2.293 ms | 34.8% | About 45% |
| PostgreSQL | 22.400 → 20.291 ms | 9.4% | About 43% |
| MySQL | 26.249 → 21.866 ms | 16.7% | About 31% |
| MariaDB | 22.990 → 20.991 ms | 8.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.
