Repository navigation
v0.20.0
[0.20.0] - 2026-10-02
Changed
- RLOG compaction keeps clustered inputs clustered and writes L1 segments at zstd level 9
- The RLOG writer stores a string column's values on one row-group dictionary when that is strictly smaller, and writes encoding tags 12 (the dictionary page) and 13 (per-block id pages)
--require-bucket-protectionreads the S3 bucket's protection configuration at startup and refuses on more conditionsravel-serversends a CRC64-NVME upload checksum on every S3 PUT by defaultravel-cli --store s3checksums every PUT with CRC-64/NVME by default, andstore qualifyreads the bucket and reports whether the endpoint returns the stored checksum- The RLOG writer now chooses each i64 and string page's encoding by its stored size, and writes encoding tags 10 (GCD i64) and 11 (an
observed_tsequal tots, stored as a reference to it) s3_e2e_benchcounts adaptive-age flushes- RLOG log objects are now written and read at trailer version 5
- SQL fetches now reserve against the process-wide memory budget
ravel-cli maintain migratenames every bucket whose refusal no re-run clearsravel-cli maintain migratenames a bucket held below the target only by a losing compaction record's parts- The background supervisor now takes an advisory claim before compacting a large bucket, so two processes whose ownership overlaps no longer both pay for the same merge
ravel-cli maintain compact-bucketandcompact-tenanttake the same advisory compaction claims, so an operator's run and a background supervisor no longer both merge one large bucket- A ranged RLOG read holds only the bytes it placed, not a buffer the size of the object
- This release reads provisioning record format 3 and still writes 2, and
ravel-cli maintain audit-versionsnow classifies every recorded format floor - The query fetchers and PromQL evaluation can run their CPU-bound work on the read CPU gate
- The catalog can decode snapshot parts, postings and column statistics on the read CPU gate
- A ranged log read's chunk-run GETs against one L0 RLOG object are now bounded at
MAX_PAGE_RANGE_GETS_PER_L0_SEGMENT(4) - Changelog entries are written as fragments under
changelog.d/ sys/gcformat version 2 records the HEAD cache TTL, andravel-cli maintain sweepreadssys/gc- The catalog's decoded-part and postings caches evict the least recently used entry at their per-tenant entry cap
Security
- A distributed SQL slice fetch no longer carries the client's credential
- The Flight SQL ticket keys come from
--sql-ticket-key-file, not from the fragment key - SQL slice fetches ride the dedicated TLS fragment listener, and the public gRPC listener refuses slice tickets once one is configured
Fixed
- A server process refused access to
sys/gcat startup now fails with an error naming the fix - A store error reads as throttled only for a 429 or 503 status
- A delete against a missing S3 bucket fails instead of reporting success
- A tiered-cache miss no longer issues a duplicate GET
- A zero loop interval is refused at startup
- The operator CRD refuses a zero maintain or fold interval at admission
- An S3 delete refused for one key now reads as access denied, so the superseded sweep holds back that one chain instead of failing the pass
- A gateway starts under a small memory limit
- One refused delete no longer stops the superseded-input sweep for every chain
- SQL answers corrupt stored data as an internal error, not a retryable unavailable
- The maintain role reaps dead query-worker records under
sys/query/workers/ ravel-cli load --signal logsloads a dictionary-encoded hextrace_idorspan_idcolumnravel-cli load --signal logsand--signal metricsrefuse a negative timestamp- The fold checks a previous postings object's tenant before reusing it
- A writer whose clock lags the object store's clock refuses its flush instead of publishing into a sealed hour
- Alert sink delivery is bounded per evaluation tick, and ADR-0117's stated per-tick publish bound is corrected
- A Parquet load resolves each dictionary-encoded column once per batch instead of once per cell
- The shipped IAM templates grant the control-plane keys each role uses
- An explicit multipart upload through a scheduled store could write parts past its class's permit budget
- A store error is no longer misclassified by its retry field name or a stray 416
- The ingest router refuses a zero JWKS refresh interval or a zero round-robin idle-entry TTL at startup
- A store error is no longer classified as throttled or as a timeout because those words appear in the bucket, endpoint or object key of the request URI
Added
- The per-tenant config record can store a clustering key and a bloom scope behind an operator opt-in
- The RLOG writer can sort an object by a clustering key and limit which columns BLOOM covers
- The log ingest flush writes each RLOG object with the tenant's clustering key and bloom scope
ravel-cli load --zstd-level,ravel-cli clustering-key showandravel-cli bloom-scope show, and the clustering fields inravel-cli rlog inspectravel-cli clustering-key set,ravel-cli clustering-key clearandravel-cli bloom-scope setwrite a tenant's clustering key and bloom scoperavel-cli store verify-protectionchecks a bucket's protection configuration/metricsexports the Flight SQL slice capability rejects/metricsexports the fragment capability rejects/metricsexports the superseded-input sweep's hold counters--audit-retentionsets the query-audit retention windowravel-serverexports the unverified-read and control-plane store counters at/metrics- A read-only bucket-protection control plane in
ravel-object-store ravel-cli export --signal metricswrites a tenant's stored metric samples to a Parquet fileload --signal metricsreads back onto the same series/metricsrenders the three writer clock-lag counters, and a shipped alert pages on a refused flush- Readers accept and validate a version 2 compaction record
- Resolution honours a version 2 compaction record
- The sweep reclaims what a version 2 compaction record supersedes, and the erasure rewrite pass treats one as a link
validate_rewriterefuses a non-canonicalsuperseded_record_keyravel-cli maintain inspectprints a compaction record'ssuperseded_record_key- Advisory compaction claims report five
/metricscounters and gain three server flags ravel-cli rlog footprintattributes every stored byte of RLOG objects- The per-tenant config record reader accepts format version 3, which adds a clustering key and a bloom scope
- The maintenance loop sweeps alert history older than
--alert-retention, default 90 days ravel-cli load --signal spansloads the spans signal/metricsrendersravel_health_heartbeat_age_secondsravel-serverhas an off-by-defaultheap-profilingcargo featureravel-cli loadtakes--signal {metrics,logs,spans}and loads the metrics signal- A documentation claims registry and its gate
- An alert-signal retention sweep that keeps every identity's current-state record
- Catalog and PromQL decodes reserve their decoded output against the process memory budget before they run
- The
ravel-operatorDeployment itself now has a securityContext, a metrics/health HTTP surface, and liveness/readiness probes - A request-budget refusal now names fold lag when fold lag is what the budget was spent on
- An end-to-end proof that a stalled fold pages before it refuses a query
ravel_maintain_bytes_reclaimed_total,ravel_maintain_retention_lag_secondsandravel_maintain_units_scan_failedrender on/metrics- A full-object GET now verifies the body against the checksum the store recorded at upload
ravel-ingesthas an opt-in idle flush byte floor, off by default- The S3 adapter observes the store's own clock from response
Dateheaders ravel-server --idle-flush-byte-floorexposes that floor, and/metricsreports what it holdsObjectStoreBackend::get_pinnedreads exactly the bytes a recorded pin names, andravel-object-storegains read-only external stores per credential profile- Parquet table location grants and in-place manifest format
ravel-cli tenant parquet-grant add|remove|lsandravel-cli parquet ls|sweepravel-parquet: DataFusion's Parquet scan over a table manifest- The logs SQL scan skips segments whose declared-column statistics exclude the predicate
- The logs SQL scan does less work per cell when it builds typed attribute columns
- Parquet tables answer
POST /api/v1/sqland Flight SQL (ADR-2040 D3, D4 ravel-parquet:snapshot::snapshot_location, the file listCREATE EXTERNAL TABLEwill pin- Tenant resolution carries an optional
ddlcapability - Parquet queries reserve the bytes they fetch against the process memory budget and obey the request and byte budgets every other SQL query obeys
- A Flight SQL statement over a Parquet table reads the manifest version
GetFlightInforesolved ravel-sqlgains a typed DDL core for the three ADR-2040 D2 Parquet statement forms
The full entries are in CHANGELOG.md at this tag.
What's Changed
- docs(adr): add ADR-2040 Parquet tables queried in place by @pmoust in #2049
- feat(query): name fold lag in a request-budget refusal by @pmoust in #2059
- feat(ingest): add an opt-in idle flush byte floor by @pmoust in #2057
- docs(adrs): point ADR-0076 and ADR-0051 at the one-tick ADR-1737 hold by @pmoust in #2072
- feat(object-store): verify full reads against the store's upload checksum by @pmoust in #2069
- feat(maintain): export bytes reclaimed and retention lag on /metrics by @pmoust in #2071
- feat(server): prove the fold pages before it refuses, and wire the threshold by @pmoust in #2075
- feat(sql): authorise a slice DoGet by the slice ticket alone by @pmoust in #2070
- feat(server): expose the idle flush byte floor and document its window by @pmoust in #2078
- docs(adr): correct ADR-2040's pinning rule to what the stores do by @pmoust in #2076
- feat(catalog): read a floor's evidence and classify it in audit-versions by @pmoust in #2043
- Harden the operator's own Deployment and give it a health and metrics surface by @pmoust in #1949
- feat(object-store): observe the store clock from response Date headers by @pmoust in #2079
- docs(adr): accept ADR-1029 and record what moved since approval by @pmoust in #2084
- feat(query): charge catalog and PromQL decodes to the memory budget by @pmoust in #2085
- fix(scripts): settle epic reconcile verdicts from live state by @pmoust in #2090
- feat(server): key Flight SQL tickets from --sql-ticket-key-file by @pmoust in #2092
- feat(maintain): add the alert-signal retention sweep by @pmoust in #2087
- feat(docs): add the documentation claims registry and its gate by @pmoust in #2083
- feat(catalog): decode catalog objects on the read CPU gate by @pmoust in #2095
- fix(alerting): bound sink delivery per tick and fix the publish bound by @pmoust in #2089
- feat(cli): load the metrics signal from Parquet by @pmoust in #2096
- docs(query): describe the stamp writers and per-key attrs projection by @pmoust in #2100
- feat(server): serve SQL slices on the dedicated fragment listener by @pmoust in #2101
- perf(query): bound the GETs a ranged log read issues by @pmoust in #2077
- feat(query): run fetcher decodes and large PromQL evals on the read gate by @pmoust in #2098
- docs(fleet): say what the worker-registry key check does not defend by @pmoust in #2104
- docs(distrib): state the log/span decode obligation once by @pmoust in #2106
- feat(maintain): add advisory claims to bucket compaction by @pmoust in #2102
- fix(ingest): refuse a flush whose clock lags the store's observed clock by @pmoust in #2105
- feat: add pinned reads and Parquet table foundations by @pmoust in #2099
- feat(maintain): report rewrite-record parts as named blocked buckets by @pmoust in #2094
- fix(catalog): give decoded-cache memory back to a refused decode by @pmoust in #2109
- perf(query): hold only placed bytes in a ranged log read by @pmoust in #2111
- feat(server): render ravel_health_heartbeat_age_seconds by @pmoust in #2115
- docs(ingest): scope the drain-termination argument and deduplicate it by @pmoust in #2114
- docs(changelog): merge the second Changed heading under Unreleased by @pmoust in #2120
- feat(server): add an off-by-default heap-profiling feature by @pmoust in #2113
- docs(adr): let a compaction record supersede another for force 2 by @pmoust in #2112
- feat(cli): take compaction claims in compact-bucket and compact-tenant by @pmoust in #2119
- feat(cli): load Parquet spans with --signal spans by @pmoust in #2117
- fix(catalog): follow-ups to the ADR-1702 decode reservations by @pmoust in #2128
- feat(server): drive the alert retention sweep from the maintain tick by @pmoust in #2122
- feat(parquet): add the Parquet grant CLI and the pinned Parquet reader by @pmoust in #2131
- fix(cli): accept dictionary id columns, refuse negative timestamps by @pmoust in #2129
- feat(commit): accept and validate a version 2 compaction record by @pmoust in #2124
- test(server): pin the alert cold-start fold across a retention sweep by @pmoust in #2148
- feat(server): render the writer clock-lag counters and alert on refusal by @pmoust in #2147
- feat(server): reserve SQL fetches against the process memory budget by @pmoust in #2155
- feat(server): report compaction claim outcomes and configure claiming by @pmoust in #2157
- docs: add ADR-2135 RLOG v5 storage footprint by @pmoust in #2136
- docs: add ADR-2121 RLOG hot path and stats pruning by @pmoust in #2150
- test(server): pin the alert-retention watermark and data deletes by @pmoust in #2158
- feat(ravel-cli): add rlog footprint by @pmoust in #2161
- feat(catalog): read tenant config record version 3 fields by @pmoust in #2162
- feat(catalog): honour a version 2 compaction record in resolution by @pmoust in #2163
- feat(maintain): name buckets held below target by losing record parts by @pmoust in #2167
- fix(catalog): keep one decoded_charge and let eviction admit 0 bytes by @pmoust in #2170
- fix(cli): name the applied ts unit in the negative-timestamp refusal by @pmoust in #2165
- docs(otlp): name private helpers as plain text in public docs by @pmoust in #2177
- feat(object-store): add read-only bucket protection control plane by @pmoust in #2125
- docs(adr): say when a zero-byte decode charge is still refused by @pmoust in #2174
- feat(logseg): bump RLOG to version 5 with a sort descriptor and bloom coverage by @pmoust in #2173
- docs(fleet-task-spec): give executors a wait they can run on long commands by @pmoust in #2166
- feat(sql): skip logs segments whose stamp excludes the predicate by @pmoust in #2183
- fix(server): refuse an invalid idle flush floor in every mode by @pmoust in #2179
- test(query): pin decoder agreement on every slice frame arm by @pmoust in #2185
- fix(server): reap dead query-worker records from maintain mode by @pmoust in #2171
- feat(cli): export the metrics signal to Parquet by @pmoust in #2184
- docs(ingest): point shard condemnation comments at docs/ingest.md by @pmoust in #2187
- refactor(query): share the sample dedup order with the SQL operator by @pmoust in #2193
- perf(sql): gather single-column declared cells and validate Str once by @pmoust in #2203
- feat(maintain): reclaim what a version 2 compaction record supersedes by @pmoust in #2192
- fix(sql): report corrupt stored data as an internal error by @pmoust in #2195
- fix(maintain): name a rewrite bucket held below only by loser parts by @pmoust in #2200
- fix(bench): count adaptive-age flushes in the e2e PUT estimate by @pmoust in #2198
- refactor(cli): take the export's dedup order from ravel-query by @pmoust in #2208
- fix(object-store): report one bucket misconfiguration once by @pmoust in #2202
- feat(server): add --audit-retention and refuse a bad floor early by @pmoust in #2196
- feat(logseg): add the RLOG GCD and column-reference codecs and choose encodings by stored size by @pmoust in #2211
- feat(server): export the unverified-read and control-plane counters by @pmoust in #2207
- fix(maintain): contain a refused sweep chain to its own bucket by @pmoust in #2210
- test(maintain): pin a rewrite-superseded winner in loser naming by @pmoust in #2215
- fix(cli): name where a spans refusal's timestamps came from by @pmoust in #2212
- feat(sql): query Parquet tables over the SQL endpoint and Flight SQL by @pmoust in #2189
- test(sql): pin whole-object logs reads to the scan phase's wire bytes by @pmoust in #2219
- feat(cli): export the spans signal to Parquet by @pmoust in #2217
- fix(maintain): keep one refused delete from stopping the sweep by @pmoust in #2223
- feat(server): send a CRC64-NVME upload checksum on every S3 PUT by @pmoust in #2225
- fix(deploy): let the kind lane start: floci probe and CR quoting by @pmoust in #2229
- feat(parquet): snapshot a granted location into manifest files by @pmoust in #2232
- feat(logseg): sort RLOG objects by a declared clustering key and scope the bloom filters by @pmoust in #2231
- fix(server): let a gateway start under a small memory limit by @pmoust in #2237
- feat(cli): verify bucket protection and checksum S3 writes by @pmoust in #2233
- feat(maintain): export the superseded sweep's hold counters by @pmoust in #2242
- fix(maintain): reclaim this process's own leftover compaction claim by @pmoust in #2245
- feat(cli): carry unmapped span attributes in an attrs map column by @pmoust in #2246
- fix(sql): answer undecodable catalog objects as corrupt, not unavailable by @pmoust in #2236
- feat(server): add the ddl capability to tenant resolution by @pmoust in #2250
- test(server): wait for ownership before asserting the first full sweep by @pmoust in #2253
- feat(ingest): flush log objects with the tenant clustering key and bloom scope by @pmoust in #2252
- feat(server): gate startup on the S3 bucket's protection report by @pmoust in #2254
- fix(maintain): tick on the injected clock and refuse a zero heartbeat by @pmoust in #2255
- feat(parquet): hold Parquet queries to the memory and request budgets by @pmoust in #2249
- fix(ci): create CI buckets the protection gate accepts by @pmoust in #2259
- feat(catalog): add opted-in storage-layout setters for tenant config by @pmoust in #2262
- perf(cli): read a spans attrs map's dictionaries once per batch by @pmoust in #2263
- fix(server): refuse zero loop intervals and inject the scrub clock by @pmoust in #2268
- feat(logseg): store repeated strings on row-group dictionaries by @pmoust in #2261
- feat(cli): add load --zstd-level, storage layout show commands and inspect fields by @pmoust in #2274
- feat(maintain): keep clustered RLOG inputs clustered at zstd level 9 by @pmoust in #2276
- fix(store): map per-key delete refusals and state S3 delete semantics by @pmoust in #2270
- feat(sql): pin Parquet manifests in the Flight SQL ticket by @pmoust in #2278
- fix(sql): classify every catalog version and chain fault by one rule by @pmoust in #2277
- feat(cli): set and clear a tenant's clustering key and bloom scope by @pmoust in #2281
- fix(commit): check the format version before field shapes by @pmoust in #2275
- fix(store): carry upload integrity to KMS-routed tenant stores by @pmoust in #2267
- docs(adr-2040): amend D3 grants and drops, and bound a CREATE by @pmoust in #2282
- fix(operator): refuse a zero maintain or fold interval at admission by @pmoust in #2269
- fix(ci): retry bucket config puts and read the lifecycle back whole by @pmoust in #2285
- fix(maintain): surface failed scans and count superseded bytes freed by @pmoust in #2288
- fix(sql): answer a catalog checksum mismatch as corrupt by @pmoust in #2290
- fix(logseg): close the ADR-2135 review carry-overs and accept the ADR by @pmoust in #2296
- fix(store): read a missing S3 bucket as an error, not a missing key by @pmoust in #2298
- test(guards): wait for the watchdog's line instead of a fixed sleep by @pmoust in #2300
- test(maintain): pin the alert keyspace gate's continuation guard by @pmoust in #2302
- fix(sql): answer a Parquet checksum mismatch as corrupt by @pmoust in #2303
- fix(parquet): collapse every concurrent tiered miss onto one GET by @pmoust in #2292
- fix(parquet): harden snapshot_location before CREATE calls it by @pmoust in #2299
- fix(ci): close the bucket launcher and DR harness corners by @pmoust in #2301
- fix(sql): answer a newer Parquet manifest or grants version as 503 by @pmoust in #2309
- fix(server): classify fold lag against the maintain tier's interval by @pmoust in #2305
- test(scripts): pin the demo createbucket container filter by @pmoust in #2313
- fix(store): read a missing external container as an error by @pmoust in #2306
- feat(server): export Flight SQL slice-capability rejects on /metrics by @pmoust in #2315
- perf(cli): read every dictionary column in place on the load paths by @pmoust in #2317
- refactor(pqtable): classify newer manifest and grants versions by helper by @pmoust in #2316
- fix(cache): close the RAM-only late-caller duplicate GET by @pmoust in #2310
- feat(server): export fragment capability rejects on /metrics by @pmoust in #2321
- fix(store): read throttling from the reason phrase, not stray digits by @pmoust in #2324
- chore: add changelog fragments so merges stop conflicting by @pmoust in #2328
- refactor(cli): split the bulk loader into a module per concern by @pmoust in #2329
- fix(ingest-router): refuse zero refresh and sweep intervals at startup by @pmoust in #2333
- docs: add ADR-1133 unnamed-since marker for the pinned-query window by @pmoust in #2320
- fix(ingest-router): strip the ;ddl tenant-token suffix like the server by @pmoust in #2341
- fix(parquet): stop bloom-filter pruning and pin the decoder-panic boundary by @pmoust in #2342
- feat(sql): add the Parquet table DDL core (CREATE EXTERNAL TABLE, DROP) by @pmoust in #2319
- fix(object-store): schedule multipart parts and pin their checksums by @pmoust in #2326
- fix(cache): charge no GET to a late serve and uncount the log re-peek by @pmoust in #2336
- test(cli): make the end-of-input tail flush test deterministic by @pmoust in #2345
- fix(server): refuse a zero loop interval where each loop is spawned by @pmoust in #2344
- test(parquet): ignore the race-sampling tiered single-flight test by @pmoust in #2349
- fix(cli): pick a non-empty object for the parquet-grant probe by @pmoust in #2343
- fix(deploy): grant the control-plane keys each IAM role uses by @pmoust in #2331
- fix(store): stop reading retry_timeout and stray 416s as error classes by @pmoust in #2348
- test(cli): pin the tail to the end-of-input flush, not the ticker by @pmoust in #2356
- feat(maintain): record head_cache_ttl in sys/gc format version 2 by @pmoust in #2360
- test(formal): prove the ADR-1133 window gate in the lifecycle model by @pmoust in #2357
- test: pin the control-plane key builders the IAM tests copy by @pmoust in #2361
- fix(store): classify S3 errors from the error text, not the request URI by @pmoust in #2366
- docs(adr): pin when each clock in the ADR-1133 window is read by @pmoust in #2354
- fix(parquet): retry a single-flight follower under its own budget by @pmoust in #2352
- fix(server): name the start-order fix when sys/gc cannot be created by @pmoust in #2364
- fix(cache): label a tiered follower of a recheck serve a late serve by @pmoust in #2372
- chore(release): prepare 0.20.0 by @pmoust in #2373
- test(parquet): make the tiered concurrent-miss test deterministic by @pmoust in #2376
- chore(release): fold the late fragments into 0.20.0 by @pmoust in #2377
Full Changelog: v0.19.0...v0.20.0
Downloads
The attached binaries were extracted unmodified from the signed container
images this release published, so each one is byte-identical to the file inside
the image you can pull. They are glibc-dynamic, built against Debian 12
(the runtime image is gcr.io/distroless/cc-debian12), so they need glibc 2.36
or newer. On an older distribution, run the container image instead:
docker pull ghcr.io/nofireai/ravel-server:0.20.0One binary per platform, named <binary>-<os>-<arch>:
ravel-server-linux-amd64,ravel-server-linux-arm64ravel-cli-linux-amd64,ravel-cli-linux-arm64ravel-operator-linux-amd64,ravel-operator-linux-arm64ravel-ingest-router-linux-amd64,ravel-ingest-router-linux-arm64
Verifying the downloads
SHA256SUMS covers every attached binary and symbol archive. Check the files
you downloaded, then verify SHA256SUMS itself against the signature and
certificate beside it. The signature is keyless, bound to this release
workflow's identity, so there is no key to obtain:
sha256sum --check --ignore-missing SHA256SUMS
cosign verify-blob \
--certificate SHA256SUMS.pem \
--signature SHA256SUMS.sig \
--certificate-identity 'https://github.com/NOFireAI/ravel/.github/workflows/publish-images.yml@refs/tags/v0.20.0' \
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
SHA256SUMSDebug symbols
The shipped binaries are stripped of debug info but keep their symbol table,
so a backtrace resolves function names with nothing extra installed. Line
numbers come from the matching symbols archive. Each binary records the
basename of its symbol file, so unpacking the archive next to the binary is
enough and no symbol-file step is needed:
tar -xzf ravel-debug-symbols-linux-amd64.tar.gz
gdb ./ravel-server-linux-amd64gdb looks for ravel-server.debug in the same directory (and in
.debug/), which is the name inside the archive. Do not rename the extracted
.debug files.