Cayenne Data Accelerator Deployment Guide
Production operating guide for Spice Cayenne — a high-performance Vortex-based accelerator with file-mode storage. Covers storage layout, metastore durability, cache sizing, and observability.
Authentication & Secrets​
When Cayenne stores segments on S3 / S3 Express One Zone, authentication follows the same model as the S3 connector: the AWS credential chain with iam_role_source for explicit scoping. For local-disk Cayenne, no auth is required — the runtime process needs read/write on the storage path.
Resilience & Durability​
Storage Modes​
Cayenne supports two storage modes. In mode: file (durable, the recommended production mode), segments are written as Vortex files on local disk or S3 / S3 Express One Zone and the acceleration survives restarts — this guide is oriented to operating it. In mode: memory (ephemeral), all data lives fully in RAM with an in-memory metastore, nothing is written to disk, and the dataset reloads from its source on restart; it does not support partitioned tables and enforces a hard per-table RAM bound (no disk spill). Use mode: file when persistence across restarts is required.
Metastore Durability​
Cayenne's metastore (table list, segment index, delete vectors) is backed by SQLite (default) or Turso. With the default SQLite backend, the metastore configures:
journal_mode=WALfor crash-safe writes.busy_timeoutto handle concurrent access.synchronous=NORMALfor WAL-safe durability with acceptable write latency.
The Turso backend (opt-in, requires the turso feature flag) uses its MVCC journal mode (journal_mode='mvcc') instead of WAL.
On shutdown, Cayenne performs a WAL checkpoint (SQLite) and runs PRAGMA optimize to minimize restart overhead. Graceful shutdown via SIGTERM is important — abrupt kills leave the WAL un-checkpointed (still recoverable, but restart is slower).
Append WAL Crash Safety​
Staged appends use a crash-safe WAL. On startup Cayenne verifies each staged segment's checksum; corrupted or partially-uploaded segments are rejected and re-materialized from the source connector.
Single-Writer Concurrency​
Cayenne enforces single-writer-per-table concurrency via the metastore. Multiple Spice instances backed by the same Cayenne storage + metastore must not be configured as writers simultaneously; reader-only replicas are supported.
Capacity & Sizing​
Cache Tuning​
Two in-memory caches tune the random-read vs memory tradeoff:
| Parameter | Scope | Description |
|---|---|---|
cayenne_footer_cache_mb | runtime.params | Engine-global footer cache (Vortex file footers), shared by all Cayenne datasets. Low memory cost; enables fast plan-time decisions. |
cayenne_segment_cache_mb | runtime.params | Process-wide segment (data page) cache, shared by every Cayenne table. Set proportional to your hot working set. |
Both caches are engine-global: one segment cache serves every Cayenne table in the process, so adding a table divides this budget rather than reserving another cache of its own. Set it under runtime.params — a cayenne_segment_cache_mb under a dataset's acceleration.params (or a catalog's params) is reported at startup and otherwise ignored. When unset, the budget is derived as ~1/64 of the process's memory entitlement, clamped to 256 MB–2 GB; 0 disables segment caching.
For point-lookup-heavy workloads, size cayenne_segment_cache_mb generously — Vortex random-access reads are ~100× faster for cached segments than cold S3 reads.
Upload Concurrency​
| Parameter | Description |
|---|---|
cayenne_upload_concurrency | Parallel segment uploads during refresh / append commits. |
For S3 Express One Zone, 8–16 parallel uploads typically maximize throughput. For standard S3 across regions, higher concurrency helps hide per-request latency.
Partitioning​
Cayenne supports partition_by (single and multi-expression). Partition on the column(s) that dominate query filters; this prunes segments at plan time.
Storage Footprint​
Vortex compression typically delivers 2–4× better compression than Parquet Snappy for analytical datasets. Plan storage for 0.25–0.5× the raw data size as a starting estimate, plus headroom for a compaction pass to hold the old and new copies of the files it rewrites. The runtime warns at startup when the data or metastore volume has under 10% or under 2 GiB free.
Storage Tier​
Store data files and the metastore on local NVMe — per-I/O latency, not IOPS, is what Vortex's dependent segment reads and the metastore's fsync commits are sensitive to — and put runtime.query.temp_directory on the same fast volume. At registration Cayenne resolves the storage class behind the data directory and the metastore directory separately (the acceleration's storage_profile, auto by default) and tunes for it:
| Resolved tier | Detected from | Cayenne behavior |
|---|---|---|
local_ssd | NVMe and other non-rotational devices, including EC2 NVMe instance storage | Engine defaults; full write concurrency |
ebs | Amazon EBS, Azure managed disks, and NFS/SMB mounts | Slow-tier tuning bias (larger inline flushes, earlier memory drain, fewer write shards), O_DIRECT compaction output writer, encode-concurrency cap from the instance's EBS baseline bandwidth or the measured write throughput |
tmpfs | tmpfs/ramfs mounts | 64 MB target files; no bias |
unknown | S3 Express One Zone, rotating disks, non-Linux hosts | Slow-tier bias; 512 MB target files on S3 Express |
An 8 MiB calibration probe measures each volume's write throughput once, and on EC2 an IMDS query supplies the instance's EBS baseline bandwidth and burstable-CPU status. Set storage_profile: ebs explicitly on network block devices auto-detection cannot identify (GCP Persistent Disk and Hyperdisk, SAN, Ceph). The cayenne_data_storage_class and cayenne_metastore_storage_class gauges (0 local SSD, 1 network-attached, 2 tmpfs, 3 unknown) and cayenne_data_storage_write_mibps / cayenne_metastore_storage_write_mibps report what was detected. Network file systems are not recommended at all — the metastore is a SQLite database and SQLite locking is unreliable on NFS and SMB; keep cayenne_metadata_dir on local disk in every configuration. See Storage in the performance guide.
Metrics​
Generic acceleration metrics are available with the dataset_acceleration_ prefix. Cayenne also registers OpenTelemetry instruments for CDC ingestion, write/compaction, scan-path, segment-cache, maintenance, storage-footprint, and memory observability. Per-table series carry a table label (the accelerated dataset); metastore-wide series carry catalog (the metastore path).
CDC Apply Metrics​
| Metric | Type | Unit | Description |
|---|---|---|---|
dataset_acceleration_cdc_apply_burst_duration_ms | Histogram | ms | Duration to apply one coalesced CDC burst. |
dataset_acceleration_cdc_apply_burst_bytes | Histogram | By | Arrow in-memory bytes in one coalesced CDC apply burst. |
dataset_acceleration_cdc_apply_burst_envelopes | Histogram | envelopes | Number of source envelopes in one coalesced CDC apply burst. |
dataset_acceleration_cdc_apply_fixed_cost_ms | Histogram | ms | Duration for fixed-cost phases of CDC apply (with phase label: finalize_wait, commit_wait, etc.). |
dataset_acceleration_cdc_source_recv_wait_ms | Histogram | ms | Duration the CDC apply loop waited to receive the next batch from the source-reader channel. High values indicate the apply loop is source-bound (slot read / WAL decode can't keep up); near-zero indicates it is apply-bound. |
Scan-Path Metrics​
| Metric | Type | Unit | Description |
|---|---|---|---|
cayenne_scan_listing_table_cache_entries | Gauge | entries | Number of entries in the scan ListingTable cache. Cleared on snapshot change (compaction/sort/overwrite). |
cayenne_listing_fence_wait_duration_ms | Histogram | ms | Time spent waiting on listing-fence reads during scans. |
cayenne_listing_scan_duration_ms | Histogram | ms | Duration of listing-table scans. |
Write & Compaction Metrics​
| Metric | Type | Unit | Description |
|---|---|---|---|
cayenne_write_phase_duration_ms | Histogram | ms | Time spent in Cayenne write-path phases. Labelled by table and phase (see Write-phase labels). |
cayenne_compaction_duration_ms | Histogram | ms | Wall-clock time of Cayenne background compaction passes. The histogram's count doubles as the compaction-pass counter. |
cayenne_compaction_memory_pool_bytes | Gauge | By | Size of the dedicated compaction memory pool carved from the query memory limit (see cayenne_compaction_memory_fraction). |
cayenne_compaction_memory_exhausted_total | Counter | passes | Compaction passes that hit ResourcesExhausted on the dedicated compaction memory pool. |
cayenne_delete_main_visibility_downgrade_total | Counter | deletes | Delete passes that deferred row cleanup to protect concurrent replacement rows. Label: table. A sustained increase indicates deferred cleanup under ingest load. |
Memory Reconciliation Metrics​
The process gauges are sampled on a fixed 2-second timer; the per-table cayenne_* gauges refresh on the maintenance tick — every cayenne_compaction_background_interval_ms (10–30 s by default), or only on writes when the background compactor is disabled — so a flat per-table value can be a stale sample. Read them together: the pool gauges report what the memory accounting believes is reserved, the process_resident_* gauges what the process actually holds, and the gap against process_resident_anon_bytes is off-pool memory (encode buffers, caches, allocator retention) that no budget covers.
| Metric | Type | Unit | Description |
|---|---|---|---|
query_memory_pool_used_bytes | Gauge | By | Live bytes reserved in the query memory pool (runtime.query.memory_limit), excluding the in-memory CDC tier's mirror account so the off-pool tier is not double-counted as query usage. |
cayenne_compaction_memory_pool_used_bytes | Gauge | By | Live bytes reserved in the dedicated compaction memory pool (whose size is reported by cayenne_compaction_memory_pool_bytes). |
process_resident_memory_bytes | Gauge | By | Total resident set size of the spiced process. |
process_resident_anon_bytes | Gauge | By | Anonymous resident bytes: heap and stacks, which the kernel cannot reclaim. Take the gap against this figure rather than the total. |
process_resident_file_bytes | Gauge | By | File-backed resident bytes: mapped files and page cache the kernel evicts on demand. |
cayenne_memory_account_bytes | Gauge | By | Memory Cayenne computed for one table and registered against the DataFusion query pool, by kind (keyset, deletion_index, cold_existence, lookup_index). |
cayenne_memory_account_reserved_bytes | Gauge | By | Bytes the table's reservation actually holds on that pool. Components far above reserved means the accounting is not reaching it. |
cayenne_inline_cache_bytes | Gauge | By | Resident Arrow bytes of the table's decoded inline (level-0) view cache. |
cayenne_inline_cache_batches | Gauge | batches | Record batches held in that cache. |
cayenne_mem_tier_bytes | Gauge | By | Resident bytes of one table's in-memory CDC tier. |
cayenne_scan_file_statistics_entries | Gauge | entries | Cached scan statistics, one entry per data file. |
cayenne_lookup_index_probe_total | Counter | probes | Lookups a secondary index served, labeled table and shape (the indexed columns as the indexes entry names them). |
cayenne_lookup_index_files | Gauge | files | mode: file data files of a table that each secondary index covers, labeled table, shape and coverage (covered, uncovered). A rising uncovered count is files that lookups read in full. |
Write-phase labels​
cayenne_write_phase_duration_ms carries a table label (the accelerated dataset) and a phase label that attributes time across the write path. The phase values are:
phase | Description |
|---|---|
cdc_path_synchronous | Total latency of a synchronous CDC write, from slot-apply through publish completion. Also covers a staged inline-bearing upsert that could not be represented as a staged commit and fell back to the synchronous write path. |
cdc_path_inlined | A pipelined CDC append that completed as a small inlined write. |
cdc_path_staged | A staged (pipelined) CDC write: time to durable WAL and return. Publish/finalize is backgrounded, so this excludes publish. |
cdc_path_inmemory | An in-memory CDC append (cayenne_cdc_durability: memory, serial path): end-to-end latency from slot-apply through the RAM-tier append under the listing fence. The deferred source-slot acknowledgement is checkpointed separately. |
cdc_path_inmemory_sharded | An in-memory CDC append applied across PK-hash shards (intra-apply sharding) rather than the single serial index. |
cdc_path_inmemory_fallback | An in-memory CDC append that could not be admitted to the RAM tier (the process-global mem-tier byte budget was exhausted after waiting and spilling) and fell back to the durable write path. |
cdc_path_inmemory_sharded_fallback | A sharded in-memory apply that bailed under sustained overload before any tier mutation and re-streamed through the durable serial path. |
inmemory_stream_drain | Draining the prepared CDC stream into RAM and running deferred primary-key conflict validation — the upstream-bound produce-and-validate slice of cdc_path_inmemory. |
inmemory_spill | A synchronous RAM-tier checkpoint (spill) triggered when the per-table byte cap (cayenne_cdc_mem_tier_max_bytes) is breached, before the batch is appended. |
inmemory_budget_wait | Time spent waiting (bounded) for the process-global mem-tier byte budget to admit the batch, released by another table's checkpoint. |
vortex_write | Encoding and writing Vortex data files. |
lookup_index | Building a secondary index — sorting and compressing it on the blocking pool. Recorded for a background rebuild as well as a write. |
stage_wal_prepare | Preparing the staged-append write-ahead log. |
apply_on_conflict_deletions | Applying merge-on-read deletions for on-conflict (upsert) writes. |
publish | Total publish/finalization of a new snapshot. |
publish_lock_wait | Waiting to acquire the visibility and listing-fence locks before publishing. |
publish_seq | Durably recording the new snapshot's sequence number before it becomes visible. |
publish_cas | The compare-and-swap that makes the new protected snapshot visible. |
publish_wal_write | Writing the staging WAL during backgrounded finalize. |
publish_move_files | Moving staged files into place during finalize. |
publish_commit | Committing the new snapshot during finalize. |
The cdc_path_* phases are the mutually-exclusive terminal phase of a write — exactly one is recorded per write. The cdc_path_inmemory* phases and the inmemory_* sub-phases are emitted only under cayenne_cdc_durability: memory. The remaining phases (vortex_write, stage_wal_prepare, apply_on_conflict_deletions, lookup_index, inmemory_*, and publish*) are sub-components useful for attributing where write time is spent.
Maintenance Decision Metrics​
Maintenance passes often decline to run. Each decline is a correct decision that still leaves the table slightly larger, so these counters name which pass declined and why.
| Metric | Type | Unit | Description |
|---|---|---|---|
cayenne_compaction_outcome_total | Counter | passes | One compaction-family attempt and how it ended. Labelled by table, kind, and outcome. Every exit records exactly one outcome, so sum by (outcome) over a kind is that pass's complete decision history. |
cayenne_compaction_trigger_total | Counter | passes | Compaction passes attempted, by the threshold that asked for the pass. Labelled by table, kind, and trigger. |
cayenne_maintenance_outcome_total | Counter | passes | The same grammar for the non-compaction passes. Labelled by table, op (orphan_dv_sweep, retention, retired_dir_sweep), and outcome. |
kind uses the same vocabulary as cayenne_compaction_duration_ms, so an outcome joins to the duration of the pass that produced it:
kind | Pass |
|---|---|
full | Full current-snapshot re-encode (also folds the protected set). |
subset_current | Current-snapshot small-file rewrite (hard-links the unpicked files). |
subset | Size-tiered merge over the protected-snapshot set. |
bake | Seq-prefix bake — consolidate the clean older prefix and prune the deletion index. |
datalake | Cold-tier graduation. |
outcome falls into four classes:
- Work happened —
committed, orno_op(the pass ran its selection and found nothing to merge). - Work was paid and thrown away —
aborted_concurrent_change(the merge finished, then a concurrent append, compaction, or overwrite invalidated its inputs at commit). - The pass errored —
failed. Distinct from every decline: a decline is a decision, this is a fault, and it is the class that warrants an alert rather than a dashboard. - The pass never ran — a
declined_<reason>.
trigger names which threshold asked for a pass — small_file_count, protected_snapshot_count, protected_snapshot_age, deletion_index, deletion_index_memory_ceiling. Read against the outcome counter, it separates "the trigger never fired" from "it fired and the pass was declined".
Reclamation Metrics​
What each maintenance pass reclaimed. A footprint gauge that climbs while its reclaim counter stays flat means reclamation is running but freeing nothing.
| Metric | Type | Unit | Description |
|---|---|---|---|
cayenne_maintenance_reclaimed_files_total | Counter | files | Files physically unlinked, labelled by table and op. |
cayenne_maintenance_reclaimed_bytes_total | Counter | By | On-disk bytes of the files it unlinked, labelled by table and op. |
cayenne_maintenance_reclaimed_rows_total | Counter | rows | Tombstones a deletion-vector sweep retired from the metastore, labelled by table and op. |
cayenne_maintenance_tombstoned_rows_total | Counter | rows | Rows a pass marked deleted without freeing anything, labelled by table and op. Retention is the only producer today. |
Storage Footprint Metrics​
cayenne_storage_* is derived from the metastore manifest and split by the layer that produced it, which makes growth attributable: rising protected files are read amplification, rising delete_vector bytes a deletion set outgrowing the data it shadows.
These ride a background tick, at most every 30 s per table and every 5 min for cayenne_data_dir_*, so a flat value can be a stale sample rather than a stable table. Tables with the compactor disabled (cayenne_compaction_background_interval_ms: 0) are sampled too.
| Metric | Type | Unit | Description |
|---|---|---|---|
cayenne_storage_files | Gauge | files | Data-file paths the table holds, by tier (current, protected, cold, delete_vector; the inline tier reports bytes and rows only). |
cayenne_storage_bytes | Gauge | By | On-disk bytes the table holds, by tier. |
cayenne_storage_rows | Gauge | rows | Rows the table holds, by tier, before deletions are applied. On the delete_vector tier this is the tombstone count. |
cayenne_snapshot_manifest_rows | Gauge | rows | cayenne_snapshot_file manifest rows, split by reachable — whether the snapshot they name is still live. |
cayenne_data_dir_files | Gauge | files | Files in the table's data directory by kind (data, deletion_vector, staging, lookup_index, other), measured by walking the directory rather than reading the manifest. Local filesystem only. |
cayenne_data_dir_bytes | Gauge | By | Bytes present in the table's data directory by kind. |
cayenne_data_dir_snapshot_dirs | Gauge | directories | Snapshot directories present on disk. A count far above the live snapshot count is retired directories the sweep has not reclaimed. |
Metastore Metrics​
| Metric | Type | Unit | Description |
|---|---|---|---|
cayenne_metastore_db_bytes | Gauge | By | Current size of the metastore SQLite database file, labelled by catalog. |
cayenne_metastore_wal_bytes | Gauge | By | Current size of the metastore -wal file, labelled by catalog. |
cayenne_metastore_table_rows | Gauge | rows | Metastore rows attributable to one dataset table, labelled by table and metastore_table. |
The database file plus its -wal is the whole metadata footprint. Both carry a catalog label — the metastore path — because one metastore is shared by the pod's Cayenne tables, and a deployment can hold more than one.
Segment Cache Metrics​
The segment cache is the process-wide Vortex decompressed-segment cache (cayenne_segment_cache_mb). All five instruments are observable — sampled on every collection — and each series carries a cache label naming which cache it describes rather than a dataset: shared is the process-wide cache every Cayenne table reads through, which is what a spiced deployment reports. accesses and hits are monotonic counters and keep counting across a cache being recreated, so query them with counter operations such as rate() or increase() (hit rate over a window = rate(cayenne_segment_cache_hits[5m]) / rate(cayenne_segment_cache_accesses[5m])).
| Metric | Type | Unit | Description |
|---|---|---|---|
cayenne_segment_cache_accesses | Counter | accesses | Cumulative Vortex segment cache get() calls. |
cayenne_segment_cache_hits | Counter | hits | Cumulative Vortex segment cache hits. |
cayenne_segment_cache_entries | Gauge | entries | Live Vortex segment cache entry count. |
cayenne_segment_cache_weighted_bytes | Gauge | By | Live Vortex segment cache size in bytes. |
cayenne_segment_cache_capacity_bytes | Gauge | By | Configured Vortex segment cache capacity in bytes. |
See Component Metrics for enabling and exporting metrics.
Task History​
Cayenne refresh, append, and query operations participate in task history through the shared acceleration spans (acceleration_refresh, sql_query) plus Cayenne's own internal spans for segment uploads and metastore commits.
Known Limitations​
- Memory mode is ephemeral:
mode: memorykeeps all data in RAM with no durable storage — the dataset reloads from its source on restart and enforces a hard RAM bound (no disk spill). Usemode: filewhen persistence across restarts is required; for a non-Cayenne pure in-memory accelerator, see Arrow. Ephemeral does not mean read-only —INSERT,UPDATEandDELETEapply to the in-RAM tier as they do inmode: file, on a runtime newer than v2.3.0 (see Writes in memory mode). - Single-writer per table: Two Spice instances cannot write the same Cayenne table concurrently.
- Vortex version compatibility: Cayenne files are tied to the Vortex binary version shipped with Spice. Cross-version reads may be supported but not cross-version writes.
- Major-version upgrades: After upgrading to a new major Spice release, stop Spice and recreate persistent volumes (or otherwise clear the Cayenne acceleration storage) for datasets that reload fully from their source, so files produced by the previous version are not reused. Do not clear storage that holds data the source cannot reload: back up or migrate an accelerator-only table first, and for a write-back dataset, stop writes and wait for
dataset_acceleration_write_back_pending_keysto reach zero while write-back is still enabled. - Object-store write atomicity: Standard S3 is eventually consistent for multipart uploads. S3 Express One Zone provides strong read-after-write consistency and is recommended for latency-sensitive workloads.
Troubleshooting​
| Symptom | Likely cause | Resolution |
|---|---|---|
| Slow restart after a crash | WAL not checkpointed due to ungraceful shutdown. | Use graceful shutdown (SIGTERM); first restart will catch up the WAL automatically. |
database is locked metastore errors | Two writers sharing one metastore path. | Ensure only one writer; use distinct metastore paths per instance. |
| Metastore lock errors or corruption on a network share | cayenne_metadata_dir on NFS/SMB, where SQLite locking is unreliable. | Move cayenne_metadata_dir to local disk; keep only data files on the share if it cannot be avoided. |
Large query fails with ResourcesExhausted while the query pool shows headroom | Spill directory on a small or full volume — the OS temporary directory by default, usually the root volume. | Set runtime.query.temp_directory to a local NVMe path with free space; see Spill-to-Disk. |
cayenne_data_storage_class reports 0 on a network block volume | Auto-detection recognizes EBS and Azure disks by device identity only. | Set storage_profile: ebs on the dataset (GCP Persistent Disk, Hyperdisk, SAN, Ceph). |
| Ingest slows or compaction stalls on EBS | Volume or instance EBS bandwidth saturated, or per-I/O latency too high for the ingest rate. | Move to local NVMe or a sub-millisecond tier (io2 Block Express); provision IOPS/throughput; choose an instance that sustains its EBS bandwidth. Watch cayenne_write_phase_duration_ms and the EBS VolumeQueueLength / EBSIOBalance% metrics. |
Startup fails with Invalid Cayenne configuration: datasets use different cayenne_file_path values... | Two or more file-mode Cayenne datasets put their data on different roots without agreeing on one metastore, so a restart could open the wrong catalog. | Set the same cayenne_metadata_dir on every Cayenne dataset. See Metastore location. |
| Dataset fails to load naming a data directory that contains the metastore directory | The resolved metastore sits inside the dataset's data directory — commonly a dataset named metadata under the stock defaults. | Set cayenne_metadata_dir outside the data directory, or rename the dataset. See Metastore location. |
Acceleration appears empty after a restart, or after changing cayenne_metadata_dir | The parameter now points at a different directory than the catalog that holds the manifests. Prior Vortex files are orphaned without an error. | Point cayenne_metadata_dir back at the existing metadata location (that adopts the catalog in place). To move the catalog, stop Spice with a graceful shutdown (SIGTERM) first so the WAL is checkpointed and no writer is active, then move cayenne.db together with its -wal and -shm sidecars. Moving those files while Cayenne is writing can leave the catalog inconsistent. Set the same explicit cayenne_metadata_dir on every Cayenne dataset. See Metastore location. |
Load refused: datasets use different cayenne_file_path values without a shared cayenne_metadata_dir | A local cayenne_file_path with no cayenne_metadata_dir resolves the catalog to {cayenne_file_path}/metadata for that data root, not to the {spice_data_path}/metadata fallback. The runtime opens one shared catalog from whichever dataset initializes it first, so the other root's manifests would be unused. | Set the same cayenne_metadata_dir on every Cayenne dataset. See Metastore location. |
File count keeps growing on a primary-key table under continuous writes, and the runtime warns Protected-snapshot compaction is being starved | With position deletes, compaction must take the table's write lock, and a continuous writer holds it on every attempt. auto resolves to position deletes except on a CDC dataset or a table with cayenne_datalake_location set that has a primary key; both resolve to key deletes. | Set cayenne_deletion_mode: key. Key-delete compaction runs concurrently with writers. See Deletion Strategies. |
| Query slower than expected for cold data | Segment cache too small for the working set of every table sharing it. | Increase runtime.params.cayenne_segment_cache_mb. |
| High S3 request cost | Segment cache misses on every query. | Increase runtime.params.cayenne_segment_cache_mb; consider partition_by aligned with query filters. |
| Upload throughput does not scale with concurrency | Network or S3 Express One Zone TPS limit. | Use S3 Express One Zone in the same AZ; benchmark with upload_concurrency to find the right setting. |
| Corrupted segment refused on startup | Crash mid-upload; checksum mismatch. | Segments are re-materialized on refresh. Check storage for partial uploads and remove if orphaned. |
