Performance results
Measured on 2026-11-13 on the development machine (Ryzen 7, 16 threads, local PostgreSQL 17), release build, one process running every role unless stated. The scripts are in tests/e2e in the repository.
Against the targets
Section titled “Against the targets”The targets were set before the first slice was built, on one core with a warm cache.
| Path | Target | Measured |
|---|---|---|
| Delivery, content by path | p99 under 5 ms | p50 0.09 ms, p99 0.22 ms (2,000 sequential requests, one connection) |
| Delivery, conditional GET (304) | p99 under 1 ms | p50 0.09 ms, p99 0.22 ms |
| Cold start to first served request | under 500 ms | 13–25 ms (authoring, projector and delivery roles; measured at the first slice) |
| Resident memory, idle | under 60 MB | 28.5 MB with every role, after all scenarios; 9 MB with authoring, projector and delivery only |
| Image rendition, 4000 px JPEG to 1200 px WebP | under 150 ms | 105 ms median; to JPEG 51 ms |
| Editor, initial JavaScript | under 250 kB gzipped | 173 kB |
| Authoring save and publish latency | p99 under 30 ms and 50 ms | Not measured separately |
The binary is 30 MB.
How. No ORM, prepared statements, a schema snapshot shared by reference, responses written straight from stored JSON where the shape allows, and the ETag computed at projection time. Renditions are pure Rust plus libwebp built from bundled source, off the async threads on a bounded pool; WebP encoding is about half of the 105 ms.
Overlay on Optimizely
Section titled “Overlay on Optimizely”seeamess-migrate verifyagainst a live Optimizely CMS 12: 12 responses compared, no unexpected differences. One documented difference: Optimizely shows its global assets folder under a UI display string.- The existing front-end renders the same visible text from either CMS for all six demo pages.
- After
adopt, all delivery responses are byte-identical to before.
Split deployment
Section titled “Split deployment”Authoring, projector and delivery as three processes on one database, outbox poll 250 ms:
- A publish is visible on the delivery process after 245 ms (median of 20).
- Delivery keeps answering with the authoring process stopped.
- Two projectors do not double-apply.
Volume: 20,000 contents, 42,000 versions
Section titled “Volume: 20,000 contents, 42,000 versions”| Measure | Result |
|---|---|
| First sync | 40 s (about 500 contents per second) |
| Repeat sync, nothing changed | 8 s, no outbox growth |
| Engine memory after sync | 35 MB |
| Route lookup | p50 0.11 ms, p99 0.24 ms |
| Search, rare term / common term | 12 ms / about 100 ms |
| Subtree delete | 2.3 s, grows engine memory by about 65 MB |
| Restore of that subtree | 10 s, grows engine memory by about 50 MB |
| Purge of that subtree | 1.1 s |
Known limits
Section titled “Known limits”- Deleting or restoring a very large subtree holds one event per content in memory and projects inline. It is correct but heavy; moving those events and projection changes into SQL is the fix.
- Search has no ranking, and a common term costs about 100 ms at this size.
- The S3 blob backend is tested against an in-memory object store, not a real S3 service.
- No AVIF output.
- Preview tokens unlock referenced media one level deep.