From 8a4fb8b8ca1d45a820a801d6c8c77571d3414272 Mon Sep 17 00:00:00 2001 From: Gian Date: Mon, 28 Sep 2026 20:28:15 +0800 Subject: [PATCH 01/20] Stabilize Iceberg and ChunkDB CI acceptance tests --- lib/crowdb-chunk-client/tests/common/e2e_stack.rs | 8 ++++++-- tools/pixi-tasks/test-java-iceberg-e2e.sh | 3 ++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/crowdb-chunk-client/tests/common/e2e_stack.rs b/lib/crowdb-chunk-client/tests/common/e2e_stack.rs index 776bf583b..0ca4ffdfd 100644 --- a/lib/crowdb-chunk-client/tests/common/e2e_stack.rs +++ b/lib/crowdb-chunk-client/tests/common/e2e_stack.rs @@ -211,9 +211,13 @@ impl E2eStack { drop(chunkdb); let replacement = ChunkdbProcess::start_with_options(&self.cluster.mgmt_endpoints, options); replacement.wait_for_ready().await; + let kv = Arc::new(CrowdbKvClient::new(ClientConfig::new( + self.cluster.mgmt_endpoints.clone(), + ))); + let registry = ServiceRegistryClient::from_shared(kv); + replacement.wait_for_registry_ready(®istry).await; self.chunkdb = Some(replacement); self.chunkdb_options = options; - tokio::time::sleep(Duration::from_secs(3)).await; } pub async fn query_chunk(&self, location: &Location) -> Chunk { @@ -255,6 +259,6 @@ impl E2eStack { .expect("send disk read"); let (code, data) = DiskioClient::await_read_response(response).await.unwrap(); assert_eq!(code, DiskIoRetCode::Success); - data.expect("successful disk read data") + data.expect("successful disk read data").to_vec() } } diff --git a/tools/pixi-tasks/test-java-iceberg-e2e.sh b/tools/pixi-tasks/test-java-iceberg-e2e.sh index 2ade415da..d60a2ff1f 100644 --- a/tools/pixi-tasks/test-java-iceberg-e2e.sh +++ b/tools/pixi-tasks/test-java-iceberg-e2e.sh @@ -6,7 +6,8 @@ cd "${PIXI_PROJECT_ROOT:?}" source tools/pixi-tasks/prepare-iceberg.sh pixi run -e iceberg-e2e -- mvn --batch-mode --no-transfer-progress \ - -f app/crowdb-access-server/tests/common/iceberg_java/pom.xml compile exec:help + -f app/crowdb-access-server/tests/common/iceberg_java/pom.xml \ + dependency:go-offline compile exec:help pixi run -e default -- cargo test --release -p crowdb-access-server --features iceberg-e2e \ --test iceberg_namespace_sdk_test official_catalog_continues_through_empty_namespace_pages -- --ignored --exact pixi run -e default -- cargo test --release -p crowdb-access-server --features iceberg-e2e \ From 70fff15146c3712bb79e7756d32e2c98b082e13d Mon Sep 17 00:00:00 2001 From: Gian Date: Mon, 28 Sep 2026 20:34:32 +0800 Subject: [PATCH 02/20] Implement shared access streaming with bounded Chunk I/O --- Cargo.lock | 12 + app/crowdb-access-server/Cargo.toml | 3 +- .../conf/crowdb_access_server_config.toml | 61 ++ app/crowdb-access-server/src/config.rs | 263 +++++++ app/crowdb-access-server/src/http_receive.rs | 53 ++ .../src/iceberg/file_body.rs | 74 +- .../src/iceberg/file_encoding.rs | 42 +- .../src/iceberg/file_encoding/chunks.rs | 4 +- .../src/iceberg/file_encoding/content_md5.rs | 35 +- .../src/iceberg/file_http.rs | 117 ++- .../src/iceberg/file_http/multipart.rs | 63 +- .../src/iceberg/file_http/stream.rs | 196 +++++ .../src/iceberg/file_response.rs | 24 +- .../src/iceberg/file_selection.rs | 25 +- .../src/iceberg/file_upload.rs | 18 +- .../src/iceberg/gc_control.rs | 68 +- .../src/iceberg/gc_runtime.rs | 100 ++- app/crowdb-access-server/src/iceberg/http.rs | 42 +- .../src/iceberg/metrics.rs | 4 + .../src/iceberg/runtime.rs | 94 ++- .../src/iceberg/table_credentials.rs | 8 - .../src/iceberg/table_read.rs | 3 +- app/crowdb-access-server/src/lib.rs | 2 + app/crowdb-access-server/src/main.rs | 229 ++++-- app/crowdb-access-server/src/s3.rs | 37 +- app/crowdb-access-server/src/s3/dispatcher.rs | 4 +- app/crowdb-access-server/src/s3/operations.rs | 29 +- app/crowdb-access-server/src/storage.rs | 24 +- .../tests/common/iceberg_file_worker.rs | 5 +- .../tests/common/iceberg_multipart.rs | 5 +- app/crowdb-access-server/tests/config_test.rs | 54 ++ .../tests/iceberg_file_encoding_test.rs | 29 +- .../tests/iceberg_file_http_test.rs | 72 +- .../tests/iceberg_file_response_test.rs | 7 +- .../tests/iceberg_file_selection_test.rs | 14 +- .../tests/iceberg_gc_control_test.rs | 30 +- .../tests/iceberg_table_credentials_test.rs | 14 - .../tests/s3_full_stack_test.rs | 12 +- app/crowdb-cli/src/commands/bench/io.rs | 8 +- container/single-node-container/profile.toml | 10 +- .../templates/access.toml | 60 ++ doc/backlog/R167-s3-multipart-upload.md | 25 +- ...R190-access-iceberg-shared-streaming-io.md | 118 ++- doc/backlog/R92-chunkdb-in-chunk-gc.md | 13 +- doc/design/config/design-crowdb-config.md | 31 +- doc/user-manual/user-guide.md | 11 + doc/working/plan-access-streaming-io.md | 146 +++- lib/crowdb-access-iceberg/Cargo.toml | 2 + .../src/commit/provenance.rs | 1 + lib/crowdb-access-iceberg/src/file.rs | 2 +- lib/crowdb-access-iceberg/src/file/blocks.rs | 44 +- lib/crowdb-access-iceberg/src/file/content.rs | 90 +++ .../src/file/multipart.rs | 93 ++- .../src/file/multipart_recovery.rs | 13 + .../file/multipart_repository/completion.rs | 4 +- .../src/file/multipart_repository/parts.rs | 4 +- .../file/multipart_repository/publication.rs | 126 ++- lib/crowdb-access-iceberg/src/file/reader.rs | 15 +- lib/crowdb-access-iceberg/src/file/record.rs | 7 +- .../src/file/repository.rs | 42 +- lib/crowdb-access-iceberg/src/file/seal.rs | 1 + lib/crowdb-access-iceberg/src/gc.rs | 3 - lib/crowdb-access-iceberg/src/gc/candidate.rs | 31 +- lib/crowdb-access-iceberg/src/gc/discovery.rs | 33 + lib/crowdb-access-iceberg/src/gc/pins.rs | 165 ---- .../src/gc/protection.rs | 150 ---- lib/crowdb-access-iceberg/src/gc/tree.rs | 2 + .../src/gc/worker/cleanup.rs | 1 - .../src/gc/worker/inactive.rs | 1 - .../src/gc/worker/live.rs | 12 - .../src/gc/worker/sweep.rs | 49 ++ lib/crowdb-access-iceberg/src/key/codec.rs | 9 - .../src/record/envelope.rs | 12 +- lib/crowdb-access-iceberg/src/record/file.rs | 38 +- lib/crowdb-access-iceberg/src/record/gc.rs | 51 +- .../src/record/gc_key.rs | 1 - .../src/record/multipart.rs | 37 +- lib/crowdb-access-iceberg/src/table/load.rs | 52 -- .../tests/file_record_test.rs | 40 + .../tests/gc_fence_test.rs | 70 +- .../tests/gc_proof_test.rs | 260 ------- .../tests/gc_tree_test.rs | 1 + .../tests/gc_worker_test.rs | 137 +--- .../tests/multipart_completion_test.rs | 5 +- .../tests/multipart_list_test.rs | 5 +- .../tests/multipart_model_test.rs | 5 +- .../tests/multipart_observed_recovery_test.rs | 5 +- .../tests/multipart_publication_test.rs | 98 ++- .../tests/multipart_record_test.rs | 3 +- .../tests/multipart_recovery_budget_test.rs | 5 +- .../tests/multipart_recovery_test.rs | 8 +- .../tests/multipart_repository_test.rs | 3 +- lib/crowdb-access-s3/src/integrity.rs | 19 +- lib/crowdb-access-s3/src/metrics.rs | 77 +- lib/crowdb-access-s3/src/streaming.rs | 66 +- lib/crowdb-access-s3/tests/integrity_test.rs | 4 +- lib/crowdb-access-s3/tests/streaming_test.rs | 65 +- lib/crowdb-chunk-client/src/benchmark.rs | 12 +- lib/crowdb-chunk-client/src/chunk.rs | 1 + .../src/chunk/chunk_reader.rs | 729 ++++++++++++++---- .../src/chunk/read_credit.rs | 129 ++++ .../src/chunk/strip_reader.rs | 4 +- lib/crowdb-chunk-client/src/client.rs | 52 +- lib/crowdb-chunk-client/src/config.rs | 10 +- lib/crowdb-chunk-client/src/lib.rs | 2 +- lib/crowdb-chunk-client/src/metrics.rs | 146 +++- .../src/writer/shared_object.rs | 114 ++- .../src/writer/small_pipeline.rs | 39 +- .../writer/small_pipeline/stream_object.rs | 222 ++++++ .../src/writer/small_pool.rs | 19 + .../tests/benchmark_e2e.rs | 14 + .../tests/chunk_reader_e2e.rs | 55 +- .../tests/chunk_reader_test.rs | 440 ++++++++++- .../tests/large_object_writer_e2e.rs | 37 +- .../tests/small_object_writer_e2e.rs | 91 ++- .../src/production_chunk.rs | 1 + lib/crowdb-common/rust/tests/ec_test.rs | 43 ++ lib/crowdb-diskio-client/src/client.rs | 5 +- lib/crowdb-diskio-client/src/semantic.rs | 2 +- lib/crowdb-protocol/Cargo.toml | 1 + lib/crowdb-protocol/src/fbs/iceberg.fbs | 22 +- lib/crowdb-protocol/src/frame.rs | 111 ++- lib/crowdb-protocol/tests/frame_test.rs | 55 +- lib/crowdb-rpc/ffi/src/buffer.rs | 12 + lib/crowdb-rpc/ffi/src/client.rs | 15 +- lib/crowdb-rpc/ffi/src/sys.rs | 1 + lib/crowdb-rpc/ffi/tests/buffer_test.rs | 16 +- lib/crowdb-rpc/include/crowdb-rpc/buffer.h | 9 + lib/crowdb-rpc/include/crowdb-rpc/c_api.h | 1 + lib/crowdb-rpc/src/buffer.cpp | 10 + lib/crowdb-rpc/src/c_api.cpp | 14 + lib/crowdb-rpc/tests/buffer_test.cpp | 21 + 132 files changed, 4976 insertions(+), 1744 deletions(-) create mode 100644 app/crowdb-access-server/conf/crowdb_access_server_config.toml create mode 100644 app/crowdb-access-server/src/config.rs create mode 100644 app/crowdb-access-server/src/http_receive.rs create mode 100644 app/crowdb-access-server/src/iceberg/file_http/stream.rs create mode 100644 app/crowdb-access-server/tests/config_test.rs create mode 100644 container/single-node-container/templates/access.toml delete mode 100644 lib/crowdb-access-iceberg/src/gc/pins.rs delete mode 100644 lib/crowdb-access-iceberg/src/gc/protection.rs create mode 100644 lib/crowdb-chunk-client/src/chunk/read_credit.rs create mode 100644 lib/crowdb-chunk-client/src/writer/small_pipeline/stream_object.rs diff --git a/Cargo.lock b/Cargo.lock index bd65d2d3d..6e4c7ca91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -510,6 +510,15 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853" +[[package]] +name = "crc32c" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a47af21622d091a8f0fb295b88bc886ac74efcc613efc19f5d0b21de5c89e47" +dependencies = [ + "rustc_version", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -614,6 +623,7 @@ dependencies = [ "arc-swap", "async-trait", "base64", + "bincode", "bytes", "chrono", "crc32fast", @@ -627,6 +637,7 @@ dependencies = [ "flate2", "hmac", "lz4_flex", + "md-5", "serde", "serde_json", "sha2", @@ -1132,6 +1143,7 @@ version = "0.1.0-dev" dependencies = [ "bincode", "bytes", + "crc32c", "crc32fast", "flatbuffers", "fs2", diff --git a/app/crowdb-access-server/Cargo.toml b/app/crowdb-access-server/Cargo.toml index 9d38a1e8a..ccdbe5109 100644 --- a/app/crowdb-access-server/Cargo.toml +++ b/app/crowdb-access-server/Cargo.toml @@ -16,7 +16,6 @@ test-util = [] s3-e2e = ["s3"] iceberg-e2e = [] s3 = [ - "dep:crowdb-common", "dep:futures", ] @@ -27,7 +26,7 @@ crowdb-access-iceberg = { path = "../../lib/crowdb-access-iceberg" } crowdb-access-s3 = { path = "../../lib/crowdb-access-s3" } crowdb-chunk-client = { path = "../../lib/crowdb-chunk-client" } crowdb-chunk-kv-client = { path = "../../lib/crowdb-chunk-kv-client" } -crowdb-common = { path = "../../lib/crowdb-common/rust", optional = true } +crowdb-common = { path = "../../lib/crowdb-common/rust" } crowdb-kv-client = { path = "../../lib/crowdb-kv-client" } crowdb-protocol = { path = "../../lib/crowdb-protocol" } futures = { version = "0.3", optional = true } diff --git a/app/crowdb-access-server/conf/crowdb_access_server_config.toml b/app/crowdb-access-server/conf/crowdb_access_server_config.toml new file mode 100644 index 000000000..12b4d04b2 --- /dev/null +++ b/app/crowdb-access-server/conf/crowdb_access_server_config.toml @@ -0,0 +1,61 @@ +# Canonical configuration for the S3 and Iceberg access processes. +# Credentials and bearer tokens are supplied through environment variables. + +[common] +management_seeds = ["http://127.0.0.1:10000"] +diskio_connections_per_endpoint = 2 +diskio_rpc_workers = 2 + +[read] +stream_window_bytes = 1048576 +stream_slots = 3 +global_stream_bytes = 268435456 +recovery_memory_bytes = 268435456 + +[small_write] +threshold_ratio = 0.9 +conversion_enabled = true +ec_data = 8 +ec_code = 4 +memory_budget_bytes = 1342177280 +queue_capacity = 1024 +min_pipelines = 1 +max_pipelines = 32 +max_batch_bytes = 1048576 + +[s3] +listen = "127.0.0.1:8081" +tenant = "preview" +region = "us-east-1" +trusted_network = false +small_object_limit = 8388608 +list_scan_items = 1001 +list_scan_bytes = 4194304 +continuation_ttl_seconds = 900 +native_budget_bytes = 268435456 +cleanup_backlog_limit = 10000 +ec_data = 8 +ec_code = 4 +max_chunk_size = 1073741824 + +[iceberg] +listen = "127.0.0.1:8181" +native_budget_bytes = 268435456 + +[iceberg.gc] +enabled = false +interval_ms = 1000 +step_bytes = 8388608 +step_ms = 1000 +page_items = 64 +page_bytes = 49152 +concurrency = 1 +kv_bytes = 67108864 +kv_requests = 128 +chunk_bytes = 8388608 +chunk_requests = 128 +retry_base_ms = 1000 +retry_max_ms = 60000 +corruption_attempts = 3 +minimum_retention_ms = 604800000 +catalogs = [] diff --git a/app/crowdb-access-server/src/config.rs b/app/crowdb-access-server/src/config.rs new file mode 100644 index 000000000..24b0f3b2f --- /dev/null +++ b/app/crowdb-access-server/src/config.rs @@ -0,0 +1,263 @@ +// Copyright 2026-present Gian +// Licensed under the Apache License, Version 2.0. + +//! Startup configuration shared by the S3 and Iceberg access processes. + +use std::path::Path; + +use crowdb_chunk_client::{ChunkReadPolicy, SmallWritePolicy}; +use crowdb_common::config::{load_from_file, BaseConfig}; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(default)] +pub struct AccessConfig { + pub common: CommonConfig, + pub read: ReadConfig, + pub small_write: SmallWriteConfig, + pub s3: S3Config, + pub iceberg: IcebergConfig, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(default)] +pub struct CommonConfig { + pub management_seeds: Vec, + pub diskio_connections_per_endpoint: usize, + pub diskio_rpc_workers: u32, +} + +impl Default for CommonConfig { + fn default() -> Self { + Self { + management_seeds: Vec::new(), + diskio_connections_per_endpoint: 2, + diskio_rpc_workers: 2, + } + } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(default)] +pub struct ReadConfig { + pub stream_window_bytes: usize, + pub stream_slots: usize, + pub global_stream_bytes: usize, + pub recovery_memory_bytes: usize, +} + +impl Default for ReadConfig { + fn default() -> Self { + Self { + stream_window_bytes: 1024 * 1024, + stream_slots: 3, + global_stream_bytes: 256 * 1024 * 1024, + recovery_memory_bytes: 256 * 1024 * 1024, + } + } +} + +impl ReadConfig { + #[must_use] + pub fn policy(&self) -> ChunkReadPolicy { + ChunkReadPolicy { + stream_window_bytes: self.stream_window_bytes, + stream_slots: self.stream_slots, + global_stream_bytes: self.global_stream_bytes, + recovery_memory_bytes: self.recovery_memory_bytes, + ..ChunkReadPolicy::default() + } + } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(default)] +pub struct SmallWriteConfig { + pub threshold_ratio: f64, + pub conversion_enabled: bool, + pub ec_data: usize, + pub ec_code: usize, + pub memory_budget_bytes: usize, + pub queue_capacity: usize, + pub min_pipelines: usize, + pub max_pipelines: usize, + pub max_batch_bytes: usize, +} + +impl Default for SmallWriteConfig { + fn default() -> Self { + let policy = SmallWritePolicy::default(); + Self { + threshold_ratio: 0.9, + conversion_enabled: policy.conversion_enabled, + ec_data: policy.conversion_data_num, + ec_code: policy.conversion_code_num, + memory_budget_bytes: policy.memory_budget, + queue_capacity: policy.queue_capacity, + min_pipelines: policy.min_pipelines, + max_pipelines: policy.max_pipelines, + max_batch_bytes: policy.max_batch_bytes, + } + } +} + +impl SmallWriteConfig { + #[must_use] + #[allow( + clippy::cast_possible_truncation, + clippy::cast_sign_loss, + clippy::cast_precision_loss + )] + pub fn threshold_exclusive(&self) -> usize { + let data_shards = if self.conversion_enabled { self.ec_data } else { 1 }; + (self.threshold_ratio * (data_shards * 1024 * 1024) as f64).ceil() as usize + } + + #[must_use] + pub fn policy(&self) -> SmallWritePolicy { + SmallWritePolicy { + conversion_enabled: self.conversion_enabled, + conversion_data_num: self.ec_data, + conversion_code_num: self.ec_code, + memory_budget: self.memory_budget_bytes, + queue_capacity: self.queue_capacity, + min_pipelines: self.min_pipelines, + max_pipelines: self.max_pipelines, + max_batch_bytes: self.max_batch_bytes, + ..SmallWritePolicy::default() + } + } +} + +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(default)] +pub struct S3Config { + pub listen: Option, + pub tenant: Option, + pub region: Option, + pub trusted_network: Option, + pub small_object_limit: Option, + pub list_scan_items: Option, + pub list_scan_bytes: Option, + pub continuation_ttl_seconds: Option, + pub native_budget_bytes: Option, + pub cleanup_backlog_limit: Option, + pub ec_data: Option, + pub ec_code: Option, + pub max_chunk_size: Option, +} + +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(default)] +pub struct IcebergConfig { + pub listen: Option, + pub native_budget_bytes: Option, + pub gc: IcebergGcConfig, +} + +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(default)] +pub struct IcebergGcConfig { + pub enabled: Option, + pub interval_ms: Option, + pub catalogs: Option>, + pub step_bytes: Option, + pub step_ms: Option, + pub page_items: Option, + pub page_bytes: Option, + pub concurrency: Option, + pub retry_base_ms: Option, + pub retry_max_ms: Option, + pub corruption_attempts: Option, + pub minimum_retention_ms: Option, + pub kv_bytes: Option, + pub kv_requests: Option, + pub chunk_bytes: Option, + pub chunk_requests: Option, +} + +impl BaseConfig for AccessConfig { + fn validate(&self) -> Result<(), String> { + if self.common.diskio_connections_per_endpoint == 0 || self.common.diskio_rpc_workers == 0 { + return Err("common DiskIO connections and RPC workers must be nonzero".into()); + } + if self.read.stream_slots == 0 || self.read.stream_slots > 64 { + return Err("read.stream_slots must be between 1 and 64".into()); + } + if self.read.stream_window_bytes < 64 * 1024 || self.read.stream_window_bytes > 1024 * 1024 { + return Err("read.stream_window_bytes must be between 64 KiB and 1 MiB".into()); + } + if self.read.global_stream_bytes < 1024 * 1024 || self.read.global_stream_bytes > u32::MAX as usize { + return Err("read.global_stream_bytes must be at least 1 MiB and below 4 GiB".into()); + } + if self.read.recovery_memory_bytes < 1024 * 1024 + || self.read.recovery_memory_bytes > u32::MAX as usize + { + return Err("read.recovery_memory_bytes must be between 1 MiB and 4 GiB".into()); + } + self.small_write + .policy() + .validate() + .map_err(|error| format!("invalid small_write config: {error}"))?; + if !self.small_write.threshold_ratio.is_finite() + || self.small_write.threshold_ratio <= 0.0 + || self.small_write.threshold_ratio > 1.0 + || self.small_write.ec_data == 0 + || self.small_write.ec_data > 32 + { + return Err("small_write threshold ratio and EC data count are invalid".into()); + } + if self.s3.ec_data == Some(0) || self.s3.ec_code == Some(0) { + return Err("S3 EC data and code counts must be nonzero".into()); + } + if self.s3.max_chunk_size == Some(0) || self.s3.native_budget_bytes == Some(0) { + return Err("S3 chunk and native budgets must be nonzero".into()); + } + if self.iceberg.native_budget_bytes == Some(0) { + return Err("Iceberg native budget must be nonzero".into()); + } + if self.s3.small_object_limit == Some(0) + || self.s3.list_scan_items == Some(0) + || self.s3.list_scan_bytes == Some(0) + || self.s3.continuation_ttl_seconds == Some(0) + { + return Err("S3 object and listing limits must be nonzero".into()); + } + for listen in [self.s3.listen.as_deref(), self.iceberg.listen.as_deref()] + .into_iter() + .flatten() + { + listen + .parse::() + .map_err(|error| error.to_string())?; + } + Ok(()) + } +} + +/// Remove a single `--config ` pair and load the named TOML file. +/// +/// # Errors +/// Rejects duplicate or missing paths and invalid configuration files. +pub fn load_args(mut args: Vec) -> Result<(AccessConfig, Vec), String> { + let positions: Vec<_> = args + .iter() + .enumerate() + .filter(|(_, value)| *value == "--config") + .map(|(index, _)| index) + .collect(); + if positions.len() > 1 { + return Err("--config may be specified only once".into()); + } + let Some(index) = positions.first().copied() else { + return Ok((AccessConfig::default(), args)); + }; + if index + 1 >= args.len() { + return Err("--config requires a path".into()); + } + let path = args.remove(index + 1); + args.remove(index); + let config = + load_from_file(Path::new(&path)).map_err(|error| format!("failed to load {path}: {error}"))?; + Ok((config, args)) +} diff --git a/app/crowdb-access-server/src/http_receive.rs b/app/crowdb-access-server/src/http_receive.rs new file mode 100644 index 000000000..6cc38b67c --- /dev/null +++ b/app/crowdb-access-server/src/http_receive.rs @@ -0,0 +1,53 @@ +// Copyright 2026-present Gian +// Licensed under the Apache License, Version 2.0. + +//! HTTP body receive ownership shared by the S3 and Iceberg listeners. + +use std::sync::Arc; + +use crowdb_access_s3::native_buffer::{NativeBodyAllocator, NativeBodyReceiver}; +use hyper::body::{Http1BodyReceiveProvider, Incoming}; +use hyper::Request; + +#[derive(Clone)] +pub(crate) struct DeferredBodyReceiveProvider { + provider: Arc, + native: Option>, +} + +impl DeferredBodyReceiveProvider { + pub(crate) fn generic(provider: Arc) -> Self { + Self { + provider, + native: None, + } + } + + pub(crate) fn native(receiver: Arc) -> Self { + Self { + provider: receiver.clone(), + native: Some(receiver), + } + } +} + +/// Installs a provider only after the request has passed authentication and admission. +pub fn install_body_receive_provider(request: &mut Request) -> Option> { + let deferred = request.extensions_mut().remove::()?; + request + .body_mut() + .set_http1_body_receive_provider(deferred.provider); + deferred.native +} + +/// Selects a bounded native owner for one admitted upload. +pub(crate) fn install_native_body_receive_provider( + request: &mut Request, + allocator: &NativeBodyAllocator, +) -> Arc { + let receiver = Arc::new(allocator.object_receiver()); + request + .body_mut() + .set_http1_body_receive_provider(receiver.clone()); + receiver +} diff --git a/app/crowdb-access-server/src/iceberg/file_body.rs b/app/crowdb-access-server/src/iceberg/file_body.rs index d5e16329c..fb98fe282 100644 --- a/app/crowdb-access-server/src/iceberg/file_body.rs +++ b/app/crowdb-access-server/src/iceberg/file_body.rs @@ -7,6 +7,7 @@ use std::sync::{ use std::task::{Context, Poll}; use crowdb_access_iceberg::file::{ByteRange, FileBlockStore, FileIoError, FileReader, FileRecord}; +use crowdb_chunk_client::{ChunkReadStream, ReadError}; use hyper::body::{Body, Bytes, Frame, SizeHint}; #[derive(Debug, thiserror::Error)] @@ -56,10 +57,35 @@ impl FileResponseBudget { .map_err(|_| FileBodyError::Busy)?; let permit = Permit(self.active.clone()); let length = range.map_or(record.length, |range| range.end.saturating_sub(range.start)); - let reader = FileReader::new(store, record, range, 16 * 1024)?; + let stream = if let Some(locations) = record + .content + .locations(record.length) + .map_err(FileIoError::from)? + { + let interval = range.unwrap_or(ByteRange { + start: 0, + end: record.length, + }); + Some( + store + .stream_client() + .ok_or(FileIoError::Bounds)? + .read_range_stream(&locations, interval.start, interval.end) + .map_err(FileIoError::from)?, + ) + } else { + None + }; + let reader = if stream.is_none() { + Some(FileReader::new(store, record, range, 16 * 1024)?) + } else { + None + }; Ok(FileReadBody { - reader: (length > 0).then_some(reader), + reader, pending: None, + stream, + stream_pending: None, remaining: length, permit: (length > 0).then_some(permit), }) @@ -74,10 +100,13 @@ impl Drop for Permit { } type ReadFuture = Pin>, FileIoError>)> + Send>>; +type StreamFuture = Pin>)> + Send>>; pub struct FileReadBody { reader: Option, pending: Option, + stream: Option, + stream_pending: Option, remaining: u64, permit: Option, } @@ -86,6 +115,8 @@ impl FileReadBody { fn finish(&mut self) { self.reader = None; self.pending = None; + self.stream = None; + self.stream_pending = None; self.remaining = 0; self.permit = None; } @@ -103,6 +134,45 @@ impl Body for FileReadBody { if body.remaining == 0 { return Poll::Ready(None); } + if body.stream.is_some() || body.stream_pending.is_some() { + if body.stream_pending.is_none() { + let mut stream = body.stream.take().expect("stream exists"); + body.stream_pending = Some(Box::pin(async move { + let next = stream.next_chunk().await; + (stream, next) + })); + } + let (stream, next) = match body + .stream_pending + .as_mut() + .expect("stream read exists") + .as_mut() + .poll(context) + { + Poll::Pending => return Poll::Pending, + Poll::Ready(value) => value, + }; + body.stream_pending = None; + return match next { + Some(Ok(bytes)) if !bytes.is_empty() && bytes.len() as u64 <= body.remaining => { + body.remaining -= bytes.len() as u64; + if body.remaining == 0 { + body.finish(); + } else { + body.stream = Some(stream); + } + Poll::Ready(Some(Ok(Frame::data(bytes)))) + } + Some(Err(error)) => { + body.finish(); + Poll::Ready(Some(Err(FileIoError::Read(error)))) + } + _ => { + body.finish(); + Poll::Ready(Some(Err(FileIoError::Bounds))) + } + }; + } if body.pending.is_none() { let Some(mut reader) = body.reader.take() else { body.finish(); diff --git a/app/crowdb-access-server/src/iceberg/file_encoding.rs b/app/crowdb-access-server/src/iceberg/file_encoding.rs index 3b44a86a4..2a61b9692 100644 --- a/app/crowdb-access-server/src/iceberg/file_encoding.rs +++ b/app/crowdb-access-server/src/iceberg/file_encoding.rs @@ -9,6 +9,8 @@ mod checksum; mod chunks; mod content_md5; +const MAX_RECEIVE_FRAME_BYTES: usize = 1024 * 1024; + #[derive(Clone, Copy, Debug, thiserror::Error)] pub enum FileEncodingError { #[error("invalid upload framing")] @@ -28,11 +30,12 @@ pub struct FileUploadBody { buffered: Bytes, chunks: Option, checksum: Option, - content_md5: Option, + content_md5: content_md5::ContentMd5, length: Option, wire_length: Option, wire_bytes: u64, max_wire_bytes: u64, + has_integrity: bool, done: bool, failure: Option, } @@ -52,7 +55,7 @@ impl FileUploadBody { if wire_length.is_some_and(|length| length > max_wire_bytes) { return Err(FileEncodingError::Length); } - let (length, chunks, checksum) = if let Some(verifier) = verifier { + let (length, chunks, checksum, streaming_integrity) = if let Some(verifier) = verifier { if header(headers, "content-encoding")? != Some("aws-chunked") { return Err(FileEncodingError::Framing); } @@ -65,10 +68,12 @@ impl FileUploadBody { return Err(FileEncodingError::Framing); } let checksum = checksum::Checksum::from_headers(headers, verifier.has_trailer())?; + let streaming_integrity = verifier.is_signed() || checksum.is_some(); ( Some(length), Some(chunks::Chunks::new(verifier, checksum, length)), None, + streaming_integrity, ) } else { if headers.contains_key("x-amz-decoded-content-length") @@ -82,18 +87,22 @@ impl FileUploadBody { wire_length, None, checksum::Checksum::from_headers(headers, false)?, + false, ) }; + let content_md5 = content_md5::ContentMd5::from_headers(headers)?; + let has_integrity = streaming_integrity || checksum.is_some() || content_md5.is_declared(); Ok(Self { input, buffered: Bytes::new(), chunks, checksum, - content_md5: content_md5::ContentMd5::from_headers(headers)?, + content_md5, length, wire_length, wire_bytes: 0, max_wire_bytes, + has_integrity, done: false, failure: None, }) @@ -104,6 +113,21 @@ impl FileUploadBody { self.length } + #[must_use] + pub const fn has_integrity(&self) -> bool { + self.has_integrity + } + + #[must_use] + pub const fn native_handoff_eligible(&self) -> bool { + self.chunks.is_none() && self.length.is_some() + } + + #[must_use] + pub fn md5(&self) -> [u8; 16] { + self.content_md5.digest() + } + pub(super) const fn failure(&self) -> Option { self.failure } @@ -118,9 +142,7 @@ impl FileUploadBody { if let Some(checksum) = &self.checksum { checksum.verify()?; } - if let Some(checksum) = &self.content_md5 { - checksum.verify()?; - } + self.content_md5.verify()?; Ok(()) } } @@ -134,7 +156,9 @@ impl + Unpin> FileUploadBody { return Poll::Ready(Ok(Some(bytes))); } } else { - let bytes = self.buffered.split_to(self.buffered.len().min(64 * 1024)); + let bytes = self + .buffered + .split_to(self.buffered.len().min(MAX_RECEIVE_FRAME_BYTES)); if let Some(checksum) = &mut self.checksum { checksum.update(&bytes); } @@ -179,9 +203,7 @@ impl + Unpin> Body for FileUploadBody { } match std::task::ready!(body.poll_data(context)) { Ok(Some(bytes)) => { - if let Some(checksum) = &mut body.content_md5 { - checksum.update(&bytes); - } + body.content_md5.update(&bytes); Poll::Ready(Some(Ok(Frame::data(bytes)))) } Ok(None) => { diff --git a/app/crowdb-access-server/src/iceberg/file_encoding/chunks.rs b/app/crowdb-access-server/src/iceberg/file_encoding/chunks.rs index 9ea22be47..3580dddc1 100644 --- a/app/crowdb-access-server/src/iceberg/file_encoding/chunks.rs +++ b/app/crowdb-access-server/src/iceberg/file_encoding/chunks.rs @@ -2,7 +2,7 @@ use crowdb_access_s3::auth::StreamingPayloadVerifier; use hyper::body::Bytes; use sha2::{Digest, Sha256}; -use super::{checksum::Checksum, FileEncodingError}; +use super::{checksum::Checksum, FileEncodingError, MAX_RECEIVE_FRAME_BYTES}; enum State { Header, @@ -44,7 +44,7 @@ impl Chunks { if let State::Data(remaining) = self.state { let length = input .len() - .min(64 * 1024) + .min(MAX_RECEIVE_FRAME_BYTES) .min(usize::try_from(remaining).unwrap_or(usize::MAX)); let bytes = input.split_to(length); self.hash.update(&bytes); diff --git a/app/crowdb-access-server/src/iceberg/file_encoding/content_md5.rs b/app/crowdb-access-server/src/iceberg/file_encoding/content_md5.rs index ec8465249..767696863 100644 --- a/app/crowdb-access-server/src/iceberg/file_encoding/content_md5.rs +++ b/app/crowdb-access-server/src/iceberg/file_encoding/content_md5.rs @@ -5,24 +5,33 @@ use md5::{Digest, Md5}; use super::FileEncodingError; pub(super) struct ContentMd5 { - expected: [u8; 16], + expected: Option<[u8; 16]>, digest: Md5, } impl ContentMd5 { - pub(super) fn from_headers(headers: &HeaderMap) -> Result, FileEncodingError> { - let Some(value) = super::header(headers, "content-md5")? else { - return Ok(None); - }; - let expected = STANDARD - .decode(value) - .map_err(|_| FileEncodingError::Framing)? - .try_into() - .map_err(|_| FileEncodingError::Framing)?; - Ok(Some(Self { + pub(super) fn from_headers(headers: &HeaderMap) -> Result { + let expected = super::header(headers, "content-md5")? + .map(|value| { + STANDARD + .decode(value) + .map_err(|_| FileEncodingError::Framing)? + .try_into() + .map_err(|_| FileEncodingError::Framing) + }) + .transpose()?; + Ok(Self { expected, digest: Md5::new(), - })) + }) + } + + pub(super) const fn is_declared(&self) -> bool { + self.expected.is_some() + } + + pub(super) fn digest(&self) -> [u8; 16] { + self.digest.clone().finalize().into() } pub(super) fn update(&mut self, bytes: &[u8]) { @@ -30,7 +39,7 @@ impl ContentMd5 { } pub(super) fn verify(&self) -> Result<(), FileEncodingError> { - if <[u8; 16]>::from(self.digest.clone().finalize()) != self.expected { + if self.expected.is_some_and(|expected| self.digest() != expected) { return Err(FileEncodingError::Checksum); } Ok(()) diff --git a/app/crowdb-access-server/src/iceberg/file_http.rs b/app/crowdb-access-server/src/iceberg/file_http.rs index f1de58464..63f783176 100644 --- a/app/crowdb-access-server/src/iceberg/file_http.rs +++ b/app/crowdb-access-server/src/iceberg/file_http.rs @@ -10,6 +10,7 @@ use crowdb_access_iceberg::file::{ }; use crowdb_access_iceberg::key::OperationId; use crowdb_access_s3::auth::{RawAuthRequest, StreamingPayloadVerifier}; +use crowdb_access_s3::native_buffer::{NativeBodyAllocator, NativeBodyReceiver}; use hyper::body::Incoming; use hyper::http::header::{ACCEPT_RANGES, CONTENT_LENGTH, CONTENT_RANGE, ETAG, RANGE}; use hyper::{Method, Request, Response, StatusCode}; @@ -24,9 +25,9 @@ use super::file_response::{FileS3ErrorCode, MultipartResponses}; use super::file_upload::FileUploadBudget; mod multipart; +mod stream; pub(super) struct FileHttp { - pins: crowdb_access_iceberg::gc::ReaderPins, repository: FileRepository, multipart: MultipartRepository, admission: MultipartAdmission, @@ -35,16 +36,30 @@ pub(super) struct FileHttp { issuer: FileGrantIssuer, responses: FileResponseBudget, uploads: FileUploadBudget, + small_threshold_exclusive: usize, + native_allocator: Option>, region: String, limits: FileServiceLimits, } impl FileHttp { + pub(super) fn chunk_metrics( + &self, + ) -> Option<( + crowdb_chunk_client::ReadFlowMetricsSnapshot, + crowdb_chunk_client::SmallWriteMetricsSnapshot, + )> { + self.blocks + .stream_client() + .map(|client| (client.read_flow_metrics(), client.small_write_metrics())) + } + pub(super) fn new( store: Arc, blocks: Arc, secret: [u8; 32], region: String, + native_allocator: Option>, ) -> Result { if region.is_empty() || region.len() > 64 @@ -55,7 +70,6 @@ impl FileHttp { return Err(FileGrantError::Invalid); } Ok(Self { - pins: crowdb_access_iceberg::gc::ReaderPins::new(store.clone()), repository: FileRepository::new(store.clone()), multipart: MultipartRepository::new(store.clone()), admission: MultipartAdmission::new(store.clone()), @@ -64,6 +78,8 @@ impl FileHttp { issuer: FileGrantIssuer::new(secret, 15 * 60 * 1000)?, responses: FileResponseBudget::new(64).map_err(|_| FileGrantError::Invalid)?, uploads: FileUploadBudget::new(64).map_err(|_| FileGrantError::Invalid)?, + small_threshold_exclusive: (9_usize * 8 * 1024 * 1024).div_ceil(10), + native_allocator, region, limits: FileServiceLimits { max_request_bytes: 1024 * 1024 * 1024, @@ -74,6 +90,14 @@ impl FileHttp { }) } + pub(super) fn set_small_threshold(&mut self, threshold_exclusive: usize) -> Result<(), FileGrantError> { + if threshold_exclusive == 0 || threshold_exclusive > 32 * 1024 * 1024 { + return Err(FileGrantError::Invalid); + } + self.small_threshold_exclusive = threshold_exclusive; + Ok(()) + } + pub(super) async fn dispatch( self: &Arc, catalog: &CatalogRepository, @@ -93,7 +117,7 @@ impl FileHttp { async fn execute( self: &Arc, catalog: &CatalogRepository, - request: Request, + mut request: Request, request_timeout: Duration, ) -> Result, FileS3ErrorCode> { let file_request = FileRequest::parse(request.method(), request.uri()).map_err(request_error)?; @@ -117,45 +141,41 @@ impl FileHttp { grant .authorize(file_request.operation, &file_request.location, 0, 0) .map_err(|_| FileS3ErrorCode::AccessDenied)?; - let expires_ms = self - .pins - .request_expiry(root.context, now_ms) - .await - .map_err(catalog_error)?; - if matches!(file_request.operation, FileOperation::Head | FileOperation::Get) { - self.pins - .protect_file_reads( - root.context, - file_request.location.table().table, - "file-request", - expires_ms, - now_ms, - ) - .await - .map_err(catalog_error)?; - } else { - self.pins - .protect_files( - root.context, - file_request.location.table().table, - "file-request", - expires_ms, - now_ms, - ) - .await - .map_err(catalog_error)?; - } let session = self.load_session(root.context, &file_request).await?; let admission = FileTransferAdmission::authorize(&grant, &file_request, self.limits, session.as_ref(), now_ms) .map_err(admission_error)?; + let declared_receive = request + .headers() + .get("x-amz-decoded-content-length") + .or_else(|| request.headers().get(CONTENT_LENGTH)) + .and_then(|value| value.to_str().ok()) + .and_then(|value| value.parse::().ok()); + let native_receiver = if matches!( + file_request.operation, + FileOperation::Put | FileOperation::UploadPart + ) && declared_receive.map_or(true, |length| length >= 1024 * 1024) + { + self.native_allocator.as_ref().map(|allocator| { + crate::http_receive::install_native_body_receive_provider(&mut request, allocator) + }) + } else { + None + }; match file_request.operation { FileOperation::Head | FileOperation::Get => { self.read(&file_request, &request, root.context, &admission).await } FileOperation::Put => { - self.put(&file_request, request, root.context, &admission, streaming) - .await + self.put( + &file_request, + request, + root.context, + &admission, + streaming, + native_receiver.as_deref(), + ) + .await } _ => { Box::pin(self.multipart_request( @@ -165,6 +185,7 @@ impl FileHttp { &admission, now_ms, streaming, + native_receiver.as_deref(), )) .await } @@ -178,6 +199,7 @@ impl FileHttp { context: crowdb_access_iceberg::catalog::CatalogContext, admission: &FileTransferAdmission, streaming: Option, + native_receiver: Option<&NativeBodyReceiver>, ) -> Result, FileS3ErrorCode> { let digest = if streaming.is_some() { None @@ -187,11 +209,37 @@ impl FileHttp { let (parts, body) = request.into_parts(); let mut body = FileUploadBody::new(body, &parts.headers, streaming, admission.request_byte_limit()) .map_err(multipart::encoding_error)?; + if digest.is_none() && !body.has_integrity() { + return Err(FileS3ErrorCode::InvalidRequest); + } let length = body.decoded_length(); let owner = crowdb_access_iceberg::file::FileIdentity { table: file_request.location.table(), file: crowdb_access_iceberg::key::FileId::random(), }; + if let Some(client) = self.blocks.stream_client() { + let sealed = stream::upload( + client, + &self.uploads, + admission, + &mut body, + owner, + file_request.location.clone(), + length, + digest, + native_receiver, + self.small_threshold_exclusive, + ) + .await?; + let published = self + .repository + .publish(context, &sealed) + .await + .map_err(catalog_error)?; + let mut response = Response::new(IcebergBody::new(Vec::new())); + set_header(&mut response, ETAG, &etag(&published))?; + return Ok(response); + } let tree = admission .receive( &self.uploads, @@ -292,6 +340,9 @@ fn set_header( } fn etag(record: &FileRecord) -> String { + if let Some(etag) = record.content.etag() { + return format!("\"{etag}\""); + } let mut value = String::from("\""); for byte in record.digest { write!(value, "{byte:02x}").expect("string writes do not fail"); diff --git a/app/crowdb-access-server/src/iceberg/file_http/multipart.rs b/app/crowdb-access-server/src/iceberg/file_http/multipart.rs index 962003acf..792bec8e1 100644 --- a/app/crowdb-access-server/src/iceberg/file_http/multipart.rs +++ b/app/crowdb-access-server/src/iceberg/file_http/multipart.rs @@ -3,10 +3,11 @@ use std::sync::Arc; use crowdb_access_iceberg::catalog::{CatalogContext, CatalogError}; use crowdb_access_iceberg::file::{ FileIdentity, FileOperation, FileSealError, FileSealer, MultipartAdmissionLimits, MultipartPart, - MultipartPhase, MultipartSession, MultipartWorkError, + MultipartPhase, MultipartSession, MultipartStreamPart, MultipartWorkError, }; use crowdb_access_iceberg::key::{FileId, OperationId}; use crowdb_access_s3::auth::StreamingPayloadVerifier; +use crowdb_access_s3::native_buffer::NativeBodyReceiver; use http_body_util::BodyExt; use hyper::body::Incoming; use hyper::http::header::HeaderValue; @@ -45,6 +46,7 @@ impl FileHttp { .map(Some) } + #[allow(clippy::too_many_arguments)] pub(super) async fn multipart_request( self: &Arc, file: &FileRequest, @@ -53,6 +55,7 @@ impl FileHttp { admission: &FileTransferAdmission, now_ms: u64, streaming: Option, + native_receiver: Option<&NativeBodyReceiver>, ) -> Result, FileS3ErrorCode> { match (&file.multipart, file.operation) { (Some(MultipartRequest::Create), FileOperation::CreateMultipart) => { @@ -66,6 +69,7 @@ impl FileHttp { admission, now_ms, streaming, + native_receiver, ) .await } @@ -159,6 +163,7 @@ impl FileHttp { .map_err(|_| FileS3ErrorCode::InternalError) } + #[allow(clippy::too_many_arguments, clippy::too_many_lines)] async fn upload( &self, session: MultipartSession, @@ -167,6 +172,7 @@ impl FileHttp { admission: &FileTransferAdmission, now_ms: u64, streaming: Option, + native_receiver: Option<&NativeBodyReceiver>, ) -> Result, FileS3ErrorCode> { let digest = if streaming.is_some() { None @@ -176,25 +182,52 @@ impl FileHttp { let (parts, body) = request.into_parts(); let mut body = FileUploadBody::new(body, &parts.headers, streaming, admission.request_byte_limit()) .map_err(encoding_error)?; + if digest.is_none() && !body.has_integrity() { + return Err(FileS3ErrorCode::InvalidRequest); + } let length = body.decoded_length(); let owner = FileIdentity { table: session.owner.table, file: FileId::random(), }; - let tree = admission - .receive( + let (tree, stream) = if let Some(client) = self.blocks.stream_client() { + let record = super::stream::upload( + client, &self.uploads, + admission, &mut body, - self.blocks.clone(), owner, + session.location.clone(), length, digest, + native_receiver, + self.small_threshold_exclusive, ) - .await - .map_err(|error| { - body.failure() - .map_or_else(|| admission_error(error), encoding_error) - })?; + .await?; + ( + None, + Some(MultipartStreamPart { + length: record.length, + content: record.content, + }), + ) + } else { + let tree = admission + .receive( + &self.uploads, + &mut body, + self.blocks.clone(), + owner, + length, + digest, + ) + .await + .map_err(|error| { + body.failure() + .map_or_else(|| admission_error(error), encoding_error) + })?; + (Some(tree), None) + }; let before = self .multipart .part(&session, part_number) @@ -207,6 +240,7 @@ impl FileHttp { modified_ms: now_ms, owner, tree, + stream, }; if !self .multipart @@ -358,6 +392,17 @@ impl FileHttp { .completion .as_ref() .ok_or(FileS3ErrorCode::InternalError)?; + if completion.progress.next_part == 0 + && self + .multipart + .prepare_stream_publication(&session, now_ms) + .await + .map_err(catalog_error)? + .is_some() + { + session = self.current(&session).await?; + continue; + } if completion.progress.next_part < completion.selected_parts { self.multipart .advance_completion( diff --git a/app/crowdb-access-server/src/iceberg/file_http/stream.rs b/app/crowdb-access-server/src/iceberg/file_http/stream.rs new file mode 100644 index 000000000..e4459472c --- /dev/null +++ b/app/crowdb-access-server/src/iceberg/file_http/stream.rs @@ -0,0 +1,196 @@ +use std::fmt::Write; +use std::sync::Arc; + +use crowdb_access_iceberg::file::{ + ContentFormat, FileContent, FileIdentity, FileKind, FileLocation, FileRecord, +}; +use crowdb_access_s3::native_buffer::NativeBodyReceiver; +use crowdb_chunk_client::{ChunkClientConfig, ChunkIoClient, ChunkIoWriter, LargeWritePolicy}; +use crowdb_common::ec::EcScheme; +use crowdb_protocol::frame::MAX_FRAME_PAYLOAD_BYTES; +use http_body_util::BodyExt; +use hyper::body::Bytes; +use hyper::body::Incoming; +use sha2::{Digest, Sha256}; + +use super::{ + admission_error, multipart, FileS3ErrorCode, FileTransferAdmission, FileUploadBody, FileUploadBudget, +}; + +#[allow(clippy::too_many_arguments, clippy::too_many_lines)] +pub(super) async fn upload( + client: &ChunkIoClient, + budget: &FileUploadBudget, + admission: &FileTransferAdmission, + body: &mut FileUploadBody, + owner: FileIdentity, + location: FileLocation, + declared_length: Option, + expected_sha256: Option<[u8; 32]>, + native_receiver: Option<&NativeBodyReceiver>, + small_threshold_exclusive: usize, +) -> Result { + let _permit = budget.acquire().map_err(|_| FileS3ErrorCode::SlowDown)?; + let small = declared_length + .and_then(|length| usize::try_from(length).ok()) + .filter(|length| *length < small_threshold_exclusive); + let handoff = native_receiver.filter(|_| small.is_none() && body.native_handoff_eligible()); + let mut writer: Box = if let Some(length) = small { + let key = location.to_string(); + if length <= MAX_FRAME_PAYLOAD_BYTES { + let mut small = client + .prepare_small_write_for_key(length, key.as_bytes()) + .await + .map_err(|_| FileS3ErrorCode::SlowDown)?; + small.require_durable_completion(); + Box::new(small) + } else { + Box::new( + client + .prepare_shared_object_write_for_key(length, key.as_bytes()) + .await + .map_err(|_| FileS3ErrorCode::SlowDown)?, + ) + } + } else { + let mut large = client.prepare_large_write( + declared_length, + LargeWritePolicy { + ec_scheme: EcScheme::new(8, 4), + client: Arc::new(ChunkClientConfig::default()), + }, + ); + large + .wait_until_prepared() + .await + .map_err(|_| FileS3ErrorCode::SlowDown)?; + Box::new(large) + }; + if let Some(receiver) = handoff { + receiver.enable_owner_handoff(); + } + let mut length = 0u64; + let mut sha256 = expected_sha256.map(|_| Sha256::new()); + let target_buffer = usize::try_from(declared_length.unwrap_or(1024 * 1024)) + .unwrap_or(1024 * 1024) + .clamp(1, 1024 * 1024); + let mut pending = Vec::with_capacity(if handoff.is_some() { 0 } else { target_buffer }); + let transfer = async { + while let Some(frame) = body.frame().await { + let mut bytes = frame + .map_err(multipart::encoding_error)? + .into_data() + .map_err(|_| FileS3ErrorCode::InvalidRequest)?; + if bytes.is_empty() { + continue; + } + length = length + .checked_add(bytes.len() as u64) + .ok_or(FileS3ErrorCode::EntityTooLarge)?; + admission.check_bytes(length, length).map_err(admission_error)?; + if declared_length.is_some_and(|declared| length > declared) { + return Err(FileS3ErrorCode::InvalidRequest); + } + if let Some(hash) = &mut sha256 { + hash.update(&bytes); + } + while !writer.require_data() && !writer.input_complete() { + writer.wait_for_capacity().await; + } + if let Some(receiver) = handoff { + if let Some(owner) = receiver.take_ready_owner() { + writer + .on_framed_data(Box::new(owner)) + .await + .map_err(|_| FileS3ErrorCode::SlowDown)?; + } + } else { + while !bytes.is_empty() { + let count = (target_buffer - pending.len()).min(bytes.len()); + pending.extend_from_slice(&bytes.split_to(count)); + if pending.len() == target_buffer { + writer + .on_data(Bytes::from(std::mem::replace( + &mut pending, + Vec::with_capacity(target_buffer), + ))) + .await + .map_err(|_| FileS3ErrorCode::SlowDown)?; + } + } + } + } + if let Some(receiver) = handoff { + if let Some(owner) = receiver + .finish_owner_when_ready() + .await + .map_err(|_| FileS3ErrorCode::InvalidRequest)? + { + writer + .on_framed_data(Box::new(owner)) + .await + .map_err(|_| FileS3ErrorCode::SlowDown)?; + } + } + if !pending.is_empty() { + writer + .on_data(Bytes::from(pending)) + .await + .map_err(|_| FileS3ErrorCode::SlowDown)?; + } + if declared_length.is_some_and(|declared| declared != length) + || expected_sha256 + .zip(sha256) + .is_some_and(|(expected, hash)| <[u8; 32]>::from(hash.finalize()) != expected) + { + return Err(FileS3ErrorCode::InvalidRequest); + } + writer.on_finish().await.map_err(|_| FileS3ErrorCode::SlowDown) + } + .await; + let locations = match transfer { + Ok(locations) => locations, + Err(error) => { + let _ = writer.on_error().await; + return Err(error); + } + }; + let mut etag = String::with_capacity(32); + for byte in body.md5() { + write!(&mut etag, "{byte:02x}").expect("string write cannot fail"); + } + let content = + FileContent::from_locations(&locations, length, etag).map_err(|_| FileS3ErrorCode::InternalError)?; + let (kind, format) = format_for_location(&location); + let record = FileRecord { + file: owner.file, + location, + kind, + format, + length, + digest: [0; 32], + content, + hint: None, + }; + record.validate().map_err(|_| FileS3ErrorCode::InternalError)?; + Ok(record) +} + +fn format_for_location(location: &FileLocation) -> (FileKind, ContentFormat) { + let path = location.relative_key(); + let extension = std::path::Path::new(path).extension(); + let has_extension = |wanted: &str| extension.is_some_and(|value| value.eq_ignore_ascii_case(wanted)); + if has_extension("json") { + (FileKind::Metadata, ContentFormat::Json) + } else if has_extension("avro") { + (FileKind::Unbound, ContentFormat::Avro) + } else if has_extension("parquet") { + (FileKind::Unbound, ContentFormat::Parquet) + } else if has_extension("orc") { + (FileKind::Unbound, ContentFormat::Orc) + } else if has_extension("puffin") { + (FileKind::Unbound, ContentFormat::Puffin) + } else { + (FileKind::Unbound, ContentFormat::Opaque) + } +} diff --git a/app/crowdb-access-server/src/iceberg/file_response.rs b/app/crowdb-access-server/src/iceberg/file_response.rs index 9fbf3bed3..b4d858990 100644 --- a/app/crowdb-access-server/src/iceberg/file_response.rs +++ b/app/crowdb-access-server/src/iceberg/file_response.rs @@ -110,7 +110,7 @@ impl MultipartResponses { let mut response = Response::new(Vec::new()); response.headers_mut().insert( ETAG, - HeaderValue::from_str(&etag(part.tree.digest)).map_err(|_| FileResponseError::Invalid)?, + HeaderValue::from_str(&format!("\"{}\"", part.etag())).map_err(|_| FileResponseError::Invalid)?, ); Ok(response) } @@ -169,8 +169,8 @@ impl MultipartResponses { "LastModified", ×tamp.to_rfc3339_opts(SecondsFormat::Millis, true), ); - element(&mut body, "ETag", &etag(part.tree.digest)); - element(&mut body, "Size", &part.tree.length.to_string()); + element(&mut body, "ETag", &format!("\"{}\"", part.etag())); + element(&mut body, "Size", &part.length().to_string()); body.push_str(""); } end(&mut body, "ListPartsResult"); @@ -191,20 +191,24 @@ impl MultipartResponses { || session.location != record.location || response_url.len() > 2048 || !(response_url.starts_with("https://") || response_url.starts_with("http://")) - || session - .completion - .as_ref() - .and_then(|completion| completion.candidate.as_ref()) - .map_or(true, |candidate| { + || session.completion.as_ref().map_or(true, |completion| { + if let Some(candidate) = &completion.candidate { candidate.length != record.length || candidate.digest != record.digest - }) + } else { + completion.publication.is_none() || completion.progress.completed_bytes != record.length + } + }) { return Err(FileResponseError::Invalid); } let mut body = start("CompleteMultipartUploadResult"); element(&mut body, "Location", response_url); location_fields(&mut body, session); - element(&mut body, "ETag", &etag(record.digest)); + let tag = record + .content + .etag() + .map_or_else(|| etag(record.digest), |value| format!("\"{value}\"")); + element(&mut body, "ETag", &tag); end(&mut body, "CompleteMultipartUploadResult"); Ok(xml(StatusCode::OK, body)) } diff --git a/app/crowdb-access-server/src/iceberg/file_selection.rs b/app/crowdb-access-server/src/iceberg/file_selection.rs index 88a1dc5b2..51e46e778 100644 --- a/app/crowdb-access-server/src/iceberg/file_selection.rs +++ b/app/crowdb-access-server/src/iceberg/file_selection.rs @@ -7,10 +7,10 @@ const MAX_COMPLETE_XML_BYTES: usize = 2 * 1024 * 1024; const MAX_COMPLETE_PARTS: usize = 10_000; const S3_NAMESPACE: &[u8] = b"http://s3.amazonaws.com/doc/2006-03-01/"; -#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct CompletePart { pub number: u16, - pub digest: [u8; 32], + pub etag: String, } #[derive(Clone, Debug, Eq, PartialEq)] @@ -96,7 +96,7 @@ impl CompleteSelection { } (State::Part, b"Part") => { let number = number.take().ok_or(CompleteRequestError)?; - let digest = digest.take().ok_or(CompleteRequestError)?; + let etag = digest.take().ok_or(CompleteRequestError)?; if number == 0 || number > 10_000 || parts @@ -105,7 +105,7 @@ impl CompleteSelection { { return Err(CompleteRequestError); } - parts.push(CompletePart { number, digest }); + parts.push(CompletePart { number, etag }); State::Root } (State::Root, b"CompleteMultipartUpload") if !parts.is_empty() => State::Done, @@ -140,16 +140,16 @@ impl CompleteSelection { .part(session, requested.number) .await? .ok_or(CompleteResolveError::InvalidPart)?; - if part.tree.digest != requested.digest { + if part.etag() != requested.etag { return Err(CompleteResolveError::InvalidPart); } - if index + 1 < self.parts.len() && part.tree.length < 5 * 1024 * 1024 { + if index + 1 < self.parts.len() && part.length() < 5 * 1024 * 1024 { return Err(CompleteResolveError::EntityTooSmall); } selected.push(SelectedPart { number: part.number, revision: part.revision, - digest: part.tree.digest, + digest: part.selection_digest(), }); } MultipartSelection::new(selected).map_err(|_| CompleteResolveError::InvalidPart) @@ -166,19 +166,18 @@ enum State { Done, } -fn parse_etag(bytes: &[u8]) -> Result<[u8; 32], CompleteRequestError> { +fn parse_etag(bytes: &[u8]) -> Result { let hex = bytes .strip_prefix(b"\"") .and_then(|bytes| bytes.strip_suffix(b"\"")) .ok_or(CompleteRequestError)?; - if hex.len() != 64 { + if hex.len() != 32 && hex.len() != 64 { return Err(CompleteRequestError); } - let mut digest = [0; 32]; - for (target, pair) in digest.iter_mut().zip(hex.chunks_exact(2)) { - *target = (hex_digit(pair[0])? << 4) | hex_digit(pair[1])?; + for pair in hex.chunks_exact(2) { + let _ = (hex_digit(pair[0])? << 4) | hex_digit(pair[1])?; } - Ok(digest) + String::from_utf8(hex.to_vec()).map_err(|_| CompleteRequestError) } fn append_etag(etag: &mut Vec, bytes: &[u8]) -> Result<(), CompleteRequestError> { diff --git a/app/crowdb-access-server/src/iceberg/file_upload.rs b/app/crowdb-access-server/src/iceberg/file_upload.rs index 6a7992505..4888e6159 100644 --- a/app/crowdb-access-server/src/iceberg/file_upload.rs +++ b/app/crowdb-access-server/src/iceberg/file_upload.rs @@ -69,6 +69,15 @@ impl FileUploadBudget { self.active.load(Ordering::Acquire) } + pub(crate) fn acquire(&self) -> Result { + self.active + .fetch_update(Ordering::AcqRel, Ordering::Acquire, |active| { + (active < self.limit).then_some(active + 1) + }) + .map_err(|_| FileUploadError::Busy)?; + Ok(Permit(self.active.clone())) + } + /// Stages bytes only; the caller must authorize intersected limits and seal before publication. /// Writes each received frame in bounded slices and never polls ahead of a pending storage write. /// # Errors @@ -82,12 +91,7 @@ impl FileUploadBudget { constraints: FileUploadConstraints, ) -> Result { constraints.validate()?; - self.active - .fetch_update(Ordering::AcqRel, Ordering::Acquire, |active| { - (active < self.limit).then_some(active + 1) - }) - .map_err(|_| FileUploadError::Busy)?; - let _permit = Permit(self.active.clone()); + let _permit = self.acquire()?; let mut writer = FileTreeWriter::new(store, owner, NATIVE_FILE_BLOCK_BYTES)?; while let Some(frame) = body.frame().await { let bytes = frame @@ -125,7 +129,7 @@ impl FileUploadBudget { } } -struct Permit(Arc); +pub(crate) struct Permit(Arc); impl Drop for Permit { fn drop(&mut self) { diff --git a/app/crowdb-access-server/src/iceberg/gc_control.rs b/app/crowdb-access-server/src/iceberg/gc_control.rs index 3b8aeac3a..3a784ebc5 100644 --- a/app/crowdb-access-server/src/iceberg/gc_control.rs +++ b/app/crowdb-access-server/src/iceberg/gc_control.rs @@ -4,7 +4,7 @@ use crowdb_access_iceberg::{ catalog::{ CatalogContext, CatalogRepository, CatalogStore, ManagementPrivilege, RootState, RoutedCatalogStore, }, - gc::{GcLimits, GcPin, GcRepository, GcTask, ReaderPins}, + gc::{GcLimits, GcRepository, GcTask}, key::{CatalogId, OperationId, TableId}, operation::{ManagementAction, ManagementPhase}, record::StorageRecord, @@ -13,6 +13,7 @@ use crowdb_access_iceberg::{ }; use super::gc_runtime::GcRuntimeConfig; +use crate::config::IcebergGcConfig; type BoxError = Box; @@ -21,6 +22,7 @@ pub(super) async fn manage( store: Arc, authentication: &BearerAuthenticator, arguments: &[String], + gc_settings: &IcebergGcConfig, ) -> Result<(), BoxError> { let token = std::env::var("CROWDB_ICEBERG_TOKEN")?; let principal = authentication @@ -29,10 +31,9 @@ pub(super) async fn manage( if principal.management == ManagementPrivilege::None { return Err("management privilege is required".into()); } - let config = GcRuntimeConfig::from_env()?; + let config = GcRuntimeConfig::from_config(gc_settings)?; let limits = config.limits; let repository = GcRepository::new(store.clone()); - let pins = ReaderPins::new(store.clone()); match arguments.iter().map(String::as_str).collect::>().as_slice() { ["limits"] => { println!("{}", serde_json::json!({ @@ -74,13 +75,7 @@ pub(super) async fn manage( }; show(&task); } - ["pin", identity, table] => { - pin_table(catalog, store.as_ref(), &pins, principal.name, identity, table).await?; - } - ["unpin", catalog_id, table, identity] => { - unpin_table(&pins, principal.name, catalog_id, table, identity).await?; - } - _ => return Err("usage: crowdb-iceberg gc limits | start-table UUID TABLE_ID | start-retired UUID CATALOG_ID EPOCH | inspect|pause|resume|retry CATALOG_ID TASK_ID | pin UUID TABLE_ID | unpin CATALOG_ID TABLE_ID PIN_ID".into()), + _ => return Err("usage: crowdb-iceberg gc limits | start-table UUID TABLE_ID | start-retired UUID CATALOG_ID EPOCH | inspect|pause|resume|retry CATALOG_ID TASK_ID".into()), } Ok(()) } @@ -154,59 +149,6 @@ async fn start_retired( Ok(()) } -async fn pin_table( - catalog: &CatalogRepository, - store: &RoutedCatalogStore, - pins: &ReaderPins, - principal: &str, - identity: &str, - table: &str, -) -> Result<(), BoxError> { - let (root, _) = catalog.status().await?; - if root.state != RootState::Ready { - return Err("catalog is not ready".into()); - } - let table: TableId = table.parse()?; - let pin = GcPin { - context: root.context, - identity: identity.parse()?, - head: load_head(store, root.context.catalog, table).await?, - principal: principal.to_owned(), - expires_ms: 0, - released: false, - operator: true, - protects_uploads: true, - }; - pins.acquire(&pin).await?; - println!( - "{}", - serde_json::json!({"pin": pin.identity.to_string(), "table": table.to_string()}) - ); - Ok(()) -} - -async fn unpin_table( - pins: &ReaderPins, - principal: &str, - catalog_id: &str, - table: &str, - identity: &str, -) -> Result<(), BoxError> { - let catalog_id: CatalogId = catalog_id.parse()?; - let table: TableId = table.parse()?; - let identity: OperationId = identity.parse()?; - let pin = pins - .get(catalog_id, table, identity) - .await? - .ok_or("GC pin is missing")?; - if !pin.operator || pin.principal != principal { - return Err("operator pin is owned by another principal".into()); - } - pins.release(&pin).await?; - println!("{}", serde_json::json!({"released": identity.to_string()})); - Ok(()) -} - async fn load_head( store: &RoutedCatalogStore, catalog: CatalogId, diff --git a/app/crowdb-access-server/src/iceberg/gc_runtime.rs b/app/crowdb-access-server/src/iceberg/gc_runtime.rs index abafaa9ea..e84dc73bd 100644 --- a/app/crowdb-access-server/src/iceberg/gc_runtime.rs +++ b/app/crowdb-access-server/src/iceberg/gc_runtime.rs @@ -1,5 +1,6 @@ use std::{sync::Arc, time::Duration}; +use crate::config::IcebergGcConfig; use crowdb_access_iceberg::{ catalog::{CatalogContext, CatalogRepository, RootState, RoutedCatalogStore}, file::FileBlockStore, @@ -32,21 +33,35 @@ pub(super) struct GcRuntimeConfig { } impl GcRuntimeConfig { - pub fn from_env() -> Result> { + pub fn from_config(file: &IcebergGcConfig) -> Result> { let mut limits = GcLimits::default(); - limits.step_bytes = setting("CROWDB_ICEBERG_GC_STEP_BYTES", limits.step_bytes)?; - limits.step_ms = setting("CROWDB_ICEBERG_GC_STEP_MS", limits.step_ms)?; - limits.page_items = setting("CROWDB_ICEBERG_GC_PAGE_ITEMS", limits.page_items)?; - limits.page_bytes = setting("CROWDB_ICEBERG_GC_PAGE_BYTES", limits.page_bytes)?; - limits.concurrency = setting("CROWDB_ICEBERG_GC_CONCURRENCY", limits.concurrency)?; - limits.retry_base_ms = setting("CROWDB_ICEBERG_GC_RETRY_BASE_MS", limits.retry_base_ms)?; - limits.retry_max_ms = setting("CROWDB_ICEBERG_GC_RETRY_MAX_MS", limits.retry_max_ms)?; - limits.corruption_attempts = setting( + limits.step_bytes = setting_or("CROWDB_ICEBERG_GC_STEP_BYTES", file.step_bytes, limits.step_bytes)?; + limits.step_ms = setting_or("CROWDB_ICEBERG_GC_STEP_MS", file.step_ms, limits.step_ms)?; + limits.page_items = setting_or("CROWDB_ICEBERG_GC_PAGE_ITEMS", file.page_items, limits.page_items)?; + limits.page_bytes = setting_or("CROWDB_ICEBERG_GC_PAGE_BYTES", file.page_bytes, limits.page_bytes)?; + limits.concurrency = setting_or( + "CROWDB_ICEBERG_GC_CONCURRENCY", + file.concurrency, + limits.concurrency, + )?; + limits.retry_base_ms = setting_or( + "CROWDB_ICEBERG_GC_RETRY_BASE_MS", + file.retry_base_ms, + limits.retry_base_ms, + )?; + limits.retry_max_ms = setting_or( + "CROWDB_ICEBERG_GC_RETRY_MAX_MS", + file.retry_max_ms, + limits.retry_max_ms, + )?; + limits.corruption_attempts = setting_or( "CROWDB_ICEBERG_GC_CORRUPTION_ATTEMPTS", + file.corruption_attempts, limits.corruption_attempts, )?; - limits.minimum_retention_ms = setting( + limits.minimum_retention_ms = setting_or( "CROWDB_ICEBERG_GC_MINIMUM_RETENTION_MS", + file.minimum_retention_ms, limits.minimum_retention_ms, )?; limits.validate()?; @@ -56,35 +71,47 @@ impl GcRuntimeConfig { if limits.minimum_retention_ms < GcLimits::default().minimum_retention_ms { return Err("GC retention must be at least seven days".into()); } - let interval_ms = setting("CROWDB_ICEBERG_GC_INTERVAL_MS", 1000_u64)?; + let interval_ms = setting_or("CROWDB_ICEBERG_GC_INTERVAL_MS", file.interval_ms, 1000_u64)?; if !(100..=60_000).contains(&interval_ms) { return Err("GC interval must be between 100 and 60000 milliseconds".into()); } - let catalogs = match std::env::var("CROWDB_ICEBERG_GC_CATALOGS") { - Ok(value) => value, - Err(std::env::VarError::NotPresent) => String::new(), - Err(error) => return Err(error.into()), - } - .split(',') - .map(str::trim) - .filter(|value| !value.is_empty()) - .map(str::parse) - .collect::, _>>()?; + let names = match &file.catalogs { + Some(names) => names.clone(), + None => match std::env::var("CROWDB_ICEBERG_GC_CATALOGS") { + Ok(value) => value.split(',').map(str::to_owned).collect(), + Err(std::env::VarError::NotPresent) => Vec::new(), + Err(error) => return Err(error.into()), + }, + }; + let catalogs = names + .iter() + .map(String::as_str) + .map(str::trim) + .filter(|value| !value.is_empty()) + .map(str::parse) + .collect::, _>>()?; if catalogs.len() > 64 { return Err("too many GC catalog scopes".into()); } let mut catalogs: Vec = catalogs; catalogs.sort_unstable(); catalogs.dedup(); - let enabled = match std::env::var("CROWDB_ICEBERG_GC_ENABLED").as_deref() { - Ok("1") => true, - Ok("0") | Err(std::env::VarError::NotPresent) => false, - _ => return Err("CROWDB_ICEBERG_GC_ENABLED must be 0 or 1".into()), + let enabled = match file.enabled { + Some(enabled) => enabled, + None => match std::env::var("CROWDB_ICEBERG_GC_ENABLED").as_deref() { + Ok("1") => true, + Ok("0") | Err(std::env::VarError::NotPresent) => false, + _ => return Err("CROWDB_ICEBERG_GC_ENABLED must be 0 or 1".into()), + }, }; - let kv_bytes = setting("CROWDB_ICEBERG_GC_KV_BYTES", 64 * 1024 * 1024_u64)?; - let kv_requests = setting("CROWDB_ICEBERG_GC_KV_REQUESTS", 128_u32)?; - let chunk_bytes = setting("CROWDB_ICEBERG_GC_CHUNK_BYTES", 8 * 1024 * 1024_u64)?; - let chunk_requests = setting("CROWDB_ICEBERG_GC_CHUNK_REQUESTS", 128_u32)?; + let kv_bytes = setting_or("CROWDB_ICEBERG_GC_KV_BYTES", file.kv_bytes, 64 * 1024 * 1024_u64)?; + let kv_requests = setting_or("CROWDB_ICEBERG_GC_KV_REQUESTS", file.kv_requests, 128_u32)?; + let chunk_bytes = setting_or( + "CROWDB_ICEBERG_GC_CHUNK_BYTES", + file.chunk_bytes, + 8 * 1024 * 1024_u64, + )?; + let chunk_requests = setting_or("CROWDB_ICEBERG_GC_CHUNK_REQUESTS", file.chunk_requests, 128_u32)?; if !(4 * 1024 * 1024..=256 * 1024 * 1024).contains(&kv_bytes) || !(8..=4096).contains(&kv_requests) || !(256 * 1024..=64 * 1024 * 1024).contains(&chunk_bytes) @@ -117,6 +144,21 @@ where } } +fn setting_or( + name: &str, + configured: Option, + default: T, +) -> Result> +where + T: std::str::FromStr, + T::Err: std::error::Error + Send + Sync + 'static, +{ + match configured { + Some(value) => Ok(value), + None => setting(name, default), + } +} + pub(super) async fn run( catalog: Arc, store: Arc, diff --git a/app/crowdb-access-server/src/iceberg/http.rs b/app/crowdb-access-server/src/iceberg/http.rs index a823f1631..2865880c3 100644 --- a/app/crowdb-access-server/src/iceberg/http.rs +++ b/app/crowdb-access-server/src/iceberg/http.rs @@ -13,6 +13,7 @@ use crowdb_access_iceberg::catalog::{ Capabilities, CatalogError, CatalogLifecycle, CatalogRepository, ManagementPrivilege, RootState, }; use crowdb_access_iceberg::wire::{BearerAuthenticator, CatalogConfig, IcebergErrorResponse}; +use crowdb_access_s3::native_buffer::NativeBodyAllocator; use hyper::body::Incoming; use hyper::server::conn::http1; use hyper::service::service_fn; @@ -55,7 +56,12 @@ impl IcebergHttpService { #[must_use] pub fn metrics_snapshot(&self) -> IcebergMetricsSnapshot { - self.metrics.snapshot() + let mut snapshot = self.metrics.snapshot(); + if let Some((read, write)) = self.files.as_ref().and_then(|files| files.chunk_metrics()) { + snapshot.chunk_read = Some(read); + snapshot.chunk_small_write = Some(write); + } + snapshot } /// # Errors @@ -65,16 +71,48 @@ impl IcebergHttpService { store: Arc, blocks: Arc, region: String, + ) -> Result { + self = self.with_fileio_native(store, blocks, region, None)?; + Ok(self) + } + + /// Installs native receive owners for authenticated file uploads. + /// # Errors + /// Rejects invalid native file listener limits or signing configuration. + pub fn with_fileio_native( + mut self, + store: Arc, + blocks: Arc, + region: String, + native_allocator: Option>, ) -> Result { self.files = Some(Arc::new(FileHttp::new( store, blocks, self.authentication.namespace_token_key(), region, + native_allocator, )?)); Ok(self) } + /// Applies the configured shared-chunk routing threshold to native file uploads. + /// # Errors + /// Rejects an unavailable file service or an invalid threshold. + pub fn with_small_object_threshold( + mut self, + threshold_exclusive: usize, + ) -> Result { + Arc::get_mut( + self.files + .as_mut() + .ok_or(crowdb_access_iceberg::file::FileGrantError::Invalid)?, + ) + .ok_or(crowdb_access_iceberg::file::FileGrantError::Invalid)? + .set_small_threshold(threshold_exclusive)?; + Ok(self) + } + /// # Errors /// Rejects invalid namespace token signing configuration. pub fn with_namespaces( @@ -217,7 +255,7 @@ impl IcebergHttpService { } return Ok(response( 200, - serde_json::to_vec(&self.metrics.snapshot()).map_err(|_| service_unavailable())?, + serde_json::to_vec(&self.metrics_snapshot()).map_err(|_| service_unavailable())?, )); } let (root, authority) = self diff --git a/app/crowdb-access-server/src/iceberg/metrics.rs b/app/crowdb-access-server/src/iceberg/metrics.rs index 02722c428..73c51d4f1 100644 --- a/app/crowdb-access-server/src/iceberg/metrics.rs +++ b/app/crowdb-access-server/src/iceberg/metrics.rs @@ -49,6 +49,8 @@ pub struct IcebergMetricsSnapshot { pub retry_resume: u64, pub retry_replay: u64, pub selected_versions: [u64; 3], + pub chunk_read: Option, + pub chunk_small_write: Option, } struct Counters { @@ -105,6 +107,8 @@ impl IcebergMetrics { retry_resume: self.retry[1].load(Ordering::Relaxed), retry_replay: self.retry[2].load(Ordering::Relaxed), selected_versions: array::from_fn(|index| self.selected_versions[index].load(Ordering::Relaxed)), + chunk_read: None, + chunk_small_write: None, } } } diff --git a/app/crowdb-access-server/src/iceberg/runtime.rs b/app/crowdb-access-server/src/iceberg/runtime.rs index cc2c78e4d..072c31653 100644 --- a/app/crowdb-access-server/src/iceberg/runtime.rs +++ b/app/crowdb-access-server/src/iceberg/runtime.rs @@ -7,7 +7,8 @@ use crowdb_access_iceberg::catalog::{ }; use crowdb_access_iceberg::operation::{ManagementAction, ManagementRequest, RequestIdentity}; use crowdb_access_iceberg::wire::BearerAuthenticator; -use crowdb_chunk_client::{ChunkIoClient, ChunkIoClientConfig, SmallWritePolicy}; +use crowdb_access_s3::native_buffer::NativeBodyAllocator; +use crowdb_chunk_client::{ChunkIoClient, ChunkIoClientConfig, ChunkReadPolicy, SmallWritePolicy}; use crowdb_chunk_kv_client::{ ChunkKvClient, ChunkKvRpcTransport, ClientConfig, Group0ChunkKvRangeCatalogSource, }; @@ -15,6 +16,7 @@ use crowdb_kv_client::{ClientConfig as KvConfig, CrowdbKvClient}; use tokio::net::TcpListener; use super::{serve, IcebergHttpService}; +use crate::config::{load_args, AccessConfig}; type BoxError = Box; @@ -28,16 +30,26 @@ impl IcebergRuntimeConfig { /// # Errors /// Rejects missing/invalid credentials, seeds or listener configuration. pub fn from_env() -> Result { - let seeds = std::env::var("CROWDB_MANAGEMENT_SEEDS")?; - if seeds.len() > 8192 { - return Err("management seed configuration is oversized".into()); - } - let management_seeds: Vec<_> = seeds - .split(',') - .map(str::trim) - .filter(|value| !value.is_empty()) - .map(str::to_owned) - .collect(); + Self::from_config(&AccessConfig::default()) + } + + /// # Errors + /// Rejects missing/invalid credentials, seeds or listener configuration. + pub fn from_config(access: &AccessConfig) -> Result { + let management_seeds: Vec<_> = if access.common.management_seeds.is_empty() { + let seeds = std::env::var("CROWDB_MANAGEMENT_SEEDS")?; + if seeds.len() > 8192 { + return Err("management seed configuration is oversized".into()); + } + seeds + .split(',') + .map(str::trim) + .filter(|value| !value.is_empty()) + .map(str::to_owned) + .collect() + } else { + access.common.management_seeds.clone() + }; if management_seeds.is_empty() || management_seeds.len() > 16 { return Err("one to sixteen management seeds are required".into()); } @@ -49,7 +61,12 @@ impl IcebergRuntimeConfig { &std::env::var("CROWDB_ICEBERG_CLEAR_TOKEN")?, ) .map_err(|error| format!("invalid Iceberg bearer credential configuration: {error}"))?; - let listen = std::env::var("CROWDB_ICEBERG_LISTEN").unwrap_or_else(|_| "127.0.0.1:8181".into()); + let listen = access + .iceberg + .listen + .clone() + .or_else(|| std::env::var("CROWDB_ICEBERG_LISTEN").ok()) + .unwrap_or_else(|| "127.0.0.1:8181".into()); let _: std::net::SocketAddr = listen.parse()?; Ok(Self { listen, @@ -62,12 +79,19 @@ impl IcebergRuntimeConfig { /// # Errors /// Returns configuration, authentication, storage, management or listener failures. pub async fn run() -> Result<(), BoxError> { - let config = IcebergRuntimeConfig::from_env()?; - let arguments: Vec<_> = std::env::args().skip(1).collect(); + let (access_config, arguments) = load_args(std::env::args().skip(1).collect())?; + let config = IcebergRuntimeConfig::from_config(&access_config)?; if arguments.len() > 7 { return Err("too many Iceberg command arguments".into()); } - let (repository, store, chunks) = connect(config.management_seeds.clone()).await?; + let (repository, store, chunks) = connect( + config.management_seeds.clone(), + access_config.read.policy(), + access_config.small_write.policy(), + access_config.common.diskio_connections_per_endpoint, + access_config.common.diskio_rpc_workers, + ) + .await?; let result = if arguments.is_empty() || arguments == ["serve"] { Box::pin(start_listener( &config.listen, @@ -76,6 +100,7 @@ pub async fn run() -> Result<(), BoxError> { config.authentication, chunks.clone(), config.management_seeds, + access_config, )) .await } else if arguments.first().is_some_and(|argument| argument == "gc") { @@ -84,6 +109,7 @@ pub async fn run() -> Result<(), BoxError> { store.clone(), &config.authentication, &arguments[1..], + &access_config.iceberg.gc, ) .await } else { @@ -97,6 +123,10 @@ pub async fn run() -> Result<(), BoxError> { async fn connect( seeds: Vec, + read_policy: ChunkReadPolicy, + small_write: SmallWritePolicy, + diskio_connections_per_endpoint: usize, + diskio_rpc_workers: u32, ) -> Result<(Arc, Arc, ChunkIoClient), BoxError> { let control = Arc::new(CrowdbKvClient::new(KvConfig::new(seeds.clone()))); let client_config = ClientConfig::default(); @@ -108,14 +138,15 @@ async fn connect( )); let client = Arc::new(ChunkKvClient::new(client_config, source, transport)?); client.refresh_catalog().await?; - let chunks = ChunkIoClient::connect_with_kv( + let chunks = ChunkIoClient::connect_with_kv_read_policy( ChunkIoClientConfig { management_seeds: seeds, - diskio_connections_per_endpoint: 2, - diskio_rpc_workers: 2, - small_write: SmallWritePolicy::default(), + diskio_connections_per_endpoint, + diskio_rpc_workers, + small_write, }, control, + read_policy, ) .await?; let store = Arc::new(RoutedCatalogStore::new(client)); @@ -137,8 +168,9 @@ async fn start_listener( authentication: BearerAuthenticator, chunks: ChunkIoClient, management_seeds: Vec, + access_config: AccessConfig, ) -> Result<(), BoxError> { - let gc_config = super::gc_runtime::GcRuntimeConfig::from_env()?; + let gc_config = super::gc_runtime::GcRuntimeConfig::from_config(&access_config.iceberg.gc)?; for _ in 0..600 { match repository.recover(now_ms()?).await { Ok(()) => break, @@ -157,9 +189,20 @@ async fn start_listener( let blocks: Arc = Arc::new( crowdb_access_iceberg::file::NativeFileBlocks::new(chunks.clone(), store.clone()), ); + let native_budget = access_config + .iceberg + .native_budget_bytes + .unwrap_or(256 * 1024 * 1024); + let native_allocator = Arc::new(NativeBodyAllocator::new(native_budget, 1024 * 1024)?); let mut service = IcebergHttpService::new(repository.clone(), authentication, timeout) .with_namespaces(store.clone())? - .with_fileio(store.clone(), blocks.clone(), "us-east-1".into())?; + .with_fileio_native( + store.clone(), + blocks.clone(), + "us-east-1".into(), + Some(native_allocator), + )? + .with_small_object_threshold(access_config.small_write.threshold_exclusive())?; if authority.admission_bounds.delegated_access_ms >= 900_000 { let endpoint = std::env::var("CROWDB_ICEBERG_PUBLIC_URI").unwrap_or_else(|_| format!("http://{address}")); @@ -182,7 +225,14 @@ async fn start_listener( )); let tables = super::table_recovery::run(repository.clone(), store.clone(), blocks.clone()); let (gc_store, gc_chunks) = if gc_config.enabled { - let (_, gc_store, gc_chunks) = connect(management_seeds).await?; + let (_, gc_store, gc_chunks) = connect( + management_seeds, + access_config.read.policy(), + access_config.small_write.policy(), + access_config.common.diskio_connections_per_endpoint, + access_config.common.diskio_rpc_workers, + ) + .await?; (gc_store, Some(gc_chunks)) } else { (store.clone(), None) diff --git a/app/crowdb-access-server/src/iceberg/table_credentials.rs b/app/crowdb-access-server/src/iceberg/table_credentials.rs index 9d40b2dd1..155f2b4da 100644 --- a/app/crowdb-access-server/src/iceberg/table_credentials.rs +++ b/app/crowdb-access-server/src/iceberg/table_credentials.rs @@ -227,14 +227,6 @@ impl TableCredentials { } .issue(&self.issuer, principal, context, &authority, target, now) .map_err(|_| service_unavailable())?; - let pins = crowdb_access_iceberg::gc::ReaderPins::new(self.store.clone()); - let expires_ms = pins - .request_expiry(context, credentials.grant().expires_ms) - .await - .map_err(|_| service_unavailable())?; - pins.protect_files(context, target.table, principal.name, expires_ms, now) - .await - .map_err(|_| service_unavailable())?; Ok(response( 200, serde_json::to_vec(&LoadCredentialsResponse::from(credentials)) diff --git a/app/crowdb-access-server/src/iceberg/table_read.rs b/app/crowdb-access-server/src/iceberg/table_read.rs index d8d80e422..1b047f157 100644 --- a/app/crowdb-access-server/src/iceberg/table_read.rs +++ b/app/crowdb-access-server/src/iceberg/table_read.rs @@ -45,8 +45,7 @@ impl TableHttp { string_bytes: 1024 * 1024, collection_entries: 10_000, }, - ) - .with_catalog_reader_pins(), + ), lister: TableLister::new(store, secret)?, spools: Arc::new(AtomicUsize::new(0)), file_config: None, diff --git a/app/crowdb-access-server/src/lib.rs b/app/crowdb-access-server/src/lib.rs index 306c81425..d7b637f7c 100644 --- a/app/crowdb-access-server/src/lib.rs +++ b/app/crowdb-access-server/src/lib.rs @@ -3,6 +3,8 @@ //! Independent listener lifecycle for external access protocols. +pub mod config; +mod http_receive; pub mod iceberg; #[cfg(feature = "s3")] diff --git a/app/crowdb-access-server/src/main.rs b/app/crowdb-access-server/src/main.rs index 1294b36d6..91b7b0c54 100644 --- a/app/crowdb-access-server/src/main.rs +++ b/app/crowdb-access-server/src/main.rs @@ -18,13 +18,14 @@ use crowdb_access_s3::metrics::{DependencyHealth, S3Health, S3Metrics}; #[cfg(feature = "s3")] use crowdb_access_s3::native_buffer::NativeBodyAllocator; #[cfg(feature = "s3")] +use crowdb_access_server::config::{load_args, AccessConfig}; +#[cfg(feature = "s3")] use crowdb_access_server::credentials::CredentialAuthority; #[cfg(feature = "s3")] use crowdb_access_server::s3::{serve, ProductionS3Operations, S3Dispatcher, S3ServiceConfig}; #[cfg(feature = "s3")] use crowdb_access_server::storage::S3StorageClients; #[cfg(feature = "s3")] -use crowdb_chunk_client::SmallWritePolicy; #[cfg(feature = "s3")] use crowdb_common::ec::EcScheme; #[cfg(feature = "s3")] @@ -36,58 +37,72 @@ use tokio::net::TcpListener; async fn main() -> Result<(), Box> { tracing_subscriber::fmt().with_writer(std::io::stderr).init(); #[cfg(feature = "s3")] + let (access_config, remaining_args) = load_args(std::env::args().skip(1).collect())?; + #[cfg(feature = "s3")] if matches!( - std::env::args().nth(1).as_deref(), + remaining_args.first().map(String::as_str), Some("issue-user" | "ensure-user" | "lookup-user") ) { - return issue_user().await; + return issue_user(&remaining_args, &access_config).await; } #[cfg(feature = "s3")] - if let Ok(address) = std::env::var("CROWDB_S3_LISTEN") { - let management_seeds = management_seeds()?; - let tenant = TenantId::new(required_env("CROWDB_S3_TENANT")?.into_bytes())?; + if !remaining_args.is_empty() { + return Err("unexpected S3 server arguments".into()); + } + #[cfg(feature = "s3")] + run_s3(&access_config).await?; + Ok(()) +} + +#[cfg(feature = "s3")] +async fn run_s3(access_config: &AccessConfig) -> Result<(), Box> { + if let Some(address) = access_config + .s3 + .listen + .clone() + .or_else(|| std::env::var("CROWDB_S3_LISTEN").ok()) + { + let management_seeds = if access_config.common.management_seeds.is_empty() { + management_seeds()? + } else { + access_config.common.management_seeds.clone() + }; + let tenant_name = match &access_config.s3.tenant { + Some(name) => name.clone(), + None => required_env("CROWDB_S3_TENANT")?, + }; + let tenant = TenantId::new(tenant_name.into_bytes())?; let master_key = MasterKey::from_hex(&required_env("CROWDB_S3_MASTER_KEY")?)?; let credential_cipher = Arc::new(CredentialCipher::new(&master_key)); let continuation_key = credential_cipher.continuation_key().to_vec(); - let small_write = SmallWritePolicy::default(); - let storage = S3StorageClients::connect(management_seeds, 2, 2, small_write.clone()).await?; + let small_write = access_config.small_write.policy(); + let storage = S3StorageClients::connect_with_read_policy( + management_seeds, + access_config.common.diskio_connections_per_endpoint, + access_config.common.diskio_rpc_workers, + small_write.clone(), + access_config.read.policy(), + ) + .await?; let chunks = Arc::clone(&storage.chunks); let authority = Arc::new(CredentialAuthority::new( Arc::clone(&storage.control), Arc::clone(&credential_cipher), )); - let trusted_network = std::env::var("CROWDB_S3_TRUSTED_NETWORK").as_deref() == Ok("true"); - let (authenticator, credential_refresh): ( - Arc, - Option>, - ) = if trusted_network { - tracing::warn!(%address, "starting S3 with explicit trusted-network authentication bypass"); - (Arc::new(TrustedNetworkAuthenticator::new()), None) - } else { - let cache = Arc::new(CredentialCache::new(90)); - refresh_credentials(&authority, &cache).await?; - let refresh_authority = Arc::clone(&authority); - let refresh_cache = Arc::clone(&cache); - let task = tokio::spawn(async move { - let mut interval = tokio::time::interval(Duration::from_secs(30)); - interval.tick().await; - loop { - interval.tick().await; - if let Err(error) = refresh_credentials(&refresh_authority, &refresh_cache).await { - tracing::error!(%error, "S3 credential refresh failed; cache will fail closed when stale"); - } - } - }); - let region = std::env::var("CROWDB_S3_REGION").unwrap_or_else(|_| "us-east-1".into()); - (Arc::new(SigV4Verifier::new(cache, region, 900)), Some(task)) - }; - let mut service_config = S3ServiceConfig::basic(tenant, continuation_key, small_write.object_limit); - service_config.small_object_limit = - optional_usize("CROWDB_S3_SMALL_OBJECT_LIMIT")?.unwrap_or(service_config.small_object_limit); - configure_large_write(&mut service_config)?; + let (authenticator, credential_refresh, trusted_network) = + authenticate_s3(access_config, &address, &authority).await?; + let service_config = s3_service_config( + access_config, + tenant, + continuation_key, + access_config.small_write.threshold_exclusive(), + )?; let metrics = Arc::new(S3Metrics::default()); - let cleanup_backlog_limit = optional_usize("CROWDB_S3_CLEANUP_BACKLOG_LIMIT")? - .map_or(10_000, |value| u64::try_from(value).unwrap_or(u64::MAX)); + let cleanup_backlog_limit = configured_u64( + access_config.s3.cleanup_backlog_limit, + "CROWDB_S3_CLEANUP_BACKLOG_LIMIT", + )? + .unwrap_or(10_000); let health = Arc::new(S3Health::starting(cleanup_backlog_limit)); health.set_metadata(DependencyHealth::Ready); health.set_chunks(DependencyHealth::Ready); @@ -98,7 +113,11 @@ async fn main() -> Result<(), Box> { .with_metrics(Arc::clone(&metrics)) .with_health(Arc::clone(&health)), ); - let native_budget = optional_usize("CROWDB_S3_NATIVE_BUDGET_BYTES")?.unwrap_or(256 * 1024 * 1024); + let native_budget = configured_usize( + access_config.s3.native_budget_bytes, + "CROWDB_S3_NATIVE_BUDGET_BYTES", + )? + .unwrap_or(256 * 1024 * 1024); let body_allocator = Arc::new(NativeBodyAllocator::new(native_budget, 1024 * 1024)?); let handler = Arc::new( S3Dispatcher::new( @@ -130,36 +149,118 @@ async fn main() -> Result<(), Box> { } #[cfg(feature = "s3")] -fn configure_large_write(config: &mut S3ServiceConfig) -> Result<(), Box> { - let ec_data = optional_usize("CROWDB_S3_EC_DATA")?.unwrap_or(config.large_write.ec_scheme.data_num); - let ec_code = optional_usize("CROWDB_S3_EC_CODE")?.unwrap_or(config.large_write.ec_scheme.code_num); +async fn authenticate_s3( + access: &AccessConfig, + address: &str, + authority: &Arc, +) -> Result< + ( + Arc, + Option>, + bool, + ), + Box, +> { + let trusted_network = access + .s3 + .trusted_network + .unwrap_or(std::env::var("CROWDB_S3_TRUSTED_NETWORK").as_deref() == Ok("true")); + if trusted_network { + tracing::warn!(%address, "starting S3 with explicit trusted-network authentication bypass"); + return Ok((Arc::new(TrustedNetworkAuthenticator::new()), None, true)); + } + let cache = Arc::new(CredentialCache::new(90)); + refresh_credentials(authority, &cache).await?; + let refresh_authority = Arc::clone(authority); + let refresh_cache = Arc::clone(&cache); + let task = tokio::spawn(async move { + let mut interval = tokio::time::interval(Duration::from_secs(30)); + interval.tick().await; + loop { + interval.tick().await; + if let Err(error) = refresh_credentials(&refresh_authority, &refresh_cache).await { + tracing::error!(%error, "S3 credential refresh failed; cache will fail closed when stale"); + } + } + }); + let region = access + .s3 + .region + .clone() + .or_else(|| std::env::var("CROWDB_S3_REGION").ok()) + .unwrap_or_else(|| "us-east-1".into()); + Ok(( + Arc::new(SigV4Verifier::new(cache, region, 900)), + Some(task), + false, + )) +} + +#[cfg(feature = "s3")] +fn s3_service_config( + access: &AccessConfig, + tenant: TenantId, + continuation_key: Vec, + small_write_limit: usize, +) -> Result> { + let mut config = S3ServiceConfig::basic(tenant, continuation_key, small_write_limit); + if let Some(limit) = access.s3.list_scan_items { + config.list_scan_items = limit; + } + if let Some(limit) = access.s3.list_scan_bytes { + config.list_scan_bytes = limit; + } + if let Some(ttl) = access.s3.continuation_ttl_seconds { + config.continuation_ttl_seconds = ttl; + } + config.small_object_limit = + configured_usize(access.s3.small_object_limit, "CROWDB_S3_SMALL_OBJECT_LIMIT")? + .unwrap_or(config.small_object_limit) + .min(small_write_limit.saturating_sub(1)); + configure_large_write(&mut config, access)?; + Ok(config) +} + +#[cfg(feature = "s3")] +fn configure_large_write( + config: &mut S3ServiceConfig, + access: &AccessConfig, +) -> Result<(), Box> { + let ec_data = configured_usize(access.s3.ec_data, "CROWDB_S3_EC_DATA")? + .unwrap_or(config.large_write.ec_scheme.data_num); + let ec_code = configured_usize(access.s3.ec_code, "CROWDB_S3_EC_CODE")? + .unwrap_or(config.large_write.ec_scheme.code_num); if ec_data == 0 || ec_code == 0 { return Err("CROWDB S3 EC data and code counts must be nonzero".into()); } config.large_write.ec_scheme = EcScheme::new(ec_data, ec_code); - if let Some(max_chunk_size) = optional_usize("CROWDB_S3_MAX_CHUNK_SIZE")? { + if let Some(max_chunk_size) = configured_u64(access.s3.max_chunk_size, "CROWDB_S3_MAX_CHUNK_SIZE")? { if max_chunk_size == 0 { return Err("CROWDB S3 max chunk size must be nonzero".into()); } - Arc::make_mut(&mut config.large_write.client).max_chunk_size = - u64::try_from(max_chunk_size).unwrap_or(u64::MAX); + Arc::make_mut(&mut config.large_write.client).max_chunk_size = max_chunk_size; } Ok(()) } #[cfg(feature = "s3")] -async fn issue_user() -> Result<(), Box> { - let command = std::env::args().nth(1).ok_or("missing S3 user command")?; - let user = std::env::args() - .nth(2) +async fn issue_user(args: &[String], access: &AccessConfig) -> Result<(), Box> { + let command = args.first().ok_or("missing S3 user command")?; + let user = args + .get(1) .filter(|value| !value.is_empty()) .ok_or("usage: crowdb-access-server issue-user|ensure-user|lookup-user USER")?; - if std::env::args().nth(3).is_some() { + if args.len() != 2 { return Err("usage: crowdb-access-server issue-user|ensure-user|lookup-user USER".into()); } let master_key = MasterKey::from_hex(&required_env("CROWDB_S3_MASTER_KEY")?)?; let cipher = Arc::new(CredentialCipher::new(&master_key)); - let control = Arc::new(CrowdbKvClient::new(KvConfig::new(management_seeds()?))); + let seeds = if access.common.management_seeds.is_empty() { + management_seeds()? + } else { + access.common.management_seeds.clone() + }; + let control = Arc::new(CrowdbKvClient::new(KvConfig::new(seeds))); let authority = CredentialAuthority::new(control, cipher); let token = match command.as_str() { "ensure-user" => authority.ensure_user(user.as_bytes()).await?, @@ -211,6 +312,30 @@ fn optional_usize(name: &str) -> Result, Box, + env_name: &str, +) -> Result, Box> { + match configured { + Some(value) => Ok(Some(value)), + None => optional_usize(env_name), + } +} + +#[cfg(feature = "s3")] +fn configured_u64( + configured: Option, + env_name: &str, +) -> Result, Box> { + match configured { + Some(value) => Ok(Some(value)), + None => { + optional_usize(env_name).map(|value| value.map(|value| u64::try_from(value).unwrap_or(u64::MAX))) + } + } +} + #[cfg(feature = "s3")] fn required_env(name: &str) -> Result> { std::env::var(name).map_err(|_| format!("{name} is required when S3 is enabled").into()) diff --git a/app/crowdb-access-server/src/s3.rs b/app/crowdb-access-server/src/s3.rs index d60d2bf5c..8c8201029 100644 --- a/app/crowdb-access-server/src/s3.rs +++ b/app/crowdb-access-server/src/s3.rs @@ -10,10 +10,9 @@ use std::time::Instant; use crowdb_access_s3::error::S3Error; use crowdb_access_s3::metrics::{OutcomeClass, S3Metrics}; -use crowdb_access_s3::native_buffer::NativeBodyReceiver; use crowdb_access_s3::route::S3Operation; use http_body_util::{BodyExt, Full}; -use hyper::body::{Body, Bytes, Frame, Http1BodyReceiveProvider, Incoming, SizeHint}; +use hyper::body::{Body, Bytes, Frame, Incoming, SizeHint}; use hyper::server::conn::http1; use hyper::service::service_fn; use hyper::{Request, Response}; @@ -23,6 +22,7 @@ use tokio::net::TcpListener; mod dispatcher; mod operations; +pub use crate::http_receive::install_body_receive_provider; pub use dispatcher::S3Dispatcher; pub use operations::{ProductionS3Operations, S3Operations, S3OperationsFuture, S3ServiceConfig}; @@ -35,39 +35,6 @@ pub trait S3HttpHandler: Send + Sync + 'static { fn handle(&self, request: Request) -> HandlerFuture; } -#[derive(Clone)] -pub(crate) struct DeferredBodyReceiveProvider { - provider: Arc, - native: Option>, -} - -impl DeferredBodyReceiveProvider { - fn generic(provider: Arc) -> Self { - Self { - provider, - native: None, - } - } - - fn native(receiver: Arc) -> Self { - Self { - provider: receiver.clone(), - native: Some(receiver), - } - } -} - -/// Installs the admitted request's provider immediately before body polling. -pub fn install_body_receive_provider(request: &mut Request) -> Option> { - if let Some(deferred) = request.extensions_mut().remove::() { - request - .body_mut() - .set_http1_body_receive_provider(deferred.provider); - return deferred.native; - } - None -} - /// Runs one independent HTTP/1 S3 listener until shutdown. /// /// # Errors diff --git a/app/crowdb-access-server/src/s3/dispatcher.rs b/app/crowdb-access-server/src/s3/dispatcher.rs index 318fb88fb..90cd14586 100644 --- a/app/crowdb-access-server/src/s3/dispatcher.rs +++ b/app/crowdb-access-server/src/s3/dispatcher.rs @@ -16,9 +16,9 @@ use hyper::{Method, Request, Response, StatusCode}; use tracing::Instrument; use super::{ - error_response, full_body, measured_body, DeferredBodyReceiveProvider, HandlerFuture, ResponseBody, - S3HttpHandler, S3Operations, + error_response, full_body, measured_body, HandlerFuture, ResponseBody, S3HttpHandler, S3Operations, }; +use crate::http_receive::DeferredBodyReceiveProvider; pub struct S3Dispatcher { authenticator: Arc, diff --git a/app/crowdb-access-server/src/s3/operations.rs b/app/crowdb-access-server/src/s3/operations.rs index 7b44287d2..8e6616621 100644 --- a/app/crowdb-access-server/src/s3/operations.rs +++ b/app/crowdb-access-server/src/s3/operations.rs @@ -17,8 +17,8 @@ use crowdb_access_s3::publication::PublicationRequest; use crowdb_access_s3::retrieval::{self, ObjectHeaders, RetrievalError}; use crowdb_access_s3::route::{S3Operation, S3Route}; use crowdb_access_s3::streaming::{ - publish_completed_locations, write_body_with_checksums_metered, write_native_body_with_checksums_metered, - PutErrorCode, PutOutcome, + publish_completed_locations, write_body_with_checksums_buffered, + write_native_body_with_checksums_metered, PutErrorCode, PutOutcome, }; use crowdb_chunk_client::{ ChunkClientConfig, ChunkIoWriter, IoError, LargeWritePolicy, PreparedLargeWrite, SharedObjectWriter, @@ -245,8 +245,11 @@ impl ProductionS3Operations { route_key.extend_from_slice(bucket_id.as_bytes()); route_key.extend_from_slice(&key); let mut writer = self.prepare_writer(content_length, &route_key).await?; - let native_receiver = install_body_receive_provider(&mut request); - let native_receiver = native_receiver.filter(|_| writer.is_large() && content_length.is_some()); + let native_receiver = if writer.is_large() && content_length.is_some() { + install_body_receive_provider(&mut request) + } else { + None + }; if let Some(receiver) = &native_receiver { receiver.enable_owner_handoff(); } @@ -262,11 +265,16 @@ impl ProductionS3Operations { ) .await } else { - write_body_with_checksums_metered( + let receive_bytes = content_length + .and_then(|length| usize::try_from(length).ok()) + .unwrap_or(1024 * 1024) + .clamp(1, 1024 * 1024); + write_body_with_checksums_buffered( &mut body, &mut writer, content_md5.as_deref(), payload_sha256.as_deref(), + receive_bytes, self.metrics.as_deref(), ) .await @@ -455,6 +463,15 @@ impl ProductionS3Operations { .and_then(|length| usize::try_from(length).ok()) .filter(|length| *length <= self.config.small_object_limit) { + if length > crowdb_protocol::frame::MAX_FRAME_PAYLOAD_BYTES { + return self + .storage + .chunks + .prepare_shared_object_write_for_key(length, key) + .await + .map(ObjectWriter::Small) + .map_err(|_| S3ErrorCode::SlowDown); + } match self.storage.chunks.prepare_small_write_for_key(length, key).await { Ok(writer) => return Ok(ObjectWriter::Small(writer)), Err(IoError::ObjectTooLarge { .. }) => {} @@ -523,7 +540,7 @@ impl ChunkIoWriter for ObjectWriter { async fn on_finish(&mut self) -> crowdb_chunk_client::Result> { match self { - Self::Small(writer) => writer.on_finish().await, + Self::Small(writer) => writer.finish_durable().await, Self::Large(writer) => writer.on_finish().await, } } diff --git a/app/crowdb-access-server/src/storage.rs b/app/crowdb-access-server/src/storage.rs index 3983c63db..ff9d1eb45 100644 --- a/app/crowdb-access-server/src/storage.rs +++ b/app/crowdb-access-server/src/storage.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use crowdb_access_s3::metadata::ChunkKvMetadataStore; -use crowdb_chunk_client::{ChunkIoClient, ChunkIoClientConfig, SmallWritePolicy}; +use crowdb_chunk_client::{ChunkIoClient, ChunkIoClientConfig, ChunkReadPolicy, SmallWritePolicy}; use crowdb_chunk_kv_client::{ ChunkKvClient, ChunkKvRpcTransport, ClientConfig as ChunkKvConfig, Group0ChunkKvRangeCatalogSource, }; @@ -38,6 +38,25 @@ impl S3StorageClients { diskio_connections_per_endpoint: usize, diskio_rpc_workers: u32, small_write: SmallWritePolicy, + ) -> Result { + Self::connect_with_read_policy( + management_seeds, + diskio_connections_per_endpoint, + diskio_rpc_workers, + small_write, + ChunkReadPolicy::default(), + ) + .await + } + + /// # Errors + /// Returns an error when the management or `DiskIO` connection cannot be established. + pub async fn connect_with_read_policy( + management_seeds: Vec, + diskio_connections_per_endpoint: usize, + diskio_rpc_workers: u32, + small_write: SmallWritePolicy, + read_policy: ChunkReadPolicy, ) -> Result { let kv = Arc::new(CrowdbKvClient::new(KvConfig::new(management_seeds.clone()))); let config = ChunkKvConfig::default(); @@ -49,7 +68,7 @@ impl S3StorageClients { .refresh_catalog() .await .map_err(|error| StorageConnectError::ChunkKv(error.to_string()))?; - let chunks = ChunkIoClient::connect_with_kv( + let chunks = ChunkIoClient::connect_with_kv_read_policy( ChunkIoClientConfig { management_seeds, diskio_connections_per_endpoint, @@ -57,6 +76,7 @@ impl S3StorageClients { small_write, }, Arc::clone(&kv), + read_policy, ) .await .map_err(|error| StorageConnectError::ChunkIo(error.to_string()))?; diff --git a/app/crowdb-access-server/tests/common/iceberg_file_worker.rs b/app/crowdb-access-server/tests/common/iceberg_file_worker.rs index ffdd4b0be..2f0b35194 100644 --- a/app/crowdb-access-server/tests/common/iceberg_file_worker.rs +++ b/app/crowdb-access-server/tests/common/iceberg_file_worker.rs @@ -64,11 +64,12 @@ pub async fn verify(stack: &TestIcebergStack, context: CatalogContext, table: Ta table, file: FileId::random(), }, - tree: FileTree { + tree: Some(FileTree { root: None, length: 0, digest: Sha256::digest([]).into(), - }, + }), + stream: None, }; assert!(repository.reserve_part(&admitted, &part, 2).await.unwrap()); let mut worker = TestWorker::start(&stack.cluster.mgmt_endpoints); diff --git a/app/crowdb-access-server/tests/common/iceberg_multipart.rs b/app/crowdb-access-server/tests/common/iceberg_multipart.rs index ca149a34f..cbb9d55a8 100644 --- a/app/crowdb-access-server/tests/common/iceberg_multipart.rs +++ b/app/crowdb-access-server/tests/common/iceberg_multipart.rs @@ -54,7 +54,8 @@ pub async fn verify_restart(stack: &mut TestIcebergStack, context: CatalogContex revision: 1, modified_ms: 101, owner, - tree: writer.finish().await.unwrap(), + tree: Some(writer.finish().await.unwrap()), + stream: None, }; assert!(repository.reserve_part(&initial, &part, 101).await.unwrap()); let recovery = MultipartRecovery::new(stack.store().await, blocks.clone(), 7, 8).unwrap(); @@ -66,7 +67,7 @@ pub async fn verify_restart(stack: &mut TestIcebergStack, context: CatalogContex let selection = MultipartSelection::new(vec![SelectedPart { number: 1, revision: 1, - digest: part.tree.digest, + digest: part.selection_digest(), }]) .unwrap(); assert!(repository diff --git a/app/crowdb-access-server/tests/config_test.rs b/app/crowdb-access-server/tests/config_test.rs new file mode 100644 index 000000000..2d620bc67 --- /dev/null +++ b/app/crowdb-access-server/tests/config_test.rs @@ -0,0 +1,54 @@ +// Copyright 2026-present Gian +// Licensed under the Apache License, Version 2.0. + +use std::path::Path; + +use crowdb_access_server::config::{load_args, AccessConfig}; +use crowdb_common::config::{load_from_file, BaseConfig}; + +#[test] +fn tracked_access_configs_load_and_set_bounded_read_resources() { + let root = Path::new(env!("CARGO_MANIFEST_DIR")); + let canonical: AccessConfig = + load_from_file(&root.join("conf/crowdb_access_server_config.toml")).unwrap(); + let container: AccessConfig = + load_from_file(&root.join("../../container/single-node-container/templates/access.toml")).unwrap(); + for config in [canonical, container] { + assert_eq!(config.read.stream_slots, 3); + assert_eq!(config.read.stream_window_bytes, 1024 * 1024); + assert_eq!(config.read.global_stream_bytes, 256 * 1024 * 1024); + assert_eq!(config.read.recovery_memory_bytes, 256 * 1024 * 1024); + assert_eq!(config.read.policy().stream_slots, 3); + assert_eq!(config.small_write.memory_budget_bytes, 1280 * 1024 * 1024); + assert_eq!(config.small_write.policy().max_pipelines, 32); + assert!(config.s3.listen.is_some()); + assert!(config.iceberg.listen.is_some()); + assert_eq!(config.iceberg.native_budget_bytes, Some(256 * 1024 * 1024)); + assert_eq!(config.s3.list_scan_bytes, Some(4 * 1024 * 1024)); + assert_eq!(config.iceberg.gc.kv_bytes, Some(64 * 1024 * 1024)); + } +} + +#[test] +fn config_argument_is_removed_from_service_commands() { + let root = Path::new(env!("CARGO_MANIFEST_DIR")); + let path = root.join("conf/crowdb_access_server_config.toml"); + let (_, remaining) = load_args(vec![ + "serve".into(), + "--config".into(), + path.display().to_string(), + ]) + .unwrap(); + assert_eq!(remaining, ["serve"]); + assert!(load_args(vec!["--config".into()]).is_err()); +} + +#[test] +fn invalid_read_budget_is_rejected() { + let mut config = AccessConfig::default(); + config.read.stream_slots = 0; + assert!(config.validate().is_err()); + config.read.stream_slots = 3; + config.small_write.memory_budget_bytes = 1; + assert!(config.validate().is_err()); +} diff --git a/app/crowdb-access-server/tests/iceberg_file_encoding_test.rs b/app/crowdb-access-server/tests/iceberg_file_encoding_test.rs index 20411ebc0..766a1abd1 100644 --- a/app/crowdb-access-server/tests/iceberg_file_encoding_test.rs +++ b/app/crowdb-access-server/tests/iceberg_file_encoding_test.rs @@ -14,6 +14,33 @@ use hyper::{header::HeaderValue, HeaderMap}; struct TestFrames(VecDeque); +#[test] +fn upload_integrity_is_present_only_with_a_declared_checksum_or_signed_chunks() { + let empty = HeaderMap::new(); + assert!(!FileUploadBody::new(Full::new(Bytes::new()), &empty, None, 100) + .unwrap() + .has_integrity()); + let mut md5 = HeaderMap::new(); + md5.insert( + "content-md5", + HeaderValue::from_static("1B2M2Y8AsgTpgAmY7PhCfg=="), + ); + assert!(FileUploadBody::new(Full::new(Bytes::new()), &md5, None, 100) + .unwrap() + .has_integrity()); + let mut crc = HeaderMap::new(); + crc.insert("x-amz-checksum-crc32c", HeaderValue::from_static("AAAAAA==")); + assert!(FileUploadBody::new(Full::new(Bytes::new()), &crc, None, 100) + .unwrap() + .has_integrity()); + let (headers, verifier, wire) = signed::fixture(); + assert!( + FileUploadBody::new(Full::new(Bytes::from(wire)), &headers, Some(verifier), 100_000) + .unwrap() + .has_integrity() + ); +} + #[tokio::test] async fn content_md5_checks_decoded_bytes_before_successful_eof() { use base64::{engine::general_purpose::STANDARD, Engine}; @@ -85,7 +112,7 @@ async fn aws_published_signed_trailer_vector_survives_arbitrary_http_boundaries( let mut output = Vec::new(); while let Some(frame) = body.frame().await { let bytes = frame.unwrap().into_data().unwrap(); - assert!(bytes.len() <= 64 * 1024); + assert!(bytes.len() <= 1024 * 1024); output.extend_from_slice(&bytes); } assert!(body.is_end_stream()); diff --git a/app/crowdb-access-server/tests/iceberg_file_http_test.rs b/app/crowdb-access-server/tests/iceberg_file_http_test.rs index 36e79e384..18135b7d1 100644 --- a/app/crowdb-access-server/tests/iceberg_file_http_test.rs +++ b/app/crowdb-access-server/tests/iceberg_file_http_test.rs @@ -22,6 +22,7 @@ use crowdb_access_iceberg::file::{ use crowdb_access_iceberg::key::OperationId; use crowdb_access_iceberg::operation::{ManagementAction, ManagementRequest, RequestIdentity}; use crowdb_access_iceberg::wire::BearerAuthenticator; +use md5::{Digest, Md5}; use reqwest::{Client, Method}; #[path = "common/iceberg_signed_file.rs"] @@ -184,10 +185,52 @@ async fn signed_standard_put_get_and_multipart_publish_unbound_files() { assert_eq!(record.kind, FileKind::Unbound); assert!(record.bind_kind(FileKind::EqualityDelete).is_ok()); let conflict = client - .send(Method::PUT, &object, "", b"PAR1difffoot\x04\0\0\0PAR1", false) + .send(Method::PUT, &object, "", b"PAR1difffoot\x04\0\0\0PAR1", true) .await; assert_eq!(conflict.status(), 409); + let medium = path(table, "data/medium.parquet"); + let medium_bytes = (0..1_200_000) + .map(|index| (index % 251) as u8) + .collect::>(); + let response = client.send(Method::PUT, &medium, "", &medium_bytes, true).await; + assert_eq!(response.status(), 200, "{}", response.text().await.unwrap()); + let stored = repository + .load( + client.credentials.grant().context, + &table.file("data/medium.parquet").unwrap(), + ) + .await + .unwrap() + .unwrap(); + assert_eq!(stored.content.locations(stored.length).unwrap().unwrap().len(), 1); + let range = client + .send_range( + Method::GET, + &medium, + "", + b"", + false, + Some("bytes=1048550-1048600"), + ) + .await; + assert_eq!(range.status(), 206); + assert_eq!( + range.bytes().await.unwrap().as_ref(), + &medium_bytes[1048550..1048601] + ); + let metrics: serde_json::Value = Client::new() + .get(format!("http://{}/_crowdb/metrics", client.address)) + .bearer_auth("m".repeat(32)) + .send() + .await + .unwrap() + .json() + .await + .unwrap(); + assert!(metrics["chunk_read"]["stream_windows"].as_u64().unwrap() > 0); + assert!(metrics["chunk_small_write"]["completed"].as_u64().unwrap() > 0); + let metadata = path(table, "metadata/b.json"); let create = client.send(Method::POST, &metadata, "uploads=", b"", false).await; assert_eq!(create.status(), 200); @@ -236,6 +279,33 @@ async fn signed_standard_put_get_and_multipart_publish_unbound_files() { .await .unwrap() .ends_with("")); + let mut composite = Md5::new(); + composite.update(Md5::digest(first)); + composite.update(Md5::digest(second)); + let expected_etag = composite + .finalize() + .iter() + .map(|byte| format!("{byte:02x}")) + .collect::() + + "-2"; + let published = repository + .load( + client.credentials.grant().context, + &table.file("metadata/b.json").unwrap(), + ) + .await + .unwrap() + .unwrap(); + assert_eq!(published.content.etag(), Some(expected_etag.as_str())); + assert_eq!( + published + .content + .locations(published.length) + .unwrap() + .unwrap() + .len(), + 2 + ); let replay = client .send( Method::POST, diff --git a/app/crowdb-access-server/tests/iceberg_file_response_test.rs b/app/crowdb-access-server/tests/iceberg_file_response_test.rs index d50c9fe03..59d36f71e 100644 --- a/app/crowdb-access-server/tests/iceberg_file_response_test.rs +++ b/app/crowdb-access-server/tests/iceberg_file_response_test.rs @@ -60,11 +60,12 @@ fn part(session: &MultipartSession, number: u16, modified_ms: u64) -> MultipartP file: FileId::random(), ..session.owner }, - tree: FileTree { + tree: Some(FileTree { root: None, length: 0, digest: Sha256::digest([]).into(), - }, + }), + stream: None, } } @@ -161,7 +162,7 @@ fn complete_uses_only_a_published_matching_record() { let candidate = FileTree { root: match &record.content { FileContent::Chunks { root } => root.clone(), - FileContent::Inline { .. } => None, + FileContent::Inline { .. } | FileContent::Locations { .. } => None, }, length: record.length, digest: record.digest, diff --git a/app/crowdb-access-server/tests/iceberg_file_selection_test.rs b/app/crowdb-access-server/tests/iceberg_file_selection_test.rs index bb0eea7c2..c21181761 100644 --- a/app/crowdb-access-server/tests/iceberg_file_selection_test.rs +++ b/app/crowdb-access-server/tests/iceberg_file_selection_test.rs @@ -1,26 +1,26 @@ use crowdb_access_server::iceberg::CompleteSelection; #[test] -fn complete_xml_accepts_only_ordered_sha256_parts() { +fn complete_xml_accepts_ordered_part_etags() { let first = "01".repeat(32); let second = "ab".repeat(32); let xml = format!("1\"{first}\"10000\"{second}\""); let selection = CompleteSelection::parse(xml.as_bytes()).unwrap(); assert_eq!(selection.parts().len(), 2); assert_eq!(selection.parts()[0].number, 1); - assert_eq!(selection.parts()[0].digest, [1; 32]); + assert_eq!(selection.parts()[0].etag, first); assert_eq!(selection.parts()[1].number, 10_000); - assert_eq!(selection.parts()[1].digest, [0xab; 32]); + assert_eq!(selection.parts()[1].etag, second); let sdk_xml = format!("\"{first}\"1"); assert_eq!( - CompleteSelection::parse(sdk_xml.as_bytes()).unwrap().parts()[0].digest, - [1; 32] + CompleteSelection::parse(sdk_xml.as_bytes()).unwrap().parts()[0].etag, + first ); for quote in [""", """, """] { let escaped = sdk_xml.replace(&format!("\"{first}\""), &format!("{quote}{first}{quote}")); assert_eq!( - CompleteSelection::parse(escaped.as_bytes()).unwrap().parts()[0].digest, - [1; 32] + CompleteSelection::parse(escaped.as_bytes()).unwrap().parts()[0].etag, + first ); } } diff --git a/app/crowdb-access-server/tests/iceberg_gc_control_test.rs b/app/crowdb-access-server/tests/iceberg_gc_control_test.rs index d9c6b6732..e3c0204b1 100644 --- a/app/crowdb-access-server/tests/iceberg_gc_control_test.rs +++ b/app/crowdb-access-server/tests/iceberg_gc_control_test.rs @@ -10,7 +10,7 @@ use crowdb_access_iceberg::{ CatalogContext, CatalogRepository, CatalogStore, ClearBounds, ManagementPrivilege, RoutedCatalogStore, }, file::{file_key, ContentFormat, FileContent, FileKind, FileRecord, TableLocation}, - gc::{GcLimits, GcRepository, GcStalledReason, ReaderPins}, + gc::{GcLimits, GcRepository, GcStalledReason}, key::{FileId, NamespaceId, OperationId, TableId}, operation::{mutation_identity, ManagementAction, ManagementRequest, RequestIdentity}, record::StorageRecord, @@ -110,33 +110,6 @@ async fn seed_head(store: &RoutedCatalogStore, context: CatalogContext) -> Table table } -async fn check_operator_pin( - stack: &common::TestIcebergStack, - store: Arc, - context: CatalogContext, - table: TableId, -) { - let pin_id = OperationId::random().to_string(); - let table_id = table.to_string(); - let catalog_id = context.catalog.to_string(); - response(command(stack, 'm', &["pin", &pin_id, &table_id])); - let pin_identity = pin_id.parse().unwrap(); - let pins = ReaderPins::new(store); - assert!(pins - .get(context.catalog, table, pin_identity) - .await - .unwrap() - .unwrap() - .protects(common::now_ms())); - response(command(stack, 'm', &["unpin", &catalog_id, &table_id, &pin_id])); - assert!(!pins - .get(context.catalog, table, pin_identity) - .await - .unwrap() - .unwrap() - .protects(common::now_ms())); -} - async fn tombstone_head(store: &RoutedCatalogStore, context: CatalogContext, table: TableId) { let key = head_key(context.catalog, table); let previous = store.get(&key.encode().unwrap()).await.unwrap().unwrap(); @@ -218,7 +191,6 @@ async fn authenticated_gc_controls_survive_separate_processes() { assert!(!live.status.success()); assert!(String::from_utf8_lossy(&live.stderr).contains("live-table GC is disabled")); - check_operator_pin(&stack, store.clone(), context, table).await; tombstone_head(store.as_ref(), context, table).await; let created = response(command(&stack, 'm', &["start-table", &identity, &table_id])); assert_eq!(created["phase"], "Discover"); diff --git a/app/crowdb-access-server/tests/iceberg_table_credentials_test.rs b/app/crowdb-access-server/tests/iceberg_table_credentials_test.rs index b0dfd6690..21613ac96 100644 --- a/app/crowdb-access-server/tests/iceberg_table_credentials_test.rs +++ b/app/crowdb-access-server/tests/iceberg_table_credentials_test.rs @@ -140,7 +140,6 @@ async fn same_name_drafts_refresh_only_the_exact_original_writer_scope() { .parse() .unwrap(); assert_eq!(grant.grant().table, table.table); - assert_credential_pin(&fixture, grant.grant()).await; assert!(grant.grant().operations.allows(FileOperation::Put)); let wrong = path.replace("/events/", "/other/"); assert_eq!( @@ -176,19 +175,6 @@ async fn same_name_drafts_refresh_only_the_exact_original_writer_scope() { fixture.finish().await; } -async fn assert_credential_pin(fixture: &TestTableHttp, grant: &crowdb_access_iceberg::file::FileGrant) { - use crowdb_access_iceberg::{key::IcebergKey, record::StorageRecord}; - let repository = CatalogRepository::new(fixture.store.clone(), ClearBounds::default()).unwrap(); - let (_, authority) = repository.status().await.unwrap(); - let expiry = - grant.expires_ms + authority.admission_bounds.request_ms + authority.admission_bounds.clock_skew_ms; - assert!(fixture.store.values.load().iter().any(|(key, value)| { - matches!(IcebergKey::decode(key).and_then(|key| StorageRecord::decode(&key, &value.bytes)), - Ok(StorageRecord::GcPin(pin)) if pin.head.table == grant.table && pin.expires_ms == expiry - && pin.protects_uploads && !pin.released) - })); -} - async fn expire_first(fixture: &TestTableHttp, first: &Value, second: &Value) { let first_table: TableLocation = format!("{}/", first["metadata"]["location"].as_str().unwrap()) .parse() diff --git a/app/crowdb-access-server/tests/s3_full_stack_test.rs b/app/crowdb-access-server/tests/s3_full_stack_test.rs index b1091722c..d0d58c9af 100644 --- a/app/crowdb-access-server/tests/s3_full_stack_test.rs +++ b/app/crowdb-access-server/tests/s3_full_stack_test.rs @@ -458,6 +458,14 @@ fn assert_native_write_metrics(listen: &str) { metric_value(&exported, "crowdb_s3_large_write_payload_copy_operations_total"), 0 ); + assert!(metric_value(&exported, "crowdb_s3_chunk_read_location_normalizations_total") > 0); + assert!(metric_value(&exported, "crowdb_s3_chunk_read_range_locations_examined_total") > 0); + assert!(metric_value(&exported, "crowdb_s3_chunk_read_stream_windows_total") > 0); + assert!(metric_value(&exported, "crowdb_s3_chunk_read_layout_queries_total") > 0); + assert!(metric_value(&exported, "crowdb_s3_chunk_read_layout_query_wait_ns_total") > 0); + assert!(metric_value(&exported, "crowdb_s3_chunk_read_strip_read_wait_ns_total") > 0); + assert!(metric_value(&exported, "crowdb_s3_chunk_range_read_wait_ns_total") > 0); + assert!(metric_value(&exported, "crowdb_s3_chunk_read_frame_decode_wait_ns_total") > 0); } fn issue_credentials(access_binary: &Path, seeds: &str) -> (String, String) { @@ -669,14 +677,14 @@ async fn run_direct_chunk_benchmark(seeds: &[String], artifacts_dir: &Path) { .await .expect("direct chunk read"); let get_ns = started.elapsed().as_nanos(); - assert_eq!(read, payload); + assert_eq!(read.concat(), payload); let started = Instant::now(); let range = chunks .read_range(&result.locations, 0, 4096) .await .expect("direct chunk range read"); let range_ns = started.elapsed().as_nanos(); - assert_eq!(range, payload.slice(..4096)); + assert_eq!(range.concat(), payload.slice(..4096)); json!({ "size": size, "concurrency": concurrency, diff --git a/app/crowdb-cli/src/commands/bench/io.rs b/app/crowdb-cli/src/commands/bench/io.rs index e176a1916..57b20f1fc 100644 --- a/app/crowdb-cli/src/commands/bench/io.rs +++ b/app/crowdb-cli/src/commands/bench/io.rs @@ -294,13 +294,19 @@ fn print_read(workload: ReadBenchmarkWorkload, args: &ChunkioReadArgs, result: & ReadBenchmarkWorkload::Mixed => "read-mix", }; println!( - "chunkio {name}: requested={} dataset_objects={} prepare_s={:.3} reads={} small_reads={} large_reads={} errors={} incomplete={} stop={} reads_s={:.2} logical_mib_s={:.1} logical_bytes={} avg_us={} p50_us={} p99_us={} dram_read_mib_s={} dram_write_mib_s={} dram_total_mib_s={}", + "chunkio {name}: requested={} dataset_objects={} prepare_s={:.3} reads={} small_reads={} large_reads={} errors={} incomplete={} stop={} reads_s={:.2} logical_mib_s={:.1} logical_bytes={} avg_us={} p50_us={} p99_us={} dram_read_mib_s={} dram_write_mib_s={} dram_total_mib_s={} location_normalizations={} locations_examined={} range_locations_examined={} stream_windows={} layout_queries={} layout_query_wait_ns={} strip_read_wait_ns={} chunk_read_wait_ns={} frame_decode_wait_ns={} frame_parse_wait_ns={}", result.requested_reads, args.dataset_objects, result.preparation_secs, result.reads, result.small_reads, result.large_reads, result.errors, result.incomplete_reads, result.stop_reason, result.reads_per_sec, result.logical_mib_per_sec, result.logical_bytes, result.latency_avg_us, result.latency_p50_us, result.latency_p99_us, dram(result.dram_read_mib_s), dram(result.dram_write_mib_s), dram(result.dram_total_mib_s), + result.read_flow.location_normalizations, result.read_flow.locations_examined, + result.read_flow.range_locations_examined, result.read_flow.stream_windows, + result.read_flow.layout_queries, + result.read_flow.layout_query_wait_ns, result.read_flow.strip_read_wait_ns, + result.read_flow.chunk_read_wait_ns, result.read_flow.frame_decode_wait_ns, + result.read_flow.frame_parse_wait_ns, ); print_errors(name, &result.error_messages); } diff --git a/container/single-node-container/profile.toml b/container/single-node-container/profile.toml index 92ebcda5e..b3bf07dd4 100644 --- a/container/single-node-container/profile.toml +++ b/container/single-node-container/profile.toml @@ -173,10 +173,11 @@ backoff_max_ms = 5000 [[services]] id = "s3" program = "/opt/crowdb/bin/crowdb-access-server" -args = [] -env = { CROWDB_S3_LISTEN = "0.0.0.0:81", CROWDB_S3_PUBLIC_URI = "http://localhost:81", CROWDB_MANAGEMENT_SEEDS = "http://127.0.0.1:10000", CROWDB_S3_TENANT = "preview", CROWDB_S3_REGION = "us-east-1", CROWDB_S3_EC_DATA = "2", CROWDB_S3_EC_CODE = "1" } +args = ["--config", "/opt/crowdb/run/config/s3.toml"] +env = { CROWDB_S3_PUBLIC_URI = "http://localhost:81", CROWDB_MANAGEMENT_SEEDS = "http://127.0.0.1:10000" } dependencies = ["kv", "chunk-kv", "chunkdb", "diskio"] fence_listeners = ["127.0.0.1:81"] +config_template = "/opt/crowdb/etc/templates/access.toml" [services.probe] kind = "http" target = "http://127.0.0.1:81/_crowdb/health/ready" @@ -190,10 +191,11 @@ backoff_max_ms = 5000 [[services]] id = "iceberg" program = "/opt/crowdb/bin/crowdb-iceberg" -args = ["serve"] -env = { CROWDB_ICEBERG_LISTEN = "0.0.0.0:80", CROWDB_ICEBERG_PUBLIC_URI = "http://localhost", CROWDB_MANAGEMENT_SEEDS = "http://127.0.0.1:10000", CROWDB_ICEBERG_GC_ENABLED = "0" } +args = ["serve", "--config", "/opt/crowdb/run/config/iceberg.toml"] +env = { CROWDB_ICEBERG_PUBLIC_URI = "http://localhost", CROWDB_ICEBERG_GC_ENABLED = "0" } dependencies = ["kv", "chunk-kv", "chunkdb", "diskio"] fence_listeners = ["127.0.0.1:80"] +config_template = "/opt/crowdb/etc/templates/access.toml" [services.probe] kind = "http" target = "http://127.0.0.1:80/v1/config" diff --git a/container/single-node-container/templates/access.toml b/container/single-node-container/templates/access.toml new file mode 100644 index 000000000..0b30383e5 --- /dev/null +++ b/container/single-node-container/templates/access.toml @@ -0,0 +1,60 @@ +# Access configuration shared by S3 and Iceberg processes. +# Secrets stay in /opt/crowdb/data/secrets/server.env. + +[common] +management_seeds = ["http://127.0.0.1:10000"] +diskio_connections_per_endpoint = 2 +diskio_rpc_workers = 2 + +[read] +stream_window_bytes = 1048576 +stream_slots = 3 +global_stream_bytes = 268435456 +recovery_memory_bytes = 268435456 + +[small_write] +threshold_ratio = 0.9 +conversion_enabled = true +ec_data = 8 +ec_code = 4 +memory_budget_bytes = 1342177280 +queue_capacity = 1024 +min_pipelines = 1 +max_pipelines = 32 +max_batch_bytes = 1048576 + +[s3] +listen = "0.0.0.0:81" +tenant = "preview" +region = "us-east-1" +small_object_limit = 8388608 +list_scan_items = 1001 +list_scan_bytes = 4194304 +continuation_ttl_seconds = 900 +native_budget_bytes = 268435456 +cleanup_backlog_limit = 10000 +ec_data = 2 +ec_code = 1 +max_chunk_size = 1073741824 + +[iceberg] +listen = "0.0.0.0:80" +native_budget_bytes = 268435456 + +[iceberg.gc] +enabled = false +interval_ms = 1000 +step_bytes = 8388608 +step_ms = 1000 +page_items = 64 +page_bytes = 49152 +concurrency = 1 +kv_bytes = 67108864 +kv_requests = 128 +chunk_bytes = 8388608 +chunk_requests = 128 +retry_base_ms = 1000 +retry_max_ms = 60000 +corruption_attempts = 3 +minimum_retention_ms = 604800000 +catalogs = [] diff --git a/doc/backlog/R167-s3-multipart-upload.md b/doc/backlog/R167-s3-multipart-upload.md index 0ba860157..c7927bd96 100644 --- a/doc/backlog/R167-s3-multipart-upload.md +++ b/doc/backlog/R167-s3-multipart-upload.md @@ -22,17 +22,20 @@ The scope boundary is ## Solution 1. Add create, upload-part, list-parts, complete, abort, and required upload - listing operations as a separate S3-owned state machine. + listing operations through S3-owned API and namespace adapters over the + protocol-neutral multipart session, part, and completion core from R190. 2. Store immutable part identities and integrity records durably; a retried part number replaces only that part's selected generation and schedules old private data for cleanup. 3. Complete with one fenced metadata transaction that validates ordered part identities, sizes, checksums, and expected upload state before publishing - one immutable object generation. No concatenation through access-server - memory is allowed. -4. Define multipart-specific ETag/checksum behavior without changing the basic - single-part generation rules. Abort and expiry create bounded, - idempotent cleanup records. + one immutable object generation. Compose the selected parts' chunk-location + arrays with adjusted logical offsets. Complete does not read part data or + concatenate it through access-server memory. +4. Persist each uploaded part's raw 16-byte MD5. The multipart ETag is the + lowercase hexadecimal MD5 of the selected parts' raw MD5 bytes in order, + followed by `-`. Keep the basic single-part ETag rule unchanged. + Abort and expiry create bounded, idempotent cleanup records. 5. Preserve the basic admission bounds for parallel part traffic and wire compatibility for all retry and conflict outcomes. @@ -41,6 +44,8 @@ The scope boundary is - Depends on R152–R166. - Reuses the basic milestone's publication/recovery, streaming input, logical deletion, and integrity contracts. +- Reuses R190's protocol-neutral multipart core; S3 retains its own + authorization, namespace, wire errors, ETag response, and object publication. - R170 owns any accelerated multipart transfer and additionally depends on this requirement before enabling that operation. @@ -48,8 +53,12 @@ The scope boundary is - Given parts uploaded out of order with part retries, when completion names a valid order, assert exact concatenated bytes become visible through one - generation without gateway concatenation. Invariant: completion is atomic - and storage-backed. E2E test. + generation without gateway concatenation or part reads. Invariant: completion + is metadata-only, atomic and storage-backed. E2E test. +- Given completed parts with known MD5 values, when completion selects and + reorders them, assert the ETag uses only the selected raw part MD5 values in + completion order and the part count suffix. Invariant: multipart ETag matches + the S3-compatible composite algorithm. Unit test. - Given missing, duplicated, undersized, checksum-mismatched, or concurrently replaced parts, when completion runs, assert no object publishes and exact errors are stable. Invariant: only the validated part set can publish. diff --git a/doc/backlog/R190-access-iceberg-shared-streaming-io.md b/doc/backlog/R190-access-iceberg-shared-streaming-io.md index 10b976b79..848ad88b7 100644 --- a/doc/backlog/R190-access-iceberg-shared-streaming-io.md +++ b/doc/backlog/R190-access-iceberg-shared-streaming-io.md @@ -33,26 +33,80 @@ S3 and Iceberg metadata semantics: published locations -> Chunk read stream -> owner-backed HTTP response 1. Share deferred HTTP receive-provider installation and bounded native owner - allocation. Authenticate and admit before reading bodies. Preserve signed - AWS-chunked decoding, checksums and unknown-length bounded streaming. + allocation. Authenticate and admit before reading bodies. For objects under + 1 MiB, receive the complete body and submit it to the selected write pipeline + once. For larger bodies, submit each ready 1 MiB receive owner to the write + pipeline while reception continues, then submit the final partial owner. + This receive cadence is independent of the small-object routing threshold + and the 64 KiB integrity frame. Preserve signed AWS-chunked decoding, + checksums and unknown-length bounded streaming. 2. Write a whole file or multipart part through the Chunk writer selected by object size. Do not register catalog intents or await a durable cursor per frame. A frame is transport/integrity granularity, not a catalog transaction. -3. Publish complete immutable file or part metadata only after data completion - and validation. Keep fencing, conflicting-path rejection and ambiguous-result - resolution. Readers cannot observe partial data. + Select the shared small-object pipeline when logical bytes are strictly below + a configurable ratio, initially 0.9, of one strip's data capacity. Larger + objects and uploads whose total length is unknown at admission own their + chunks. Unknown-length uploads stream into that dedicated writer without + buffering to the small-object threshold. EC tails encode only present data + shards; absent data positions contribute zero without writing empty blocks + to DiskIO. +3. Publish complete immutable file or part metadata only after data completion, + length validation and HTTP transfer checksum verification. Require at least + one verified request integrity declaration for Iceberg PUT and UploadPart: + `Content-MD5`, an S3 checksum header/trailer, or a signed payload. Reject a + request with none before reading its body. Treat + content as opaque bytes; do not parse JSON, Avro, Parquet, ORC or Puffin on + the file IO path. A client that supplied a format validates it after reading; + CROWDB modules that generate format files validate their own output. Keep + fencing, conflicting-path rejection and ambiguous-result resolution. + Readers cannot observe partial data. 4. Stream GET and Range through the same Chunk read machinery as S3, retaining - owner-backed buffers and bounded backpressure. Keep Iceberg credentials, - generation checks, format validation, full-file integrity and GC protection. -5. Make multipart completion consume complete part references without restoring - the per-leaf write/commit path. Preserve ordering, replay, format validation - and atomic final-file visibility. -6. Retain crash-safe allocation ownership and reclamation below the per-frame - catalog path. Use durable Chunk allocation/lifecycle ownership rather than - deleting protection and assuming S3 already implements all orphan GC. - Drain submitted writes before reclaim; never free published or pinned data. + owner-backed buffers and bounded backpressure. Read complete overlapping + 64 KiB frames and verify their CRC before exposing requested bytes. Keep + Iceberg credentials, generation checks and GC retention protection. + Use the same 64 KiB frame for Iceberg in this requirement. A possible larger + Iceberg frame remains a later measured choice; do not assume Parquet or + Iceberg checksums replace Chunk frame validation. + Schedule selected strip blocks as an ordered producer-consumer pipeline. + Each read stream has a configurable number of slots, initially three; each + slot holds at most 1 MiB of physical read data and its verified frame views. + Read slots concurrently, but deliver frames in logical order. If the first + slot waits for recovery, completed later slots wait within the fixed budget; + do not schedule a replacement until the consumer releases a slot. Keep the + slot charged while its owner-backed payload remains in the HTTP body, so + socket backpressure bounds retained RPC buffers as well as queued reads. + Also bound total retained read bytes across concurrent streams; a per-stream + slot count alone cannot bound server memory. Account for recovery scratch + space separately from the normal read slots. + Verify each complete 64 KiB frame before exposing its requested payload. + Normal reads must not copy payload in the RPC, DiskIO, EC, Chunk or HTTP + layers; recovery may allocate the reconstructed bytes. Ensure the RPC pool + outlives every buffer retained by a response, including cancellation. +5. Make multipart completion concatenate the selected parts' chunk-location + arrays with adjusted logical offsets. It does not read part bytes or restore + the per-leaf write/commit path. Save each part's raw MD5 on upload and compute + the multipart ETag from the MD5 of the ordered raw MD5 values, followed by + `-`. Share protocol-neutral session, part and completion logic + with the later S3 MPU implementation, while keeping API-specific publication. + Preserve ordering, replay and atomic final-file visibility. + New file records do not require a whole-file SHA-256. Preserve the old + SHA-256 record variant and its full-read verification for existing files; + use a versioned descriptor for new files and verify complete 64 KiB frames + on reads. Calculate a payload SHA-256 only when the request explicitly + declares one, including signed payload verification or an optional SHA-256 + checksum. Multipart SHA-256, when requested, is a composite of part checksums, + not the SHA-256 of concatenated object bytes. Do not reread parts at Complete. + Keep the HTTP ETag separate from storage integrity metadata. +6. Publish one complete descriptor only after durable data completion. A crash + before publication may leave orphan chunks or shared ranges; do not add + per-chunk catalog intents or upload-owner records to the write hot path. + R92's future ChunkDB scanner will identify and reclaim these orphans after + proving they are absent from every published access descriptor and active + writer. Current delete/GC must never free published data before the + configured grace period, and + submitted writes must drain before any physical reuse. 7. Review delete and GC end to end alongside reads and writes: logical - invisibility, reader pins, owner discovery, grace periods, cancelled writes, + invisibility, reference discovery, grace periods, cancelled writes, shared ranges, compaction and physical reuse must form one coherent model. 8. Keep existing stored file descriptors readable, or implement an explicit migration within this work; do not silently invalidate persisted volumes. @@ -61,9 +115,10 @@ S3 and Iceberg metadata semantics: - Existing native receive owners, prepared Chunk writers and Chunk read streams. - Existing Iceberg file, multipart and GC contracts remain acceptance obligations. -- R168/R169/R147 contain deferred shared-storage reclamation work. Do not claim - those are implemented or weaken Iceberg recovery to bypass them; implement - any ownership support required for this path within this requirement. +- R92 will handle scanner discovery and reclamation of allocations abandoned + before publication; R168/R169/R147 contain other deferred shared-storage + reclamation work. R190 may leave unpublished allocations as orphans until + those requirements are implemented, but must preserve all published data. - R188 remains a separate console-authority follow-up. ## Acceptance @@ -72,6 +127,12 @@ S3 and Iceberg metadata semantics: through S3 and Iceberg -> both use bounded 1 MiB owners and 64 KiB frames; no catalog operation is issued per frame. **Bounded shared ingress. Integration test.** +- Given a configured 0.9 ratio and 8+2 or mirror strip geometry, upload bodies + immediately below, at and above the derived data-capacity threshold -> only + below-threshold objects use a shared chunk. **Configurable routing. Integration test.** +- Given an 8+2 tail containing two data shards, seal and lose two real data + segments or one data and one parity segment -> recover exact bytes without + writing six empty data segments. **Partial EC recovery. Integration test.** - Given signed chunks, corrupted signatures/checksums, short bodies and cancelled requests, upload -> reject without publishing metadata; release owner credits and drain in-flight writes. **No partial visibility. Integration test.** @@ -82,15 +143,24 @@ S3 and Iceberg metadata semantics: retained buffers, integrity checks and cancellation propagation. **Shared bounded reads. Integration test.** - Given a 100 MiB multipart upload (twenty 5 MiB parts), complete/replay/restart - -> one correct immutable file, - no per-leaf rewrite/commit loop and no premature part reclamation. + -> one correct immutable file, no per-leaf rewrite/commit loop and no + premature part reclamation. **Multipart correctness. E2E test.** +- Given saved MD5 values for selected MPU parts, Complete in a reordered subset + -> return the MD5 of their ordered raw digests with the selected part-count + suffix without a part read. **Metadata-only ETag. Integration test.** +- Given an unsigned upload without a requested SHA-256 checksum, write without + hashing the payload with SHA-256; given a declared signed payload or SHA-256 + checksum, validate it before publication. Existing SHA-256 file records remain + readable. **Conditional SHA-256. Integration test.** - Given process loss before/after data completion and metadata publication, - recover -> unpublished allocations remain discoverable and eventually reclaim; - published/pinned data remain readable. **Crash-safe ownership. E2E test.** -- Given published, pinned and unpublished data, delete and run GC across restart + recover -> no partial file is visible; unpublished allocations may remain + orphaned for R92; published data remain readable through the grace period. + **Crash publication boundary. E2E test.** +- Given published and unpublished data, delete and run GC across restart -> logical deletion precedes physical reclamation; retained reads remain valid; - ownership is discoverable and freed ranges are not reused before writes drain. + freed ranges are not reused before writes drain; unpublished orphan discovery + is deferred to R92. **Delete/GC consistency. E2E test.** - Given existing file descriptors, restart and read -> preserve bytes and ranges. **Persisted-data readability. Integration test.** diff --git a/doc/backlog/R92-chunkdb-in-chunk-gc.md b/doc/backlog/R92-chunkdb-in-chunk-gc.md index 194f0e299..7257a945e 100644 --- a/doc/backlog/R92-chunkdb-in-chunk-gc.md +++ b/doc/backlog/R92-chunkdb-in-chunk-gc.md @@ -10,6 +10,15 @@ to avoid global merge overhead. **Solution**: Implement in-chunk GC operations (ReclaimStrip, CollapseStrip, MergeStrips) for shared chunks. Add logical-to-physical offset mapping -to support GC while keeping chunk IDs stable. +to support GC while keeping chunk IDs stable. Add a ChunkDB orphan scanner for +chunks and shared ranges allocated by access uploads that crash or fail before +their complete file/object descriptor is published. R190 intentionally does not +write per-chunk catalog intents or upload-owner records on its write hot path. +The scanner must compare candidates with authoritative published S3 and Iceberg +references and reader protection before reclaiming, and must not infer orphan +status merely from age or a missing intermediate upload record. Account for +in-flight writers and delayed publication so physical ranges are never reused +while a writer or reader can still own them. Report candidate and reclaimed +bytes separately. -**Scope**: Placeholder - detailed design to be refined before implementation. \ No newline at end of file +**Scope**: Placeholder - detailed design to be refined before implementation. diff --git a/doc/design/config/design-crowdb-config.md b/doc/design/config/design-crowdb-config.md index bc910ef2f..84aee0207 100644 --- a/doc/design/config/design-crowdb-config.md +++ b/doc/design/config/design-crowdb-config.md @@ -22,10 +22,10 @@ validated, and activated. ## 1. Scope -The `crowdb-kv-server`, `crowdb-diskdb`, `crowdb-chunkdb`, and -`crowdb-diskio` processes accept typed TOML startup configuration. A service -may require a file or make it optional, but a supplied file follows the same -resolution and failure rules in every process. +The `crowdb-kv-server`, `crowdb-diskdb`, `crowdb-chunkdb`, `crowdb-diskio`, +`crowdb-access-server`, and `crowdb-iceberg` processes accept typed TOML startup +configuration. A service may require a file or make it optional, but a supplied +file follows the same resolution and failure rules in every process. This contract covers process configuration. Durable cluster topology, membership, allocation metadata, and application data keep their existing @@ -79,12 +79,23 @@ Common section names describe process roles: The service schemas retain their different domain sections: -| Service | File policy | `server.rpc_workers` default | Principal domain sections | -|---------|-------------|------------------------------|-------------------------------------------------| -| KV | Optional | 2 | Paxos, WAL, engine, metrics | -| diskdb | Required | 2 | Storage, heartbeat, persistence, scanner, sync | -| chunkdb | Required | 2 | Storage, placement, lifecycle, topology, clients| -| diskio | Optional | 4 | Engine, group-0 discovery, metrics, disk entries| +| Service | File policy | `server.rpc_workers` default | Principal domain sections | +| ------- | ----------- | ---------------------------- | ------------------------------------------------ | +| KV | Optional | 2 | Paxos, WAL, engine, metrics | +| diskdb | Required | 2 | Storage, heartbeat, persistence, scanner, sync | +| chunkdb | Required | 2 | Storage, placement, lifecycle, topology, clients | +| diskio | Optional | 4 | Engine, group-0 discovery, metrics, disk entries | +| Access | Optional | N/A | HTTP listeners, shared read, S3, Iceberg GC | + +The S3 and Iceberg access processes accept the same `AccessConfig` schema. +Each process activates only its own protocol section plus `[common]`, `[read]`, +and `[small_write]`. The file is selected with `--config`; a supplied file is +validated before connecting to dependencies or binding listeners. Read slots, +the per-read window, the global retained-byte budget, the separate EC recovery +budget, and small-write memory limits are static startup settings. Credentials +and bearer tokens remain in the environment. For access settings that predate the +file, an explicitly populated TOML value takes precedence over the legacy +environment variable; an omitted TOML value keeps the legacy behavior. All schemas tolerate unknown keys so a newer file can be staged before a binary upgrade. A known key with the wrong type or invalid value rejects the diff --git a/doc/user-manual/user-guide.md b/doc/user-manual/user-guide.md index 668fcfe23..e5c8d01ed 100644 --- a/doc/user-manual/user-guide.md +++ b/doc/user-manual/user-guide.md @@ -957,6 +957,17 @@ export CROWDB_MANAGEMENT_SEEDS=127.0.0.1:10000 export CROWDB_ICEBERG_LISTEN=127.0.0.1:8181 ``` +Both `crowdb-iceberg` and `crowdb-access-server` accept `--config `. +The tracked Access TOML example is +`app/crowdb-access-server/conf/crowdb_access_server_config.toml`; it contains +shared DiskIO, read and small-write budgets, S3 resources, and Iceberg GC limits. The +processes read the same schema and use their respective sections. A provided +file takes precedence over legacy environment settings for fields it sets; +omitted fields retain their defaults or legacy environment values. Keep +credentials and bearer tokens in the environment. In the single-node +container, the rendered files are `/opt/crowdb/run/config/s3.toml` and +`/opt/crowdb/run/config/iceberg.toml`. + Supply three distinct, randomly generated 32–256-character ASCII tokens through your secret-management environment: `CROWDB_ICEBERG_READ_TOKEN`, `CROWDB_ICEBERG_MANAGE_TOKEN` and `CROWDB_ICEBERG_CLEAR_TOKEN`. Configure every diff --git a/doc/working/plan-access-streaming-io.md b/doc/working/plan-access-streaming-io.md index e6b949529..2698148ad 100644 --- a/doc/working/plan-access-streaming-io.md +++ b/doc/working/plan-access-streaming-io.md @@ -7,27 +7,74 @@ Implements [R190](../backlog/R190-access-iceberg-shared-streaming-io.md). Goal: share S3's whole-object write/read path with Iceberg, preserving authority and crash recovery while eliminating per-frame catalog operations. -Status: Ready after R187 completion. No production refactor has started. +Status: Bounded S3 and Iceberg reads, access configuration, conditional S3 +SHA-256, shared HTTP receive plumbing, Iceberg pin removal, location descriptors, +whole-object uploads and metadata-only MPU completion pass focused tests. +Fault coverage, operation-count measurements and final gates remain. + +The access processes now have a shared typed TOML startup schema. The single-node +container renders it separately for S3 and Iceberg; it exposes read slots, +window size, retained bytes, recovery memory, small-write memory and pipeline +limits, S3 resources and Iceberg GC limits +while keeping secrets in the environment. ## Execution -- [ ] **Complete flow review**: map read/write/delete/GC, ownership, buffer +- [~] **Complete flow review and counters**: map read/write/delete/GC, ownership, buffer lifetime, publication and physical reuse for S3 and Iceberg. Review the whole - flow before implementation; include cancellation and crash boundaries. -- [ ] **Shared receive plumbing**: extract deferred native HTTP receive-provider + flow before implementation; include cancellation and crash boundaries. Record + read-window, location-normalization and Chunk layout-query counts through + CROWDB metrics, then compare with perf counters before changing read flow. +- [~] **Remove legacy request-level metadata amplification**: removed the + file-request, file-publication, delegated-credential and table-load GC pin writes; + removed the pin record, protocol schema, GC handling and manual commands. + No old Iceberg pin data exists, so no compatibility path is needed. + UploadPart still updates its session through multiple serialized CAS + operations, and the old sealer still rereads complete non-JSON files. + Treat new-path bytes as opaque and keep part publication independent across + part numbers. Measure catalog operations per request. +- [x] **Bounded ordered read pipeline**: replace the 64 MiB fetch window with + configurable per-stream slots (default three, at most 1 MiB physical data + each). Schedule strip reads concurrently, verify complete frames, retain + out-of-order results within those slots, and hand owner-backed payload views + to HTTP in order. Add a global retained-byte budget across streams and a + separate recovery scratch budget. Hold credits until the HTTP body releases the buffers; + cancel outstanding work when the response ends. Remove copies in normal EC + reads and in the RPC-to-DiskIO response handoff. Before retaining pooled RPC + buffers in HTTP, prove or extend pool lifetime through their final release. +- [x] **Remove repeated read-stream location work**: normalize a descriptor + once per stream, select only locations overlapping each window, and export + lock-free normalization, range-scan, window and layout-query counters from + Chunk I/O through S3 `/metrics`. Files: `lib/crowdb-chunk-client/src/chunk/chunk_reader.rs`, + `lib/crowdb-chunk-client/src/metrics.rs`, `lib/crowdb-chunk-client/src/client.rs`, + `lib/crowdb-access-s3/src/metrics.rs`. Focused Chunk reader and S3 metrics + tests pass; perf attribution remains under the active review task. +- [x] **Conditional S3 payload SHA-256**: maintain MD5 for the existing ETag, + and initialize payload SHA-256 only when a request declares one. Both generic + and native-owner upload paths select the digest before receiving body bytes. +- [x] **Shared receive plumbing**: extract deferred native HTTP receive-provider installation from the S3 facade into a protocol-neutral access-server module; - keep S3 behavior covered by existing receive-provider tests. Inspect native - owner handoff and Iceberg signed-body decoding before wiring the fast path. -- [ ] **Complete stream descriptors and ownership**: define bounded durable - locations for complete files/parts and allocation ownership for unpublished - data. Preserve old descriptors; integrate GC with the new owner references. -- [ ] **Whole-object uploads**: reuse prepared Chunk writers, 1 MiB native owners + install bounded native owners for admitted Iceberg PUT and UploadPart requests. + Keep S3 receive-provider behavior covered and verify Iceberg signed-body + decoding before handing native owners directly to the new Chunk writer. +- [x] **Complete stream descriptors and ownership**: define bounded durable + locations for complete files/parts. Preserve old SHA-256 descriptors and their verification; new descriptors + default to frame CRC and carry optional requested checksums separately from + the HTTP ETag. Published references remain protected through the GC grace period; the + user assigned unpublished orphan discovery to R92's future ChunkDB scanner, + so do not add upload-owner or per-chunk intent writes to the data path. +- [x] **Whole-object uploads**: reuse prepared Chunk writers, 1 MiB native owners and 64 KiB frames; remove per-leaf catalog intent/durable-completion waits. - Publish only after checksum, format and storage completion. -- [ ] **Shared reads**: use Chunk read streams and owner-backed Bytes for full - GET and ranges; preserve integrity, pins and cancellation. -- [ ] **Multipart completion**: compose validated completed parts without the + Reject Iceberg PUT/UploadPart lacking Content-MD5, an S3 checksum or a signed + payload before reading the body. Publish only after declared transfer checksum + and durable storage completion; leave client file formats opaque. +- [x] **Shared reads**: use Chunk read streams and owner-backed Bytes for full + GET and ranges; preserve integrity, the GC grace period and cancellation. +- [~] **Multipart completion**: compose validated completed parts without the old serial per-leaf rewrite/commit path; retain recovery and terminal credits. + Save part MD5 values at upload and produce the final ETag from those values. + Compute SHA-256 for a part only when explicitly requested; preserve any + requested composite checksum as separate metadata without rereading parts. - [ ] **Faults and measurements**: test cancellation, lost replies, crash points, ownership reclamation, stale grants and existing records; compare identical 5 MiB baseline plus ordinary 10 KiB/1 MiB/12 MiB/100 MiB PUTs and a @@ -42,11 +89,82 @@ Status: Ready after R187 completion. No production refactor has started. and one conditional write, then forces readable-cursor completion. - Existing S3 selects the prepared large writer for this size and publishes object metadata after `on_finish`; receive owners are configured at 1 MiB. +- Confirmed write receive cadence: an object below 1 MiB is received in full + before one push to the selected pipeline; larger objects hand off each ready + 1 MiB owner as reception continues, plus a final partial owner. This is + separate from both the 0.9 × strip-capacity routing threshold and 64 KiB + frame validation. Current S3 native owner handoff applies to known-length + large writers; R190 must extend the shared path accordingly. - Release/null-DiskIO measurements: ordinary PUT 2791/2682/2752 ms; UploadPart 2091/2184/2159 ms. All HTTP 200. Temporary probe was removed. - Debug multipart intermittently exceeds its existing 10 s deadline even after streaming MD5 verification. This remains unresolved; do not hide it by only changing test profiles or raising the timeout. +- Confirmed 8+2 partial EC with two 1 MiB data shards: incremental parity matches + the zero-filled reference and recovers two data failures or mixed data/code + failures. The focused EC test is in `lib/crowdb-common/rust/tests/ec_test.rs`. +- Chunk read stream normalizes locations once per stream, schedules up to three + concurrent physical reads of at most 1 MiB each, and caches valid Chunk layouts + across windows. Credits remain reserved while HTTP holds payload views. +- The user confirmed 64 KiB write frames for both S3 and Iceberg for now. + Revisit a larger Iceberg frame only with evidence about actual read units + and end-to-end integrity coverage. +- Legacy Iceberg `FileHttp::execute` persisted one `GcPin` per file request, + `FileRepository::publish` added a publication pin, and the table loader and + credential endpoint also created pins; those writes have been removed. + `MultipartRepository::part` still reloads the session before and after + reading a part. `reserve_part` and `settle_part` serialize part uploads through + session revisions. `FileSealer::seal` reads complete non-JSON files before + format validation. These are separate from the per-leaf write intents and + must be removed or amortized in the R190 path. +- Frame decode previously copied each physical frame and payload, then copied + the growing output again for every adjacent range (quadratic total copying). + Extraction and payload slices now reuse `Bytes` ownership. All Chunk read + APIs return verified buffer arrays; streams send each buffer without joining. + `frame_decode_wait_ns` measures the decoder and `frame_parse_wait_ns` + measures parsing plus CRC. The removed range-merging counter previously + measured 5.9–7.5 ms on a 4 MiB release run; that merge no longer executes. +- The original bitwise CRC32C and byte-table replacement were too slow on the + 4 MiB mixed-read release run: `frame_parse_wait_ns` summed to 17.5 ms with + the table implementation. Hardware-dispatched CRC32C retains the seed-zero, + no-final-XOR wire checksum and reduced that sum to 1.49 ms. The independent + bitwise CRC reference and existing cross-language frame vector pass. On two + otherwise identical four-request release runs, measured read throughput was + 94.9 then 165.2 MiB/s; RPC and host noise remain, so this is directional. +- After removing payload range merging, the same four-request release fixture + passed at 173.8 MiB/s with 2.27 ms cumulative frame parse time. This is a + small concurrency-2 null-DiskIO sample, not a throughput target. Whole-object + and range reads now return arrays of verified `Bytes`; HTTP streams emit each + frame buffer without an object-sized copy. Callers that require contiguous + bytes, such as legacy Iceberg block parsing, materialize them explicitly. +- The normal TCP RPC receive path now detaches the buffer owner from its pool, + so DiskIO and the ordered S3 body retain the original payload allocation. + Split frames are CRC checked across buffer views without joining the payload. + The RDMA/non-system-pool fallback still copies once and needs a separate review. +- Legacy Iceberg tree records still use their per-leaf reader and full-file + digest. New location descriptors use the shared Chunk read stream and emit + verified owner-backed buffers without joining the object. +- The bounded Chunk read tests pass all 11 focused cases, the Access Server + target suite passes, and the S3 full-stack suite passes all 17 cases after + configuration wiring. These checks establish correctness of this slice; + they do not establish an Iceberg throughput improvement or complete R190. +- S3 single-part integrity now initializes payload SHA-256 only when a signed + payload digest is declared. Focused integrity and streaming tests, S3 + Clippy, Rust formatting, and all 17 S3 full-stack cases passed before the + streamed-writer change; the full stack is being rerun. The old Iceberg tree + descriptor retains its whole-file SHA-256, while new location descriptors + rely on frame checksums and verified request integrity. +- Shared objects larger than one frame enter the pipeline with the first 1 MiB + receive buffer. A bounded two-buffer channel feeds subsequent data while the + pipeline writes the current buffer. Cancellation seals the abandoned chunk + and opens another chunk for queued objects. S3 and Iceberg wait for the + readable cursor before publishing even a sub-64 KiB object. The focused + shared-object and cancellation E2E cases pass; the full suite is running. +- After rebasing onto `origin/main` at `a59d24fb`, the Java acceptance setup + prefetches Maven runtime dependencies before its offline SDK invocations. + Offline `exec:java` reached the Java program with all dependencies present. + The ChunkDB restart fixture now waits for a usable registry read instead of + sleeping for three seconds; its focused and full reader E2E suites pass. ## Files diff --git a/lib/crowdb-access-iceberg/Cargo.toml b/lib/crowdb-access-iceberg/Cargo.toml index 39bb0faef..252c27795 100644 --- a/lib/crowdb-access-iceberg/Cargo.toml +++ b/lib/crowdb-access-iceberg/Cargo.toml @@ -16,12 +16,14 @@ test-util = [] [dependencies] async-trait = "0.1" base64 = "0.22" +bincode = "1.3" bytes = "1" chrono = { version = "0.4", default-features = false, features = ["std"] } crc32fast = "1" data-encoding = "2" hmac = "0.12" lz4_flex = { version = "0.11", default-features = false, features = ["std", "safe-encode", "safe-decode", "frame"] } +md-5 = "0.10" crowdb-chunk-kv-client = { path = "../crowdb-chunk-kv-client" } crowdb-chunk-client = { path = "../crowdb-chunk-client" } crowdb-common = { workspace = true } diff --git a/lib/crowdb-access-iceberg/src/commit/provenance.rs b/lib/crowdb-access-iceberg/src/commit/provenance.rs index 36c883e0e..2f09b5a5c 100644 --- a/lib/crowdb-access-iceberg/src/commit/provenance.rs +++ b/lib/crowdb-access-iceberg/src/commit/provenance.rs @@ -127,6 +127,7 @@ impl PriorManifestSource { let payload = match &record.content { FileContent::Inline { bytes, .. } => bytes.capacity(), FileContent::Chunks { .. } => 0, + FileContent::Locations { bytes, etag } => bytes.capacity() + etag.capacity(), }; let bytes = std::mem::size_of::() + 128 + path.len() * 2 + payload; *retained = retained diff --git a/lib/crowdb-access-iceberg/src/file.rs b/lib/crowdb-access-iceberg/src/file.rs index 98dcff43f..ef4fd3618 100644 --- a/lib/crowdb-access-iceberg/src/file.rs +++ b/lib/crowdb-access-iceberg/src/file.rs @@ -60,7 +60,7 @@ pub(crate) use location::validate_relative_key; pub use location::{FileLocation, TableLocation, MAX_OBJECT_KEY_BYTES}; pub use multipart::{ MultipartCompletion, MultipartLimits, MultipartPart, MultipartPartMutation, MultipartPhase, - MultipartSession, + MultipartSession, MultipartStreamPart, }; pub use multipart_admission::{ MultipartAdmissionLimits, MultipartAdmissionRecord, MultipartCredit, MultipartCreditAction, diff --git a/lib/crowdb-access-iceberg/src/file/blocks.rs b/lib/crowdb-access-iceberg/src/file/blocks.rs index 6ca7d8af1..86e3ac7d1 100644 --- a/lib/crowdb-access-iceberg/src/file/blocks.rs +++ b/lib/crowdb-access-iceberg/src/file/blocks.rs @@ -31,11 +31,30 @@ pub enum FileIoError { #[async_trait] pub trait FileBlockStore: Send + Sync { + fn stream_client(&self) -> Option<&ChunkIoClient> { + None + } + + async fn read_locations( + &self, + _locations: &[Location], + _start: u64, + _end: u64, + ) -> Result, FileIoError> { + Err(FileIoError::Bounds) + } + async fn put(&self, owner: FileIdentity, height: u8, bytes: &[u8]) -> Result; async fn read(&self, root: &ChunkRoot) -> Result, FileIoError>; async fn reclaim(&self, _root: &ChunkRoot) -> Result { Ok(crowdb_chunk_client::ReclaimOutcome::Deferred) } + async fn reclaim_location( + &self, + _location: &Location, + ) -> Result { + Ok(crowdb_chunk_client::ReclaimOutcome::Deferred) + } } #[derive(Clone)] @@ -53,6 +72,27 @@ impl NativeFileBlocks { #[async_trait] impl FileBlockStore for NativeFileBlocks { + async fn reclaim_location( + &self, + location: &Location, + ) -> Result { + let (allocator, _) = self.client.storage_parts(); + Ok(crowdb_chunk_client::reclaim_location(allocator.as_ref(), location).await?) + } + + fn stream_client(&self) -> Option<&ChunkIoClient> { + Some(&self.client) + } + + async fn read_locations( + &self, + locations: &[Location], + start: u64, + end: u64, + ) -> Result, FileIoError> { + Ok(self.client.read_range(locations, start, end).await?.concat()) + } + async fn reclaim(&self, root: &ChunkRoot) -> Result { root.validate()?; let location = Location { @@ -116,9 +156,9 @@ impl FileBlockStore for NativeFileBlocks { logical_offset: root.logical_offset, logical_length: root.logical_length, }; - let bytes = self.client.read_object(&[location]).await?; + let bytes = self.client.read_object(&[location]).await?.concat(); verify_block(root, &bytes)?; - Ok(bytes.to_vec()) + Ok(bytes) } } diff --git a/lib/crowdb-access-iceberg/src/file/content.rs b/lib/crowdb-access-iceberg/src/file/content.rs index f2a6bf11f..bdef743af 100644 --- a/lib/crowdb-access-iceberg/src/file/content.rs +++ b/lib/crowdb-access-iceberg/src/file/content.rs @@ -1,3 +1,4 @@ +use crowdb_protocol::chunkdb::rpc::Location; use crowdb_protocol::common::ChunkId; use sha2::{Digest, Sha256}; @@ -50,9 +51,54 @@ impl ChunkRoot { pub enum FileContent { Inline { codec: InlineCodec, bytes: Vec }, Chunks { root: Option }, + Locations { bytes: Vec, etag: String }, } impl FileContent { + /// Encodes complete Chunk locations once, after the whole file is durable. + /// # Errors + /// Rejects gaps, overlapping locations and metadata that cannot fit one record. + pub fn from_locations( + locations: &[Location], + length: u64, + etag: String, + ) -> Result { + validate_locations(locations, length)?; + validate_etag(&etag)?; + let bytes = bincode::serialize(locations).map_err(|_| ValidationError::Record)?; + if bytes.len() > crate::record::MAX_RECORD_BYTES - 4096 { + return Err(ValidationError::RecordTooLarge); + } + Ok(Self::Locations { bytes, etag }) + } + + /// # Errors + /// Rejects malformed location encodings and inconsistent logical ranges. + pub fn locations(&self, length: u64) -> Result>, ValidationError> { + let Self::Locations { bytes, etag } = self else { + return Ok(None); + }; + validate_etag(etag)?; + if bytes.len() < 8 || bytes.len() > crate::record::MAX_RECORD_BYTES - 4096 { + return Err(ValidationError::RecordTooLarge); + } + let count = u64::from_le_bytes(bytes[..8].try_into().map_err(|_| ValidationError::Record)?); + if count > 1250 { + return Err(ValidationError::RecordTooLarge); + } + let locations: Vec = bincode::deserialize(bytes).map_err(|_| ValidationError::Record)?; + validate_locations(&locations, length)?; + Ok(Some(locations)) + } + + #[must_use] + pub fn etag(&self) -> Option<&str> { + match self { + Self::Locations { etag, .. } => Some(etag), + _ => None, + } + } + pub(crate) fn validate(&self, length: u64, digest: &[u8; 32]) -> Result<(), ValidationError> { match self { Self::Inline { .. } => { @@ -69,6 +115,12 @@ impl FileContent { return Err(ValidationError::Record); } } + Self::Locations { .. } => { + if *digest != [0; 32] { + return Err(ValidationError::Record); + } + self.locations(length)?; + } } Ok(()) } @@ -120,3 +172,41 @@ impl FileContent { Ok(Some(decoded)) } } + +fn validate_locations(locations: &[Location], length: u64) -> Result<(), ValidationError> { + let mut cursor = 0; + for location in locations { + if location.chunk_id.is_none() + || location.length == 0 + || location.logical_length == 0 + || location.logical_offset != cursor + || location.offset.checked_add(location.length).is_none() + { + return Err(ValidationError::Record); + } + cursor = cursor + .checked_add(location.logical_length) + .ok_or(ValidationError::Record)?; + } + if cursor != length { + return Err(ValidationError::Record); + } + Ok(()) +} + +fn validate_etag(etag: &str) -> Result<(), ValidationError> { + let (digest, count) = etag + .split_once('-') + .map_or((etag, None), |(digest, count)| (digest, Some(count))); + if digest.len() != 32 + || !digest + .bytes() + .all(|byte| byte.is_ascii_hexdigit() && !byte.is_ascii_uppercase()) + || count.is_some_and(|count| { + count.is_empty() || count.starts_with('0') || count.parse::().map_or(true, |n| n == 0) + }) + { + return Err(ValidationError::Record); + } + Ok(()) +} diff --git a/lib/crowdb-access-iceberg/src/file/multipart.rs b/lib/crowdb-access-iceberg/src/file/multipart.rs index c7345469a..4f13efa3e 100644 --- a/lib/crowdb-access-iceberg/src/file/multipart.rs +++ b/lib/crowdb-access-iceberg/src/file/multipart.rs @@ -2,6 +2,8 @@ use crate::catalog::CatalogContext; use crate::error::ValidationError; use crate::key::{CatalogScope, FileId, IcebergKey, OperationId}; use crate::operation::PayloadReference; +use sha2::{Digest, Sha256}; +use std::fmt::Write; use super::{AssemblyProgress, FileContent, FileDigest, FileIdentity, FileLocation, FileTree}; @@ -114,19 +116,21 @@ impl MultipartSession { if let Some(pending) = &self.pending { pending.validate(self)?; } - let candidate = self - .completion - .as_ref() - .and_then(|completion| completion.candidate.as_ref()); + let completion = self.completion.as_ref(); let valid = match self.phase { MultipartPhase::Open => self.completion.is_none() && self.published.is_none(), MultipartPhase::Completing => { - self.completion.is_some() && candidate.is_none() && self.published.is_none() + completion.is_some_and(|completion| completion.publication.is_none()) + && self.published.is_none() } MultipartPhase::Publishing | MultipartPhase::Conflicted => { - candidate.is_some() && self.published.is_none() + completion.is_some_and(|completion| completion.publication.is_some()) + && self.published.is_none() + } + MultipartPhase::Published => { + completion.is_some_and(|completion| completion.publication.is_some()) + && self.published.is_some() } - MultipartPhase::Published => candidate.is_some() && self.published.is_some(), MultipartPhase::Aborted => self.published.is_none(), }; if !valid { @@ -152,7 +156,9 @@ impl MultipartCompletion { || progress.part_offset > progress.completed_bytes || progress.active.is_some() != progress.part_digest.is_some() || progress.active.is_some() != (progress.part_offset > 0) - || (progress.writer.is_none() && (progress.next_part != 0 || progress.completed_bytes != 0)) + || (progress.writer.is_none() + && self.publication.is_none() + && (progress.next_part != 0 || progress.completed_bytes != 0)) { return Err(ValidationError::Record); } @@ -183,7 +189,10 @@ impl MultipartCompletion { return Err(ValidationError::Record); } } - if self.candidate.is_some() != self.publication.is_some() { + if self.candidate.is_none() + && self.publication.is_some() + && (progress.writer.is_some() || progress.next_part != self.selected_parts) + { return Err(ValidationError::Record); } if let Some(publication) = &self.publication { @@ -207,7 +216,14 @@ pub struct MultipartPart { pub revision: u64, pub modified_ms: u64, pub owner: FileIdentity, - pub tree: FileTree, + pub tree: Option, + pub stream: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct MultipartStreamPart { + pub length: u64, + pub content: FileContent, } impl MultipartPart { @@ -228,7 +244,58 @@ impl MultipartPart { if self.number == 0 || self.number > 10_000 || self.revision == 0 || self.modified_ms == 0 { return Err(ValidationError::Record); } - validate_tree(&self.tree) + match (&self.tree, &self.stream) { + (Some(tree), None) => validate_tree(tree), + (None, Some(stream)) => stream.content.validate(stream.length, &[0; 32]), + _ => Err(ValidationError::Record), + } + } + + #[must_use] + pub fn length(&self) -> u64 { + self.tree.as_ref().map_or_else( + || self.stream.as_ref().map_or(0, |stream| stream.length), + |tree| tree.length, + ) + } + + #[must_use] + pub fn etag(&self) -> String { + self.stream + .as_ref() + .and_then(|stream| stream.content.etag()) + .map_or_else( + || { + let mut etag = String::with_capacity(64); + if let Some(tree) = &self.tree { + for byte in tree.digest { + write!(&mut etag, "{byte:02x}").expect("string write cannot fail"); + } + } + etag + }, + str::to_owned, + ) + } + + #[must_use] + /// # Panics + /// Panics if the part has neither a legacy tree nor a streamed descriptor. + pub fn selection_digest(&self) -> [u8; 32] { + if let Some(tree) = &self.tree { + return tree.digest; + } + let stream = self + .stream + .as_ref() + .expect("validated multipart part has content"); + let mut digest = Sha256::new(); + digest.update(stream.length.to_le_bytes()); + if let FileContent::Locations { bytes, etag } = &stream.content { + digest.update(bytes); + digest.update(etag.as_bytes()); + } + digest.finalize().into() } /// # Errors @@ -244,7 +311,7 @@ impl MultipartPart { || self.owner.table != session.owner.table || self.owner.file == session.owner.file || self.number > session.limits.max_parts - || self.tree.length > session.limits.max_part_bytes + || self.length() > session.limits.max_part_bytes { return Err(ValidationError::Record); } @@ -263,7 +330,7 @@ impl MultipartPartMutation { self.after.validate_binding(session)?; if session.phase != MultipartPhase::Open || session.part_count == 0 - || session.staged_bytes < self.after.tree.length + || session.staged_bytes < self.after.length() { return Err(ValidationError::Record); } diff --git a/lib/crowdb-access-iceberg/src/file/multipart_recovery.rs b/lib/crowdb-access-iceberg/src/file/multipart_recovery.rs index caac87869..b916cc691 100644 --- a/lib/crowdb-access-iceberg/src/file/multipart_recovery.rs +++ b/lib/crowdb-access-iceberg/src/file/multipart_recovery.rs @@ -154,6 +154,19 @@ impl MultipartRecovery { self.repository.abort(session).await? } else if session.phase == MultipartPhase::Completing { let completion = session.completion.as_ref().ok_or(ValidationError::Record)?; + if completion.progress.next_part == 0 { + if let Some(changed) = self + .repository + .prepare_stream_publication(session, now_ms) + .await? + { + return Ok(if changed { + RecoveryAction::Progressed + } else { + RecoveryAction::Deferred + }); + } + } if completion.progress.next_part == completion.selected_parts { return Ok(RecoveryAction::AwaitingSeal); } diff --git a/lib/crowdb-access-iceberg/src/file/multipart_repository/completion.rs b/lib/crowdb-access-iceberg/src/file/multipart_repository/completion.rs index 59bd6e8bf..ec5034f6a 100644 --- a/lib/crowdb-access-iceberg/src/file/multipart_repository/completion.rs +++ b/lib/crowdb-access-iceberg/src/file/multipart_repository/completion.rs @@ -103,7 +103,7 @@ impl MultipartRepository { .part(session, selected.number) .await? .ok_or(ValidationError::Record)?; - if part.revision != selected.revision || part.tree.digest != selected.digest { + if part.revision != selected.revision || part.selection_digest() != selected.digest { return Err(ValidationError::Record.into()); } let progress = assembly @@ -112,7 +112,7 @@ impl MultipartRepository { &AssemblyPart { ordinal: completion.progress.next_part, owner: part.owner, - tree: part.tree, + tree: part.tree.ok_or(ValidationError::Record)?, }, ) .await?; diff --git a/lib/crowdb-access-iceberg/src/file/multipart_repository/parts.rs b/lib/crowdb-access-iceberg/src/file/multipart_repository/parts.rs index 2e0f29319..927e99cf8 100644 --- a/lib/crowdb-access-iceberg/src/file/multipart_repository/parts.rs +++ b/lib/crowdb-access-iceberg/src/file/multipart_repository/parts.rs @@ -75,8 +75,8 @@ impl MultipartRepository { .ok_or(ValidationError::Record)?; next.staged_bytes = session .staged_bytes - .checked_sub(before.as_ref().map_or(0, |part| part.tree.length)) - .and_then(|bytes| bytes.checked_add(part.tree.length)) + .checked_sub(before.as_ref().map_or(0, MultipartPart::length)) + .and_then(|bytes| bytes.checked_add(part.length())) .ok_or(ValidationError::Record)?; next.pending = Some(MultipartPartMutation { before, after }); self.exchange(session, &next).await diff --git a/lib/crowdb-access-iceberg/src/file/multipart_repository/publication.rs b/lib/crowdb-access-iceberg/src/file/multipart_repository/publication.rs index 2d374d6ae..52440f941 100644 --- a/lib/crowdb-access-iceberg/src/file/multipart_repository/publication.rs +++ b/lib/crowdb-access-iceberg/src/file/multipart_repository/publication.rs @@ -1,14 +1,122 @@ use crate::catalog::CatalogError; use crate::error::ValidationError; use crate::file::{ - file_key, FileContent, FileRecord, FileRepository, FileTree, MultipartPhase, MultipartSession, + file_key, ContentFormat, FileContent, FileKind, FileRecord, FileRepository, FileTree, MultipartPhase, + MultipartSelection, MultipartSession, }; use crate::operation::PayloadStore; use crate::record::StorageRecord; +use crowdb_protocol::chunkdb::rpc::Location; +use md5::{Digest, Md5}; +use std::fmt::Write; use super::{check_live, increment, MultipartRepository}; impl MultipartRepository { + /// Publishes selected durable part locations without reading or rewriting part bytes. + /// # Errors + /// Rejects changed parts, mixed storage formats, oversized descriptors and stale sessions. + pub async fn prepare_stream_publication( + &self, + session: &MultipartSession, + now_ms: u64, + ) -> Result, CatalogError> { + session.validate()?; + check_live(session, now_ms)?; + if session.phase != MultipartPhase::Completing { + return Err(CatalogError::Conflict); + } + let completion = session.completion.as_ref().ok_or(ValidationError::Record)?; + if completion.progress.next_part != 0 { + return Ok(None); + } + if self.load(session.context, session.upload).await?.as_ref() != Some(session) { + return Ok(Some(false)); + } + let bytes = PayloadStore::new(self.store.clone()) + .get(&completion.selection) + .await?; + let selection = MultipartSelection::decode(&bytes)?; + let mut locations = Vec::::new(); + let mut length = 0_u64; + let mut md5 = Md5::new(); + for selected in selection.parts() { + let part = self + .part(session, selected.number) + .await? + .ok_or(ValidationError::Record)?; + if part.revision != selected.revision || part.selection_digest() != selected.digest { + return Err(ValidationError::Record.into()); + } + let Some(stream) = &part.stream else { + return Ok(None); + }; + let etag = stream.content.etag().ok_or(ValidationError::Record)?; + for pair in etag.as_bytes().chunks_exact(2) { + let pair = std::str::from_utf8(pair).map_err(|_| ValidationError::Record)?; + md5.update([u8::from_str_radix(pair, 16).map_err(|_| ValidationError::Record)?]); + } + for mut location in stream + .content + .locations(stream.length)? + .ok_or(ValidationError::Record)? + { + location.logical_offset = location + .logical_offset + .checked_add(length) + .ok_or(ValidationError::Record)?; + locations.push(location); + } + length = length + .checked_add(stream.length) + .filter(|length| *length <= session.limits.max_file_bytes) + .ok_or(ValidationError::Record)?; + } + let mut etag = String::with_capacity(40); + for byte in md5.finalize() { + write!(&mut etag, "{byte:02x}").expect("string write cannot fail"); + } + write!(&mut etag, "-{}", selection.count()).expect("string write cannot fail"); + let content = FileContent::from_locations(&locations, length, etag)?; + let path = session.location.relative_key(); + let extension = std::path::Path::new(path).extension(); + let has_extension = |wanted: &str| extension.is_some_and(|value| value.eq_ignore_ascii_case(wanted)); + let (kind, format) = if has_extension("json") { + (FileKind::Metadata, ContentFormat::Json) + } else if has_extension("avro") { + (FileKind::Unbound, ContentFormat::Avro) + } else if has_extension("parquet") { + (FileKind::Unbound, ContentFormat::Parquet) + } else if has_extension("orc") { + (FileKind::Unbound, ContentFormat::Orc) + } else if has_extension("puffin") { + (FileKind::Unbound, ContentFormat::Puffin) + } else { + (FileKind::Unbound, ContentFormat::Opaque) + }; + let record = FileRecord { + file: session.owner.file, + location: session.location.clone(), + kind, + format, + length, + digest: [0; 32], + content, + hint: None, + }; + record.validate()?; + let value = StorageRecord::File(Box::new(record)).encode()?; + let publication = PayloadStore::new(self.store.clone()) + .put(session.context.catalog, session.upload, &value) + .await?; + let mut next = increment(session)?; + next.phase = MultipartPhase::Publishing; + let completion = next.completion.as_mut().ok_or(ValidationError::Record)?; + completion.progress.next_part = selection.count(); + completion.progress.completed_bytes = length; + completion.publication = Some(publication); + Ok(Some(self.exchange(session, &next).await?)) + } /// Freezes a semantically sealed record; callers must validate its canonical format first. /// # Errors /// Rejects incomplete assembly, changed byte identity, expiry and invalid file records. @@ -67,6 +175,7 @@ impl MultipartRepository { || selected.digest != candidate.digest || selected.kind != candidate.kind || selected.format != candidate.format + || selected.content != candidate.content { return Err(ValidationError::Record.into()); } @@ -97,6 +206,7 @@ impl MultipartRepository { || selected.digest != candidate.digest || selected.kind != candidate.kind || selected.format != candidate.format + || selected.content != candidate.content { let mut next = increment(session)?; next.phase = MultipartPhase::Conflicted; @@ -114,11 +224,15 @@ impl MultipartRepository { let StorageRecord::File(record) = StorageRecord::decode(&key, &bytes)? else { return Err(ValidationError::Record.into()); }; - validate_candidate( - session, - completion.candidate.as_ref().ok_or(ValidationError::Record)?, - &record, - )?; + if let Some(tree) = &completion.candidate { + validate_candidate(session, tree, &record)?; + } else if record.file != session.owner.file + || record.location != session.location + || record.length != completion.progress.completed_bytes + || !matches!(record.content, FileContent::Locations { .. }) + { + return Err(ValidationError::Record.into()); + } Ok(*record) } } diff --git a/lib/crowdb-access-iceberg/src/file/reader.rs b/lib/crowdb-access-iceberg/src/file/reader.rs index 739f96a97..1c17e717f 100644 --- a/lib/crowdb-access-iceberg/src/file/reader.rs +++ b/lib/crowdb-access-iceberg/src/file/reader.rs @@ -21,6 +21,7 @@ pub struct FileReader { cached: Option<(u64, Vec)>, leaf_directory: Option<(ChunkRoot, Vec)>, digest: Option, + locations: Option>, failed: bool, } @@ -94,7 +95,9 @@ impl FileReader { .content .inline_bytes(record.length, &record.digest)? .map(|bytes| (0, bytes)); - let digest = (range.start == 0 && range.end == record.length).then(Sha256::new); + let digest = (range.start == 0 && range.end == record.length && record.content.etag().is_none()) + .then(Sha256::new); + let locations = record.content.locations(record.length)?; Ok(Self { store, record, @@ -104,6 +107,7 @@ impl FileReader { cached, leaf_directory: None, digest, + locations, failed: false, }) } @@ -161,6 +165,15 @@ impl FileReader { } async fn select_leaf(&mut self) -> Result<(u64, Vec), FileIoError> { + if let Some(locations) = &self.locations { + let start = self.cursor; + let end = start.saturating_add(self.frame_bytes as u64).min(self.end); + let bytes = self.store.read_locations(locations, start, end).await?; + if bytes.len() as u64 != end - start { + return Err(ValidationError::Record.into()); + } + return Ok((start, bytes)); + } let FileContent::Chunks { root: Some(root) } = &self.record.content else { return Err(ValidationError::Record.into()); }; diff --git a/lib/crowdb-access-iceberg/src/file/record.rs b/lib/crowdb-access-iceberg/src/file/record.rs index f635862c1..705b4ea69 100644 --- a/lib/crowdb-access-iceberg/src/file/record.rs +++ b/lib/crowdb-access-iceberg/src/file/record.rs @@ -32,6 +32,7 @@ pub enum ContentFormat { Parquet = 2, Orc = 3, Puffin = 4, + Opaque = 5, } #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -69,7 +70,11 @@ impl FileRecord { FileKind::Statistics => matches!(self.format, ContentFormat::Puffin | ContentFormat::Parquet), FileKind::Unbound => matches!( self.format, - ContentFormat::Avro | ContentFormat::Parquet | ContentFormat::Orc | ContentFormat::Puffin + ContentFormat::Avro + | ContentFormat::Parquet + | ContentFormat::Orc + | ContentFormat::Puffin + | ContentFormat::Opaque ), }; if !valid_format { diff --git a/lib/crowdb-access-iceberg/src/file/repository.rs b/lib/crowdb-access-iceberg/src/file/repository.rs index f445d1ffe..99b4c413a 100644 --- a/lib/crowdb-access-iceberg/src/file/repository.rs +++ b/lib/crowdb-access-iceberg/src/file/repository.rs @@ -40,46 +40,7 @@ impl FileRepository { candidate: &FileRecord, ) -> Result { candidate.validate()?; - let pin = Box::pin(self.publication_pin(context, &candidate.location)).await?; - let result = self.publish_inner(context, candidate).await; - if result.is_ok() { - if let Some(pin) = pin { - crate::gc::ReaderPins::new(self.store.clone()) - .release(&pin) - .await?; - } - } - result - } - - async fn publication_pin( - &self, - context: CatalogContext, - location: &FileLocation, - ) -> Result, CatalogError> { - let key = crate::table::head_key(context.catalog, location.table().table); - if self.store.get(&key.encode()?).await?.is_none() { - return Ok(None); - } - let now_ms = u64::try_from( - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .map_err(|_| ValidationError::Deadline)? - .as_millis(), - ) - .map_err(|_| ValidationError::Deadline)?; - let pins = crate::gc::ReaderPins::new(self.store.clone()); - let expiry = pins.request_expiry(context, now_ms).await?; - Ok(Some( - pins.protect_files( - context, - location.table().table, - "file-publication", - expiry, - now_ms, - ) - .await?, - )) + self.publish_inner(context, candidate).await } async fn publish_inner( @@ -241,6 +202,7 @@ fn compatible(existing: FileRecord, candidate: &FileRecord) -> Result Ok(None), } } } diff --git a/lib/crowdb-access-iceberg/src/gc.rs b/lib/crowdb-access-iceberg/src/gc.rs index 6c4b6ed4d..934b24a58 100644 --- a/lib/crowdb-access-iceberg/src/gc.rs +++ b/lib/crowdb-access-iceberg/src/gc.rs @@ -9,9 +9,7 @@ mod limits; mod mark; mod node; mod page; -mod pins; mod proof; -mod protection; mod reachability; mod repository; mod retirement; @@ -25,7 +23,6 @@ pub use limits::GcLimits; pub use mark::GcMarkError; pub use node::GcNode; pub use page::GcPage; -pub use pins::{GcPin, ReaderPins}; pub use proof::GcProofState; pub use reachability::{ avro_links, metadata_links, AvroMarkCursor, AvroMarkLimits, AvroMarkPage, ReachableFile, ReachableKind, diff --git a/lib/crowdb-access-iceberg/src/gc/candidate.rs b/lib/crowdb-access-iceberg/src/gc/candidate.rs index 6ae1121ad..515420b52 100644 --- a/lib/crowdb-access-iceberg/src/gc/candidate.rs +++ b/lib/crowdb-access-iceberg/src/gc/candidate.rs @@ -75,6 +75,7 @@ impl GcCandidate { owner: session.owner, frames: Vec::new(), pending: None, + next_location: 0, }) } else { TreeReclaimCursor::new(&self.file) @@ -89,16 +90,26 @@ impl GcCandidate { .owner .table .file(&format!("gc-parts/{}/{:05}.parquet", part.upload, part.number))?; + let (length, digest, content) = if let Some(tree) = &part.tree { + ( + tree.length, + tree.digest, + FileContent::Chunks { + root: tree.root.clone(), + }, + ) + } else { + let stream = part.stream.as_ref().ok_or(ValidationError::Record)?; + (stream.length, [0; 32], stream.content.clone()) + }; let file = FileRecord { file: part.owner.file, location, kind: FileKind::Unbound, format: ContentFormat::Parquet, - length: part.tree.length, - digest: part.tree.digest, - content: FileContent::Chunks { - root: part.tree.root.clone(), - }, + length, + digest, + content, hint: None, }; file.validate()?; @@ -133,6 +144,16 @@ impl GcCandidate { pub fn validate(&self) -> Result<(), ValidationError> { self.file.validate()?; self.cursor.validate()?; + if let Some(locations) = self.file.content.locations(self.file.length)? { + if usize::from(self.cursor.next_location) > locations.len() + || !self.cursor.frames.is_empty() + || self.cursor.pending.is_some() + { + return Err(ValidationError::Record); + } + } else if self.cursor.next_location != 0 { + return Err(ValidationError::Record); + } if let Some(session) = &self.assembly { if self.part.is_some() || Self::assembly_file(session)? != self.file diff --git a/lib/crowdb-access-iceberg/src/gc/discovery.rs b/lib/crowdb-access-iceberg/src/gc/discovery.rs index 2ba1f7ded..5f64fcfdc 100644 --- a/lib/crowdb-access-iceberg/src/gc/discovery.rs +++ b/lib/crowdb-access-iceberg/src/gc/discovery.rs @@ -232,6 +232,39 @@ impl GcRepository { self.session_is_abandoned(task, &session, now_ms).await } + pub(super) async fn part_is_selected( + &self, + task: &GcTask, + part: &crate::file::MultipartPart, + ) -> Result { + let key = IcebergKey::Catalog { + catalog: task.context.catalog, + scope: CatalogScope::MultipartSession, + suffix: part.upload.as_bytes().to_vec(), + }; + let value = self + .store + .get(&key.encode()?) + .await? + .ok_or(ValidationError::Record)?; + let StorageRecord::MultipartSession(session) = StorageRecord::decode(&key, &value.bytes)? else { + return Err(ValidationError::Record.into()); + }; + if session.phase != MultipartPhase::Published { + return Ok(false); + } + let completion = session.completion.as_ref().ok_or(ValidationError::Record)?; + let bytes = crate::operation::PayloadStore::new(self.store.clone()) + .get(&completion.selection) + .await?; + let selection = crate::file::MultipartSelection::decode(&bytes)?; + Ok(selection.parts().iter().any(|selected| { + selected.number == part.number + && selected.revision == part.revision + && selected.digest == part.selection_digest() + })) + } + pub(super) async fn session_is_abandoned( &self, task: &GcTask, diff --git a/lib/crowdb-access-iceberg/src/gc/pins.rs b/lib/crowdb-access-iceberg/src/gc/pins.rs deleted file mode 100644 index c5570a12d..000000000 --- a/lib/crowdb-access-iceberg/src/gc/pins.rs +++ /dev/null @@ -1,165 +0,0 @@ -use std::sync::Arc; - -use crate::{ - catalog::{check_context, CasOutcome, CatalogContext, CatalogError, CatalogStore}, - error::ValidationError, - key::{CatalogScope, IcebergKey, OperationId}, - operation::mutation_identity, - record::StorageRecord, - table::{head_key, TableHead, TableLifecycle}, -}; - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct GcPin { - pub context: CatalogContext, - pub identity: OperationId, - pub head: TableHead, - pub principal: String, - pub expires_ms: u64, - pub released: bool, - pub operator: bool, - pub protects_uploads: bool, -} - -#[derive(Clone)] -pub struct ReaderPins { - pub(super) store: Arc, -} - -impl ReaderPins { - #[must_use] - pub fn new(store: Arc) -> Self { - Self { store } - } - - /// # Errors - /// Rejects malformed or mismatched persisted pins. - pub async fn get( - &self, - catalog: crate::key::CatalogId, - table: crate::key::TableId, - identity: OperationId, - ) -> Result, CatalogError> { - let mut suffix = table.as_bytes().to_vec(); - suffix.extend_from_slice(identity.as_bytes()); - let key = IcebergKey::Catalog { - catalog, - scope: CatalogScope::GcPin, - suffix, - }; - let Some(value) = self.store.get(&key.encode()?).await? else { - return Ok(None); - }; - let StorageRecord::GcPin(pin) = StorageRecord::decode(&key, &value.bytes)? else { - return Err(ValidationError::Record.into()); - }; - if pin.context.catalog != catalog || pin.head.table != table || pin.identity != identity { - return Err(ValidationError::IdentityMismatch.into()); - } - Ok(Some(*pin)) - } - - /// Persists protection before checking the selected head against a concurrent sweep. - /// # Errors - /// Rejects retired catalogs, changed heads and reused identities. - pub async fn acquire(&self, pin: &GcPin) -> Result<(), CatalogError> { - self.acquire_selected(pin, false).await - } - - pub(super) async fn acquire_selected( - &self, - pin: &GcPin, - allow_tombstone: bool, - ) -> Result<(), CatalogError> { - pin.validate()?; - if pin.released - || pin.head.lifecycle == TableLifecycle::Reclaiming - || (!pin.operator && !allow_tombstone && pin.head.lifecycle != TableLifecycle::Ready) - { - return Err(CatalogError::Busy); - } - check_context(self.store.as_ref(), pin.context).await?; - let key = pin.key().encode()?; - let bytes = StorageRecord::GcPin(Box::new(pin.clone())).encode()?; - match self - .store - .compare_exchange(&key, None, &bytes, mutation_identity(&key, None, &bytes)) - .await? - { - CasOutcome::Applied(_) => {} - CasOutcome::Conflict(Some(existing)) if existing.bytes == bytes => {} - CasOutcome::Conflict(_) => return Err(CatalogError::Conflict), - } - let key = head_key(pin.head.catalog, pin.head.table); - let current = self - .store - .get(&key.encode()?) - .await? - .ok_or(CatalogError::Conflict)?; - if StorageRecord::decode(&key, ¤t.bytes)? - != StorageRecord::TableHead(Box::new(pin.head.clone())) - { - return Err(CatalogError::Busy); - } - check_context(self.store.as_ref(), pin.context).await - } - - /// # Errors - /// Rejects a changed pin; retries preserve the same release result. - pub async fn release(&self, pin: &GcPin) -> Result<(), CatalogError> { - pin.validate()?; - let mut released = pin.clone(); - released.released = true; - let key = pin.key().encode()?; - let before = StorageRecord::GcPin(Box::new(pin.clone())).encode()?; - let after = StorageRecord::GcPin(Box::new(released)).encode()?; - match self - .store - .compare_exchange( - &key, - Some(&before), - &after, - mutation_identity(&key, Some(&before), &after), - ) - .await? - { - CasOutcome::Applied(_) => Ok(()), - CasOutcome::Conflict(Some(existing)) if existing.bytes == after => Ok(()), - CasOutcome::Conflict(_) => Err(CatalogError::Conflict), - } - } -} - -impl GcPin { - #[must_use] - pub fn key(&self) -> IcebergKey { - let mut suffix = self.head.table.as_bytes().to_vec(); - suffix.extend_from_slice(self.identity.as_bytes()); - IcebergKey::Catalog { - catalog: self.context.catalog, - scope: CatalogScope::GcPin, - suffix, - } - } - - /// # Errors - /// Rejects mismatched roots, unbounded principals and unbounded reader lifetimes. - pub fn validate(&self) -> Result<(), ValidationError> { - self.context.validate()?; - self.head.validate()?; - if self.head.catalog != self.context.catalog - || self.principal.is_empty() - || self.principal.len() > 256 - || self.principal.contains('\0') - || (!self.operator && self.expires_ms == 0) - { - return Err(ValidationError::Record); - } - Ok(()) - } - - #[must_use] - pub const fn protects(&self, now_ms: u64) -> bool { - !self.released && (self.expires_ms == 0 || now_ms < self.expires_ms) - } -} diff --git a/lib/crowdb-access-iceberg/src/gc/protection.rs b/lib/crowdb-access-iceberg/src/gc/protection.rs deleted file mode 100644 index b7866a30a..000000000 --- a/lib/crowdb-access-iceberg/src/gc/protection.rs +++ /dev/null @@ -1,150 +0,0 @@ -use crate::{ - catalog::{check_context, CatalogContext, CatalogError, CatalogLifecycle}, - commit::{TableCreateJournal, TableCreatePhase}, - error::ValidationError, - key::{CatalogScope, IcebergKey, OperationId, TableId}, - operation::mutation_identity, - record::StorageRecord, - table::head_key, -}; - -use super::{GcPin, ReaderPins}; - -impl ReaderPins { - /// # Errors - /// Rejects inactive authority and overflowing persisted protection bounds. - pub async fn request_expiry(&self, context: CatalogContext, starts_ms: u64) -> Result { - check_context(self.store.as_ref(), context).await?; - let key = IcebergKey::Catalog { - catalog: context.catalog, - scope: CatalogScope::Authority, - suffix: Vec::new(), - }; - let value = self - .store - .get(&key.encode()?) - .await? - .ok_or(ValidationError::Record)?; - let StorageRecord::Authority(authority) = StorageRecord::decode(&key, &value.bytes)? else { - return Err(ValidationError::Record.into()); - }; - if authority.lifecycle != CatalogLifecycle::Ready || authority.admission_bounds.request_ms == 0 { - return Err(CatalogError::Busy); - } - starts_ms - .checked_add(authority.admission_bounds.request_ms) - .and_then(|deadline| deadline.checked_add(authority.admission_bounds.clock_skew_ms)) - .ok_or(ValidationError::Deadline.into()) - } - - /// # Errors - /// Rejects fenced tables and missing or changed staged-create authority. - pub async fn protect_files( - &self, - context: CatalogContext, - table: TableId, - principal: &str, - expires_ms: u64, - now_ms: u64, - ) -> Result { - self.protect_selected_files(context, table, principal, expires_ms, now_ms, false) - .await - } - - /// Protects an authenticated file read, including a logically dropped table. - /// # Errors - /// Rejects reclamation fences and changed authority after persisting the pin. - pub async fn protect_file_reads( - &self, - context: CatalogContext, - table: TableId, - principal: &str, - expires_ms: u64, - now_ms: u64, - ) -> Result { - self.protect_selected_files(context, table, principal, expires_ms, now_ms, true) - .await - } - - async fn protect_selected_files( - &self, - context: CatalogContext, - table: TableId, - principal: &str, - expires_ms: u64, - now_ms: u64, - allow_tombstone: bool, - ) -> Result { - if expires_ms <= now_ms { - return Err(ValidationError::Deadline.into()); - } - let key = head_key(context.catalog, table); - let current = self.store.get(&key.encode()?).await?; - let stage = if current.is_none() { - let identity = OperationId::from_bytes(table.as_bytes())?; - let operation = TableCreateJournal::new(self.store.clone()) - .load(context, identity) - .await? - .ok_or(CatalogError::Conflict)?; - if operation.phase != TableCreatePhase::Staged - || operation.candidate.table != table - || operation.stage.as_ref().map_or(true, |stage| { - u64::try_from(stage.expires_ms).map_or(true, |expiry| now_ms >= expiry) - }) - { - return Err(CatalogError::Busy); - } - Some(operation) - } else { - None - }; - let head = if let Some(value) = current { - let StorageRecord::TableHead(head) = StorageRecord::decode(&key, &value.bytes)? else { - return Err(ValidationError::Record.into()); - }; - *head - } else { - stage.as_ref().ok_or(ValidationError::Record)?.candidate.clone() - }; - let pin = GcPin { - context, - identity: OperationId::random(), - head, - principal: principal.into(), - expires_ms, - released: false, - operator: false, - protects_uploads: true, - }; - if let Some(operation) = stage { - pin.validate()?; - let key = pin.key().encode()?; - let bytes = StorageRecord::GcPin(Box::new(pin.clone())).encode()?; - match self - .store - .compare_exchange(&key, None, &bytes, mutation_identity(&key, None, &bytes)) - .await? - { - crate::catalog::CasOutcome::Applied(_) => {} - crate::catalog::CasOutcome::Conflict(_) => return Err(CatalogError::Conflict), - } - if TableCreateJournal::new(self.store.clone()) - .load(context, operation.identity.operation) - .await? - .as_ref() - != Some(&operation) - || self - .store - .get(&head_key(context.catalog, table).encode()?) - .await? - .is_some() - { - return Err(CatalogError::Busy); - } - check_context(self.store.as_ref(), context).await?; - } else { - self.acquire_selected(&pin, allow_tombstone).await?; - } - Ok(pin) - } -} diff --git a/lib/crowdb-access-iceberg/src/gc/tree.rs b/lib/crowdb-access-iceberg/src/gc/tree.rs index 2027e0205..b86373984 100644 --- a/lib/crowdb-access-iceberg/src/gc/tree.rs +++ b/lib/crowdb-access-iceberg/src/gc/tree.rs @@ -17,6 +17,7 @@ pub struct TreeReclaimCursor { pub owner: FileIdentity, pub frames: Vec, pub pending: Option, + pub next_location: u16, } #[derive(Clone, Debug, Eq, PartialEq)] @@ -46,6 +47,7 @@ impl TreeReclaimCursor { }, frames, pending: None, + next_location: 0, }) } diff --git a/lib/crowdb-access-iceberg/src/gc/worker/cleanup.rs b/lib/crowdb-access-iceberg/src/gc/worker/cleanup.rs index e618b637e..5071ae863 100644 --- a/lib/crowdb-access-iceberg/src/gc/worker/cleanup.rs +++ b/lib/crowdb-access-iceberg/src/gc/worker/cleanup.rs @@ -209,7 +209,6 @@ impl GcWorker { .await? .is_none()) } - StorageRecord::GcPin(pin) => Ok(!pin.protects(now_ms)), _ => Ok(true), } } diff --git a/lib/crowdb-access-iceberg/src/gc/worker/inactive.rs b/lib/crowdb-access-iceberg/src/gc/worker/inactive.rs index 734481530..420532401 100644 --- a/lib/crowdb-access-iceberg/src/gc/worker/inactive.rs +++ b/lib/crowdb-access-iceberg/src/gc/worker/inactive.rs @@ -129,7 +129,6 @@ pub(super) fn protects(task: &GcTask, record: &StorageRecord, now_ms: u64, grace .saturating_add(grace_ms) }; match record { - StorageRecord::GcPin(pin) => owns(pin.head.table) && pin.protects(now_ms), StorageRecord::TableCommitOperation(operation) => { owns(operation.before.table) && (retained(operation.identity.issued_ms) diff --git a/lib/crowdb-access-iceberg/src/gc/worker/live.rs b/lib/crowdb-access-iceberg/src/gc/worker/live.rs index bd36069d5..33372a6f1 100644 --- a/lib/crowdb-access-iceberg/src/gc/worker/live.rs +++ b/lib/crowdb-access-iceberg/src/gc/worker/live.rs @@ -105,18 +105,6 @@ impl GcWorker { continue; } let record = StorageRecord::decode(&key, &item.value)?; - if let StorageRecord::GcPin(pin) = &record { - if pin.protects(now_ms) - && task - .head - .as_ref() - .is_some_and(|head| head.table == pin.head.table) - && !pin.protects_uploads - { - self.repository.push_proof_root(&mut next, &pin.head).await?; - continue; - } - } if super::inactive::protects(task, &record, now_ms, grace_ms) { next.stalled = GcStalledReason::Protected; next.scan_after.clear(); diff --git a/lib/crowdb-access-iceberg/src/gc/worker/sweep.rs b/lib/crowdb-access-iceberg/src/gc/worker/sweep.rs index 01d8bcba7..37d291926 100644 --- a/lib/crowdb-access-iceberg/src/gc/worker/sweep.rs +++ b/lib/crowdb-access-iceberg/src/gc/worker/sweep.rs @@ -1,4 +1,5 @@ use crate::{catalog::CasOutcome, operation::mutation_identity}; +use crowdb_protocol::chunkdb::rpc::Location; use super::{ file_key, location_key, CandidatePhase, CatalogError, GcCandidate, GcPhase, GcStalledReason, GcTask, @@ -179,6 +180,23 @@ impl GcWorker { self.repository.candidate(Some(candidate), &next).await?; return Ok(DeleteProgress::Advanced); } + if candidate.part.as_ref().is_some_and(|part| part.stream.is_some()) + && self + .repository + .part_is_selected(task, candidate.part.as_ref().ok_or(ValidationError::Record)?) + .await? + { + self.remove_file_authority(candidate).await?; + next.phase = CandidatePhase::Complete; + next.completed_round = sweep_round; + self.repository.candidate(Some(candidate), &next).await?; + return Ok(DeleteProgress::Complete); + } + if let Some(locations) = candidate.file.content.locations(candidate.file.length)? { + return self + .delete_location_candidate(candidate, next, &locations, sweep_round) + .await; + } if let Some(root) = &candidate.cursor.pending { if self.blocks.reclaim(root).await? == ReclaimOutcome::Deferred { next.phase = CandidatePhase::Deferred; @@ -213,6 +231,37 @@ impl GcWorker { }) } + async fn delete_location_candidate( + &self, + candidate: &GcCandidate, + mut next: GcCandidate, + locations: &[Location], + sweep_round: u64, + ) -> Result { + if let Some(location) = locations.get(usize::from(candidate.cursor.next_location)) { + if self.blocks.reclaim_location(location).await? == ReclaimOutcome::Deferred { + next.phase = CandidatePhase::Deferred; + self.repository.candidate(Some(candidate), &next).await?; + return Ok(DeleteProgress::Deferred); + } + next.cursor.next_location = next + .cursor + .next_location + .checked_add(1) + .ok_or(ValidationError::GenerationExhausted)?; + } else { + self.remove_file_authority(candidate).await?; + next.phase = CandidatePhase::Complete; + next.completed_round = sweep_round; + } + self.repository.candidate(Some(candidate), &next).await?; + Ok(if next.phase == CandidatePhase::Complete { + DeleteProgress::Complete + } else { + DeleteProgress::Advanced + }) + } + async fn remove_file_authority(&self, candidate: &GcCandidate) -> Result<(), GcWorkError> { if candidate.assembly.is_some() { return Ok(()); diff --git a/lib/crowdb-access-iceberg/src/key/codec.rs b/lib/crowdb-access-iceberg/src/key/codec.rs index 4e57022fd..a717a3c82 100644 --- a/lib/crowdb-access-iceberg/src/key/codec.rs +++ b/lib/crowdb-access-iceberg/src/key/codec.rs @@ -44,7 +44,6 @@ pub enum CatalogScope { GcTask = 18, GcCandidate = 19, GcPage = 20, - GcPin = 21, GcNode = 22, GcPending = 23, GcClaim = 24, @@ -194,7 +193,6 @@ fn catalog_scope(value: u8) -> Result { 18 => Ok(CatalogScope::GcTask), 19 => Ok(CatalogScope::GcCandidate), 20 => Ok(CatalogScope::GcPage), - 21 => Ok(CatalogScope::GcPin), 22 => Ok(CatalogScope::GcNode), 23 => Ok(CatalogScope::GcPending), 24 => Ok(CatalogScope::GcClaim), @@ -281,13 +279,6 @@ fn validate_catalog(scope: CatalogScope, suffix: &[u8]) -> Result<(), Validation } super::OperationId::from_bytes(&suffix[..16]).map(|_| ()) } - CatalogScope::GcPin => { - if suffix.len() != 32 { - return Err(ValidationError::Key); - } - super::TableId::from_bytes(&suffix[..16])?; - super::OperationId::from_bytes(&suffix[16..]).map(|_| ()) - } CatalogScope::GcNode | CatalogScope::GcPending => { if suffix.len() != 32 { return Err(ValidationError::Key); diff --git a/lib/crowdb-access-iceberg/src/record/envelope.rs b/lib/crowdb-access-iceberg/src/record/envelope.rs index 5ac1e5d8e..eb0581cd2 100644 --- a/lib/crowdb-access-iceberg/src/record/envelope.rs +++ b/lib/crowdb-access-iceberg/src/record/envelope.rs @@ -21,7 +21,6 @@ pub enum StorageRecord { GcTask(Box), GcCandidate(Box), GcPage(Box), - GcPin(Box), TableLifecycleOperation(Box), TablePurgeTask(Box), TableCreateOperation(Box), @@ -78,7 +77,7 @@ impl StorageRecord { FBRecordValue::FBFileWriteIntent, super::write_intent::encode(builder, intent)?.as_union_value(), ), - Self::GcNode(_) | Self::GcTask(_) | Self::GcCandidate(_) | Self::GcPage(_) | Self::GcPin(_) => { + Self::GcNode(_) | Self::GcTask(_) | Self::GcCandidate(_) | Self::GcPage(_) => { return Err(ValidationError::Record); } Self::TableLifecycleOperation(operation) => ( @@ -186,10 +185,6 @@ impl StorageRecord { FBRecordValue::FBGcPage, super::gc::encode_page(builder, page)?.as_union_value(), ), - Self::GcPin(pin) => ( - FBRecordValue::FBGcPin, - super::gc::encode_pin(builder, pin)?.as_union_value(), - ), _ => return Ok(None), })) } @@ -243,11 +238,6 @@ impl StorageRecord { envelope.value_as_fbgc_page().ok_or(ValidationError::Record)?, )?))) } - FBRecordValue::FBGcPin => { - return Ok(Self::GcPin(Box::new(super::gc::decode_pin( - envelope.value_as_fbgc_pin().ok_or(ValidationError::Record)?, - )?))) - } _ => {} } if matches!( diff --git a/lib/crowdb-access-iceberg/src/record/file.rs b/lib/crowdb-access-iceberg/src/record/file.rs index 1018e90e2..366b80cc7 100644 --- a/lib/crowdb-access-iceberg/src/record/file.rs +++ b/lib/crowdb-access-iceberg/src/record/file.rs @@ -18,7 +18,7 @@ pub(super) fn encode<'buffer>( let file_id = builder.create_vector(record.file.as_bytes()); let location = builder.create_string(&record.location.to_string()); let digest = builder.create_vector(&record.digest); - let (storage, inline_bytes, root) = match &record.content { + let (storage, inline_bytes, root, locations, etag) = match &record.content { FileContent::Inline { codec, bytes } => ( match codec { InlineCodec::Raw => 0, @@ -26,8 +26,23 @@ pub(super) fn encode<'buffer>( }, Some(builder.create_vector(bytes)), None, + None, + None, + ), + FileContent::Chunks { root } => ( + 2, + None, + root.as_ref().map(|root| encode_root(builder, root)), + None, + None, + ), + FileContent::Locations { bytes, etag } => ( + 3, + None, + None, + Some(builder.create_vector(bytes)), + Some(builder.create_string(etag)), ), - FileContent::Chunks { root } => (2, None, root.as_ref().map(|root| encode_root(builder, root))), }; Ok(FBFileRecord::create( builder, @@ -44,13 +59,21 @@ pub(super) fn encode<'buffer>( has_hint: record.hint.is_some(), hint_offset: record.hint.map_or(0, |hint| hint.offset), hint_length: record.hint.map_or(0, |hint| hint.length), + locations, + etag, }, )) } pub(super) fn decode(value: FBFileRecord<'_>) -> Result { - let content = match (value.storage(), value.inline_bytes(), value.root()) { - (codec @ (0 | 1), Some(bytes), None) => FileContent::Inline { + let content = match ( + value.storage(), + value.inline_bytes(), + value.root(), + value.locations(), + value.etag(), + ) { + (codec @ (0 | 1), Some(bytes), None, None, None) => FileContent::Inline { codec: if codec == 0 { InlineCodec::Raw } else { @@ -58,9 +81,13 @@ pub(super) fn decode(value: FBFileRecord<'_>) -> Result FileContent::Chunks { + (2, None, root, None, None) => FileContent::Chunks { root: root.map(decode_root).transpose()?, }, + (3, None, None, Some(locations), Some(etag)) => FileContent::Locations { + bytes: locations.bytes().to_vec(), + etag: etag.to_owned(), + }, _ => return Err(ValidationError::Record), }; let record = FileRecord { @@ -84,6 +111,7 @@ pub(super) fn decode(value: FBFileRecord<'_>) -> Result ContentFormat::Parquet, 3 => ContentFormat::Orc, 4 => ContentFormat::Puffin, + 5 => ContentFormat::Opaque, _ => return Err(ValidationError::Record), }, length: value.length(), diff --git a/lib/crowdb-access-iceberg/src/record/gc.rs b/lib/crowdb-access-iceberg/src/record/gc.rs index 137c5c9f7..fbcac4242 100644 --- a/lib/crowdb-access-iceberg/src/record/gc.rs +++ b/lib/crowdb-access-iceberg/src/record/gc.rs @@ -1,6 +1,6 @@ use crowdb_protocol::iceberg_fb::{ FBGcCandidate, FBGcCandidateArgs, FBGcEntry, FBGcEntryArgs, FBGcFrame, FBGcFrameArgs, FBGcPage, - FBGcPageArgs, FBGcPin, FBGcPinArgs, FBGcTask, FBGcTaskArgs, + FBGcPageArgs, FBGcTask, FBGcTaskArgs, }; use flatbuffers::{FlatBufferBuilder, WIPOffset}; @@ -9,8 +9,8 @@ use crate::{ error::ValidationError, file::FileIdentity, gc::{ - CandidatePhase, GcCandidate, GcPage, GcPhase, GcPin, GcStalledReason, GcTask, GcTaskKind, - ReclaimFrame, TreeReclaimCursor, + CandidatePhase, GcCandidate, GcPage, GcPhase, GcStalledReason, GcTask, GcTaskKind, ReclaimFrame, + TreeReclaimCursor, }, key::{CatalogId, OperationId}, }; @@ -212,6 +212,7 @@ pub(super) fn encode_candidate<'buffer>( next_root: candidate.next_root, frames: Some(frames), pending, + next_location: candidate.cursor.next_location, }, )) } @@ -266,6 +267,7 @@ pub(super) fn decode_candidate(value: FBGcCandidate<'_>) -> Result) -> Result( - builder: &mut FlatBufferBuilder<'buffer>, - pin: &GcPin, -) -> Result>, ValidationError> { - pin.validate()?; - let catalog = builder.create_vector(pin.context.catalog.as_bytes()); - let identity = builder.create_vector(pin.identity.as_bytes()); - let head = super::table::encode_head(builder, &pin.head)?; - let principal = builder.create_string(&pin.principal); - Ok(FBGcPin::create( - builder, - &FBGcPinArgs { - catalog: Some(catalog), - activation_epoch: pin.context.activation_epoch, - identity: Some(identity), - head: Some(head), - principal: Some(principal), - expires_ms: pin.expires_ms, - released: pin.released, - operator_pin: pin.operator, - protects_uploads: pin.protects_uploads, - }, - )) -} - -pub(super) fn decode_pin(value: FBGcPin<'_>) -> Result { - let pin = GcPin { - context: CatalogContext { - catalog: CatalogId::from_bytes(value.catalog().bytes())?, - activation_epoch: value.activation_epoch(), - }, - identity: OperationId::from_bytes(value.identity().bytes())?, - head: super::table::decode_head(value.head())?, - principal: value.principal().to_owned(), - expires_ms: value.expires_ms(), - released: value.released(), - operator: value.operator_pin(), - protects_uploads: value.protects_uploads(), - }; - pin.validate()?; - Ok(pin) -} diff --git a/lib/crowdb-access-iceberg/src/record/gc_key.rs b/lib/crowdb-access-iceberg/src/record/gc_key.rs index d846f07aa..157575319 100644 --- a/lib/crowdb-access-iceberg/src/record/gc_key.rs +++ b/lib/crowdb-access-iceberg/src/record/gc_key.rs @@ -29,7 +29,6 @@ impl StorageRecord { Ok(()) } (Self::GcPage(page), key) if *key == page.key() => Ok(()), - (Self::GcPin(pin), key) if *key == pin.key() => Ok(()), _ => return None, }) } diff --git a/lib/crowdb-access-iceberg/src/record/multipart.rs b/lib/crowdb-access-iceberg/src/record/multipart.rs index 7cbba18aa..6ad337baf 100644 --- a/lib/crowdb-access-iceberg/src/record/multipart.rs +++ b/lib/crowdb-access-iceberg/src/record/multipart.rs @@ -6,7 +6,10 @@ use flatbuffers::{FlatBufferBuilder, WIPOffset}; use crate::catalog::CatalogContext; use crate::error::ValidationError; -use crate::file::{MultipartLimits, MultipartPart, MultipartPartMutation, MultipartPhase, MultipartSession}; +use crate::file::{ + FileContent, MultipartLimits, MultipartPart, MultipartPartMutation, MultipartPhase, MultipartSession, + MultipartStreamPart, +}; use crate::key::{FileId, OperationId}; mod completion; @@ -154,7 +157,19 @@ pub(super) fn encode_part<'buffer>( part.validate()?; let upload = builder.create_vector(part.upload.as_bytes()); let owner = fields::encode_owner(builder, part.owner); - let tree = fields::encode_tree(builder, &part.tree); + let tree = part.tree.as_ref().map(|tree| fields::encode_tree(builder, tree)); + let (locations, etag, stream_length) = if let Some(stream) = &part.stream { + let FileContent::Locations { bytes, etag } = &stream.content else { + return Err(ValidationError::Record); + }; + ( + Some(builder.create_vector(bytes)), + Some(builder.create_string(etag)), + stream.length, + ) + } else { + (None, None, 0) + }; Ok(FBMultipartPart::create( builder, &FBMultipartPartArgs { @@ -162,8 +177,11 @@ pub(super) fn encode_part<'buffer>( number: part.number, revision: part.revision, owner: Some(owner), - tree: Some(tree), + tree, modified_ms: part.modified_ms, + locations, + etag, + stream_length, }, )) } @@ -175,7 +193,18 @@ pub(super) fn decode_part(value: FBMultipartPart<'_>) -> Result Some(MultipartStreamPart { + length: value.stream_length(), + content: FileContent::Locations { + bytes: locations.bytes().to_vec(), + etag: etag.to_owned(), + }, + }), + (None, None) => None, + _ => return Err(ValidationError::Record), + }, }; part.validate()?; Ok(part) diff --git a/lib/crowdb-access-iceberg/src/table/load.rs b/lib/crowdb-access-iceberg/src/table/load.rs index 1a7631d57..5c153db1e 100644 --- a/lib/crowdb-access-iceberg/src/table/load.rs +++ b/lib/crowdb-access-iceberg/src/table/load.rs @@ -49,8 +49,6 @@ pub struct TableLoader { blocks: Arc, limits: TableMetadataLimits, projections: ProjectionStore, - pins: crate::gc::ReaderPins, - pin_lifetime_ms: Option, #[cfg(feature = "test-util")] projection_hits: std::sync::atomic::AtomicUsize, } @@ -63,8 +61,6 @@ impl TableLoader { limits: TableMetadataLimits, ) -> Self { Self { - pins: crate::gc::ReaderPins::new(store.clone()), - pin_lifetime_ms: None, namespaces: NamespaceRepository::new(store.clone()), projections: ProjectionStore::new(store.clone(), blocks.clone()), tables: TableRepository::new(store), @@ -75,22 +71,6 @@ impl TableLoader { } } - /// # Errors - /// Rejects zero or excessive request protection lifetimes. - pub fn with_reader_pins(mut self, lifetime_ms: u64) -> Result { - if lifetime_ms == 0 || lifetime_ms > 24 * 60 * 60 * 1000 { - return Err(crate::error::ValidationError::Deadline); - } - self.pin_lifetime_ms = Some(lifetime_ms); - Ok(self) - } - - #[must_use] - pub fn with_catalog_reader_pins(mut self) -> Self { - self.pin_lifetime_ms = Some(0); - self - } - /// Resolves live namespace identity before table selection. No table publisher is implied. /// # Errors /// Corruption, retirement and changed namespace/head identity remain errors, not absence. @@ -181,35 +161,6 @@ impl TableLoader { { return Err(TableLoadError::UnsupportedVersion); } - let pin = if let Some(lifetime_ms) = self.pin_lifetime_ms { - let now_ms = u64::try_from( - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .map_err(|_| TableMetadataError::Bounds)? - .as_millis(), - ) - .map_err(|_| TableMetadataError::Bounds)?; - let pin = crate::gc::GcPin { - context, - identity: crate::key::OperationId::random(), - head: selected.head.clone(), - principal: "catalog-metadata-reader".into(), - expires_ms: if lifetime_ms == 0 { - self.pins.request_expiry(context, now_ms).await? - } else { - now_ms - .checked_add(lifetime_ms) - .ok_or(TableMetadataError::Bounds)? - }, - released: false, - operator: false, - protects_uploads: false, - }; - self.pins.acquire(&pin).await?; - Some(pin) - } else { - None - }; let canonical = super::metadata::read_table_metadata_bytes(self.blocks.clone(), &selected, self.limits).await?; let etag = etag(&selected.head, mode); @@ -220,9 +171,6 @@ impl TableLoader { self.tables.ensure_current(context, &selected).await?; self.check_namespace(context, namespace, parent.namespace, parent.name_epoch) .await?; - if let Some(pin) = &pin { - self.pins.release(pin).await?; - } if if_none_match.is_some_and(|header| { header.split(',').any(|tag| { let tag = tag.trim(); diff --git a/lib/crowdb-access-iceberg/tests/file_record_test.rs b/lib/crowdb-access-iceberg/tests/file_record_test.rs index 1e7e1376e..ed251e8a8 100644 --- a/lib/crowdb-access-iceberg/tests/file_record_test.rs +++ b/lib/crowdb-access-iceberg/tests/file_record_test.rs @@ -4,6 +4,7 @@ use crowdb_access_iceberg::file::{ }; use crowdb_access_iceberg::key::{CatalogId, FileId, TableId}; use crowdb_access_iceberg::record::{StorageRecord, MAX_RECORD_BYTES}; +use crowdb_protocol::chunkdb::rpc::Location; use crowdb_protocol::common::ChunkId; use sha2::{Digest, Sha256}; @@ -25,6 +26,45 @@ fn record(input: &[u8]) -> FileRecord { } } +#[test] +fn complete_chunk_locations_round_trip_without_a_file_sha256() { + let locations = vec![ + Location { + chunk_id: Some(ChunkId { high: 1, low: 2 }), + offset: 0, + length: 65_536, + logical_offset: 0, + logical_length: 65_500, + }, + Location { + chunk_id: Some(ChunkId { high: 3, low: 4 }), + offset: 128, + length: 80, + logical_offset: 65_500, + logical_length: 44, + }, + ]; + let length = 65_544; + let content = + FileContent::from_locations(&locations, length, "d41d8cd98f00b204e9800998ecf8427e".into()).unwrap(); + let mut file = record(b"{}"); + file.kind = FileKind::Unbound; + file.format = ContentFormat::Opaque; + file.length = length; + file.digest = [0; 32]; + file.content = content; + file.validate().unwrap(); + let key = file_key(file.location.table().catalog, file.file); + let encoded = StorageRecord::File(Box::new(file.clone())).encode().unwrap(); + assert_eq!( + StorageRecord::decode(&key, &encoded).unwrap(), + StorageRecord::File(Box::new(file)) + ); + let mut broken = locations; + broken[1].logical_offset += 1; + assert!(FileContent::from_locations(&broken, length, "d41d8cd98f00b204e9800998ecf8427e".into()).is_err()); +} + #[test] fn inline_selection_enforces_kind_stored_size_and_compression_input_bounds() { for kind in [FileKind::Metadata, FileKind::ManifestList, FileKind::Manifest] { diff --git a/lib/crowdb-access-iceberg/tests/gc_fence_test.rs b/lib/crowdb-access-iceberg/tests/gc_fence_test.rs index 2527ab6bc..21a673934 100644 --- a/lib/crowdb-access-iceberg/tests/gc_fence_test.rs +++ b/lib/crowdb-access-iceberg/tests/gc_fence_test.rs @@ -1,6 +1,6 @@ use crowdb_access_iceberg::{ catalog::{CasOutcome, CatalogStore}, - gc::{GcPhase, GcPin, GcRepository, GcStalledReason, GcTask, GcTaskKind, ReaderPins}, + gc::{GcPhase, GcRepository, GcStalledReason, GcTask, GcTaskKind}, key::{NamespaceId, OperationId}, operation::mutation_identity, record::StorageRecord, @@ -170,74 +170,6 @@ async fn lost_legacy_fence_release_reply_is_recovered_on_retry() { assert!(!retired.fenced); } -#[tokio::test] -async fn new_reader_pin_cannot_be_acknowledged_during_sweep() { - let (fixture, task) = fixture().await; - let pins = ReaderPins::new(fixture.store.clone()); - let pin = GcPin { - context: fixture.context, - identity: OperationId::random(), - head: task.head.clone().unwrap(), - principal: "reader".into(), - expires_ms: 2000, - released: false, - operator: false, - protects_uploads: false, - }; - pins.acquire(&pin).await.unwrap(); - let repository = GcRepository::new(fixture.store.clone()); - seed_legacy_task(&fixture.store, &task).await; - repository.fence_table(&task).await.unwrap(); - let mut newcomer = pin.clone(); - newcomer.identity = OperationId::random(); - assert!(pins.acquire(&newcomer).await.is_err()); - assert!(fixture - .store - .get(&pin.key().encode().unwrap()) - .await - .unwrap() - .is_some()); - assert!(pin.protects(1999)); - assert!(!pin.protects(2000)); - pins.release(&pin).await.unwrap(); - pins.release(&pin).await.unwrap(); - repository.release_table_fence(&task).await.unwrap(); -} - -#[tokio::test] -async fn operator_pin_can_be_inspected_and_released_after_restart() { - let (fixture, task) = fixture().await; - let pins = ReaderPins::new(fixture.store.clone()); - let pin = GcPin { - context: fixture.context, - identity: OperationId::random(), - head: task.head.unwrap(), - principal: "manager".into(), - expires_ms: 0, - released: false, - operator: true, - protects_uploads: true, - }; - pins.acquire(&pin).await.unwrap(); - let restarted = ReaderPins::new(fixture.store); - assert_eq!( - restarted - .get(pin.context.catalog, pin.head.table, pin.identity) - .await - .unwrap(), - Some(pin.clone()) - ); - restarted.release(&pin).await.unwrap(); - assert!( - restarted - .get(pin.context.catalog, pin.head.table, pin.identity) - .await - .unwrap() - .unwrap() - .released - ); -} - #[tokio::test] async fn changed_table_generation_rejects_gc_before_deletion() { let (fixture, task) = fixture().await; diff --git a/lib/crowdb-access-iceberg/tests/gc_proof_test.rs b/lib/crowdb-access-iceberg/tests/gc_proof_test.rs index 640f9f166..b8e27f752 100644 --- a/lib/crowdb-access-iceberg/tests/gc_proof_test.rs +++ b/lib/crowdb-access-iceberg/tests/gc_proof_test.rs @@ -415,149 +415,6 @@ async fn live_sweep_keeps_proven_files_and_removes_only_an_unreferenced_file() { } } -#[tokio::test] -async fn request_protection_uses_persisted_bounds_and_rejects_fenced_heads() { - let (store, mut task, _) = fixture(3, 0).await; - let pins = crowdb_access_iceberg::gc::ReaderPins::new(store.clone()); - let key = IcebergKey::Catalog { - catalog: task.context.catalog, - scope: CatalogScope::Authority, - suffix: Vec::new(), - }; - let before = store.get(&key.encode().unwrap()).await.unwrap().unwrap(); - let StorageRecord::Authority(mut authority) = StorageRecord::decode(&key, &before.bytes).unwrap() else { - panic!() - }; - authority.admission_bounds.request_ms = 600_000; - authority.admission_bounds.clock_skew_ms = 70_000; - let bytes = StorageRecord::Authority(authority).encode().unwrap(); - let key = key.encode().unwrap(); - store - .compare_exchange( - &key, - Some(&before.bytes), - &bytes, - mutation_identity(&key, Some(&before.bytes), &bytes), - ) - .await - .unwrap(); - assert_eq!(pins.request_expiry(task.context, 100).await.unwrap(), 670_100); - assert!(pins.request_expiry(task.context, u64::MAX - 100).await.is_err()); - let pin = pins - .protect_files( - task.context, - task.head.as_ref().unwrap().table, - "reader", - 670_100, - 100, - ) - .await - .unwrap(); - assert!(pin.protects_uploads); - pins.release(&pin).await.unwrap(); - task = finish(store.clone(), task).await; - GcRepository::new(store).fence_table(&task).await.unwrap(); - assert!(pins - .protect_files( - task.context, - task.head.as_ref().unwrap().table, - "reader", - 670_100, - 100 - ) - .await - .is_err()); -} - -#[tokio::test] -async fn late_credentials_cancel_sweep_and_release_the_table() { - let (store, task, _) = fixture(3, 2).await; - let mut task = finish(store.clone(), task).await; - let pins = crowdb_access_iceberg::gc::ReaderPins::new(store.clone()); - pins.protect_files( - task.context, - task.head.as_ref().unwrap().table, - "late-credentials", - 2_000_000, - 1_000_000, - ) - .await - .unwrap(); - let worker = GcWorker::new( - GcRepository::new(store.clone()), - Arc::new(blocks::TestBlocks::default()), - GcLimits::default(), - ) - .unwrap(); - for _ in 0..100 { - task = worker.step(&task, 1_000_000).await.unwrap(); - if task.phase == GcPhase::Complete { - break; - } - } - assert_eq!(task.phase, GcPhase::Complete); - assert_eq!( - task.stalled, - crowdb_access_iceberg::gc::GcStalledReason::Protected - ); - assert!(!task.fenced); - assert_eq!(task.deleted, 0); - pins.protect_files( - task.context, - task.head.as_ref().unwrap().table, - "new-request", - 2_000_000, - 1_000_000, - ) - .await - .unwrap(); -} - -#[tokio::test] -async fn dropped_table_file_reads_remain_protected_until_reclamation_fences_them() { - let (store, task, _) = fixture(3, 0).await; - let pins = crowdb_access_iceberg::gc::ReaderPins::new(store.clone()); - let mut head = task.head.unwrap(); - let key = head_key(head.catalog, head.table).encode().unwrap(); - for lifecycle in [TableLifecycle::Tombstone, TableLifecycle::Reclaiming] { - let before = store.get(&key).await.unwrap().unwrap(); - head.lifecycle = lifecycle; - head.pending_operation = Some(OperationId::random()); - let after = StorageRecord::TableHead(Box::new(head.clone())).encode().unwrap(); - store - .compare_exchange( - &key, - Some(&before.bytes), - &after, - mutation_identity(&key, Some(&before.bytes), &after), - ) - .await - .unwrap(); - let read = pins - .protect_file_reads(task.context, head.table, "file-reader", 2000, 100) - .await; - if lifecycle == TableLifecycle::Tombstone { - let pin = read.unwrap(); - assert!(pin.protects(1999)); - assert!(!pin.protects(2000)); - assert!(pin.protects_uploads); - assert_eq!( - pins.get(head.catalog, head.table, pin.identity).await.unwrap(), - Some(pin) - ); - } else { - assert!(matches!( - read, - Err(crowdb_access_iceberg::catalog::CatalogError::Busy) - )); - } - assert!(pins - .protect_files(task.context, head.table, "file-writer", 2000, 100) - .await - .is_err()); - } -} - #[tokio::test] async fn proof_and_fence_resume_after_lost_durable_write_responses() { use std::sync::atomic::Ordering; @@ -597,123 +454,6 @@ async fn proof_and_fence_resume_after_lost_durable_write_responses() { } } -#[tokio::test] -async fn historical_reader_root_is_retained_after_the_current_head_changes() { - let (store, old, old_files) = fixture(3, 1).await; - let old_head = old.head.as_ref().unwrap(); - let pin = crowdb_access_iceberg::gc::GcPin { - context: old.context, - identity: OperationId::random(), - head: old_head.clone(), - principal: "historical-reader".into(), - expires_ms: 2_000_000, - released: false, - operator: false, - protects_uploads: false, - }; - crowdb_access_iceberg::gc::ReaderPins::new(store.clone()) - .acquire(&pin) - .await - .unwrap(); - let bytes = serde_json::to_vec(&metadata::metadata(3)).unwrap(); - let mut head = old_head.clone(); - head.generation += 1; - head.operation_fence += 1; - head.metadata_file = FileId::random(); - head.metadata_location = metadata::table().file("metadata/new.json").unwrap(); - head.metadata_digest = Sha256::digest(&bytes).into(); - let file = FileRecord { - file: head.metadata_file, - location: head.metadata_location.clone(), - kind: FileKind::Metadata, - format: ContentFormat::Json, - length: bytes.len() as u64, - digest: head.metadata_digest, - content: FileContent::select_inline(FileKind::Metadata, &bytes).unwrap(), - hint: None, - }; - FileRepository::new(store.clone()) - .publish(old.context, &file) - .await - .unwrap(); - let key = head_key(head.catalog, head.table).encode().unwrap(); - let before = StorageRecord::TableHead(Box::new(old_head.clone())) - .encode() - .unwrap(); - let after = StorageRecord::TableHead(Box::new(head.clone())).encode().unwrap(); - store - .compare_exchange( - &key, - Some(&before), - &after, - mutation_identity(&key, Some(&before), &after), - ) - .await - .unwrap(); - let task = GcTask::plan( - old.context, - OperationId::random(), - Some(head), - 1, - GcLimits::default(), - ) - .unwrap(); - let repository = GcRepository::new(store.clone()); - put(&store, task.key(), StorageRecord::GcTask(Box::new(task.clone()))).await; - let task = finish(store, task).await; - for file in old_files { - assert!(repository.proof_contains(&task, file).await.unwrap()); - } - assert!(repository.proof_contains(&task, file.file).await.unwrap()); -} - -#[tokio::test] -async fn publication_pin_prevents_sweep_while_location_publication_is_in_flight() { - use std::sync::atomic::Ordering; - let (store, mut task, _) = fixture(3, 0).await; - let file = FileRecord { - file: FileId::random(), - location: metadata::table().file("metadata/in-flight.json").unwrap(), - kind: FileKind::Metadata, - format: ContentFormat::Json, - length: 2, - digest: Sha256::digest(b"{}").into(), - content: FileContent::select_inline(FileKind::Metadata, b"{}").unwrap(), - hint: None, - }; - store.file_mapping_pause.store(true, Ordering::SeqCst); - let publishing = tokio::spawn({ - let store = store.clone(); - let file = file.clone(); - let context = task.context; - async move { FileRepository::new(store).publish(context, &file).await } - }); - store.file_mapping_entered.notified().await; - let worker = GcWorker::new( - GcRepository::new(store.clone()), - Arc::new(blocks::TestBlocks::default()), - GcLimits { - minimum_retention_ms: 1, - ..GcLimits::default() - }, - ) - .unwrap(); - for _ in 0..100 { - task = worker.step(&task, 1_000_000).await.unwrap(); - if task.phase == GcPhase::Waiting { - break; - } - } - assert_eq!(task.phase, GcPhase::Waiting); - assert_eq!( - task.stalled, - crowdb_access_iceberg::gc::GcStalledReason::Protected - ); - assert_eq!(task.deleted, 0); - store.file_mapping_release.notify_one(); - assert_eq!(publishing.await.unwrap().unwrap(), file); -} - #[tokio::test] async fn live_sweep_recovers_a_lost_final_fence_release_response() { use crowdb_access_iceberg::gc::GcScan; diff --git a/lib/crowdb-access-iceberg/tests/gc_tree_test.rs b/lib/crowdb-access-iceberg/tests/gc_tree_test.rs index 8cf81ab5f..5e2def6aa 100644 --- a/lib/crowdb-access-iceberg/tests/gc_tree_test.rs +++ b/lib/crowdb-access-iceberg/tests/gc_tree_test.rs @@ -111,6 +111,7 @@ async fn corrupt_directory_never_authorizes_a_child_deletion() { next_child: 0, }], pending: None, + next_location: 0, }; assert!(cursor.next(&blocks).await.is_err()); assert!(cursor.pending.is_none()); diff --git a/lib/crowdb-access-iceberg/tests/gc_worker_test.rs b/lib/crowdb-access-iceberg/tests/gc_worker_test.rs index df3e7661b..cd21dd3ac 100644 --- a/lib/crowdb-access-iceberg/tests/gc_worker_test.rs +++ b/lib/crowdb-access-iceberg/tests/gc_worker_test.rs @@ -180,7 +180,8 @@ async fn expired_aborted_multipart_part_reclaims_tree_before_session_record() { revision: 1, modified_ms: 100, owner, - tree: writer.finish().await.unwrap(), + tree: Some(writer.finish().await.unwrap()), + stream: None, }; for (key, record) in [ ( @@ -924,137 +925,3 @@ async fn late_candidate_discovery_starts_a_fresh_retention_window() { assert_eq!(task.phase, GcPhase::Complete, "{task:?}"); assert_eq!(task.deleted, 1); } - -#[tokio::test] -async fn purge_fences_new_readers_and_waits_for_reader_and_delegated_pins() { - let (fixture, blocks, _, limits, _) = fixture(false).await; - let (head, pin, delegated, pins) = create_purge_with_pins(&fixture).await; - let repository = GcRepository::new(fixture.store.clone()); - let mut task = GcTask::plan(fixture.context, OperationId::random(), Some(head), 1000, limits).unwrap(); - repository.create(&task).await.unwrap(); - let worker = GcWorker::new(repository.clone(), blocks.clone(), limits).unwrap(); - for _ in 0..50 { - task = worker.step(&task, 2000_u64.max(task.retry_at_ms)).await.unwrap(); - if task.phase == GcPhase::Waiting { - break; - } - } - assert!(task.fenced); - assert_eq!(task.stalled, GcStalledReason::Protected); - assert_eq!(blocks.deletes.load(Ordering::Relaxed), 0); - let mut newcomer = pin.clone(); - newcomer.identity = OperationId::random(); - assert!(pins.acquire(&newcomer).await.is_err()); - pins.release(&pin).await.unwrap(); - let restarted = GcWorker::new(repository.clone(), blocks.clone(), limits).unwrap(); - for _ in 0..30 { - task = restarted - .step(&task, 3000_u64.max(task.retry_at_ms)) - .await - .unwrap(); - assert_eq!(blocks.deletes.load(Ordering::Relaxed), 0); - if task.stalled == GcStalledReason::Protected { - break; - } - } - assert_eq!(task.stalled, GcStalledReason::Protected); - pins.release(&delegated).await.unwrap(); - for _ in 0..300 { - task = restarted - .step(&task, 10_000_u64.max(task.retry_at_ms)) - .await - .unwrap(); - if task.phase == GcPhase::Complete { - break; - } - } - assert_eq!(task.phase, GcPhase::Complete); - assert_eq!(task.deleted, 2); - assert!(!task.fenced); - assert!(blocks.blocks.values.load().is_empty()); -} - -async fn create_purge_with_pins( - fixture: &common::file::TestFile, -) -> ( - crowdb_access_iceberg::table::TableHead, - crowdb_access_iceberg::gc::GcPin, - crowdb_access_iceberg::gc::GcPin, - crowdb_access_iceberg::gc::ReaderPins, -) { - use crowdb_access_iceberg::{ - gc::{GcPin, ReaderPins}, - key::NamespaceId, - table::{head_key, TableHead, TableLifecycle, TablePurgeTask}, - }; - let metadata = fixture.record("metadata/table.json", b"{}"); - FileRepository::new(fixture.store.clone()) - .publish(fixture.context, &metadata) - .await - .unwrap(); - let mut head = TableHead { - catalog: fixture.context.catalog, - table: fixture.table.table, - namespace: NamespaceId::random(), - name: "purged".into(), - name_epoch: 1, - lifecycle: TableLifecycle::Ready, - generation: 1, - metadata_file: metadata.file, - metadata_location: metadata.location, - metadata_digest: metadata.digest, - format_version: 1, - table_uuid: None, - operation_fence: 1, - pending_operation: None, - }; - let key = head_key(head.catalog, head.table).encode().unwrap(); - let before = StorageRecord::TableHead(Box::new(head.clone())).encode().unwrap(); - fixture - .store - .compare_exchange(&key, None, &before, mutation_identity(&key, None, &before)) - .await - .unwrap(); - let pin = GcPin { - context: fixture.context, - identity: OperationId::random(), - head: head.clone(), - principal: "reader".into(), - expires_ms: 5000, - released: false, - operator: false, - protects_uploads: true, - }; - let pins = ReaderPins::new(fixture.store.clone()); - pins.acquire(&pin).await.unwrap(); - let mut delegated = pin.clone(); - delegated.identity = OperationId::random(); - delegated.principal = "delegated-credential".into(); - pins.acquire(&delegated).await.unwrap(); - head.lifecycle = TableLifecycle::Tombstone; - head.operation_fence += 1; - head.pending_operation = Some(OperationId::random()); - let after = StorageRecord::TableHead(Box::new(head.clone())).encode().unwrap(); - fixture - .store - .compare_exchange( - &key, - Some(&before), - &after, - mutation_identity(&key, Some(&before), &after), - ) - .await - .unwrap(); - let purge = TablePurgeTask { - activation_epoch: fixture.context.activation_epoch, - head: head.clone(), - }; - let key = purge.key().encode().unwrap(); - let bytes = StorageRecord::TablePurgeTask(Box::new(purge)).encode().unwrap(); - fixture - .store - .compare_exchange(&key, None, &bytes, mutation_identity(&key, None, &bytes)) - .await - .unwrap(); - (head, pin, delegated, pins) -} diff --git a/lib/crowdb-access-iceberg/tests/multipart_completion_test.rs b/lib/crowdb-access-iceberg/tests/multipart_completion_test.rs index 75f8b5f4b..13f2e89ca 100644 --- a/lib/crowdb-access-iceberg/tests/multipart_completion_test.rs +++ b/lib/crowdb-access-iceberg/tests/multipart_completion_test.rs @@ -46,12 +46,13 @@ async fn setup() -> ( revision: 1, modified_ms: 101, owner, - tree: writer.finish().await.unwrap(), + tree: Some(writer.finish().await.unwrap()), + stream: None, }; selected.push(SelectedPart { number, revision: 1, - digest: part.tree.digest, + digest: part.selection_digest(), }); assert!(repository.reserve_part(&session, &part, 101).await.unwrap()); session = load(&repository, &session).await; diff --git a/lib/crowdb-access-iceberg/tests/multipart_list_test.rs b/lib/crowdb-access-iceberg/tests/multipart_list_test.rs index a26fdc12c..3a66be7d5 100644 --- a/lib/crowdb-access-iceberg/tests/multipart_list_test.rs +++ b/lib/crowdb-access-iceberg/tests/multipart_list_test.rs @@ -42,11 +42,12 @@ async fn setup() -> (file::TestFile, MultipartSession, Vec) { file: FileId::random(), ..session.owner }, - tree: FileTree { + tree: Some(FileTree { root: None, length: 0, digest: Sha256::digest([]).into(), - }, + }), + stream: None, }) .collect(); fixture.store.values.rcu(|values| { diff --git a/lib/crowdb-access-iceberg/tests/multipart_model_test.rs b/lib/crowdb-access-iceberg/tests/multipart_model_test.rs index c7cd7af6f..cd407ccf1 100644 --- a/lib/crowdb-access-iceberg/tests/multipart_model_test.rs +++ b/lib/crowdb-access-iceberg/tests/multipart_model_test.rs @@ -91,11 +91,12 @@ fn staged_parts_are_physical_bytes_bound_to_one_upload_table_and_revision() { file: FileId::random(), ..session.owner }, - tree: FileTree { + tree: Some(FileTree { root: None, length: 0, digest: Sha256::digest([]).into(), - }, + }), + stream: None, }; part.validate_for(&session).unwrap(); part.number = 0; diff --git a/lib/crowdb-access-iceberg/tests/multipart_observed_recovery_test.rs b/lib/crowdb-access-iceberg/tests/multipart_observed_recovery_test.rs index 8ed112822..1227df778 100644 --- a/lib/crowdb-access-iceberg/tests/multipart_observed_recovery_test.rs +++ b/lib/crowdb-access-iceberg/tests/multipart_observed_recovery_test.rs @@ -41,7 +41,8 @@ async fn completing(fixture: &file::TestFile, blocks: Arc) - revision: 1, modified_ms: 101, owner, - tree: writer.finish().await.unwrap(), + tree: Some(writer.finish().await.unwrap()), + stream: None, }; repository.reserve_part(&session, &part, 101).await.unwrap(); session = repository @@ -58,7 +59,7 @@ async fn completing(fixture: &file::TestFile, blocks: Arc) - let selection = MultipartSelection::new(vec![SelectedPart { number: 1, revision: 1, - digest: part.tree.digest, + digest: part.selection_digest(), }]) .unwrap(); repository diff --git a/lib/crowdb-access-iceberg/tests/multipart_publication_test.rs b/lib/crowdb-access-iceberg/tests/multipart_publication_test.rs index 3fefe515f..8f88ffd00 100644 --- a/lib/crowdb-access-iceberg/tests/multipart_publication_test.rs +++ b/lib/crowdb-access-iceberg/tests/multipart_publication_test.rs @@ -9,15 +9,20 @@ mod fixtures; #[path = "common/multipart_recovery_store.rs"] mod scan; +use std::fmt::Write as _; use std::sync::{atomic::Ordering, Arc}; use crowdb_access_iceberg::catalog::{CatalogError, CatalogStore}; use crowdb_access_iceberg::file::{ - FileIdentity, FileRecord, FileRepository, FileTree, FileTreeWriter, MultipartPart, MultipartPhase, - MultipartRecovery, MultipartRepository, MultipartSelection, MultipartSession, SelectedPart, + FileContent, FileIdentity, FileRecord, FileRepository, FileTree, FileTreeWriter, MultipartPart, + MultipartPhase, MultipartRecovery, MultipartRepository, MultipartSelection, MultipartSession, + MultipartStreamPart, SelectedPart, }; use crowdb_access_iceberg::key::FileId; use crowdb_access_iceberg::operation::mutation_identity; +use crowdb_protocol::chunkdb::rpc::Location; +use crowdb_protocol::common::ChunkId; +use md5::{Digest, Md5}; async fn setup() -> ( file::TestFile, @@ -50,7 +55,8 @@ async fn setup() -> ( revision: 1, modified_ms: 101, owner, - tree: writer.finish().await.unwrap(), + tree: Some(writer.finish().await.unwrap()), + stream: None, }; repository.reserve_part(&session, &part, 101).await.unwrap(); session = load(&repository, &session).await; @@ -59,7 +65,7 @@ async fn setup() -> ( let selection = MultipartSelection::new(vec![SelectedPart { number: 1, revision: 1, - digest: part.tree.digest, + digest: part.selection_digest(), }]) .unwrap(); repository @@ -87,6 +93,90 @@ async fn load(repository: &MultipartRepository, session: &MultipartSession) -> M .unwrap() } +#[tokio::test] +async fn streamed_parts_complete_by_composing_locations_and_saved_md5_only() { + let fixture = file::TestFile::new(common::TestStore::default()).await; + let repository = MultipartRepository::new(fixture.store.clone()); + let mut session = fixtures::session(); + session.context = fixture.context; + session.owner.table = fixture.table; + session.location = fixture.table.file("data/composed.parquet").unwrap(); + repository.begin(&session, 100).await.unwrap(); + let mut selected = Vec::new(); + for (number, size, md5) in [ + (1_u16, 7_u64, "11111111111111111111111111111111"), + (2, 5, "22222222222222222222222222222222"), + ] { + let location = Location { + chunk_id: Some(ChunkId { + high: 7, + low: u64::from(number), + }), + offset: 100, + length: size + 34, + logical_offset: 0, + logical_length: size, + }; + let part = MultipartPart { + upload: session.upload, + number, + revision: 1, + modified_ms: 101, + owner: FileIdentity { + file: FileId::random(), + ..session.owner + }, + tree: None, + stream: Some(MultipartStreamPart { + length: size, + content: FileContent::from_locations(&[location], size, md5.into()).unwrap(), + }), + }; + repository.reserve_part(&session, &part, 101).await.unwrap(); + session = load(&repository, &session).await; + repository.settle_part(&session).await.unwrap(); + session = load(&repository, &session).await; + selected.push(SelectedPart { + number, + revision: 1, + digest: part.selection_digest(), + }); + } + let selection = MultipartSelection::new(selected).unwrap(); + repository + .freeze_completion(&session, &selection, 102) + .await + .unwrap(); + session = load(&repository, &session).await; + assert_eq!( + repository + .prepare_stream_publication(&session, 103) + .await + .unwrap(), + Some(true) + ); + session = load(&repository, &session).await; + let published = repository.publish(&session).await.unwrap().unwrap(); + let locations = published.content.locations(published.length).unwrap().unwrap(); + assert_eq!(published.length, 12); + assert_eq!( + locations + .iter() + .map(|location| location.logical_offset) + .collect::>(), + vec![0, 7] + ); + let mut digest = Md5::new(); + digest.update([0x11; 16]); + digest.update([0x22; 16]); + let mut expected = String::new(); + for byte in digest.finalize() { + write!(&mut expected, "{byte:02x}").unwrap(); + } + expected.push_str("-2"); + assert_eq!(published.content.etag(), Some(expected.as_str())); +} + #[tokio::test] async fn every_lost_publication_reply_recovers_the_same_seal_and_file_identity() { for lost in 1..=5 { diff --git a/lib/crowdb-access-iceberg/tests/multipart_record_test.rs b/lib/crowdb-access-iceberg/tests/multipart_record_test.rs index 47dd23fdd..22a1507ba 100644 --- a/lib/crowdb-access-iceberg/tests/multipart_record_test.rs +++ b/lib/crowdb-access-iceberg/tests/multipart_record_test.rs @@ -65,7 +65,8 @@ async fn multipart_records_round_trip_open_partial_publishing_published_and_abor revision: 2, modified_ms: 101, owner, - tree: part.tree, + tree: Some(part.tree), + stream: None, }; let record = StorageRecord::MultipartPart(Box::new(part.clone())); let bytes = record.encode().unwrap(); diff --git a/lib/crowdb-access-iceberg/tests/multipart_recovery_budget_test.rs b/lib/crowdb-access-iceberg/tests/multipart_recovery_budget_test.rs index 37c1ea3db..798e28ac8 100644 --- a/lib/crowdb-access-iceberg/tests/multipart_recovery_budget_test.rs +++ b/lib/crowdb-access-iceberg/tests/multipart_recovery_budget_test.rs @@ -68,7 +68,8 @@ async fn slow_first_session_does_not_starve_later_expiry_or_advance_unfinished_b revision: 1, modified_ms: 101, owner, - tree: writer.finish().await.unwrap(), + tree: Some(writer.finish().await.unwrap()), + stream: None, }; repository.reserve_part(&first, &part, 101).await.unwrap(); first = repository @@ -85,7 +86,7 @@ async fn slow_first_session_does_not_starve_later_expiry_or_advance_unfinished_b let selection = MultipartSelection::new(vec![SelectedPart { number: 1, revision: 1, - digest: part.tree.digest, + digest: part.selection_digest(), }]) .unwrap(); repository diff --git a/lib/crowdb-access-iceberg/tests/multipart_recovery_test.rs b/lib/crowdb-access-iceberg/tests/multipart_recovery_test.rs index fc469218d..4cb1454a1 100644 --- a/lib/crowdb-access-iceberg/tests/multipart_recovery_test.rs +++ b/lib/crowdb-access-iceberg/tests/multipart_recovery_test.rs @@ -51,7 +51,8 @@ async fn bounded_sweeps_settle_abandoned_part_mutations_then_expire_without_dele revision: 1, modified_ms: 101, owner, - tree: writer.finish().await.unwrap(), + tree: Some(writer.finish().await.unwrap()), + stream: None, }; assert!(repository.reserve_part(&session, &part, 101).await.unwrap()); uploads.push((session, part)); @@ -110,7 +111,8 @@ async fn recovery_advances_one_byte_window_per_visit_and_reports_unpublished_sea revision: 1, modified_ms: 101, owner, - tree: writer.finish().await.unwrap(), + tree: Some(writer.finish().await.unwrap()), + stream: None, }; repository.reserve_part(&initial, &part, 101).await.unwrap(); let pending = repository @@ -127,7 +129,7 @@ async fn recovery_advances_one_byte_window_per_visit_and_reports_unpublished_sea let selection = MultipartSelection::new(vec![SelectedPart { number: 1, revision: 1, - digest: part.tree.digest, + digest: part.selection_digest(), }]) .unwrap(); repository diff --git a/lib/crowdb-access-iceberg/tests/multipart_repository_test.rs b/lib/crowdb-access-iceberg/tests/multipart_repository_test.rs index 9c43ff34d..ce88e51b5 100644 --- a/lib/crowdb-access-iceberg/tests/multipart_repository_test.rs +++ b/lib/crowdb-access-iceberg/tests/multipart_repository_test.rs @@ -42,7 +42,8 @@ async fn part(session: &MultipartSession, number: u16, revision: u64, length: us revision, modified_ms: 101, owner, - tree: writer.finish().await.unwrap(), + tree: Some(writer.finish().await.unwrap()), + stream: None, } } diff --git a/lib/crowdb-access-s3/src/integrity.rs b/lib/crowdb-access-s3/src/integrity.rs index b7f87e502..208390972 100644 --- a/lib/crowdb-access-s3/src/integrity.rs +++ b/lib/crowdb-access-s3/src/integrity.rs @@ -28,23 +28,31 @@ pub enum IntegrityError { /// future contract. pub struct SinglePartIntegrity { md5: md5::Context, - sha256: Sha256, + sha256: Option, } impl Default for SinglePartIntegrity { fn default() -> Self { + Self::new(false) + } +} + +impl SinglePartIntegrity { + /// Enables payload SHA-256 only when the request declares that digest. + #[must_use] + pub fn new(check_payload_sha256: bool) -> Self { Self { md5: md5::Context::new(), - sha256: Sha256::new(), + sha256: check_payload_sha256.then(Sha256::new), } } -} -impl SinglePartIntegrity { /// Adds one immutable body frame without copying it. pub fn update(&mut self, bytes: &Bytes) { self.md5.consume(bytes); - self.sha256.update(bytes); + if let Some(sha256) = &mut self.sha256 { + sha256.update(bytes); + } } /// Returns the persisted single-part `ETag` and its raw checksum bytes. @@ -95,6 +103,7 @@ impl SinglePartIntegrity { if expected.len() != 64 || !expected.bytes().all(|byte| byte.is_ascii_hexdigit()) { return Err(IntegrityError::InvalidPayloadDigest); } + let sha256 = sha256.ok_or(IntegrityError::InvalidPayloadDigest)?; let actual = format!("{:x}", sha256.finalize()); if !actual.eq_ignore_ascii_case(expected) { return Err(IntegrityError::PayloadMismatch); diff --git a/lib/crowdb-access-s3/src/metrics.rs b/lib/crowdb-access-s3/src/metrics.rs index 154cc72fe..909247c88 100644 --- a/lib/crowdb-access-s3/src/metrics.rs +++ b/lib/crowdb-access-s3/src/metrics.rs @@ -7,7 +7,7 @@ use std::fmt::Write as _; use std::sync::atomic::{AtomicBool, AtomicU64, AtomicU8, Ordering}; use crowdb_chunk_client::{ - ChunkIoClient, LargeWriteBufferMetricsSnapshot, LargeWriteRepairMetricsSnapshot, + ChunkIoClient, LargeWriteBufferMetricsSnapshot, LargeWriteRepairMetricsSnapshot, ReadFlowMetricsSnapshot, SmallWriteMetricsSnapshot, }; @@ -210,6 +210,7 @@ impl S3Metrics { large_write_buffers: chunks.large_write_buffer_metrics(), large_write_repairs: chunks.large_write_repair_metrics(), small_writes: chunks.small_write_metrics(), + read_flow: chunks.read_flow_metrics(), checksum_bytes: request.checksum_bytes, metadata_retries: request.metadata_retries, cleanup_enqueued: request.cleanup_enqueued, @@ -271,6 +272,7 @@ impl S3Metrics { } if let Some(chunks) = chunks { append_chunk_metrics(&mut output, chunks.large_write_buffer_metrics()); + append_read_flow_metrics(&mut output, chunks.read_flow_metrics()); } output } @@ -341,6 +343,78 @@ fn append_chunk_metrics(output: &mut String, buffers: LargeWriteBufferMetricsSna } } +fn append_read_flow_metrics(output: &mut String, flow: ReadFlowMetricsSnapshot) { + for (name, value) in [ + ( + "crowdb_s3_chunk_read_location_normalizations_total", + flow.location_normalizations, + ), + ( + "crowdb_s3_chunk_read_locations_examined_total", + flow.locations_examined, + ), + ( + "crowdb_s3_chunk_read_range_locations_examined_total", + flow.range_locations_examined, + ), + ("crowdb_s3_chunk_read_stream_windows_total", flow.stream_windows), + ( + "crowdb_s3_chunk_read_stream_units_completed_total", + flow.stream_units_completed, + ), + ( + "crowdb_s3_chunk_read_stream_out_of_order_total", + flow.stream_out_of_order, + ), + ( + "crowdb_s3_chunk_read_stream_credit_stalls_total", + flow.stream_credit_stalls, + ), + ( + "crowdb_s3_chunk_read_stream_credit_wait_ns_total", + flow.stream_credit_wait_ns, + ), + ( + "crowdb_s3_chunk_read_stream_bytes_reserved_total", + flow.stream_bytes_reserved, + ), + ( + "crowdb_s3_chunk_read_stream_bytes_released_total", + flow.stream_bytes_released, + ), + ("crowdb_s3_chunk_read_layout_queries_total", flow.layout_queries), + ( + "crowdb_s3_chunk_read_layout_query_wait_ns_total", + flow.layout_query_wait_ns, + ), + ( + "crowdb_s3_chunk_read_strip_read_wait_ns_total", + flow.strip_read_wait_ns, + ), + ( + "crowdb_s3_chunk_range_read_wait_ns_total", + flow.chunk_read_wait_ns, + ), + ( + "crowdb_s3_chunk_read_frame_decode_wait_ns_total", + flow.frame_decode_wait_ns, + ), + ( + "crowdb_s3_chunk_read_frame_parse_wait_ns_total", + flow.frame_parse_wait_ns, + ), + ] { + append_metric(output, name, "", value); + } + append_metric( + output, + "crowdb_s3_chunk_read_stream_retained_bytes", + "", + flow.stream_bytes_reserved + .saturating_sub(flow.stream_bytes_released), + ); +} + fn append_metric(output: &mut String, name: &str, labels: &str, value: u64) { if labels.is_empty() { let _ = writeln!(output, "{name} {value}"); @@ -376,6 +450,7 @@ pub struct S3DataPathMetricsSnapshot { pub large_write_buffers: LargeWriteBufferMetricsSnapshot, pub large_write_repairs: LargeWriteRepairMetricsSnapshot, pub small_writes: SmallWriteMetricsSnapshot, + pub read_flow: ReadFlowMetricsSnapshot, pub checksum_bytes: u64, pub metadata_retries: u64, pub cleanup_enqueued: u64, diff --git a/lib/crowdb-access-s3/src/streaming.rs b/lib/crowdb-access-s3/src/streaming.rs index 3089a704f..adf54f514 100644 --- a/lib/crowdb-access-s3/src/streaming.rs +++ b/lib/crowdb-access-s3/src/streaming.rs @@ -156,8 +156,7 @@ where write_body_with_checksums_metered(body, writer, expected_content_md5, expected_payload_sha256, None).await } -/// Streams one body while accounting the exact bytes presented to both -/// checksum implementations. +/// Streams one body while accounting the exact bytes presented to integrity checks. /// /// # Errors /// @@ -174,7 +173,7 @@ where B::Error: std::fmt::Display, W: ChunkIoWriter, { - let mut integrity = SinglePartIntegrity::default(); + let mut integrity = SinglePartIntegrity::new(expected_payload_sha256.is_some()); loop { if writer.input_complete() { return finish_integrity(integrity, expected_content_md5, expected_payload_sha256); @@ -201,6 +200,65 @@ where } } +/// Coalesces receive fragments into one bounded push per receive owner. +/// # Errors +/// Returns body, writer, or declared checksum errors without publishing data. +pub async fn write_body_with_checksums_buffered( + body: &mut B, + writer: &mut W, + expected_content_md5: Option<&str>, + expected_payload_sha256: Option<&str>, + receive_bytes: usize, + metrics: Option<&crate::metrics::S3Metrics>, +) -> Result<(String, Vec), PutOutcome> +where + B: Body + Unpin, + B::Error: std::fmt::Display, + W: ChunkIoWriter, +{ + if receive_bytes == 0 || receive_bytes > 1024 * 1024 { + return Err(put_error(PutErrorCode::BodyRead, "invalid receive buffer size")); + } + let mut integrity = SinglePartIntegrity::new(expected_payload_sha256.is_some()); + let mut pending = Vec::with_capacity(receive_bytes); + loop { + if writer.input_complete() { + break; + } + while !writer.require_data() && !writer.input_complete() { + writer.wait_for_capacity().await; + } + let frame = poll_fn(|context| Pin::new(&mut *body).poll_frame(context)).await; + let Some(frame) = frame else { break }; + let frame = frame.map_err(|error| put_error(PutErrorCode::BodyRead, error))?; + let Ok(mut data) = frame.into_data() else { continue }; + if let Some(metrics) = metrics { + metrics.record_checksum_bytes(data.len()); + } + integrity.update(&data); + while !data.is_empty() { + let count = (receive_bytes - pending.len()).min(data.len()); + pending.extend_from_slice(&data.split_to(count)); + if pending.len() == receive_bytes { + writer + .on_data(Bytes::from(std::mem::replace( + &mut pending, + Vec::with_capacity(receive_bytes), + ))) + .await + .map_err(|error| put_error(PutErrorCode::ChunkWrite, error))?; + } + } + } + if !pending.is_empty() { + writer + .on_data(Bytes::from(pending)) + .await + .map_err(|error| put_error(PutErrorCode::ChunkWrite, error))?; + } + finish_integrity(integrity, expected_content_md5, expected_payload_sha256) +} + /// Streams one body through a native owner provider while calculating object /// integrity over the socket-filled payload views. Full owners and the EOF /// prefix are handed to the large writer without payload copies. @@ -252,7 +310,7 @@ where B::Error: std::fmt::Display, W: ChunkIoWriter, { - let mut integrity = SinglePartIntegrity::default(); + let mut integrity = SinglePartIntegrity::new(expected_payload_sha256.is_some()); loop { while !writer.require_data() { writer.wait_for_capacity().await; diff --git a/lib/crowdb-access-s3/tests/integrity_test.rs b/lib/crowdb-access-s3/tests/integrity_test.rs index 81ba9346e..9588e569b 100644 --- a/lib/crowdb-access-s3/tests/integrity_test.rs +++ b/lib/crowdb-access-s3/tests/integrity_test.rs @@ -35,7 +35,7 @@ fn content_md5_is_checked_before_publication() { #[test] fn signed_payload_sha256_is_checked_incrementally() { - let mut valid = SinglePartIntegrity::default(); + let mut valid = SinglePartIntegrity::new(true); valid.update(&Bytes::from_static(b"abc")); assert!(valid .finish_validated_checksums( @@ -44,7 +44,7 @@ fn signed_payload_sha256_is_checked_incrementally() { ) .is_ok()); - let mut mismatch = SinglePartIntegrity::default(); + let mut mismatch = SinglePartIntegrity::new(true); mismatch.update(&Bytes::from_static(b"abc")); assert_eq!( mismatch.finish_validated_checksums( diff --git a/lib/crowdb-access-s3/tests/streaming_test.rs b/lib/crowdb-access-s3/tests/streaming_test.rs index 79c87ab64..6828d567f 100644 --- a/lib/crowdb-access-s3/tests/streaming_test.rs +++ b/lib/crowdb-access-s3/tests/streaming_test.rs @@ -13,9 +13,9 @@ use crowdb_access_s3::metadata::{BucketId, ObjectRecord}; use crowdb_access_s3::metrics::S3Metrics; use crowdb_access_s3::native_buffer::NativeBodyAllocator; use crowdb_access_s3::streaming::{ - attach_completed_locations, cleanup_after_definite_error, write_body, - write_native_body_with_checksums_metered, FailedPublicationCleanup, FailedPublicationTarget, - PutErrorCode, PutOutcome, + attach_completed_locations, cleanup_after_definite_error, write_body, write_body_with_checksums, + write_body_with_checksums_buffered, write_native_body_with_checksums_metered, FailedPublicationCleanup, + FailedPublicationTarget, PutErrorCode, PutOutcome, }; use crowdb_chunk_client::{ChunkIoWriter, FeedStatus, FramedWriteBuffer, IoError}; use crowdb_protocol::chunkdb::rpc::Location; @@ -192,6 +192,65 @@ async fn declared_length_completion_does_not_poll_for_an_extra_body_frame() { assert_eq!(body.polls.load(Ordering::Relaxed), 1); } +#[tokio::test] +async fn payload_sha256_is_verified_only_when_declared() { + let payload = Bytes::from_static(b"abc"); + let expected = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"; + let invalid = "0".repeat(64); + for (digest, accepted) in [ + (None, true), + (Some(expected), true), + (Some(invalid.as_str()), false), + ] { + let mut body = TestBody { + frames: VecDeque::from([payload.clone()]), + polls: AtomicUsize::new(0), + }; + let mut writer = NativeOwnerWriter::default(); + let result = write_body_with_checksums(&mut body, &mut writer, None, digest).await; + assert_eq!(result.is_ok(), accepted); + assert_eq!(writer.generic_frames, 1); + } +} + +#[derive(Default)] +struct CollectingWriter(Vec); + +#[async_trait] +impl ChunkIoWriter for CollectingWriter { + async fn on_data(&mut self, buffer: Bytes) -> crowdb_chunk_client::Result { + self.0.push(buffer); + Ok(FeedStatus::Continue) + } + + async fn on_finish(&mut self) -> crowdb_chunk_client::Result> { + Ok(Vec::new()) + } + async fn on_error(&mut self) -> crowdb_chunk_client::Result> { + Ok(Vec::new()) + } + fn require_data(&self) -> bool { + true + } +} + +#[tokio::test] +async fn receive_fragments_fill_one_small_object_buffer_before_pipeline_push() { + let mut body = TestBody { + frames: VecDeque::from([ + Bytes::from_static(b"ab"), + Bytes::from_static(b"cd"), + Bytes::from_static(b"e"), + ]), + polls: AtomicUsize::new(0), + }; + let mut writer = CollectingWriter::default(); + write_body_with_checksums_buffered(&mut body, &mut writer, None, None, 5, None) + .await + .unwrap(); + assert_eq!(writer.0, vec![Bytes::from_static(b"abcde")]); +} + #[derive(Default)] struct NativeOwnerWriter { generic_frames: usize, diff --git a/lib/crowdb-chunk-client/src/benchmark.rs b/lib/crowdb-chunk-client/src/benchmark.rs index f6fdb2230..8d9f44a7d 100644 --- a/lib/crowdb-chunk-client/src/benchmark.rs +++ b/lib/crowdb-chunk-client/src/benchmark.rs @@ -8,6 +8,7 @@ use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; use std::sync::Arc; use std::time::{Duration, Instant}; +use bytes::Bytes; use crowdb_common::metrics::perf::DramBwCounter; use serde::Serialize; use tokio::io::{AsyncRead, ReadBuf}; @@ -174,6 +175,7 @@ pub struct ReadBenchmarkResult { pub dram_read_mib_s: Option, pub dram_write_mib_s: Option, pub dram_total_mib_s: Option, + pub read_flow: crate::ReadFlowMetricsSnapshot, pub error_messages: Vec, } @@ -707,6 +709,7 @@ pub async fn run_read_benchmark(client: ChunkIoClient, config: ReadBenchmarkConf Err(error) => return failed_read_before_load(&config, &error.to_string()), }; let preparation_secs = preparation_started.elapsed().as_secs_f64(); + let read_flow_before = client.read_flow_metrics(); let started = Instant::now(); let deadline = config.duration.map(|duration| started + duration); let mut dram_bw = DramBwCounter::new(); @@ -742,6 +745,7 @@ pub async fn run_read_benchmark(client: ChunkIoClient, config: ReadBenchmarkConf let requested_reads = next_read.load(Ordering::Relaxed).min(config.request_count); let incomplete_reads = requested_reads.saturating_sub(total.reads.saturating_add(total.errors)); let (dram_read_mib_s, dram_write_mib_s, dram_total_mib_s) = sample_dram(&mut dram_bw); + let read_flow = client.read_flow_metrics().since(read_flow_before); finalize_read_result( preparation_secs, elapsed_secs, @@ -749,6 +753,7 @@ pub async fn run_read_benchmark(client: ChunkIoClient, config: ReadBenchmarkConf incomplete_reads, total, (dram_read_mib_s, dram_write_mib_s, dram_total_mib_s), + read_flow, ) } @@ -776,7 +781,7 @@ async fn run_read_worker( let object = &objects[index]; let operation_started = Instant::now(); match client.read_object(&object.locations).await { - Ok(bytes) if bytes.len() as u64 == object.logical_bytes => { + Ok(bytes) if bytes.iter().map(Bytes::len).sum::() as u64 == object.logical_bytes => { result.reads += 1; result.logical_bytes += object.logical_bytes; if use_large { @@ -793,7 +798,7 @@ async fn run_read_worker( &mut result.error_messages, format!( "read {request}: length {} != {}", - bytes.len(), + bytes.iter().map(Bytes::len).sum::(), object.logical_bytes ), ); @@ -838,6 +843,7 @@ fn finalize_read_result( incomplete_reads: u64, total: ReadWorkerResult, dram: (Option, Option, Option), + read_flow: crate::ReadFlowMetricsSnapshot, ) -> ReadBenchmarkResult { ReadBenchmarkResult { preparation_secs, @@ -866,6 +872,7 @@ fn finalize_read_result( dram_read_mib_s: dram.0, dram_write_mib_s: dram.1, dram_total_mib_s: dram.2, + read_flow, error_messages: total.error_messages, } } @@ -935,6 +942,7 @@ fn failed_read_before_load(config: &ReadBenchmarkConfig, message: &str) -> ReadB dram_read_mib_s: None, dram_write_mib_s: None, dram_total_mib_s: None, + read_flow: crate::ReadFlowMetricsSnapshot::default(), error_messages: vec![format!("prepare reads: {message}")], } } diff --git a/lib/crowdb-chunk-client/src/chunk.rs b/lib/crowdb-chunk-client/src/chunk.rs index 948fec791..de3904948 100644 --- a/lib/crowdb-chunk-client/src/chunk.rs +++ b/lib/crowdb-chunk-client/src/chunk.rs @@ -21,6 +21,7 @@ pub mod mirror_chunk_writer; pub mod mirror_flow; pub mod mirror_strip_writer; pub mod parity_writer; +pub(crate) mod read_credit; pub(crate) mod segment_writer; pub mod strip; pub mod strip_reader; diff --git a/lib/crowdb-chunk-client/src/chunk/chunk_reader.rs b/lib/crowdb-chunk-client/src/chunk/chunk_reader.rs index 57a3076ad..39c47171f 100644 --- a/lib/crowdb-chunk-client/src/chunk/chunk_reader.rs +++ b/lib/crowdb-chunk-client/src/chunk/chunk_reader.rs @@ -3,34 +3,41 @@ //! Object and range reads over current chunk layouts. +use std::collections::BTreeMap; use std::ops::Range; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::time::{Duration, Instant}; use bytes::{Bytes, BytesMut}; use crowdb_protocol::chunkdb::rpc::{ AdHocEcRecoveryDisposition, AdHocEcRecoveryRequest, Chunk, ChunkState, Location, QueryChunkRequest, + QueryChunkResponse, }; use crowdb_protocol::common::ChunkId; use crowdb_protocol::diskdb::rpc::Segment; use crowdb_protocol::frame::{ - parse_frame, ChunkLocation, FrameMagic, FRAME_FOOTER_BYTES, FRAME_HEADER_PREFIX_BYTES, MAX_FRAME_BYTES, - MAX_FRAME_PAYLOAD_BYTES, + parse_frame, parse_frame_views, ChunkLocation, FrameMagic, FRAME_FOOTER_BYTES, FRAME_HEADER_PREFIX_BYTES, + MAX_FRAME_BYTES, MAX_FRAME_PAYLOAD_BYTES, }; use tokio::sync::Semaphore; use tokio::task::JoinSet; use super::client_recovery::ClientRecovery; +use super::read_credit::{retain, ReadBudget, ReadLease, StreamSlots}; use super::strip_reader::StripReader; +use crate::metrics::ReadFlowMetrics; use crate::{ChunkAllocator, DiskWriter, IoError, ReadError, ReadResult}; const KIB: u64 = 1024; -const DEFAULT_STREAM_WINDOW: usize = 64 * 1024 * 1024; +const DEFAULT_STREAM_WINDOW: usize = 1024 * 1024; /// Bounded retry, streaming, and EC-recovery memory policy. #[derive(Debug, Clone)] pub struct ChunkReadPolicy { pub stream_window_bytes: usize, + pub stream_slots: usize, + pub global_stream_bytes: usize, pub recovery_memory_bytes: usize, pub layout_safety_margin: Duration, pub max_layout_retries: usize, @@ -62,10 +69,30 @@ pub struct PartialReadResult { pub failures: Vec, } +#[derive(Clone)] +struct LayoutSnapshot { + chunk_id: ChunkId, + chunk: Arc, + deadline: Instant, + valid: Arc, +} + +impl LayoutSnapshot { + fn usable(&self) -> bool { + self.valid.load(Ordering::Acquire) && Instant::now() < self.deadline + } + + fn invalidate(&self) { + self.valid.store(false, Ordering::Release); + } +} + impl Default for ChunkReadPolicy { fn default() -> Self { Self { stream_window_bytes: DEFAULT_STREAM_WINDOW, + stream_slots: 3, + global_stream_bytes: 256 * 1024 * 1024, recovery_memory_bytes: 256 * 1024 * 1024, layout_safety_margin: Duration::from_millis(5), max_layout_retries: 3, @@ -79,6 +106,8 @@ impl Default for ChunkReadPolicy { impl ChunkReadPolicy { fn validate(&self) -> ReadResult<()> { if self.stream_window_bytes == 0 + || self.stream_slots == 0 + || self.global_stream_bytes < DEFAULT_STREAM_WINDOW || self.recovery_memory_bytes == 0 || self.recovery_memory_bytes > u32::MAX as usize || self.max_layout_retries == 0 @@ -98,6 +127,8 @@ pub struct ChunkReader { chunkdb: Arc, strip_reader: StripReader, policy: ChunkReadPolicy, + flow_metrics: Arc, + stream_budget: Arc, } impl ChunkReader { @@ -106,7 +137,7 @@ impl ChunkReader { disk_io: Arc, policy: ChunkReadPolicy, ) -> ReadResult { - Self::new_with_metrics(chunkdb, disk_io, policy, Arc::default()) + Self::new_with_metrics(chunkdb, disk_io, policy, Arc::default(), Arc::default()) } pub(crate) fn new_with_metrics( @@ -114,6 +145,7 @@ impl ChunkReader { disk_io: Arc, policy: ChunkReadPolicy, metrics: Arc, + flow_metrics: Arc, ) -> ReadResult { policy.validate()?; let recovery_memory = Arc::new(Semaphore::new(policy.recovery_memory_bytes)); @@ -125,34 +157,75 @@ impl ChunkReader { policy.ad_hoc_window, metrics, )); + let stream_budget = Arc::new(ReadBudget::new( + policy.global_stream_bytes, + Arc::clone(&flow_metrics), + )); Ok(Self { chunkdb, strip_reader: StripReader::new(disk_io, recovery_memory, policy.recovery_memory_bytes) .with_ad_hoc(ad_hoc), policy, + flow_metrics, + stream_budget, + }) + } + + /// Cumulative work counters for the shared read path. + pub fn flow_metrics_snapshot(&self) -> crate::metrics::ReadFlowMetricsSnapshot { + self.flow_metrics.snapshot() + } + + async fn query_chunk_timed(&self, chunk_id: ChunkId) -> crate::Result { + let started = Instant::now(); + self.flow_metrics.layout_queries.inc(); + let result = self + .chunkdb + .query_chunk(QueryChunkRequest { + chunk_id: Some(chunk_id), + }) + .await; + self.flow_metrics + .layout_query_wait_ns + .inc_by(u64::try_from(started.elapsed().as_nanos()).unwrap_or(u64::MAX)); + result + } + + async fn query_layout(&self, chunk_id: ChunkId) -> ReadResult { + let started = Instant::now(); + let response = self + .query_chunk_timed(chunk_id) + .await + .map_err(map_metadata_error)?; + let deadline = started + + Duration::from_millis(response.layout_validity_ms) + .saturating_sub(self.policy.layout_safety_margin); + let chunk = response + .chunk + .ok_or_else(|| ReadError::ChunkDeleted(format!("{}:{}", chunk_id.high, chunk_id.low)))?; + Ok(LayoutSnapshot { + chunk_id, + chunk: Arc::new(chunk), + deadline, + valid: Arc::new(AtomicBool::new(true)), }) } - pub async fn read_object(&self, locations: &[Location]) -> ReadResult { - let (_, object_length) = normalize_locations(locations)?; - self.read_range(locations, 0, object_length).await + pub async fn read_object(&self, locations: &[Location]) -> ReadResult> { + self.flow_metrics.location_normalizations.inc(); + self.flow_metrics + .locations_examined + .inc_by(u64::try_from(locations.len()).unwrap_or(u64::MAX)); + let (locations, object_length) = normalize_locations(locations)?; + let partial = self + .read_range_partial_normalized(&locations, object_length, 0, object_length) + .await?; + complete_read(partial, 0, object_length) } - pub async fn read_range(&self, locations: &[Location], start: u64, end: u64) -> ReadResult { + pub async fn read_range(&self, locations: &[Location], start: u64, end: u64) -> ReadResult> { let partial = self.read_range_partial(locations, start, end).await?; - if let Some(failure) = partial.failures.into_iter().next() { - return Err(ReadError::FailedRange { - start: failure.start, - end: failure.end, - message: failure.error.to_string(), - }); - } - let expected = usize::try_from(end - start).map_err(|_| ReadError::InvalidRange { - start, - end, - object_length: end, - })?; - assemble_ranges(partial.ranges, start, expected) + complete_read(partial, start, end) } pub async fn read_range_partial( @@ -161,7 +234,22 @@ impl ChunkReader { start: u64, end: u64, ) -> ReadResult { + self.flow_metrics.location_normalizations.inc(); + self.flow_metrics + .locations_examined + .inc_by(u64::try_from(locations.len()).unwrap_or(u64::MAX)); let (locations, object_length) = normalize_locations(locations)?; + self.read_range_partial_normalized(&locations, object_length, start, end) + .await + } + + async fn read_range_partial_normalized( + &self, + locations: &[Location], + object_length: u64, + start: u64, + end: u64, + ) -> ReadResult { if start > end || end > object_length { return Err(ReadError::InvalidRange { start, @@ -174,8 +262,16 @@ impl ChunkReader { } let mut reads = JoinSet::new(); - for location in locations { + let first = locations.partition_point(|location| { + location.logical_offset.saturating_add(location.logical_length) <= start + }); + let mut examined = 0u64; + for location in &locations[first..] { let loc_start = location.logical_offset; + if loc_start >= end { + break; + } + examined += 1; let loc_end = loc_start + location.logical_length; let overlap_start = start.max(loc_start); let overlap_end = end.min(loc_end); @@ -183,6 +279,7 @@ impl ChunkReader { continue; } let reader = self.clone(); + let location = location.clone(); reads.spawn(async move { let local_start = overlap_start - loc_start; let length = overlap_end - overlap_start; @@ -191,6 +288,7 @@ impl ChunkReader { .await }); } + self.flow_metrics.range_locations_examined.inc_by(examined); let mut partial = PartialReadResult::default(); while let Some(result) = reads.join_next().await { let location = result.map_err(|error| ReadError::DiskIo(error.to_string()))??; @@ -199,10 +297,26 @@ impl ChunkReader { } partial.ranges.sort_unstable_by_key(|range| range.start); partial.failures.sort_unstable_by_key(|range| range.start); - Ok(coalesce_partial_ranges(partial)) + let mut failures: Vec = Vec::with_capacity(partial.failures.len()); + for failure in partial.failures { + if let Some(previous) = failures + .last_mut() + .filter(|previous| previous.end == failure.start) + { + previous.end = failure.end; + } else { + failures.push(failure); + } + } + partial.failures = failures; + Ok(partial) } pub fn read_stream(&self, locations: &[Location]) -> ReadResult { + self.flow_metrics.location_normalizations.inc(); + self.flow_metrics + .locations_examined + .inc_by(u64::try_from(locations.len()).unwrap_or(u64::MAX)); let (locations, object_length) = normalize_locations(locations)?; self.range_stream(locations, 0, object_length, object_length) } @@ -214,6 +328,10 @@ impl ChunkReader { start: u64, end: u64, ) -> ReadResult { + self.flow_metrics.location_normalizations.inc(); + self.flow_metrics + .locations_examined + .inc_by(u64::try_from(locations.len()).unwrap_or(u64::MAX)); let (locations, object_length) = normalize_locations(locations)?; self.range_stream(locations, start, end, object_length) } @@ -236,9 +354,16 @@ impl ChunkReader { reader: self.clone(), locations: Arc::from(locations), cursor: start, + fetch_cursor: start, + delivery_cursor: start, end, window_bytes: self.policy.stream_window_bytes as u64, + slots: Arc::new(StreamSlots::new(self.policy.stream_slots)), + reads: JoinSet::new(), + completed: BTreeMap::new(), + layouts: Vec::new(), pending_error: None, + pending_ranges: Vec::new().into_iter(), }) } @@ -272,13 +397,7 @@ impl ChunkReader { .ok_or_else(|| ReadError::InvalidLocations("frame end overflows".into()))?; for attempt in 1..=self.policy.max_layout_retries { let query_started = Instant::now(); - let response = match self - .chunkdb - .query_chunk(QueryChunkRequest { - chunk_id: Some(chunk_id), - }) - .await - { + let response = match self.query_chunk_timed(chunk_id).await { Ok(response) => response, Err(error) => { tracing::warn!( @@ -362,7 +481,12 @@ impl ChunkReader { continue; } }; - match parse_frame(&bytes, chunk_id) { + let decode_started = Instant::now(); + let parsed_frame = parse_frame(&bytes, chunk_id); + let parse_ns = u64::try_from(decode_started.elapsed().as_nanos()).unwrap_or(u64::MAX); + self.flow_metrics.frame_decode_wait_ns.inc_by(parse_ns); + self.flow_metrics.frame_parse_wait_ns.inc_by(parse_ns); + match parsed_frame { Ok(frame) if frame.header.magic == expected_magic => { if Instant::now() >= deadline { tracing::warn!( @@ -444,10 +568,22 @@ impl ChunkReader { local_start: u64, length: u64, logical_start: u64, + ) -> ReadResult { + self.read_location_partial_cached(location, local_start, length, logical_start, None) + .await + } + + async fn read_location_partial_cached( + &self, + location: &Location, + local_start: u64, + length: u64, + logical_start: u64, + mut first_layout: Option, ) -> ReadResult { if location.length != location.logical_length { return self - .read_framed_location(location, local_start, length, logical_start) + .read_framed_location(location, local_start, length, logical_start, first_layout) .await; } let chunk_id = location @@ -458,34 +594,32 @@ impl ChunkReader { .checked_add(local_start) .ok_or_else(|| ReadError::InvalidLocations("location physical offset overflows".into()))?; for _ in 0..self.policy.max_layout_retries { - let query_started = Instant::now(); - let response = self - .chunkdb - .query_chunk(QueryChunkRequest { - chunk_id: Some(chunk_id), - }) - .await - .map_err(map_metadata_error)?; - let validity = Duration::from_millis(response.layout_validity_ms); - let usable = validity.saturating_sub(self.policy.layout_safety_margin); - let deadline = query_started + usable; - let mut chunk = response - .chunk - .ok_or_else(|| ReadError::ChunkDeleted(format!("{}:{}", chunk_id.high, chunk_id.low)))?; + let layout = match first_layout.take().filter(LayoutSnapshot::usable) { + Some(layout) => layout, + None => self.query_layout(chunk_id).await?, + }; let (partial, observations) = self - .read_chunk_range_partial(&chunk, physical_start, length, logical_start) + .read_chunk_range_partial(&layout.chunk, physical_start, length, logical_start) .await?; - if Instant::now() >= deadline { + if Instant::now() >= layout.deadline { + layout.invalidate(); continue; } - if self - .mark_observed_failures(&mut chunk, observations) - .await - .is_err() + if observations + .iter() + .any(|observation| !observation.failed_segments.is_empty()) { - continue; + layout.invalidate(); + let mut chunk = (*layout.chunk).clone(); + if self + .mark_observed_failures(&mut chunk, observations) + .await + .is_err() + { + continue; + } } - if Instant::now() < deadline { + if Instant::now() < layout.deadline { return Ok(partial); } } @@ -498,6 +632,7 @@ impl ChunkReader { local_start: u64, length: u64, logical_start: u64, + first_layout: Option, ) -> ReadResult { let chunk_id = location .chunk_id @@ -535,6 +670,7 @@ impl ChunkReader { selected_logical_start, local_start..local_start + length, logical_start, + first_layout, ) .await } @@ -546,58 +682,66 @@ impl ChunkReader { selected_logical_start: u64, requested: Range, output_logical_start: u64, + mut first_layout: Option, ) -> ReadResult { let chunk_id = framed.chunk_id; for _ in 0..self.policy.max_layout_retries { - let query_started = Instant::now(); - let response = self - .chunkdb - .query_chunk(QueryChunkRequest { - chunk_id: Some(chunk_id), - }) - .await - .map_err(map_metadata_error)?; - let validity = Duration::from_millis(response.layout_validity_ms); - let deadline = query_started + validity.saturating_sub(self.policy.layout_safety_margin); - let mut chunk = response - .chunk - .ok_or_else(|| ReadError::ChunkDeleted(format!("{}:{}", chunk_id.high, chunk_id.low)))?; + let layout = match first_layout.take().filter(LayoutSnapshot::usable) { + Some(layout) => layout, + None => self.query_layout(chunk_id).await?, + }; let (physical, observations) = self .read_chunk_range_partial( - &chunk, + &layout.chunk, physical_range.start, physical_range.end - physical_range.start, physical_range.start, ) .await?; - let parsed = match decode_framed_partial( + let decode_started = Instant::now(); + let decoded = decode_framed_partial( &physical, framed, selected_logical_start, requested.clone(), output_logical_start, - ) { + &self.flow_metrics, + ); + self.flow_metrics + .frame_decode_wait_ns + .inc_by(u64::try_from(decode_started.elapsed().as_nanos()).unwrap_or(u64::MAX)); + let parsed = match decoded { Ok(parsed) => parsed, Err(error) => { + layout.invalidate(); let corrupt = mark_served_segments_corrupt(observations); - if corrupt.is_empty() - || Instant::now() >= deadline - || self.mark_observed_failures(&mut chunk, corrupt).await.is_err() - { + if corrupt.is_empty() || Instant::now() >= layout.deadline { + return Err(error); + } + let mut chunk = (*layout.chunk).clone(); + if self.mark_observed_failures(&mut chunk, corrupt).await.is_err() { return Err(error); } continue; } }; - if Instant::now() >= deadline { + if Instant::now() >= layout.deadline { + layout.invalidate(); continue; } - if self - .mark_observed_failures(&mut chunk, observations) - .await - .is_err() + if observations + .iter() + .any(|observation| !observation.failed_segments.is_empty()) { - continue; + layout.invalidate(); + let mut chunk = (*layout.chunk).clone(); + if self + .mark_observed_failures(&mut chunk, observations) + .await + .is_err() + { + continue; + } } return Ok(parsed); } @@ -610,6 +754,23 @@ impl ChunkReader { start: u64, length: u64, logical_start: u64, + ) -> ReadResult<(PartialReadResult, Vec)> { + let started = Instant::now(); + let result = self + .read_chunk_range_partial_inner(chunk, start, length, logical_start) + .await; + self.flow_metrics + .chunk_read_wait_ns + .inc_by(u64::try_from(started.elapsed().as_nanos()).unwrap_or(u64::MAX)); + result + } + + async fn read_chunk_range_partial_inner( + &self, + chunk: &Chunk, + start: u64, + length: u64, + logical_start: u64, ) -> ReadResult<(PartialReadResult, Vec)> { if chunk.state == ChunkState::Deleted as i32 { let id = chunk.id.unwrap_or_default(); @@ -644,6 +805,7 @@ impl ChunkReader { for (part_start, part_end) in strip_read_parts(strip, cursor, overlap_end)? { let range_start = logical_start + (part_start - start); let range_end = range_start + (part_end - part_start); + let read_started = Instant::now(); let observed = self .strip_reader .read_observed( @@ -654,6 +816,9 @@ impl ChunkReader { part_end - part_start, ) .await; + self.flow_metrics + .strip_read_wait_ns + .inc_by(u64::try_from(read_started.elapsed().as_nanos()).unwrap_or(u64::MAX)); if !observed.failed_segments.is_empty() { observations.push(StripFailureObservation { strip_sequence: strip.strip_sequence, @@ -735,10 +900,7 @@ impl ChunkReader { ))); } *chunk = self - .chunkdb - .query_chunk(QueryChunkRequest { - chunk_id: Some(chunk_id), - }) + .query_chunk_timed(chunk_id) .await .map_err(|error| ReadError::Metadata(error.to_string()))? .chunk @@ -755,6 +917,7 @@ fn decode_framed_partial( selected_logical_start: u64, requested: Range, output_logical_start: u64, + flow_metrics: &ReadFlowMetrics, ) -> ReadResult { let first_frame = selected_logical_start / MAX_FRAME_PAYLOAD_BYTES as u64; let last_frame = (requested.end - 1) / MAX_FRAME_PAYLOAD_BYTES as u64; @@ -786,45 +949,74 @@ fn decode_framed_partial( }); continue; } - let bytes = extract_physical_range(&physical.ranges, frame_start..frame_end)?; - let frame = parse_frame(&bytes, framed.chunk_id) - .map_err(|error| ReadError::DataLoss(format!("invalid chunk frame: {error}")))?; - if !matches!( - frame.header.magic, - FrameMagic::RepoSmallV1 | FrameMagic::RepoLargeV1 - ) { + let views = extract_physical_views(&physical.ranges, frame_start..frame_end)?; + let parse_started = Instant::now(); + let parsed = if views.len() == 1 { + parse_frame(&views[0], framed.chunk_id).map(|frame| (frame.header, frame.physical_length)) + } else { + let slices: Vec<_> = views.iter().map(Bytes::as_ref).collect(); + parse_frame_views(&slices, framed.chunk_id).map(|frame| (frame.header, frame.physical_length)) + }; + flow_metrics + .frame_parse_wait_ns + .inc_by(u64::try_from(parse_started.elapsed().as_nanos()).unwrap_or(u64::MAX)); + let (header, physical_length) = + parsed.map_err(|error| ReadError::DataLoss(format!("invalid chunk frame: {error}")))?; + if !matches!(header.magic, FrameMagic::RepoSmallV1 | FrameMagic::RepoLargeV1) { return Err(ReadError::DataLoss( "framed location has unsupported frame kind".into(), )); } match magic { - Some(previous) if previous != frame.header.magic => { + Some(previous) if previous != header.magic => { return Err(ReadError::DataLoss("framed location mixes frame kinds".into())); } - None => magic = Some(frame.header.magic), + None => magic = Some(header.magic), Some(_) => {} } - if frame.physical_length as u64 != frame_end - frame_start { + if physical_length as u64 != frame_end - frame_start { return Err(ReadError::DataLoss("frame length disagrees with location".into())); } let payload_start = usize::try_from(wanted_start - frame_logical_start) .map_err(|_| ReadError::InvalidLocations("frame payload range overflows".into()))?; let payload_end = usize::try_from(wanted_end - frame_logical_start) .map_err(|_| ReadError::InvalidLocations("frame payload range overflows".into()))?; - result.ranges.push(ReadRangeData { - start: output_start, - end: output_end, - data: Bytes::copy_from_slice(&frame.payload[payload_start..payload_end]), - }); + let physical_payload_start = usize::from(header.payload_offset) + payload_start; + let physical_payload_end = usize::from(header.payload_offset) + payload_end; + let mut logical_cursor = output_start; + for view in slice_views(&views, physical_payload_start..physical_payload_end)? { + let view_end = logical_cursor + view.len() as u64; + result.ranges.push(ReadRangeData { + start: logical_cursor, + end: view_end, + data: view, + }); + logical_cursor = view_end; + } + if logical_cursor != output_end { + return Err(ReadError::InvalidLocations( + "frame payload views are incomplete".into(), + )); + } } Ok(result) } fn extract_physical_range(ranges: &[ReadRangeData], wanted: Range) -> ReadResult { + let first = ranges.partition_point(|range| range.end <= wanted.start); + if let Some(range) = ranges.get(first) { + if range.start <= wanted.start && range.end >= wanted.end { + let offset = usize::try_from(wanted.start - range.start) + .map_err(|_| ReadError::InvalidLocations("physical frame offset overflows".into()))?; + let length = usize::try_from(wanted.end - wanted.start) + .map_err(|_| ReadError::InvalidLocations("physical frame length overflows".into()))?; + return Ok(range.data.slice(offset..offset + length)); + } + } let mut output = BytesMut::with_capacity(usize::try_from(wanted.end - wanted.start).unwrap_or(usize::MAX)); let mut cursor = wanted.start; - for range in ranges { + for range in &ranges[first..] { if range.end <= cursor || range.start >= wanted.end { continue; } @@ -853,31 +1045,64 @@ fn extract_physical_range(ranges: &[ReadRangeData], wanted: Range) -> ReadR Ok(output.freeze()) } -fn coalesce_partial_ranges(partial: PartialReadResult) -> PartialReadResult { - let mut ranges: Vec = Vec::new(); - for range in partial.ranges { - if let Some(previous) = ranges.last_mut().filter(|previous| previous.end == range.start) { - previous.end = range.end; - let mut data = BytesMut::with_capacity(previous.data.len() + range.data.len()); - data.extend_from_slice(&previous.data); - data.extend_from_slice(&range.data); - previous.data = data.freeze(); - } else { - ranges.push(range); +fn extract_physical_views(ranges: &[ReadRangeData], wanted: Range) -> ReadResult> { + let first = ranges.partition_point(|range| range.end <= wanted.start); + let mut output = Vec::new(); + let mut cursor = wanted.start; + for range in &ranges[first..] { + if range.start >= wanted.end { + break; + } + if range.end <= cursor { + continue; + } + if range.start > cursor { + return Err(ReadError::InvalidLocations( + "physical frame data has a gap".into(), + )); + } + let end = wanted.end.min(range.end); + let offset = usize::try_from(cursor - range.start) + .map_err(|_| ReadError::InvalidLocations("physical frame offset overflows".into()))?; + let length = usize::try_from(end - cursor) + .map_err(|_| ReadError::InvalidLocations("physical frame length overflows".into()))?; + output.push(range.data.slice(offset..offset + length)); + cursor = end; + if cursor == wanted.end { + break; } } - let mut failures: Vec = Vec::new(); - for failure in partial.failures { - if let Some(previous) = failures - .last_mut() - .filter(|previous| previous.end == failure.start) - { - previous.end = failure.end; - } else { - failures.push(failure); + if cursor != wanted.end { + return Err(ReadError::InvalidLocations( + "physical frame data is incomplete".into(), + )); + } + Ok(output) +} + +fn slice_views(views: &[Bytes], wanted: Range) -> ReadResult> { + let mut output = Vec::new(); + let mut cursor = 0_usize; + let mut covered = wanted.start; + for view in views { + let view_end = cursor.saturating_add(view.len()); + if view_end > covered && cursor < wanted.end { + let start = covered.saturating_sub(cursor); + let end = (wanted.end - cursor).min(view.len()); + output.push(view.slice(start..end)); + covered += end - start; } + cursor = view_end; + if covered == wanted.end { + break; + } + } + if covered != wanted.end { + return Err(ReadError::InvalidLocations( + "frame payload views are incomplete".into(), + )); } - PartialReadResult { ranges, failures } + Ok(output) } struct StripFailureObservation { @@ -907,89 +1132,257 @@ fn mark_served_segments_corrupt( observations } -/// Pull-based stream whose emitted item never exceeds the configured window. +type StreamRead = (u64, u64, ReadResult, Arc); + +/// Ordered, bounded read pipeline whose frames retain their read credits. pub struct ChunkReadStream { reader: ChunkReader, locations: Arc<[Location]>, cursor: u64, + fetch_cursor: u64, + delivery_cursor: u64, end: u64, window_bytes: u64, + slots: Arc, + reads: JoinSet, + completed: BTreeMap, + layouts: Vec, pending_error: Option, + pending_ranges: std::vec::IntoIter, } impl ChunkReadStream { pub async fn next_chunk(&mut self) -> Option> { - if let Some(error) = self.pending_error.take() { - self.cursor = self.end; - return Some(Err(error)); + loop { + if let Some(pending) = self.next_pending() { + return Some(pending); + } + if self.delivery_cursor >= self.end { + return None; + } + let budget = Arc::clone(&self.reader.stream_budget); + let _registration = budget.register(); + let notified = budget.wake.notified(); + tokio::pin!(notified); + notified.as_mut().enable(); + if let Err(error) = self.fill_slots().await { + self.abort(error); + continue; + } + if let Some((start, end, result, lease)) = self.completed.remove(&self.delivery_cursor) { + self.load_unit(start, end, result, &lease); + continue; + } + if self.reads.is_empty() { + let started = Instant::now(); + notified.await; + self.reader + .flow_metrics + .stream_credit_wait_ns + .inc_by(u64::try_from(started.elapsed().as_nanos()).unwrap_or(u64::MAX)); + continue; + } + match self.reads.join_next().await { + Some(Ok(completed)) => { + self.reader.flow_metrics.stream_units_completed.inc(); + if completed.0 != self.delivery_cursor { + self.reader.flow_metrics.stream_out_of_order.inc(); + } + self.completed.insert(completed.0, completed); + } + Some(Err(error)) => self.abort(ReadError::DiskIo(error.to_string())), + None => {} + } } - if self.cursor >= self.end { - return None; + } + + async fn fill_slots(&mut self) -> ReadResult<()> { + while self.fetch_cursor < self.end { + let (next, physical_bytes) = self.next_unit(self.fetch_cursor)?; + let Some(lease) = self.slots.try_reserve(&self.reader.stream_budget, physical_bytes) else { + self.reader.flow_metrics.stream_credit_stalls.inc(); + break; + }; + let start = self.fetch_cursor; + let index = self.locations.partition_point(|location| { + location.logical_offset.saturating_add(location.logical_length) <= start + }); + let location = self.locations[index].clone(); + let layout = self + .layout_for( + location + .chunk_id + .ok_or_else(|| ReadError::InvalidLocations("location has no chunk ID".into()))?, + ) + .await?; + let reader = self.reader.clone(); + self.reader.flow_metrics.stream_windows.inc(); + self.reader.flow_metrics.range_locations_examined.inc(); + self.reads.spawn(async move { + let local_start = start - location.logical_offset; + let result = reader + .read_location_partial_cached(&location, local_start, next - start, start, Some(layout)) + .await; + (start, next, result, lease) + }); + self.fetch_cursor = next; } - let next = self.cursor.saturating_add(self.window_bytes).min(self.end); - let partial = match self - .reader - .read_range_partial(&self.locations, self.cursor, next) - .await + Ok(()) + } + + async fn layout_for(&mut self, chunk_id: ChunkId) -> ReadResult { + if let Some(layout) = self + .layouts + .iter() + .find(|layout| layout.chunk_id == chunk_id && layout.usable()) { + return Ok(layout.clone()); + } + let reader = self.reader.clone(); + let layout = tokio::spawn(async move { reader.query_layout(chunk_id).await }) + .await + .map_err(|error| ReadError::Metadata(error.to_string()))??; + self.layouts.retain(|cached| cached.chunk_id != chunk_id); + if self.layouts.len() == 8 { + self.layouts.remove(0); + } + self.layouts.push(layout.clone()); + Ok(layout) + } + + fn next_unit(&self, start: u64) -> ReadResult<(u64, usize)> { + let first = self.locations.partition_point(|location| { + location.logical_offset.saturating_add(location.logical_length) <= start + }); + let location = self + .locations + .get(first) + .ok_or_else(|| ReadError::InvalidLocations("stream has no location for requested byte".into()))?; + let local = start - location.logical_offset; + let window = self.window_bytes.min(DEFAULT_STREAM_WINDOW as u64); + if location.length == location.logical_length { + let length = window.min(location.logical_length - local).min(self.end - start); + return Ok((start + length, length as usize)); + } + let frames = (window / MAX_FRAME_BYTES as u64).max(1); + let frame_index = local / MAX_FRAME_PAYLOAD_BYTES as u64; + let local_end = ((frame_index + frames) * MAX_FRAME_PAYLOAD_BYTES as u64) + .min(location.logical_length) + .min(self.end - location.logical_offset); + let frame_location = ChunkLocation { + chunk_id: location + .chunk_id + .ok_or_else(|| ReadError::InvalidLocations("location has no chunk ID".into()))?, + frame_offset: location.offset, + logical_length: location.logical_length, + }; + let physical = frame_location + .physical_range_for_subrange(local..local_end) + .map_err(|error| ReadError::InvalidLocations(error.to_string()))?; + let bytes = usize::try_from(physical.end - physical.start) + .map_err(|_| ReadError::InvalidLocations("stream unit exceeds address space".into()))?; + Ok((location.logical_offset + local_end, bytes)) + } + + fn load_unit( + &mut self, + start: u64, + end: u64, + result: ReadResult, + lease: &Arc, + ) { + self.delivery_cursor = end; + let partial = match result { Ok(partial) => partial, Err(error) => { - self.cursor = self.end; - return Some(Err(error)); + self.abort(error); + return; } }; - if let Some(failure) = partial.failures.into_iter().next() { - let error = ReadError::FailedRange { + let failure = partial.failures.into_iter().next(); + let verified_end = failure.as_ref().map_or(end, |failure| failure.start); + let mut ranges = partial.ranges; + let prefix_len = ranges.partition_point(|range| range.end <= verified_end); + let mut cursor = start; + for range in &ranges[..prefix_len] { + if range.start != cursor || range.end - range.start != range.data.len() as u64 { + self.abort(ReadError::InvalidLocations(format!( + "read ranges are not contiguous at byte {cursor}" + ))); + return; + } + cursor = range.end; + } + if cursor != verified_end { + self.abort(ReadError::InvalidLocations(format!( + "read ranges are not contiguous at byte {cursor}" + ))); + return; + } + ranges.truncate(prefix_len); + for range in &mut ranges { + range.data = retain(std::mem::take(&mut range.data), Arc::clone(lease)); + } + self.pending_ranges = ranges.into_iter(); + if let Some(failure) = failure { + self.reads.abort_all(); + self.completed.clear(); + self.fetch_cursor = self.end; + self.delivery_cursor = self.end; + self.pending_error = Some(ReadError::FailedRange { start: failure.start, end: failure.end, message: failure.error.to_string(), - }; - if failure.start == self.cursor { - self.cursor = self.end; - return Some(Err(error)); - } - let prefix_len = usize::try_from(failure.start - self.cursor).unwrap_or(usize::MAX); - let prefix = partial - .ranges - .into_iter() - .filter(|range| range.end <= failure.start) - .collect(); - let data = assemble_ranges(prefix, self.cursor, prefix_len); - self.cursor = failure.start; - self.pending_error = Some(error); - return Some(data); + }); } - let expected = usize::try_from(next - self.cursor).unwrap_or(usize::MAX); - let result = assemble_ranges(partial.ranges, self.cursor, expected); - if result.is_ok() { - self.cursor = next; - } else { + } + + fn abort(&mut self, error: ReadError) { + self.reads.abort_all(); + self.completed.clear(); + self.fetch_cursor = self.end; + self.delivery_cursor = self.end; + self.pending_error = Some(error); + } + + fn next_pending(&mut self) -> Option> { + if let Some(range) = self.pending_ranges.next() { + self.cursor = range.end; + return Some(Ok(range.data)); + } + if let Some(error) = self.pending_error.take() { self.cursor = self.end; + return Some(Err(error)); } - Some(result) + None } } -fn assemble_ranges(mut ranges: Vec, start: u64, expected: usize) -> ReadResult { - ranges.sort_unstable_by_key(|range| range.start); - let actual = ranges.iter().map(|range| range.data.len()).sum::(); - if actual != expected { - return Err(ReadError::InvalidLocations(format!( - "read assembled {actual} bytes, expected {expected}" - ))); +fn complete_read(partial: PartialReadResult, start: u64, end: u64) -> ReadResult> { + if let Some(failure) = partial.failures.into_iter().next() { + return Err(ReadError::FailedRange { + start: failure.start, + end: failure.end, + message: failure.error.to_string(), + }); } - let mut output = BytesMut::with_capacity(actual); let mut cursor = start; - for range in ranges { + let mut buffers = Vec::with_capacity(partial.ranges.len()); + for range in partial.ranges { if range.start != cursor || range.end - range.start != range.data.len() as u64 { return Err(ReadError::InvalidLocations(format!( "read ranges are not contiguous at byte {cursor}" ))); } cursor = range.end; - output.extend_from_slice(&range.data); + buffers.push(range.data); } - Ok(output.freeze()) + if cursor != end { + return Err(ReadError::InvalidLocations(format!( + "read ranges end at byte {cursor}, expected {end}" + ))); + } + Ok(buffers) } fn segment_identity(segment: &Segment) -> (u64, u64, u32, u64, u64) { diff --git a/lib/crowdb-chunk-client/src/chunk/read_credit.rs b/lib/crowdb-chunk-client/src/chunk/read_credit.rs new file mode 100644 index 000000000..26b2b41c6 --- /dev/null +++ b/lib/crowdb-chunk-client/src/chunk/read_credit.rs @@ -0,0 +1,129 @@ +// Copyright 2026-present Gian +// Licensed under the Apache License, Version 2.0. + +//! Atomic credits retained through the last HTTP-owned buffer view. + +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Arc; + +use bytes::Bytes; +use tokio::sync::Notify; + +use crate::metrics::ReadFlowMetrics; + +pub(super) struct ReadBudget { + limit: usize, + used: AtomicUsize, + waiters: AtomicUsize, + pub wake: Notify, + metrics: Arc, +} + +impl ReadBudget { + pub fn new(limit: usize, metrics: Arc) -> Self { + Self { + limit, + used: AtomicUsize::new(0), + waiters: AtomicUsize::new(0), + wake: Notify::new(), + metrics, + } + } + + fn try_charge(&self, amount: usize) -> bool { + let accepted = self + .used + .fetch_update(Ordering::AcqRel, Ordering::Acquire, |used| { + used.checked_add(amount).filter(|next| *next <= self.limit) + }) + .is_ok(); + if accepted { + self.metrics.stream_bytes_reserved.inc_by(amount as u64); + } + accepted + } + + fn release(&self, amount: usize) { + self.used.fetch_sub(amount, Ordering::AcqRel); + self.metrics.stream_bytes_released.inc_by(amount as u64); + if self.waiters.load(Ordering::Acquire) != 0 { + self.wake.notify_waiters(); + } + } + + pub fn register(self: &Arc) -> WaitRegistration { + self.waiters.fetch_add(1, Ordering::AcqRel); + WaitRegistration(Arc::clone(self)) + } +} + +pub(super) struct WaitRegistration(Arc); + +impl Drop for WaitRegistration { + fn drop(&mut self) { + self.0.waiters.fetch_sub(1, Ordering::AcqRel); + } +} + +pub(super) struct StreamSlots { + limit: usize, + used: AtomicUsize, +} + +impl StreamSlots { + pub fn new(limit: usize) -> Self { + Self { + limit, + used: AtomicUsize::new(0), + } + } + + pub fn try_reserve(self: &Arc, global: &Arc, bytes: usize) -> Option> { + if self + .used + .fetch_update(Ordering::AcqRel, Ordering::Acquire, |used| { + used.checked_add(1).filter(|next| *next <= self.limit) + }) + .is_err() + { + return None; + } + if !global.try_charge(bytes) { + self.used.fetch_sub(1, Ordering::AcqRel); + return None; + } + Some(Arc::new(ReadLease { + slots: Arc::clone(self), + global: Arc::clone(global), + bytes, + })) + } +} + +pub(super) struct ReadLease { + slots: Arc, + global: Arc, + bytes: usize, +} + +impl Drop for ReadLease { + fn drop(&mut self) { + self.slots.used.fetch_sub(1, Ordering::AcqRel); + self.global.release(self.bytes); + } +} + +struct LeasedView { + data: Bytes, + _lease: Arc, +} + +impl AsRef<[u8]> for LeasedView { + fn as_ref(&self) -> &[u8] { + &self.data + } +} + +pub(super) fn retain(data: Bytes, lease: Arc) -> Bytes { + Bytes::from_owner(LeasedView { data, _lease: lease }) +} diff --git a/lib/crowdb-chunk-client/src/chunk/strip_reader.rs b/lib/crowdb-chunk-client/src/chunk/strip_reader.rs index 5c3b6aeef..0fa979a2f 100644 --- a/lib/crowdb-chunk-client/src/chunk/strip_reader.rs +++ b/lib/crowdb-chunk-client/src/chunk/strip_reader.rs @@ -209,7 +209,9 @@ impl StripReader { }; pieces.push((order, shard_index, local_start, read_len, result)); } - pieces.sort_unstable_by_key(|(order, _, _, _, _)| *order); + if let [(_, shard_index, _, _, Ok(data))] = pieces.as_slice() { + return Ok((data.clone(), failed_segments, vec![ec.segments[*shard_index]])); + } let mut output = BytesMut::with_capacity(usize::try_from(length).unwrap_or(usize::MAX)); let mut served_segments = Vec::new(); for (_, shard_index, local_start, read_len, result) in pieces { diff --git a/lib/crowdb-chunk-client/src/client.rs b/lib/crowdb-chunk-client/src/client.rs index 92e5ac2ac..daaaa26a9 100644 --- a/lib/crowdb-chunk-client/src/client.rs +++ b/lib/crowdb-chunk-client/src/client.rs @@ -108,6 +108,15 @@ impl ChunkIoClient { /// Discover services using an existing KV topology client shared with the /// embedding process. pub async fn connect_with_kv(config: ChunkIoClientConfig, kv: Arc) -> Result { + Self::connect_with_kv_read_policy(config, kv, ChunkReadPolicy::default()).await + } + + /// Connect with explicit shared read resource limits. + pub async fn connect_with_kv_read_policy( + config: ChunkIoClientConfig, + kv: Arc, + read_policy: ChunkReadPolicy, + ) -> Result { let service = ServiceRegistryClient::from_shared(kv.clone()); let hardware = HardwareClient::from_shared(kv.clone()); let range_binding = discover_current_range_bindings(&service, kv.clone()).await?; @@ -136,7 +145,7 @@ impl ChunkIoClient { Arc::new(SmallWriteMetrics::default()), Arc::clone(&failed_disks), )?; - let reader = ChunkReader::new(chunkdb.clone(), disk_writer.clone(), ChunkReadPolicy::default()) + let reader = ChunkReader::new(chunkdb.clone(), disk_writer.clone(), read_policy) .map_err(|error| crate::IoError::Internal(error.to_string()))?; Ok(Self { allocator: chunkdb.clone(), @@ -254,6 +263,7 @@ impl ChunkIoClient { Arc::clone(&self.disk_writer), ChunkReadPolicy::default(), Arc::clone(&metrics.read_recovery), + Arc::clone(&metrics.read_flow), ) .unwrap_or_else(|_| unreachable!("default read policy is valid")); self @@ -268,17 +278,20 @@ impl ChunkIoClient { self.metrics .as_ref() .map_or_else(Arc::default, |metrics| Arc::clone(&metrics.read_recovery)), + self.metrics + .as_ref() + .map_or_else(Arc::default, |metrics| Arc::clone(&metrics.read_flow)), )?; Ok(self) } - /// Reconstruct a complete object from writer-produced locations. - pub async fn read_object(&self, locations: &[Location]) -> ReadResult { + /// Read a complete object as verified buffers from writer-produced locations. + pub async fn read_object(&self, locations: &[Location]) -> ReadResult> { self.reader.read_object(locations).await } - /// Reconstruct the logical half-open range `[start, end)`. - pub async fn read_range(&self, locations: &[Location], start: u64, end: u64) -> ReadResult { + /// Read the logical half-open range `[start, end)` as verified buffers. + pub async fn read_range(&self, locations: &[Location], start: u64, end: u64) -> ReadResult> { self.reader.read_range(locations, start, end).await } @@ -351,6 +364,30 @@ impl ChunkIoClient { Ok(SharedObjectWriter::new(runtime, object_size, route, route_hash)) } + /// Prepares one shared object whose complete 64 KiB frame sequence has one location. + pub async fn prepare_shared_object_write_for_key( + &self, + object_size: usize, + key: &[u8], + ) -> Result { + if object_size == 0 { + return Ok(SharedObjectWriter::empty()); + } + let runtime = self.small_pool.prepare(object_size).await?; + let route_hash = stable_route_hash(key); + let route = runtime + .route_for_hash(route_hash) + .ok_or_else(|| IoError::Internal("small write has no pipeline route".into()))?; + if object_size as u64 > route.capacity_bytes { + return Err(IoError::MemoryBudgetExhausted); + } + let mut writer = SharedObjectWriter::new(runtime, object_size, route, route_hash); + while !writer.try_reserve_declared() { + writer.wait_for_route_capacity().await; + } + Ok(writer) + } + /// Stop admission, drain accepted objects, and finalize shared chunks. pub async fn shutdown_small_writes(&self) -> Result<()> { self.small_pool.shutdown().await @@ -373,6 +410,11 @@ impl ChunkIoClient { snapshot } + /// Snapshot location and layout work on the shared read path. + pub fn read_flow_metrics(&self) -> crate::ReadFlowMetricsSnapshot { + self.reader.flow_metrics_snapshot() + } + /// Snapshot in-line large-write segment replacement counters. pub fn large_write_repair_metrics(&self) -> crate::LargeWriteRepairMetricsSnapshot { self.large_write_repair.snapshot() diff --git a/lib/crowdb-chunk-client/src/config.rs b/lib/crowdb-chunk-client/src/config.rs index 91490864b..46ae37120 100644 --- a/lib/crowdb-chunk-client/src/config.rs +++ b/lib/crowdb-chunk-client/src/config.rs @@ -50,7 +50,7 @@ impl Default for SmallWritePolicy { fn default() -> Self { const MIB: usize = 1024 * 1024; Self { - object_limit: MIB, + object_limit: 8 * MIB, // 1,000 concurrent 1 MiB objects are a normal S3 small-object // workload. 3,000 and 5,000 require roughly 3.25 GiB and 5.25 // GiB respectively after pipeline and conversion headroom; set @@ -82,17 +82,17 @@ impl Default for SmallWritePolicy { impl SmallWritePolicy { pub fn validate(&self) -> Result<(), IoError> { - const HARD_LIMIT: usize = 1024 * 1024; + const HARD_LIMIT: usize = 8 * 1024 * 1024; if self.object_limit == 0 || self.object_limit > HARD_LIMIT { return Err(IoError::Internal( - "small object limit must be in 1..=1 MiB".into(), + "small object limit must be in 1..=8 MiB".into(), )); } - let shadow_budget = self.max_pipelines.saturating_mul(HARD_LIMIT); + let shadow_budget = self.max_pipelines.saturating_mul(1024 * 1024); let conversion_budget = if self.conversion_enabled { self.conversion_data_num .saturating_add(self.conversion_code_num) - .saturating_mul(HARD_LIMIT) + .saturating_mul(1024 * 1024) } else { 0 }; diff --git a/lib/crowdb-chunk-client/src/lib.rs b/lib/crowdb-chunk-client/src/lib.rs index fb37f22d5..72371e3d7 100644 --- a/lib/crowdb-chunk-client/src/lib.rs +++ b/lib/crowdb-chunk-client/src/lib.rs @@ -53,7 +53,7 @@ pub use error::{IoError, ReadError, ReadResult, Result}; pub use io::{BackpressurePolicy, ChunkIoWriter, FeedStatus, FramedWriteBuffer}; pub use metrics::{ ChunkClientMetrics, LargeWriteBufferMetricsSnapshot, LargeWriteRepairMetricsSnapshot, - SmallWriteMetricsSnapshot, + ReadFlowMetricsSnapshot, SmallWriteMetricsSnapshot, }; pub use negative_list::FailedDiskList; pub use reclamation::{reclaim_location, ReclaimOutcome}; diff --git a/lib/crowdb-chunk-client/src/metrics.rs b/lib/crowdb-chunk-client/src/metrics.rs index f5ecebfb8..f95ac5555 100644 --- a/lib/crowdb-chunk-client/src/metrics.rs +++ b/lib/crowdb-chunk-client/src/metrics.rs @@ -84,6 +84,7 @@ pub struct ChunkClientMetrics { pub large_write_buffer: Arc, pub small_write: Arc, pub read_recovery: Arc, + pub read_flow: Arc, } impl ChunkClientMetrics { @@ -107,6 +108,149 @@ impl ChunkClientMetrics { large_write_buffer: Arc::new(LargeWriteBufferMetrics::register(registry)), small_write: Arc::new(SmallWriteMetrics::register(registry)), read_recovery: Arc::new(ReadRecoveryMetrics::register(registry)), + read_flow: Arc::new(ReadFlowMetrics::register(registry)), + } + } +} + +/// Aggregate read-path work counters without object or chunk labels. +#[derive(Debug)] +pub struct ReadFlowMetrics { + pub(crate) location_normalizations: Arc, + pub(crate) locations_examined: Arc, + pub(crate) range_locations_examined: Arc, + pub(crate) stream_windows: Arc, + pub(crate) stream_units_completed: Arc, + pub(crate) stream_out_of_order: Arc, + pub(crate) stream_credit_stalls: Arc, + pub(crate) stream_credit_wait_ns: Arc, + pub(crate) stream_bytes_reserved: Arc, + pub(crate) stream_bytes_released: Arc, + pub(crate) layout_queries: Arc, + pub(crate) layout_query_wait_ns: Arc, + pub(crate) strip_read_wait_ns: Arc, + pub(crate) chunk_read_wait_ns: Arc, + pub(crate) frame_decode_wait_ns: Arc, + pub(crate) frame_parse_wait_ns: Arc, +} + +/// Cumulative read-path work visible to access-server metrics. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Serialize)] +pub struct ReadFlowMetricsSnapshot { + pub location_normalizations: u64, + pub locations_examined: u64, + pub range_locations_examined: u64, + pub stream_windows: u64, + pub stream_units_completed: u64, + pub stream_out_of_order: u64, + pub stream_credit_stalls: u64, + pub stream_credit_wait_ns: u64, + pub stream_bytes_reserved: u64, + pub stream_bytes_released: u64, + pub layout_queries: u64, + pub layout_query_wait_ns: u64, + pub strip_read_wait_ns: u64, + pub chunk_read_wait_ns: u64, + pub frame_decode_wait_ns: u64, + pub frame_parse_wait_ns: u64, +} + +impl ReadFlowMetricsSnapshot { + pub(crate) fn since(self, earlier: Self) -> Self { + Self { + location_normalizations: self + .location_normalizations + .saturating_sub(earlier.location_normalizations), + locations_examined: self.locations_examined.saturating_sub(earlier.locations_examined), + range_locations_examined: self + .range_locations_examined + .saturating_sub(earlier.range_locations_examined), + stream_windows: self.stream_windows.saturating_sub(earlier.stream_windows), + stream_units_completed: self + .stream_units_completed + .saturating_sub(earlier.stream_units_completed), + stream_out_of_order: self + .stream_out_of_order + .saturating_sub(earlier.stream_out_of_order), + stream_credit_stalls: self + .stream_credit_stalls + .saturating_sub(earlier.stream_credit_stalls), + stream_credit_wait_ns: self + .stream_credit_wait_ns + .saturating_sub(earlier.stream_credit_wait_ns), + stream_bytes_reserved: self + .stream_bytes_reserved + .saturating_sub(earlier.stream_bytes_reserved), + stream_bytes_released: self + .stream_bytes_released + .saturating_sub(earlier.stream_bytes_released), + layout_queries: self.layout_queries.saturating_sub(earlier.layout_queries), + layout_query_wait_ns: self + .layout_query_wait_ns + .saturating_sub(earlier.layout_query_wait_ns), + strip_read_wait_ns: self.strip_read_wait_ns.saturating_sub(earlier.strip_read_wait_ns), + chunk_read_wait_ns: self.chunk_read_wait_ns.saturating_sub(earlier.chunk_read_wait_ns), + frame_decode_wait_ns: self + .frame_decode_wait_ns + .saturating_sub(earlier.frame_decode_wait_ns), + frame_parse_wait_ns: self + .frame_parse_wait_ns + .saturating_sub(earlier.frame_parse_wait_ns), + } + } +} + +impl Default for ReadFlowMetrics { + fn default() -> Self { + Self::new(|name| Arc::new(Counter::new(name.into()))) + } +} + +impl ReadFlowMetrics { + fn register(registry: &mut MetricsRegistry) -> Self { + Self::new(|name| registry.register_counter(name)) + } + + fn new(mut counter: impl FnMut(&'static str) -> Arc) -> Self { + Self { + location_normalizations: counter("chunkio.read.location_normalizations.c"), + locations_examined: counter("chunkio.read.locations_examined.c"), + range_locations_examined: counter("chunkio.read.range_locations_examined.c"), + stream_windows: counter("chunkio.read.stream_windows.c"), + stream_units_completed: counter("chunkio.read.stream_units_completed.c"), + stream_out_of_order: counter("chunkio.read.stream_out_of_order.c"), + stream_credit_stalls: counter("chunkio.read.stream_credit_stalls.c"), + stream_credit_wait_ns: counter("chunkio.read.stream_credit_wait_ns.c"), + stream_bytes_reserved: counter("chunkio.read.stream_bytes_reserved.c"), + stream_bytes_released: counter("chunkio.read.stream_bytes_released.c"), + layout_queries: counter("chunkio.read.layout_queries.c"), + layout_query_wait_ns: counter("chunkio.read.layout_query_wait_ns.c"), + strip_read_wait_ns: counter("chunkio.read.strip_read_wait_ns.c"), + chunk_read_wait_ns: counter("chunkio.read.chunk_read_wait_ns.c"), + frame_decode_wait_ns: counter("chunkio.read.frame_decode_wait_ns.c"), + frame_parse_wait_ns: counter("chunkio.read.frame_parse_wait_ns.c"), + } + } + + #[must_use] + pub fn snapshot(&self) -> ReadFlowMetricsSnapshot { + ReadFlowMetricsSnapshot { + location_normalizations: self.location_normalizations.snapshot().total, + locations_examined: self.locations_examined.snapshot().total, + range_locations_examined: self.range_locations_examined.snapshot().total, + stream_windows: self.stream_windows.snapshot().total, + stream_units_completed: self.stream_units_completed.snapshot().total, + stream_out_of_order: self.stream_out_of_order.snapshot().total, + stream_credit_stalls: self.stream_credit_stalls.snapshot().total, + stream_credit_wait_ns: self.stream_credit_wait_ns.snapshot().total, + stream_bytes_reserved: self.stream_bytes_reserved.snapshot().total, + stream_bytes_released: self.stream_bytes_released.snapshot().total, + layout_queries: self.layout_queries.snapshot().total, + layout_query_wait_ns: self.layout_query_wait_ns.snapshot().total, + strip_read_wait_ns: self.strip_read_wait_ns.snapshot().total, + chunk_read_wait_ns: self.chunk_read_wait_ns.snapshot().total, + frame_decode_wait_ns: self.frame_decode_wait_ns.snapshot().total, + frame_parse_wait_ns: self.frame_parse_wait_ns.snapshot().total, } } } @@ -351,7 +495,7 @@ impl Default for SmallWriteMetrics { } } -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, serde::Serialize)] pub struct SmallWriteMetricsSnapshot { pub submitted: u64, pub completed: u64, diff --git a/lib/crowdb-chunk-client/src/writer/shared_object.rs b/lib/crowdb-chunk-client/src/writer/shared_object.rs index e1c4768a3..8ab6add8f 100644 --- a/lib/crowdb-chunk-client/src/writer/shared_object.rs +++ b/lib/crowdb-chunk-client/src/writer/shared_object.rs @@ -8,7 +8,8 @@ use std::time::Instant; use bytes::Bytes; use crowdb_protocol::chunkdb::rpc::Location as ProtoLocation; -use tokio::sync::oneshot; +use crowdb_protocol::frame::MAX_FRAME_PAYLOAD_BYTES; +use tokio::sync::{mpsc, oneshot}; use crate::io::{ChunkIoWriter, FeedStatus}; use crate::{IoError, Result}; @@ -28,7 +29,10 @@ pub struct SharedObjectWriter { route_hash: u64, route: Option>, retained_size: usize, + full_reservation: bool, fragments: Vec, + stream_tx: Option>, + stream_result: Option>>>, finished: bool, durable_completion: bool, intent: Option>, @@ -49,7 +53,10 @@ impl SharedObjectWriter { route_hash, route: Some(route), retained_size: 0, + full_reservation: false, fragments: Vec::new(), + stream_tx: None, + stream_result: None, finished: false, durable_completion: false, intent: None, @@ -64,7 +71,10 @@ impl SharedObjectWriter { route_hash: 0, route: None, retained_size: 0, + full_reservation: false, fragments: Vec::new(), + stream_tx: None, + stream_result: None, finished: false, durable_completion: false, intent: None, @@ -79,6 +89,28 @@ impl SharedObjectWriter { } } + pub(crate) fn try_reserve_declared(&mut self) -> bool { + if self + .charge + .as_mut() + .is_some_and(|charge| charge.try_reserve(self.declared_size)) + { + self.full_reservation = true; + true + } else { + false + } + } + + pub(crate) async fn wait_for_route_capacity(&self) { + if let Some(route) = &self.route { + tokio::select! { + () = route.capacity_changed.notified() => {}, + () = tokio::time::sleep(std::time::Duration::from_millis(5)) => {}, + } + } + } + /// Completes only after the readable chunk cursor covers this object's bytes. /// # Errors /// Returns admission, physical write, metadata confirmation or size failures. @@ -87,6 +119,11 @@ impl SharedObjectWriter { self.on_finish().await } + /// Require readable metadata before a caller publishes this object's location. + pub fn require_durable_completion(&mut self) { + self.durable_completion = true; + } + /// Persists exact object ownership before any physical write for the batch. /// # Errors /// A failed intent aborts the batch without issuing its disk writes. @@ -95,6 +132,11 @@ impl SharedObjectWriter { intent: Arc, ) -> Result> { self.ensure_open()?; + if self.stream_tx.is_some() { + return Err(IoError::Internal( + "a streaming shared object cannot register an intent after writing starts".into(), + )); + } self.intent = Some(intent); self.finish_durable().await } @@ -102,6 +144,8 @@ impl SharedObjectWriter { fn fail_size(&mut self, actual: usize) -> IoError { self.finished = true; self.fragments.clear(); + self.stream_tx.take(); + self.stream_result.take(); self.charge.take(); IoError::ObjectSizeMismatch { declared: self.declared_size, @@ -119,11 +163,55 @@ impl ChunkIoWriter for SharedObjectWriter { return Err(self.fail_size(actual)); } self.retained_size = actual; - self.charge - .as_mut() - .ok_or_else(|| IoError::Internal("shared writer missing route charge".into()))? - .add(buffer.len()); - self.fragments.push(buffer); + if buffer.is_empty() { + return Ok(FeedStatus::Continue); + } + if !self.full_reservation { + self.charge + .as_mut() + .ok_or_else(|| IoError::Internal("shared writer missing route charge".into()))? + .add(buffer.len()); + } + if self.declared_size > MAX_FRAME_PAYLOAD_BYTES && self.full_reservation { + if self.stream_tx.is_none() { + let runtime = self + .runtime + .take() + .ok_or_else(|| IoError::Internal("small writer missing shared pool".into()))?; + let charge = self + .charge + .take() + .ok_or_else(|| IoError::Internal("shared writer missing route charge".into()))?; + let (sender, receiver) = mpsc::channel(2); + let (completion, result) = oneshot::channel(); + let object = PendingObject { + intent: self.intent.take(), + durable_completion: true, + route_hash: self.route_hash, + route: self + .route + .take() + .ok_or_else(|| IoError::Internal("shared writer missing route".into()))?, + fragments: Vec::new(), + stream: Some(receiver), + len: self.declared_size, + enqueued_at: Instant::now(), + completion, + charge, + }; + runtime.submit(object).await?; + self.stream_tx = Some(sender); + self.stream_result = Some(result); + } + self.stream_tx + .as_ref() + .ok_or_else(|| IoError::Internal("shared object stream missing sender".into()))? + .send(buffer) + .await + .map_err(|_| IoError::WriteFailed("shared object pipeline stopped".into()))?; + } else { + self.fragments.push(buffer); + } Ok(if self.retained_size == self.declared_size { FeedStatus::Pause } else { @@ -140,6 +228,15 @@ impl ChunkIoWriter for SharedObjectWriter { if self.declared_size == 0 { return Ok(Vec::new()); } + if let Some(sender) = self.stream_tx.take() { + drop(sender); + return self + .stream_result + .take() + .ok_or_else(|| IoError::Internal("shared object stream missing completion".into()))? + .await + .map_err(|_| IoError::WriteFailed("small-write completion was lost".into()))?; + } let runtime = self .runtime .take() @@ -158,6 +255,7 @@ impl ChunkIoWriter for SharedObjectWriter { .take() .ok_or_else(|| IoError::Internal("shared writer missing route".into()))?, fragments: std::mem::take(&mut self.fragments), + stream: None, len: self.declared_size, enqueued_at: Instant::now(), completion, @@ -173,6 +271,8 @@ impl ChunkIoWriter for SharedObjectWriter { self.ensure_open()?; self.finished = true; self.fragments.clear(); + self.stream_tx.take(); + self.stream_result.take(); self.charge.take(); Ok(Vec::new()) } @@ -180,7 +280,7 @@ impl ChunkIoWriter for SharedObjectWriter { fn require_data(&self) -> bool { !self.finished && self.retained_size < self.declared_size - && self.route.as_ref().map_or(true, |route| route.has_capacity()) + && (self.full_reservation || self.route.as_ref().map_or(true, |route| route.has_capacity())) } fn input_complete(&self) -> bool { diff --git a/lib/crowdb-chunk-client/src/writer/small_pipeline.rs b/lib/crowdb-chunk-client/src/writer/small_pipeline.rs index 9f2c9729e..55c573de8 100644 --- a/lib/crowdb-chunk-client/src/writer/small_pipeline.rs +++ b/lib/crowdb-chunk-client/src/writer/small_pipeline.rs @@ -78,6 +78,7 @@ impl ManagedPipeline { } mod batch; +mod stream_object; struct PipelineWorker { runtime: Arc, @@ -127,7 +128,13 @@ impl PipelineWorker { if dequeued { self.note_dequeue(&first); } - if let Err(error) = self.ensure_object_fits(frame_bytes(first.len)?).await { + let fit = if first.len > MAX_FRAME_PAYLOAD_BYTES { + self.ensure_stream_object_fits(stream_object::physical_bytes(first.len)?) + .await + } else { + self.ensure_object_fits(frame_bytes(first.len)?).await + }; + if let Err(error) = fit { fail_one(first, &error.to_string(), &self.runtime.metrics); self.fail_remaining(&error.to_string()).await; let _ = self.finish_chunks().await; @@ -141,6 +148,18 @@ impl PipelineWorker { .last_active_ms .store(self.runtime.now_ms(), Ordering::Relaxed); if let Err(error) = result { + if matches!(error, IoError::SourceRead(_)) { + let replacement = match self.replacement.take() { + Some(chunk) => chunk, + None => { + OwnedChunk::allocate(&self.runtime, Arc::clone(&self.route.conversion_active)) + .await? + } + }; + self.chunk.finish().await?; + self.chunk = replacement; + continue; + } self.receiver.close(); self.fail_remaining(&error.to_string()).await; let _ = self.finish_chunks().await; @@ -240,6 +259,9 @@ impl PipelineWorker { } fn collect_batch(&mut self, first: PendingObject) -> Vec { + if first.len > MAX_FRAME_PAYLOAD_BYTES { + return vec![first]; + } let mut bytes = frame_bytes(first.len).unwrap_or(usize::MAX); let mut batch = vec![first]; while batch.len() < self.runtime.policy.max_batch_objects @@ -1091,8 +1113,19 @@ impl OwnedChunk { Ok(()) } - async fn write_batch(&mut self, batch: Vec, metrics: &SmallWriteMetrics) -> Result<()> { - match self.try_write_batch(&batch, metrics).await { + async fn write_batch( + &mut self, + mut batch: Vec, + metrics: &SmallWriteMetrics, + ) -> Result<()> { + let result = if batch.len() == 1 && batch[0].len > MAX_FRAME_PAYLOAD_BYTES { + self.try_write_stream_object(&mut batch[0], metrics) + .await + .map(|location| vec![location]) + } else { + self.try_write_batch(&batch, metrics).await + }; + match result { Ok(locations) => { for (object, location) in batch.into_iter().zip(locations) { metrics.completed.fetch_add(1, Ordering::Relaxed); diff --git a/lib/crowdb-chunk-client/src/writer/small_pipeline/stream_object.rs b/lib/crowdb-chunk-client/src/writer/small_pipeline/stream_object.rs new file mode 100644 index 000000000..b8c7b3ece --- /dev/null +++ b/lib/crowdb-chunk-client/src/writer/small_pipeline/stream_object.rs @@ -0,0 +1,222 @@ +use std::sync::Arc; +use std::time::{SystemTime, UNIX_EPOCH}; + +use bytes::Bytes; +use crowdb_protocol::chunkdb::rpc::Location; +use crowdb_protocol::common::ChunkId; +use crowdb_protocol::frame::{ + encode_frame, FrameMagic, FRAME_FOOTER_BYTES, FRAME_HEADER_PREFIX_BYTES, MAX_FRAME_BYTES, + MAX_FRAME_PAYLOAD_BYTES, +}; + +use super::{ + IoError, MirrorBatchStats, OwnedChunk, PendingObject, PipelineWorker, Result, SmallWriteMetrics, +}; + +pub(super) fn physical_bytes(logical: usize) -> Result { + logical + .checked_add( + logical + .div_ceil(MAX_FRAME_PAYLOAD_BYTES) + .checked_mul(FRAME_HEADER_PREFIX_BYTES + FRAME_FOOTER_BYTES) + .ok_or_else(|| IoError::WriteFailed("shared object frame length overflow".into()))?, + ) + .ok_or_else(|| IoError::WriteFailed("shared object frame length overflow".into())) +} + +impl PipelineWorker { + pub(super) async fn ensure_stream_object_fits(&mut self, physical: usize) -> Result<()> { + if physical as u64 > self.runtime.policy.chunk_capacity { + return Err(IoError::ObjectTooLarge { + size: physical, + limit: usize::try_from(self.runtime.policy.chunk_capacity).unwrap_or(usize::MAX), + }); + } + if let Ok(strip) = self.chunk.current_strip() { + if self.chunk.cursor > u64::from(strip.chunk_offset) * 1024 { + self.chunk.close_strip(&self.runtime.metrics).await?; + } + } + self.chunk.ensure_strip().await?; + if self.chunk.remaining_in_chunk() < physical as u64 { + let replacement = match self.replacement.take() { + Some(chunk) => chunk, + None => { + OwnedChunk::allocate(&self.runtime, Arc::clone(&self.route.conversion_active)).await? + } + }; + self.chunk.finish().await?; + self.chunk = replacement; + self.chunk.ensure_strip().await?; + } + Ok(()) + } +} + +struct FragmentCursor<'a> { + receiver: &'a mut tokio::sync::mpsc::Receiver, + current: Option, + offset: usize, + received: usize, +} + +impl FragmentCursor<'_> { + async fn take(&mut self, length: usize) -> Result> { + let mut output = Vec::with_capacity(length); + while output.len() < length { + if self.current.is_none() { + self.current = self.receiver.recv().await; + self.received += 1; + } + let fragment = self.current.as_ref().ok_or_else(|| { + IoError::SourceRead("shared object body ended before its declared length".into()) + })?; + let count = (length - output.len()).min(fragment.len() - self.offset); + output.extend_from_slice(&fragment[self.offset..self.offset + count]); + self.offset += count; + if self.offset == fragment.len() { + self.current = None; + self.offset = 0; + } + } + Ok(output) + } +} + +impl OwnedChunk { + pub(super) async fn try_write_stream_object( + &mut self, + object: &mut PendingObject, + metrics: &SmallWriteMetrics, + ) -> Result { + let start = self.cursor; + let chunk_id = self + .chunk + .id + .ok_or_else(|| IoError::AllocationFailed("shared chunk missing ID".into()))?; + let physical = physical_bytes(object.len)?; + let location = Location { + chunk_id: Some(chunk_id), + offset: start, + length: physical as u64, + logical_offset: 0, + logical_length: object.len as u64, + }; + if let Some(intent) = &object.intent { + intent.before_write(&location).await?; + } + let mut source = FragmentCursor { + receiver: object + .stream + .as_mut() + .ok_or_else(|| IoError::Internal("shared object stream missing receiver".into()))?, + current: None, + offset: 0, + received: 0, + }; + let mut remaining = object.len; + let write_time_ms = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map_or(0, |duration| { + u64::try_from(duration.as_millis()).unwrap_or(u64::MAX) + }); + while remaining > 0 { + self.write_stream_strip(&mut source, &mut remaining, chunk_id, write_time_ms) + .await?; + } + if self.cursor - start != physical as u64 { + return Err(IoError::Internal("shared object physical length mismatch".into())); + } + self.flush_pending_advance().await?; + if self.chunk.acknowledged_cursor < self.cursor { + self.start_pending_advance(self.cursor)?; + self.flush_pending_advance().await?; + } + if self.chunk.acknowledged_cursor < self.cursor { + return Err(IoError::MetadataConflict( + "shared object cursor was not published".into(), + )); + } + self.confirm_batch_publication(std::slice::from_ref(object), self.cursor) + .await?; + metrics.record_batch(1, object.len); + Ok(location) + } + + async fn write_stream_strip( + &mut self, + source: &mut FragmentCursor<'_>, + remaining: &mut usize, + chunk_id: ChunkId, + write_time_ms: u64, + ) -> Result<()> { + self.ensure_strip().await?; + let strip = self.current_strip()?.clone(); + let strip_start = u64::from(strip.chunk_offset) * 1024; + let strip_end = u64::from(strip.chunk_offset.saturating_add(strip.capacity)) * 1024; + let block_offset = self.cursor - strip_start; + let frame_capacity = (strip_end - self.cursor) as usize / MAX_FRAME_BYTES; + if frame_capacity == 0 { + return Err(IoError::Internal("shared object lost frame alignment".into())); + } + let mut shadow = self.take_shadow(strip.capacity as usize * 1024, block_offset as usize); + let before = shadow.len(); + let mut logical = 0usize; + for _ in 0..frame_capacity { + if *remaining == 0 { + break; + } + let length = (*remaining).min(MAX_FRAME_PAYLOAD_BYTES); + let payload = source.take(length).await?; + let frame = encode_frame(FrameMagic::RepoSmallV1, chunk_id, &payload, write_time_ms) + .map_err(|error| IoError::WriteFailed(error.to_string()))?; + shadow.extend_from_slice(&frame); + *remaining -= length; + logical += length; + } + let written = shadow.len() - before; + let end = self.cursor + written as u64; + self.consume_staged_reservation(end).await?; + let frozen = shadow.freeze(); + let view = frozen.slice(before..before + written); + let image = frozen.slice(0..before + written); + let (_, result) = self + .write_mirrors_with_repair( + &strip, + view, + image, + u64::from(strip.unit_kb) * 1024, + block_offset, + MirrorBatchStats { + object_count: 1, + buffer_count: source.received, + logical_bytes: logical, + }, + ) + .await; + self.shadow = Some( + frozen + .try_into_mut() + .unwrap_or_else(|shared| bytes::BytesMut::from(shared.as_ref())), + ); + result?; + self.cursor = end; + if end == strip_end { + let closed = self + .chunk + .strips + .iter() + .find(|current| current.strip_sequence == strip.strip_sequence) + .cloned() + .ok_or_else(|| IoError::MetadataConflict("closed mirror strip disappeared".into()))?; + self.schedule_closed_advance(end, strip.strip_sequence); + if let Err(error) = self.retain_closed_strip(closed).await { + tracing::warn!(%error, "mirror-to-EC fast path deferred to chunkdb"); + } + } else { + self.refresh_pending_advance().await?; + self.start_pending_advance(end)?; + } + Ok(()) + } +} diff --git a/lib/crowdb-chunk-client/src/writer/small_pool.rs b/lib/crowdb-chunk-client/src/writer/small_pool.rs index 46adea6f7..140cb06a8 100644 --- a/lib/crowdb-chunk-client/src/writer/small_pool.rs +++ b/lib/crowdb-chunk-client/src/writer/small_pool.rs @@ -25,6 +25,7 @@ pub(crate) struct PendingObject { pub route_hash: u64, pub route: Arc, pub fragments: Vec, + pub stream: Option>, pub len: usize, pub enqueued_at: Instant, pub completion: oneshot::Sender>>, @@ -55,6 +56,24 @@ impl RouteCharge { self.metrics.reserved_bytes.fetch_add(bytes, Ordering::Relaxed); } + pub fn try_reserve(&mut self, bytes: usize) -> bool { + let bytes = bytes as u64; + if self + .route + .used_bytes + .fetch_update(Ordering::AcqRel, Ordering::Acquire, |used| { + used.checked_add(bytes) + .filter(|next| *next <= self.route.capacity_bytes) + }) + .is_err() + { + return false; + } + self.bytes += bytes; + self.metrics.reserved_bytes.fetch_add(bytes, Ordering::Relaxed); + true + } + fn rebind(&mut self, route: Arc) { if Arc::ptr_eq(&self.route, &route) { return; diff --git a/lib/crowdb-chunk-client/tests/benchmark_e2e.rs b/lib/crowdb-chunk-client/tests/benchmark_e2e.rs index e6105d7cc..8d260ed2e 100644 --- a/lib/crowdb-chunk-client/tests/benchmark_e2e.rs +++ b/lib/crowdb-chunk-client/tests/benchmark_e2e.rs @@ -108,11 +108,25 @@ async fn mixed_read_benchmark_prepares_and_verifies_real_frames() { ) .await; + eprintln!( + "read benchmark result: {}", + serde_json::to_string(&result).unwrap() + ); assert_eq!(result.errors, 0, "{:?}", result.error_messages); assert_eq!(result.reads, 4); assert_eq!(result.small_reads, 2); assert_eq!(result.large_reads, 2); assert_eq!(result.logical_bytes, 2 * KIB as u64 + 4 * MIB as u64); + assert_eq!(result.read_flow.location_normalizations, result.reads); + assert_eq!(result.read_flow.locations_examined, result.reads); + assert_eq!(result.read_flow.range_locations_examined, result.reads); + assert!(result.read_flow.layout_queries >= result.reads); + assert!(result.read_flow.layout_query_wait_ns > 0); + assert!(result.read_flow.strip_read_wait_ns > 0); + assert!(result.read_flow.chunk_read_wait_ns >= result.read_flow.strip_read_wait_ns); + assert!(result.read_flow.frame_decode_wait_ns > 0); + assert!(result.read_flow.frame_parse_wait_ns > 0); + assert!(result.read_flow.frame_parse_wait_ns <= result.read_flow.frame_decode_wait_ns); assert_eq!(result.incomplete_reads, 0); assert_eq!(result.stop_reason, "complete"); assert!(result.preparation_secs > 0.0); diff --git a/lib/crowdb-chunk-client/tests/chunk_reader_e2e.rs b/lib/crowdb-chunk-client/tests/chunk_reader_e2e.rs index 14beaec42..058ef5d1c 100644 --- a/lib/crowdb-chunk-client/tests/chunk_reader_e2e.rs +++ b/lib/crowdb-chunk-client/tests/chunk_reader_e2e.rs @@ -230,7 +230,11 @@ async fn ec_range_read_recovers_only_the_requested_bytes() { let (reader, fault) = reader_with_failures(&stack, vec![failed]).await; let start = MIB as u64 + 123; let end = start + 16 * KIB as u64; - let actual = reader.read_range(&result.locations, start, end).await.unwrap(); + let actual = reader + .read_range(&result.locations, start, end) + .await + .unwrap() + .concat(); assert_eq!( actual, data[usize::try_from(start).unwrap()..usize::try_from(end).unwrap()] @@ -241,7 +245,10 @@ async fn ec_range_read_recovers_only_the_requested_bytes() { let frame = parse_frame(&direct, result.locations[0].chunk_id.unwrap()).unwrap(); assert_eq!(frame.payload, &data[..frame.payload.len()]); assert!(fault.max_read.load(Ordering::Relaxed) <= 64 * KIB); - assert_eq!(reader.read_object(&result.locations).await.unwrap(), data); + assert_eq!( + reader.read_object(&result.locations).await.unwrap().concat(), + data + ); let after = stack.query_chunk(&result.locations[0]).await; assert!(after.strips[0].unavailable_segments.is_empty()); @@ -328,33 +335,42 @@ async fn partial_read_preserves_healthy_ec_shard_ranges_around_data_loss() { (partial.failures[0].start, partial.failures[0].end), (payload_per_shard, 3 * payload_per_shard) ); - assert_eq!(partial.ranges.len(), 2); + assert_eq!(partial.ranges.len(), 33); assert_eq!( - (partial.ranges[0].start, partial.ranges[0].end), + (partial.ranges[0].start, partial.ranges[15].end), (0, payload_per_shard) ); assert_eq!( - partial.ranges[0].data, + partial.ranges[..16] + .iter() + .map(|range| range.data.as_ref()) + .collect::>() + .concat(), data[..usize::try_from(payload_per_shard).unwrap()] ); assert_eq!( - (partial.ranges[1].start, partial.ranges[1].end), + (partial.ranges[16].start, partial.ranges[32].end), (3 * payload_per_shard, 4 * MIB as u64) ); assert_eq!( - partial.ranges[1].data, + partial.ranges[16..] + .iter() + .map(|range| range.data.as_ref()) + .collect::>() + .concat(), data[usize::try_from(3 * payload_per_shard).unwrap()..] ); let mut stream = reader.read_stream(&result.locations).unwrap(); - assert_eq!( - stream.next_chunk().await.unwrap().unwrap(), - data[..usize::try_from(payload_per_shard).unwrap()] - ); + let mut prefix = Vec::new(); + for _ in 0..16 { + prefix.extend_from_slice(&stream.next_chunk().await.unwrap().unwrap()); + } + assert_eq!(prefix, data[..usize::try_from(payload_per_shard).unwrap()]); assert!(matches!( stream.next_chunk().await.unwrap(), Err(ReadError::FailedRange { start, end, .. }) - if start == payload_per_shard && end == 3 * payload_per_shard + if start == payload_per_shard && end == payload_per_shard + MAX_FRAME_PAYLOAD_BYTES as u64 )); assert!(stream.next_chunk().await.is_none()); } @@ -385,7 +401,8 @@ async fn mirror_read_succeeds_and_reports_replica_loss() { .client .read_object(std::slice::from_ref(&location)) .await - .unwrap(), + .unwrap() + .concat(), data ); let (unreadable, _) = reader_with_failures(&stack, vec![primary]).await; @@ -431,7 +448,8 @@ async fn transient_mirror_read_failure_does_not_mark_segment_unavailable() { .client .read_object(std::slice::from_ref(&location)) .await - .unwrap(), + .unwrap() + .concat(), data ); stack.client.shutdown_small_writes().await.unwrap(); @@ -465,7 +483,11 @@ async fn chunkdb_restart_admits_verified_corruption_and_repairs_full_shard() { let (reader, fault) = reader_with_corruption(&stack, vec![failed_disk]).await; let length = 16 * KIB as u64; assert_eq!( - reader.read_range(&result.locations, 0, length).await.unwrap(), + reader + .read_range(&result.locations, 0, length) + .await + .unwrap() + .concat(), data[..16 * KIB] ); assert!(fault.max_read.load(Ordering::Relaxed) <= 64 * KIB); @@ -504,7 +526,8 @@ async fn chunkdb_restart_admits_verified_corruption_and_repairs_full_shard() { after_restart .read_range(&result.locations, 0, length) .await - .unwrap(), + .unwrap() + .concat(), data[..16 * KIB] ); } diff --git a/lib/crowdb-chunk-client/tests/chunk_reader_test.rs b/lib/crowdb-chunk-client/tests/chunk_reader_test.rs index c61afdb9e..df59616ed 100644 --- a/lib/crowdb-chunk-client/tests/chunk_reader_test.rs +++ b/lib/crowdb-chunk-client/tests/chunk_reader_test.rs @@ -22,8 +22,8 @@ use crowdb_protocol::chunkdb::rpc::{ }; use crowdb_protocol::common::{ChunkId, DiskId}; use crowdb_protocol::diskdb::rpc::Segment; -use crowdb_protocol::frame::{encode_frame, FrameMagic}; -use tokio::sync::Semaphore; +use crowdb_protocol::frame::{encode_frame, FrameMagic, MAX_FRAME_BYTES, MAX_FRAME_PAYLOAD_BYTES}; +use tokio::sync::{Notify, Semaphore}; const KIB: usize = 1024; const SHARD: usize = 64 * KIB; @@ -34,6 +34,37 @@ struct MemoryDiskIo { reads: Arc, } +struct BlockingDiskIo { + inner: MemoryDiskIo, + release_first: Arc, + started: Arc, +} + +#[async_trait] +impl DiskWriter for BlockingDiskIo { + async fn write(&self, _seg: &Segment, _unit_bytes: u64, _data: Bytes) -> Result<()> { + unreachable!("reader test does not write") + } + + async fn write_at_byte_offset( + &self, + _seg: &Segment, + _unit_bytes: u64, + _byte_offset: u64, + _data: Bytes, + ) -> Result<()> { + unreachable!("reader test does not write") + } + + async fn read(&self, segment: &Segment, unit_bytes: u64, offset: u64, length: u32) -> Result { + self.started.fetch_add(1, Ordering::AcqRel); + if offset == 0 { + self.release_first.notified().await; + } + self.inner.read(segment, unit_bytes, offset, length).await + } +} + struct SequenceAllocator { queries: AtomicUsize, first: Chunk, @@ -402,8 +433,396 @@ async fn object_reader_discards_bytes_from_an_expired_layout() { logical_length: 4, ..Location::default() }; - assert_eq!(reader.read_object(&[location]).await.unwrap(), b"new!".as_slice()); + assert_eq!( + reader.read_object(&[location]).await.unwrap().concat(), + b"new!".as_slice() + ); assert_eq!(allocator.queries.load(Ordering::Acquire), 2); + assert_eq!(reader.flow_metrics_snapshot().location_normalizations, 1); +} + +#[tokio::test] +async fn framed_read_stream_keeps_whole_frame_under_small_window_setting() { + let id = ChunkId { high: 13, low: 37 }; + let segment = segment(1); + let payload = b"abcdefghij"; + let frame = Bytes::from(encode_frame(FrameMagic::RepoLargeV1, id, payload, 1).unwrap()); + let chunk = Chunk { + id: Some(id), + state: ChunkState::Sealed as i32, + capacity: 1, + sealed_length: 1, + strips: vec![ChunkStrip { + unit_kb: 1, + capacity: 1, + sealed_length: 1, + sealed_ts_ms: 1, + strip_type: StripType::Mirror as i32, + strip: Some(Strip::MirrorStrip(MirrorStrip { + segments: vec![segment], + })), + ..ChunkStrip::default() + }], + ..Chunk::default() + }; + let allocator = Arc::new(SequenceAllocator { + queries: AtomicUsize::new(0), + first: chunk.clone(), + current: Mutex::new(chunk), + first_layout_validity_ms: 1_000, + full_reply: None, + full_calls: AtomicUsize::new(0), + }); + let disk_io = Arc::new(MemoryDiskIo { + shards: vec![(segment.disk_id.unwrap(), frame.clone())], + failed: Vec::new(), + reads: Arc::new(AtomicUsize::new(0)), + }); + let reader = ChunkReader::new( + allocator.clone(), + disk_io, + ChunkReadPolicy { + stream_window_bytes: 4, + ..ChunkReadPolicy::default() + }, + ) + .unwrap(); + let location = Location { + chunk_id: Some(id), + length: frame.len() as u64, + logical_length: payload.len() as u64, + ..Location::default() + }; + let mut stream = reader.read_stream(&[location]).unwrap(); + assert_eq!(stream.next_chunk().await.unwrap().unwrap(), payload.as_slice()); + assert!(stream.next_chunk().await.is_none()); + assert_eq!(allocator.queries.load(Ordering::Relaxed), 1); + let metrics = reader.flow_metrics_snapshot(); + assert_eq!(metrics.location_normalizations, 1); + assert_eq!(metrics.locations_examined, 1); + assert_eq!(metrics.range_locations_examined, 1); + assert_eq!(metrics.stream_windows, 1); + assert_eq!(metrics.layout_queries, 1); +} + +#[tokio::test] +async fn read_stream_selects_only_locations_overlapping_each_window() { + let id = ChunkId { high: 19, low: 41 }; + let segment = segment(1); + let mut physical = Vec::new(); + let mut locations = Vec::new(); + for (index, payload) in [b"abcd", b"efgh", b"ijkl"].into_iter().enumerate() { + let frame = encode_frame(FrameMagic::RepoLargeV1, id, payload, 1).unwrap(); + locations.push(Location { + chunk_id: Some(id), + offset: physical.len() as u64, + length: frame.len() as u64, + logical_offset: (index * 4) as u64, + logical_length: 4, + }); + physical.extend_from_slice(&frame); + } + let chunk = Chunk { + id: Some(id), + state: ChunkState::Sealed as i32, + capacity: 1, + sealed_length: 1, + strips: vec![ChunkStrip { + unit_kb: 1, + capacity: 1, + sealed_length: 1, + sealed_ts_ms: 1, + strip_type: StripType::Mirror as i32, + strip: Some(Strip::MirrorStrip(MirrorStrip { + segments: vec![segment], + })), + ..ChunkStrip::default() + }], + ..Chunk::default() + }; + let allocator = Arc::new(SequenceAllocator { + queries: AtomicUsize::new(0), + first: chunk.clone(), + current: Mutex::new(chunk), + first_layout_validity_ms: 1_000, + full_reply: None, + full_calls: AtomicUsize::new(0), + }); + let disk_io = Arc::new(MemoryDiskIo { + shards: vec![(segment.disk_id.unwrap(), Bytes::from(physical))], + failed: Vec::new(), + reads: Arc::new(AtomicUsize::new(0)), + }); + let reader = ChunkReader::new( + allocator.clone(), + disk_io, + ChunkReadPolicy { + stream_window_bytes: 4, + ..ChunkReadPolicy::default() + }, + ) + .unwrap(); + let mut stream = reader.read_stream(&locations).unwrap(); + for expected in [b"abcd".as_slice(), b"efgh", b"ijkl"] { + assert_eq!(stream.next_chunk().await.unwrap().unwrap(), expected); + } + assert!(stream.next_chunk().await.is_none()); + let metrics = reader.flow_metrics_snapshot(); + assert_eq!(metrics.location_normalizations, 1); + assert_eq!(metrics.locations_examined, 3); + assert_eq!(metrics.range_locations_examined, 3); + assert_eq!(metrics.stream_windows, 3); + assert_eq!(metrics.layout_queries, 1); +} + +#[tokio::test] +async fn stream_waits_for_first_result_and_retains_three_slots_until_consumer_releases_buffers() { + let id = ChunkId { high: 23, low: 51 }; + let segment = segment(1); + let mut physical = Vec::new(); + let mut locations = Vec::new(); + for (index, payload) in [b"aaaa", b"bbbb", b"cccc", b"dddd"].into_iter().enumerate() { + let frame = encode_frame(FrameMagic::RepoLargeV1, id, payload, 1).unwrap(); + locations.push(Location { + chunk_id: Some(id), + offset: physical.len() as u64, + length: frame.len() as u64, + logical_offset: (index * 4) as u64, + logical_length: 4, + }); + physical.extend_from_slice(&frame); + } + let chunk = Chunk { + id: Some(id), + state: ChunkState::Sealed as i32, + capacity: 1, + sealed_length: 1, + strips: vec![ChunkStrip { + unit_kb: 1, + capacity: 1, + sealed_length: 1, + sealed_ts_ms: 1, + strip_type: StripType::Mirror as i32, + strip: Some(Strip::MirrorStrip(MirrorStrip { + segments: vec![segment], + })), + ..ChunkStrip::default() + }], + ..Chunk::default() + }; + let allocator = Arc::new(SequenceAllocator { + queries: AtomicUsize::new(0), + first: chunk.clone(), + current: Mutex::new(chunk), + first_layout_validity_ms: 1_000, + full_reply: None, + full_calls: AtomicUsize::new(0), + }); + let started = Arc::new(AtomicUsize::new(0)); + let release_first = Arc::new(Notify::new()); + let disk_io = Arc::new(BlockingDiskIo { + inner: MemoryDiskIo { + shards: vec![(segment.disk_id.unwrap(), Bytes::from(physical))], + failed: Vec::new(), + reads: Arc::new(AtomicUsize::new(0)), + }, + release_first: Arc::clone(&release_first), + started: Arc::clone(&started), + }); + let reader = ChunkReader::new( + allocator, + disk_io, + ChunkReadPolicy { + stream_slots: 3, + stream_window_bytes: 4, + layout_safety_margin: Duration::ZERO, + ..ChunkReadPolicy::default() + }, + ) + .unwrap(); + let mut stream = reader.read_stream(&locations).unwrap(); + let waiting = tokio::spawn(async move { + let first = stream.next_chunk().await.unwrap().unwrap(); + (stream, first) + }); + tokio::time::timeout(Duration::from_secs(1), async { + while started.load(Ordering::Acquire) < 3 { + tokio::task::yield_now().await; + } + }) + .await + .unwrap(); + assert_eq!(started.load(Ordering::Acquire), 3); + release_first.notify_one(); + let (mut stream, first) = waiting.await.unwrap(); + assert_eq!(&first[..], b"aaaa"); + let second = stream.next_chunk().await.unwrap().unwrap(); + let third = stream.next_chunk().await.unwrap().unwrap(); + assert_eq!(&second[..], b"bbbb"); + assert_eq!(&third[..], b"cccc"); + assert!( + tokio::time::timeout(Duration::from_millis(20), stream.next_chunk()) + .await + .is_err() + ); + assert_eq!(started.load(Ordering::Acquire), 3); + drop(first); + assert_eq!(stream.next_chunk().await.unwrap().unwrap(), b"dddd".as_slice()); + assert!(stream.next_chunk().await.is_none()); +} + +#[tokio::test] +async fn global_read_budget_remains_charged_while_http_keeps_frame_views() { + let id = ChunkId { high: 29, low: 57 }; + let mut segment = segment(1); + segment.unit_count = 2; + let payload = vec![0x5a; MAX_FRAME_PAYLOAD_BYTES]; + let frame = encode_frame(FrameMagic::RepoLargeV1, id, &payload, 1).unwrap(); + assert_eq!(frame.len(), MAX_FRAME_BYTES); + let physical = Bytes::from(frame.repeat(32)); + let chunk = Chunk { + id: Some(id), + state: ChunkState::Sealed as i32, + capacity: 2048, + sealed_length: 2048, + strips: vec![ChunkStrip { + unit_kb: 1024, + capacity: 2048, + sealed_length: 2048, + sealed_ts_ms: 1, + strip_type: StripType::Mirror as i32, + strip: Some(Strip::MirrorStrip(MirrorStrip { + segments: vec![segment], + })), + ..ChunkStrip::default() + }], + ..Chunk::default() + }; + let allocator = Arc::new(SequenceAllocator { + queries: AtomicUsize::new(0), + first: chunk.clone(), + current: Mutex::new(chunk), + first_layout_validity_ms: 1_000, + full_reply: None, + full_calls: AtomicUsize::new(0), + }); + let reads = Arc::new(AtomicUsize::new(0)); + let disk_io = Arc::new(MemoryDiskIo { + shards: vec![(segment.disk_id.unwrap(), physical)], + failed: Vec::new(), + reads: Arc::clone(&reads), + }); + let reader = ChunkReader::new( + allocator, + disk_io, + ChunkReadPolicy { + stream_slots: 3, + global_stream_bytes: 1024 * 1024, + layout_safety_margin: Duration::ZERO, + ..ChunkReadPolicy::default() + }, + ) + .unwrap(); + let location = Location { + chunk_id: Some(id), + length: 32 * MAX_FRAME_BYTES as u64, + logical_length: 32 * MAX_FRAME_PAYLOAD_BYTES as u64, + ..Location::default() + }; + let mut stream = reader.read_stream(&[location]).unwrap(); + let mut retained = Vec::new(); + for _ in 0..16 { + retained.push(stream.next_chunk().await.unwrap().unwrap()); + } + assert_eq!(reads.load(Ordering::Acquire), 1); + assert!( + tokio::time::timeout(Duration::from_millis(20), stream.next_chunk()) + .await + .is_err() + ); + drop(retained); + assert_eq!(stream.next_chunk().await.unwrap().unwrap(), payload.as_slice()); + assert_eq!(reads.load(Ordering::Acquire), 2); +} + +#[tokio::test] +async fn frame_crossing_physical_parts_returns_two_original_payload_views() { + let id = ChunkId { high: 31, low: 59 }; + let first_segment = segment(1); + let second_segment = segment(2); + let payload = b"abcdefghijklmnopqrst"; + let frame = encode_frame(FrameMagic::RepoSmallV1, id, payload, 1).unwrap(); + let frame_offset = 1000_usize; + let first_length = 1024 - frame_offset; + let mut first_data = vec![0; 1024]; + first_data[frame_offset..].copy_from_slice(&frame[..first_length]); + let first_data = Bytes::from(first_data); + let second_data = Bytes::from(frame[first_length..].to_vec()); + let strips = [first_segment, second_segment] + .into_iter() + .enumerate() + .map(|(index, segment)| ChunkStrip { + strip_sequence: u32::try_from(index).unwrap(), + chunk_offset: u32::try_from(index).unwrap(), + unit_kb: 1, + capacity: 1, + sealed_length: 1, + sealed_ts_ms: 1, + strip_type: StripType::Mirror as i32, + strip: Some(Strip::MirrorStrip(MirrorStrip { + segments: vec![segment], + })), + ..ChunkStrip::default() + }) + .collect(); + let chunk = Chunk { + id: Some(id), + state: ChunkState::Sealed as i32, + capacity: 2, + sealed_length: 2, + strips, + ..Chunk::default() + }; + let allocator = Arc::new(SequenceAllocator { + queries: AtomicUsize::new(0), + first: chunk.clone(), + current: Mutex::new(chunk), + first_layout_validity_ms: 1_000, + full_reply: None, + full_calls: AtomicUsize::new(0), + }); + let disk_io = Arc::new(MemoryDiskIo { + shards: vec![ + (first_segment.disk_id.unwrap(), first_data.clone()), + (second_segment.disk_id.unwrap(), second_data.clone()), + ], + failed: Vec::new(), + reads: Arc::new(AtomicUsize::new(0)), + }); + let reader = ChunkReader::new( + allocator, + disk_io, + ChunkReadPolicy { + layout_safety_margin: Duration::ZERO, + ..ChunkReadPolicy::default() + }, + ) + .unwrap(); + let location = Location { + chunk_id: Some(id), + offset: frame_offset as u64, + length: frame.len() as u64, + logical_length: payload.len() as u64, + ..Location::default() + }; + let views = reader + .read_range(&[location], 0, payload.len() as u64) + .await + .unwrap(); + assert_eq!(views.concat(), payload); + assert_eq!(views.len(), 2); + assert_eq!(views[0].as_ptr(), first_data.slice(frame_offset + 14..).as_ptr()); + assert_eq!(views[1].as_ptr(), second_data.as_ptr()); } #[tokio::test] @@ -456,7 +875,10 @@ async fn framed_mirror_crc_failure_uses_a_verified_fallback() { logical_length: payload.len() as u64, ..Location::default() }; - assert_eq!(reader.read_object(&[location]).await.unwrap(), payload.as_slice()); + assert_eq!( + reader.read_object(&[location]).await.unwrap().concat(), + payload.as_slice() + ); assert!(allocator.queries.load(Ordering::Acquire) >= 2); assert_eq!( allocator.current.lock().unwrap().strips[0].unavailable_segments, @@ -531,8 +953,8 @@ async fn marked_ec_fragment_shares_one_full_recovery_future() { reader.read_range(std::slice::from_ref(&location), 0, SHARD as u64), reader.read_range(std::slice::from_ref(&location), 0, SHARD as u64) ); - assert_eq!(first.unwrap(), data[..SHARD]); - assert_eq!(second.unwrap(), data[..SHARD]); + assert_eq!(first.unwrap().concat(), data[..SHARD]); + assert_eq!(second.unwrap().concat(), data[..SHARD]); assert_eq!(allocator.full_calls.load(Ordering::Acquire), 1); let bounded = ChunkReader::new( @@ -545,7 +967,11 @@ async fn marked_ec_fragment_shares_one_full_recovery_future() { ) .unwrap(); assert_eq!( - bounded.read_range(&[location], 0, SHARD as u64).await.unwrap(), + bounded + .read_range(&[location], 0, SHARD as u64) + .await + .unwrap() + .concat(), data[..SHARD] ); assert_eq!(allocator.full_calls.load(Ordering::Acquire), 1); diff --git a/lib/crowdb-chunk-client/tests/large_object_writer_e2e.rs b/lib/crowdb-chunk-client/tests/large_object_writer_e2e.rs index e2f7b1996..009ddff64 100644 --- a/lib/crowdb-chunk-client/tests/large_object_writer_e2e.rs +++ b/lib/crowdb-chunk-client/tests/large_object_writer_e2e.rs @@ -20,7 +20,7 @@ use crowdb_common::ec::{encode_parity_from_shards, EcScheme}; use crowdb_kv_client::{ClientConfig, CrowdbKvClient, HardwareClient, ServiceRegistryClient}; use crowdb_protocol::chunkdb::rpc::{Chunk, ChunkState, EcState, Location, Strip}; use crowdb_protocol::diskdb::rpc::Segment; -use crowdb_protocol::frame::ChunkLocation; +use crowdb_protocol::frame::{ChunkLocation, MAX_FRAME_PAYLOAD_BYTES}; use e2e_stack::{all_binaries_available, E2eStack}; @@ -213,13 +213,15 @@ async fn large_write_multi_strip_persists_data_metadata_and_parity() { assert_eq!(chunk.strips.len(), 4); assert_ec_parity(&stack, &chunk, location).await; let read = stack.client.read_object(&result.locations).await.unwrap(); - assert_bytes_match(&read, &data); + assert!(read.len() > 1, "complete read must retain verified frame buffers"); + assert_bytes_match(&read.concat(), &data); assert_eq!( stack .client .read_range(&result.locations, 3 * MIB as u64 + 17, 9 * MIB as u64 + 31) .await - .unwrap(), + .unwrap() + .concat(), data[3 * MIB + 17..9 * MIB + 31] ); } @@ -276,14 +278,22 @@ async fn large_write_rotates_chunks_without_losing_data() { assert_ec_parity(&stack, &chunk, location).await; } - read_back.extend(stack.client.read_object(&result.locations).await.unwrap()); + read_back.extend( + stack + .client + .read_object(&result.locations) + .await + .unwrap() + .concat(), + ); assert_eq!(read_back, data); assert_eq!( stack .client .read_range(&result.locations, 7 * MIB as u64, 10 * MIB as u64) .await - .unwrap(), + .unwrap() + .concat(), data[7 * MIB..10 * MIB] ); let read_client = stack @@ -298,7 +308,7 @@ async fn large_write_rotates_chunks_without_losing_data() { let mut streamed = Vec::new(); while let Some(part) = stream.next_chunk().await { let part = part.unwrap(); - assert!(part.len() <= 3 * MIB); + assert!(part.len() <= MAX_FRAME_PAYLOAD_BYTES); streamed.extend_from_slice(&part); } assert_eq!(streamed, data); @@ -337,7 +347,15 @@ async fn large_write_unknown_size_partial_tail_is_durable() { u32::try_from(location.length.div_ceil(1024)).unwrap() ); assert!(chunk.strips.len() >= 2); - assert_eq!(stack.client.read_object(&result.locations).await.unwrap(), data); + assert_eq!( + stack + .client + .read_object(&result.locations) + .await + .unwrap() + .concat(), + data + ); } #[tokio::test] @@ -380,7 +398,10 @@ async fn large_write_replaces_failed_data_and_parity_segments_end_to_end() { 4 ); assert_eq!(client.large_write_repair_metrics().repaired_segments, 1); - assert_eq!(client.read_object(&result.locations).await.unwrap(), data); + assert_eq!( + client.read_object(&result.locations).await.unwrap().concat(), + data + ); assert_ec_parity(&stack, &chunk, &result.locations[0]).await; } } diff --git a/lib/crowdb-chunk-client/tests/small_object_writer_e2e.rs b/lib/crowdb-chunk-client/tests/small_object_writer_e2e.rs index ae96cea52..458452bfc 100644 --- a/lib/crowdb-chunk-client/tests/small_object_writer_e2e.rs +++ b/lib/crowdb-chunk-client/tests/small_object_writer_e2e.rs @@ -110,6 +110,7 @@ fn policy() -> SmallWritePolicy { control_interval: Duration::from_millis(1), cooldown: Duration::from_millis(1), chunk_capacity: 2 * MIB as u64, + object_limit: MAX_FRAME_PAYLOAD_BYTES, mirror_copies: 1, ..SmallWritePolicy::default() } @@ -121,6 +122,81 @@ async fn write_object(client: &ChunkIoClient, data: Bytes) -> Location { writer.on_finish().await.unwrap().remove(0) } +#[tokio::test(flavor = "multi_thread", worker_threads = 4)] +async fn shared_object_spans_mirror_strips_with_one_location() { + if !all_binaries_available() { + return; + } + let mut configured = policy(); + configured.chunk_capacity = 16 * MIB as u64; + configured.object_limit = 2 * MIB; + let stack = E2eStack::start(configured).await; + let _prefix = write_object(&stack.client, Bytes::from_static(b"PAR1datafoot\x04\0\0\0PAR1")).await; + let data = Bytes::from( + (0_u32..1_200_000) + .map(|index| u8::try_from(index % 251).unwrap()) + .collect::>(), + ); + let mut writer = stack + .client + .prepare_shared_object_write_for_key(data.len(), b"shared-span") + .await + .unwrap(); + let submitted_before = stack.client.small_write_metrics().submitted; + for (index, fragment) in data.chunks(1024 * 1024).enumerate() { + writer.on_data(Bytes::copy_from_slice(fragment)).await.unwrap(); + if index == 0 { + assert_eq!(stack.client.small_write_metrics().submitted, submitted_before + 1); + } + } + let locations = writer.on_finish().await.unwrap(); + assert_eq!(locations.len(), 1); + let read = stack + .client + .read_range(&locations, 1_048_550, 1_048_601) + .await + .unwrap(); + assert_eq!(read.concat(), &data[1_048_550..1_048_601]); + let mut stream = stack + .client + .read_range_stream(&locations, 1_048_550, 1_048_601) + .unwrap(); + let mut streamed = Vec::new(); + while let Some(chunk) = stream.next_chunk().await { + streamed.extend_from_slice(&chunk.unwrap()); + } + assert_eq!(streamed, &data[1_048_550..1_048_601]); +} + +#[tokio::test(flavor = "multi_thread", worker_threads = 4)] +async fn cancelled_streaming_shared_object_keeps_pipeline_available() { + if !all_binaries_available() { + return; + } + let mut configured = policy(); + configured.chunk_capacity = 16 * MIB as u64; + configured.object_limit = 2 * MIB; + let stack = E2eStack::start(configured).await; + let mut cancelled = stack + .client + .prepare_shared_object_write_for_key(1_200_000, b"cancelled") + .await + .unwrap(); + cancelled.on_data(Bytes::from(vec![7; MIB])).await.unwrap(); + cancelled.on_error().await.unwrap(); + + let data = Bytes::from_static(b"surviving object"); + let mut survivor = stack.client.prepare_small_write(data.len()).await.unwrap(); + survivor.on_data(data.clone()).await.unwrap(); + let location = survivor.finish_durable().await.unwrap().remove(0); + let read = stack + .client + .read_range(&[location], 0, data.len() as u64) + .await + .unwrap(); + assert_eq!(read.concat(), data); +} + async fn write_full_small_strip(client: &ChunkIoClient, value: u8) -> Vec<(Bytes, Location)> { let mut frames = Vec::with_capacity(FRAMES_PER_MIB_STRIP); for _ in 0..FRAMES_PER_MIB_STRIP { @@ -269,7 +345,8 @@ async fn small_write_batches_concurrent_objects_and_reads_them_back() { .client .read_object(std::slice::from_ref(&location)) .await - .unwrap(), + .unwrap() + .concat(), data ); let chunk = stack.query_chunk(&location).await; @@ -348,7 +425,8 @@ async fn eight_closed_mirror_strips_become_one_durable_ec_strip_without_reread() .client .read_object(std::slice::from_ref(location)) .await - .unwrap(), + .unwrap() + .concat(), *expected ); } @@ -570,7 +648,8 @@ async fn manual_chunkdb_trigger_converts_closed_active_range_end_to_end() { .client .read_object(std::slice::from_ref(location)) .await - .unwrap(), + .unwrap() + .concat(), *expected ); } @@ -580,7 +659,8 @@ async fn manual_chunkdb_trigger_converts_closed_active_range_end_to_end() { .client .read_object(std::slice::from_ref(&appended)) .await - .unwrap(), + .unwrap() + .concat(), appended_data ); stack.client.shutdown_small_writes().await.unwrap(); @@ -693,7 +773,8 @@ async fn automatic_chunkdb_scan_converts_three_groups_and_preserves_tail() { .client .read_object(std::slice::from_ref(location)) .await - .unwrap(), + .unwrap() + .concat(), *expected ); } diff --git a/lib/crowdb-chunk-stream/src/production_chunk.rs b/lib/crowdb-chunk-stream/src/production_chunk.rs index 8ea70374c..3ed2c5278 100644 --- a/lib/crowdb-chunk-stream/src/production_chunk.rs +++ b/lib/crowdb-chunk-stream/src/production_chunk.rs @@ -535,6 +535,7 @@ impl StreamChunkStore for ProductionStreamChunkStore { length, ) .await + .map(|buffers| Bytes::from(buffers.concat())) .map_err(read_error) } diff --git a/lib/crowdb-common/rust/tests/ec_test.rs b/lib/crowdb-common/rust/tests/ec_test.rs index 182f2d374..7e5c0b1ac 100644 --- a/lib/crowdb-common/rust/tests/ec_test.rs +++ b/lib/crowdb-common/rust/tests/ec_test.rs @@ -202,6 +202,49 @@ fn encode_parity_from_shards_partial_2_of_4() { assert_eq!(recovered2[1], shard1); } +#[test] +fn partial_8_2_strip_recovers_two_mib_with_virtual_zero_shards() { + let scheme = EcScheme::new(8, 2); + let shard_bytes = 1024 * 1024; + let first: Vec = (0..shard_bytes).map(|index| (index % 251) as u8).collect(); + let second: Vec = (0..shard_bytes).map(|index| ((index * 7) % 253) as u8).collect(); + + let mut encoder = IncrementalParity::new(scheme).unwrap(); + encoder.push_partial(&first).unwrap(); + encoder.push_partial(&second).unwrap(); + let parity = encoder.finish_partial().unwrap(); + assert_eq!(parity.len(), 2); + assert!(parity.iter().all(|shard| shard.len() == shard_bytes)); + + let empty = vec![0; shard_bytes]; + let full_data = [ + &first[..], + &second[..], + &empty, + &empty, + &empty, + &empty, + &empty, + &empty, + ]; + assert_eq!(parity, encode_parity_from_shards(scheme, &full_data).unwrap()); + + for missing in [[0, 1], [0, 8], [1, 9]] { + let mut blocks: Vec>> = vec![Some(first.clone()), Some(second.clone())]; + blocks.extend((0..6).map(|_| Some(vec![0; shard_bytes]))); + blocks.extend(parity.iter().cloned().map(Some)); + for index in missing { + blocks[index] = None; + } + let recovered = decode(scheme, blocks).unwrap(); + assert_eq!(recovered[0], first); + assert_eq!(recovered[1], second); + assert!(recovered[2..8] + .iter() + .all(|shard| shard.iter().all(|byte| *byte == 0))); + } +} + /// Single short shard (1 of 4, < 1 MB). Encode parity → parity length /// matches shard length. #[test] diff --git a/lib/crowdb-diskio-client/src/client.rs b/lib/crowdb-diskio-client/src/client.rs index d13fed9cb..5b43033e9 100644 --- a/lib/crowdb-diskio-client/src/client.rs +++ b/lib/crowdb-diskio-client/src/client.rs @@ -3,6 +3,7 @@ //! Internal `DiskIO` wire transport. +use bytes::Bytes; use crowdb_common::RequestIdGen; use crowdb_protocol::diskio_fb::{ FBDiskFsyncRequest, FBDiskFsyncRequestArgs, FBDiskFsyncResponse, FBDiskReadRequest, @@ -397,10 +398,10 @@ impl WireClient { /// # Errors /// /// Returns `DiskioError::Rpc` if the response is missing or invalid. - pub async fn await_read_response(fut: CallFuture) -> WireResult<(DiskIoRetCode, Option>)> { + pub async fn await_read_response(fut: CallFuture) -> WireResult<(DiskIoRetCode, Option)> { let resp = fut.await.map_err(WireError::from)?; let code = parse_ret_code(&resp)?; - let data = resp.data.map(|b| b.bytes().to_vec()); + let data = resp.data.map(Bytes::from_owner); Ok((code, data)) } diff --git a/lib/crowdb-diskio-client/src/semantic.rs b/lib/crowdb-diskio-client/src/semantic.rs index 764865ddc..30def2064 100644 --- a/lib/crowdb-diskio-client/src/semantic.rs +++ b/lib/crowdb-diskio-client/src/semantic.rs @@ -1014,7 +1014,7 @@ impl DiskioClient { data.len() ))); } - Ok(Bytes::from(data)) + Ok(data) } fn classify_wire(error: WireError, operation: OperationKind) -> DiskioError { diff --git a/lib/crowdb-protocol/Cargo.toml b/lib/crowdb-protocol/Cargo.toml index c0280a60a..54cb0ee9d 100644 --- a/lib/crowdb-protocol/Cargo.toml +++ b/lib/crowdb-protocol/Cargo.toml @@ -24,6 +24,7 @@ serde = { version = "1", features = ["derive"] } serde_json = "1" bincode = "1" crc32fast = "1" +crc32c = "0.6.8" getrandom = "0.2" xxhash-rust = { version = "0.8", features = ["xxh64"] } sha2 = "0.10" diff --git a/lib/crowdb-protocol/src/fbs/iceberg.fbs b/lib/crowdb-protocol/src/fbs/iceberg.fbs index 3b64be5c3..c8a031774 100644 --- a/lib/crowdb-protocol/src/fbs/iceberg.fbs +++ b/lib/crowdb-protocol/src/fbs/iceberg.fbs @@ -171,6 +171,8 @@ table FBFileRecord { has_hint:bool; hint_offset:ulong; hint_length:ulong; + locations:[ubyte]; + etag:string; } table FBFileMapping { @@ -252,8 +254,11 @@ table FBMultipartPart { number:ushort; revision:ulong; owner:FBFileIdentity (required); - tree:FBFileTree (required); + tree:FBFileTree; modified_ms:ulong; + locations:[ubyte]; + etag:string; + stream_length:ulong; } table FBMultipartCreditMutation { @@ -426,6 +431,7 @@ table FBGcCandidate { next_root:ushort; frames:[FBGcFrame] (required); pending:FBFileChunkRoot; + next_location:ushort; } table FBFileWriteIntent { @@ -451,18 +457,6 @@ table FBGcPage { entries:[FBGcEntry] (required); } -table FBGcPin { - catalog:[ubyte] (required); - activation_epoch:ulong; - identity:[ubyte] (required); - head:FBTableHead (required); - principal:string (required); - expires_ms:ulong; - released:bool; - operator_pin:bool; - protects_uploads:bool; -} - table FBGcNode { continuation:FBPayloadReference; head:FBTableHead; @@ -476,7 +470,7 @@ table FBGcNode { complete:bool; } -union FBRecordValue { FBActiveCatalog, FBCatalogAuthority, FBManagementOperation, FBRetryRecord, FBNamespaceAuthority, FBNamespaceMapping, FBPayloadPage, FBRetryResult, FBNamespaceOperation, FBFileRecord, FBFileMapping, FBMultipartSession, FBMultipartPart, FBMultipartAdmission, FBTableHead, FBTableMapping, FBTableCommitOperation, FBTableCreateOperation, FBTableLifecycleOperation, FBTablePurgeTask, FBGcTask, FBGcCandidate, FBGcPage, FBGcPin, FBGcNode, FBFileWriteIntent } +union FBRecordValue { FBActiveCatalog, FBCatalogAuthority, FBManagementOperation, FBRetryRecord, FBNamespaceAuthority, FBNamespaceMapping, FBPayloadPage, FBRetryResult, FBNamespaceOperation, FBFileRecord, FBFileMapping, FBMultipartSession, FBMultipartPart, FBMultipartAdmission, FBTableHead, FBTableMapping, FBTableCommitOperation, FBTableCreateOperation, FBTableLifecycleOperation, FBTablePurgeTask, FBGcTask, FBGcCandidate, FBGcPage, FBGcNode, FBFileWriteIntent } table FBIcebergRecord { schema_version:ushort; diff --git a/lib/crowdb-protocol/src/frame.rs b/lib/crowdb-protocol/src/frame.rs index 8b9ec7729..60271b299 100644 --- a/lib/crowdb-protocol/src/frame.rs +++ b/lib/crowdb-protocol/src/frame.rs @@ -81,6 +81,99 @@ pub struct ParsedFrame<'a> { pub physical_length: usize, } +/// Verified frame metadata when the physical bytes span several buffers. +pub struct ParsedFrameViews { + pub header: FrameHeaderPrefix, + pub chunk_id: ChunkId, + pub physical_length: usize, +} + +/// Verify a frame over ordered views without assembling its payload. +/// +/// # Errors +/// Returns an error for an incomplete, malformed, or corrupt frame. +pub fn parse_frame_views( + views: &[&[u8]], + expected_chunk_id: ChunkId, +) -> Result { + let mut header_bytes = [0_u8; FRAME_HEADER_PREFIX_BYTES]; + copy_view_range(views, 0, &mut header_bytes)?; + let header = parse_header(&header_bytes)?; + let length = frame_length(header)?; + let available = views + .iter() + .fold(0_usize, |total, view| total.saturating_add(view.len())); + if available < length { + return Err(FrameError::Incomplete { + required_bytes: length, + }); + } + let footer_start = length - FRAME_FOOTER_BYTES; + let mut footer = [0_u8; FRAME_FOOTER_BYTES]; + copy_view_range(views, footer_start, &mut footer)?; + let expected_crc = u32::from_le_bytes( + footer[..4] + .try_into() + .map_err(|_| FrameError::InvalidRegionLength)?, + ); + let mut crc = 0_u32; + let mut cursor = 0_usize; + for view in views { + let included = footer_start.saturating_sub(cursor).min(view.len()); + if included > 0 { + crc = !crc32c::crc32c_append(!crc, &view[..included]); + } + cursor = cursor.saturating_add(view.len()); + if cursor >= footer_start { + break; + } + } + crc = !crc32c::crc32c_append(!crc, &footer[4..]); + if crc != expected_crc { + return Err(FrameError::ChecksumMismatch); + } + let chunk_id = ChunkId { + high: u64::from_be_bytes( + footer[4..12] + .try_into() + .map_err(|_| FrameError::InvalidRegionLength)?, + ), + low: u64::from_be_bytes( + footer[12..20] + .try_into() + .map_err(|_| FrameError::InvalidRegionLength)?, + ), + }; + if chunk_id != expected_chunk_id { + return Err(FrameError::ChunkIdMismatch); + } + Ok(ParsedFrameViews { + header, + chunk_id, + physical_length: length, + }) +} + +fn copy_view_range(views: &[&[u8]], start: usize, target: &mut [u8]) -> Result<(), FrameError> { + let required_bytes = start.saturating_add(target.len()); + let mut cursor = 0_usize; + let mut copied = 0_usize; + for view in views { + let view_end = cursor.saturating_add(view.len()); + if view_end > start && copied < target.len() { + let from = start.saturating_sub(cursor); + let count = (view.len() - from).min(target.len() - copied); + target[copied..copied + count].copy_from_slice(&view[from..from + count]); + copied += count; + } + cursor = view_end; + if copied == target.len() { + return Ok(()); + } + } + Err(FrameError::Incomplete { required_bytes }) +} + /// Encode a canonical v1 frame without header extensions. /// /// # Errors @@ -405,26 +498,16 @@ fn write_header_region(region: &mut [u8], header: FrameHeaderPrefix) { region[6..14].copy_from_slice(&header.write_time_ms.to_le_bytes()); } -fn crc32c(bytes: &[u8]) -> u32 { - crc32c_parts([bytes]) -} - fn crc32c_parts<'a>(parts: impl IntoIterator) -> u32 { let mut crc = 0_u32; for bytes in parts { - for byte in bytes { - crc ^= u32::from(*byte); - for _ in 0..8 { - crc = (crc >> 1) ^ (0x82F6_3B78 & (0_u32.wrapping_sub(crc & 1))); - } - } + // The frame format stores the raw seed-zero CRC, while this API applies + // initial and final XOR. Invert around each append to preserve the wire value. + crc = !crc32c::crc32c_append(!crc, bytes); } crc } fn crc32c_frame_parts(prefix: &[u8], chunk_id: &[u8]) -> u32 { - let mut bytes = Vec::with_capacity(prefix.len() + chunk_id.len()); - bytes.extend_from_slice(prefix); - bytes.extend_from_slice(chunk_id); - crc32c(&bytes) + crc32c_parts([prefix, chunk_id]) } diff --git a/lib/crowdb-protocol/tests/frame_test.rs b/lib/crowdb-protocol/tests/frame_test.rs index 8db92ea71..e130cbe6a 100644 --- a/lib/crowdb-protocol/tests/frame_test.rs +++ b/lib/crowdb-protocol/tests/frame_test.rs @@ -3,9 +3,9 @@ use crowdb_protocol::common::ChunkId; use crowdb_protocol::frame::{ - encode_frame, encode_frame_regions, encode_frames, merge_adjacent_locations, parse_frame, ChunkLocation, - FrameError, FrameMagic, FRAME_FOOTER_BYTES, FRAME_HEADER_PREFIX_BYTES, MAX_FRAME_BYTES, - MAX_FRAME_PAYLOAD_BYTES, + encode_frame, encode_frame_regions, encode_frames, merge_adjacent_locations, parse_frame, + parse_frame_views, ChunkLocation, FrameError, FrameMagic, FRAME_FOOTER_BYTES, FRAME_HEADER_PREFIX_BYTES, + MAX_FRAME_BYTES, MAX_FRAME_PAYLOAD_BYTES, }; const CHUNK: ChunkId = ChunkId { high: 7, low: 11 }; @@ -25,6 +25,55 @@ fn frame_matches_cross_language_vector() { ); } +#[test] +fn split_frame_views_verify_without_assembling_payload() { + let payload = vec![0x6b; 1024]; + let frame = encode_frame(FrameMagic::RepoSmallV1, CHUNK, &payload, 42).unwrap(); + let boundaries = [3, 11, 127, frame.len() - 9, frame.len() - 2]; + let mut views = Vec::new(); + let mut start = 0; + for end in boundaries.into_iter().chain(std::iter::once(frame.len())) { + views.push(&frame[start..end]); + start = end; + } + let parsed = parse_frame_views(&views, CHUNK).unwrap(); + assert_eq!(parsed.header.magic, FrameMagic::RepoSmallV1); + assert_eq!(parsed.physical_length, frame.len()); + + let mut corrupted = frame; + corrupted[127] ^= 1; + let mut start = 0; + let mut damaged = Vec::new(); + for end in boundaries.into_iter().chain(std::iter::once(corrupted.len())) { + damaged.push(&corrupted[start..end]); + start = end; + } + assert_eq!( + parse_frame_views(&damaged, CHUNK).err(), + Some(FrameError::ChecksumMismatch) + ); +} + +#[test] +fn frame_crc_matches_bitwise_reference_across_header_payload_and_chunk_id() { + for length in [0, 1, 17, 1024, MAX_FRAME_PAYLOAD_BYTES] { + let payload: Vec = (0..length) + .map(|index| u8::try_from(index % 251).unwrap()) + .collect(); + let frame = encode_frame(FrameMagic::RepoLargeV1, CHUNK, &payload, 42).unwrap(); + let footer = frame.len() - FRAME_FOOTER_BYTES; + let mut crc = 0_u32; + for byte in frame[..footer].iter().chain(frame[footer + 4..].iter()) { + crc ^= u32::from(*byte); + for _ in 0..8 { + crc = (crc >> 1) ^ (0x82F6_3B78 & (0_u32.wrapping_sub(crc & 1))); + } + } + assert_eq!(frame[footer..footer + 4], crc.to_le_bytes()); + assert_eq!(parse_frame(&frame, CHUNK).unwrap().payload, payload); + } +} + #[test] fn frame_round_trips_and_has_canonical_maximum_size() { let payload = vec![0xA5; MAX_FRAME_PAYLOAD_BYTES]; diff --git a/lib/crowdb-rpc/ffi/src/buffer.rs b/lib/crowdb-rpc/ffi/src/buffer.rs index 09c90f337..946c16354 100644 --- a/lib/crowdb-rpc/ffi/src/buffer.rs +++ b/lib/crowdb-rpc/ffi/src/buffer.rs @@ -123,6 +123,12 @@ impl std::fmt::Debug for Buffer { } impl Buffer { + /// Transfer a receive buffer out of its pool, preserving its allocation. + /// Returns false for pool types that cannot detach receive memory. + pub fn detach(&mut self) -> bool { + !self.handle.is_null() && unsafe { sys::crowdb_rpc_buffer_detach(self.handle) } + } + /// Allocate a new buffer from the pool with the given capacity. /// Returns `None` if the pool is exhausted. pub fn alloc(pool: &BufferPool, capacity: u32) -> Option { @@ -242,6 +248,12 @@ impl Drop for Buffer { } } +impl AsRef<[u8]> for Buffer { + fn as_ref(&self) -> &[u8] { + self.bytes() + } +} + // Buffer is Send (C++ buffers are thread-safe via atomic refcount). // Not Sync (the write path is single-threaded per buffer). unsafe impl Send for Buffer {} diff --git a/lib/crowdb-rpc/ffi/src/client.rs b/lib/crowdb-rpc/ffi/src/client.rs index 6c02a8114..6b47ea341 100644 --- a/lib/crowdb-rpc/ffi/src/client.rs +++ b/lib/crowdb-rpc/ffi/src/client.rs @@ -520,7 +520,7 @@ unsafe extern "C" fn on_complete_cb( Ok(Response { request_id, control: standalone_buffer(control), - data: standalone_buffer(data), + data: receive_data_buffer(data), }) } else { Err(RpcError::from_status(status)) @@ -538,6 +538,19 @@ fn standalone_buffer(handle: sys::crowdb_rpc_buffer_t) -> Option { Some(Buffer::from_bytes(&bytes)) } +fn receive_data_buffer(handle: sys::crowdb_rpc_buffer_t) -> Option { + if handle.is_null() { + return None; + } + let mut buffer = Buffer::from_raw(handle); + if buffer.detach() { + Some(buffer) + } else { + let copy = Buffer::from_bytes(buffer.bytes()); + Some(copy) + } +} + // Rust-side histograms (registered with the Rust MetricsRegistry::global(), // flushed in the rust section). fn e2e_histogram() -> std::sync::Arc { diff --git a/lib/crowdb-rpc/ffi/src/sys.rs b/lib/crowdb-rpc/ffi/src/sys.rs index 653eb1fe2..6b62f1505 100644 --- a/lib/crowdb-rpc/ffi/src/sys.rs +++ b/lib/crowdb-rpc/ffi/src/sys.rs @@ -122,6 +122,7 @@ extern "C" { pub fn crowdb_rpc_buffer_write(buf: crowdb_rpc_buffer_t, data: *const u8, len: u32); pub fn crowdb_rpc_buffer_data(buf: crowdb_rpc_buffer_t) -> *const u8; pub fn crowdb_rpc_buffer_len(buf: crowdb_rpc_buffer_t) -> u32; + pub fn crowdb_rpc_buffer_detach(buf: crowdb_rpc_buffer_t) -> bool; pub fn crowdb_rpc_buffer_ref(buf: crowdb_rpc_buffer_t) -> crowdb_rpc_buffer_t; pub fn crowdb_rpc_buffer_release(buf: crowdb_rpc_buffer_t); pub fn crowdb_rpc_buffer_create(data: *const u8, len: u32) -> crowdb_rpc_buffer_t; diff --git a/lib/crowdb-rpc/ffi/tests/buffer_test.rs b/lib/crowdb-rpc/ffi/tests/buffer_test.rs index 404bc7b69..859b43869 100644 --- a/lib/crowdb-rpc/ffi/tests/buffer_test.rs +++ b/lib/crowdb-rpc/ffi/tests/buffer_test.rs @@ -2,7 +2,21 @@ // Licensed under the Apache License, Version 2.0. use bytes::Bytes; -use crowdb_rpc_ffi::{Buffer, BufferChain, BufferChainError}; +use crowdb_rpc_ffi::{Buffer, BufferChain, BufferChainError, BufferPool}; + +#[test] +fn detached_read_buffer_survives_pool_and_keeps_pointer() { + let pool = BufferPool::new(1); + let mut buffer = pool.alloc_buffer(4).expect("pool allocation"); + buffer.write(b"read"); + let original = buffer.bytes().as_ptr(); + assert!(buffer.detach()); + drop(pool); + + let bytes = Bytes::from_owner(buffer); + assert_eq!(bytes.as_ptr(), original); + assert_eq!(&bytes[..], b"read"); +} #[test] fn owned_bytes_buffer_keeps_the_original_allocation() { diff --git a/lib/crowdb-rpc/include/crowdb-rpc/buffer.h b/lib/crowdb-rpc/include/crowdb-rpc/buffer.h index ca214a00a..a83207c30 100644 --- a/lib/crowdb-rpc/include/crowdb-rpc/buffer.h +++ b/lib/crowdb-rpc/include/crowdb-rpc/buffer.h @@ -69,6 +69,14 @@ class BufferPool // Called by Buffer::release when ref == 0. Returns the buffer to the // free list for reuse. virtual void recycle(Buffer *buf) = 0; + + // Transfer an immutable receive buffer out of the pool without copying. + // The caller must hold the only live reference before detaching. + virtual bool detach(Buffer *buf) + { + (void)buf; + return false; + } }; // SystemBufferPool: direct heap allocation (posix_memalign + new/delete), @@ -85,6 +93,7 @@ class SystemBufferPool : public BufferPool Buffer *alloc(uint32_t capacity) override; void recycle(Buffer *buf) override; + bool detach(Buffer *buf) override; private: uint32_t max_buffers_; diff --git a/lib/crowdb-rpc/include/crowdb-rpc/c_api.h b/lib/crowdb-rpc/include/crowdb-rpc/c_api.h index 9735e6ffa..450f4db1b 100644 --- a/lib/crowdb-rpc/include/crowdb-rpc/c_api.h +++ b/lib/crowdb-rpc/include/crowdb-rpc/c_api.h @@ -37,6 +37,7 @@ crowdb_rpc_buffer_t crowdb_rpc_buffer_alloc(crowdb_rpc_pool_t pool, uint32_t cap void crowdb_rpc_buffer_write(crowdb_rpc_buffer_t buf, const uint8_t *data, uint32_t len); const uint8_t *crowdb_rpc_buffer_data(crowdb_rpc_buffer_t buf); uint32_t crowdb_rpc_buffer_len(crowdb_rpc_buffer_t buf); +bool crowdb_rpc_buffer_detach(crowdb_rpc_buffer_t buf); crowdb_rpc_buffer_t crowdb_rpc_buffer_ref(crowdb_rpc_buffer_t buf); void crowdb_rpc_buffer_release(crowdb_rpc_buffer_t buf); // Create a standalone buffer (not pool-allocated) from raw bytes. The diff --git a/lib/crowdb-rpc/src/buffer.cpp b/lib/crowdb-rpc/src/buffer.cpp index 4d2f67858..545317569 100644 --- a/lib/crowdb-rpc/src/buffer.cpp +++ b/lib/crowdb-rpc/src/buffer.cpp @@ -101,4 +101,14 @@ void SystemBufferPool::recycle(Buffer *buf) outstanding_.fetch_sub(1, std::memory_order_relaxed); } +bool SystemBufferPool::detach(Buffer *buf) +{ + if (buf == nullptr || buf->pool != this || buf->ref->load(std::memory_order_acquire) != 1) { + return false; + } + buf->pool = nullptr; + outstanding_.fetch_sub(1, std::memory_order_relaxed); + return true; +} + } // namespace crowdb::rpc diff --git a/lib/crowdb-rpc/src/c_api.cpp b/lib/crowdb-rpc/src/c_api.cpp index 28b2b811c..cb8cd19e0 100644 --- a/lib/crowdb-rpc/src/c_api.cpp +++ b/lib/crowdb-rpc/src/c_api.cpp @@ -110,6 +110,20 @@ uint32_t crowdb_rpc_buffer_len(crowdb_rpc_buffer_t buf) } } +bool crowdb_rpc_buffer_detach(crowdb_rpc_buffer_t buf) +{ + try { + if (buf == nullptr || buf->buf == nullptr) { + return false; + } + auto *buffer = buf->buf; + return buffer->pool == nullptr || buffer->pool->detach(buffer); + } + catch (...) { + return false; + } +} + crowdb_rpc_buffer_t crowdb_rpc_buffer_ref(crowdb_rpc_buffer_t buf) { try { diff --git a/lib/crowdb-rpc/tests/buffer_test.cpp b/lib/crowdb-rpc/tests/buffer_test.cpp index 30860defe..dab729c92 100644 --- a/lib/crowdb-rpc/tests/buffer_test.cpp +++ b/lib/crowdb-rpc/tests/buffer_test.cpp @@ -6,6 +6,7 @@ #include #include +#include using crowdb::rpc::Buffer; using crowdb::rpc::BufferType; @@ -82,3 +83,23 @@ TEST(BufferTest, ExactCapacityNoBucketing) EXPECT_EQ(buf->capacity, 200U); // exact, not bucketed to 256 buf->release(); } + +TEST(BufferTest, DetachedReceiveBufferOutlivesPoolWithoutCopy) +{ + auto pool = std::make_unique(1); + Buffer *buf = pool->alloc(4); + ASSERT_NE(buf, nullptr); + const uint8_t data[] = {1, 2, 3, 4}; + buf->write(data, 4); + uint8_t *original = buf->data; + + EXPECT_TRUE(pool->detach(buf)); + EXPECT_EQ(buf->data, original); + Buffer *next = pool->alloc(4); + ASSERT_NE(next, nullptr); + next->release(); + pool.reset(); + + EXPECT_EQ(std::memcmp(buf->data, data, 4), 0); + buf->release(); +} From 1665aef4930f6860c1a12b353c0a39757ceb81dc Mon Sep 17 00:00:00 2001 From: Gian Date: Mon, 28 Sep 2026 20:46:49 +0800 Subject: [PATCH 03/20] Reduce Iceberg multipart catalog reads --- .../src/iceberg/file_http/multipart.rs | 26 ++--------- doc/working/plan-access-streaming-io.md | 10 +++++ .../src/file/multipart_repository/parts.rs | 44 ++++++++++++++++++- .../tests/common/store.rs | 2 + .../tests/multipart_publication_test.rs | 14 ++++++ 5 files changed, 72 insertions(+), 24 deletions(-) diff --git a/app/crowdb-access-server/src/iceberg/file_http/multipart.rs b/app/crowdb-access-server/src/iceberg/file_http/multipart.rs index 792bec8e1..d8f7512ab 100644 --- a/app/crowdb-access-server/src/iceberg/file_http/multipart.rs +++ b/app/crowdb-access-server/src/iceberg/file_http/multipart.rs @@ -230,7 +230,7 @@ impl FileHttp { }; let before = self .multipart - .part(&session, part_number) + .part_for_upload(&session, part_number) .await .map_err(catalog_error)?; let part = MultipartPart { @@ -242,20 +242,12 @@ impl FileHttp { tree, stream, }; - if !self - .multipart - .reserve_part(&session, &part, now_ms) - .await - .map_err(catalog_error)? - { - return Err(FileS3ErrorCode::SlowDown); - } let pending = self .multipart - .load(session.context, session.upload) + .reserve_part_state(&session, &part, now_ms) .await .map_err(catalog_error)? - .ok_or(FileS3ErrorCode::InternalError)?; + .ok_or(FileS3ErrorCode::SlowDown)?; if !self .multipart .settle_part(&pending) @@ -264,18 +256,6 @@ impl FileHttp { { return Err(FileS3ErrorCode::SlowDown); } - let settled = self - .multipart - .load(session.context, session.upload) - .await - .map_err(catalog_error)? - .ok_or(FileS3ErrorCode::InternalError)?; - let part = self - .multipart - .part(&settled, part_number) - .await - .map_err(catalog_error)? - .ok_or(FileS3ErrorCode::InternalError)?; MultipartResponses::upload_part(&part) .map(|response| response.map(IcebergBody::new)) .map_err(|_| FileS3ErrorCode::InternalError) diff --git a/doc/working/plan-access-streaming-io.md b/doc/working/plan-access-streaming-io.md index 2698148ad..ac054839e 100644 --- a/doc/working/plan-access-streaming-io.md +++ b/doc/working/plan-access-streaming-io.md @@ -165,6 +165,16 @@ while keeping secrets in the environment. Offline `exec:java` reached the Java program with all dependencies present. The ChunkDB restart fixture now waits for a usable registry read instead of sleeping for three seconds; its focused and full reader E2E suites pass. +- The Iceberg UploadPart response can use the part value just successfully + settled; reloading the session and part added four catalog reads. A separate + upload lookup now performs one part read and leaves the session CAS to reject + stale snapshots, removing two more catalog reads. The reserve CAS now + returns its pending session to settlement, removing another reload. The + focused repository test confirms exactly one read for the part lookup. + The remaining session + reservation/settlement CAS operations still serialize different part numbers; + removing them needs a completion snapshot that keeps selected overwritten + parts reachable when UploadPart races Complete. ## Files diff --git a/lib/crowdb-access-iceberg/src/file/multipart_repository/parts.rs b/lib/crowdb-access-iceberg/src/file/multipart_repository/parts.rs index 927e99cf8..685b67c40 100644 --- a/lib/crowdb-access-iceberg/src/file/multipart_repository/parts.rs +++ b/lib/crowdb-access-iceberg/src/file/multipart_repository/parts.rs @@ -8,6 +8,36 @@ use crate::record::StorageRecord; use super::{check_live, increment, MultipartRepository}; impl MultipartRepository { + /// Reads the current part for an `UploadPart` replacement. The later session + /// compare-and-swap rejects a stale snapshot before the part becomes visible. + /// # Errors + /// Rejects invalid part numbers, phases and corrupt stored parts. + pub async fn part_for_upload( + &self, + session: &MultipartSession, + number: u16, + ) -> Result, CatalogError> { + session.validate()?; + if session.phase != MultipartPhase::Open || session.pending.is_some() { + return Err(CatalogError::Busy); + } + if number == 0 || number > session.limits.max_parts { + return Err(ValidationError::Record.into()); + } + let mut suffix = session.upload.as_bytes().to_vec(); + suffix.extend_from_slice(&number.to_be_bytes()); + let key = IcebergKey::Catalog { + catalog: session.context.catalog, + scope: CatalogScope::MultipartPart, + suffix, + }; + let part = self.read_part(&key).await?; + if let Some(part) = &part { + part.validate_for(session)?; + } + Ok(part) + } + /// Reads a committed part only while the supplied session snapshot stays current. /// # Errors /// Rejects unresolved mutations, stale snapshots, invalid numbers and corrupt parts. @@ -52,6 +82,18 @@ impl MultipartRepository { part: &MultipartPart, now_ms: u64, ) -> Result { + Ok(self.reserve_part_state(session, part, now_ms).await?.is_some()) + } + + /// Reserves a part and returns the exact pending session written by the CAS. + /// # Errors + /// Rejects stale revisions, expired sessions, exhausted limits and pending mutations. + pub async fn reserve_part_state( + &self, + session: &MultipartSession, + part: &MultipartPart, + now_ms: u64, + ) -> Result, CatalogError> { check_live(session, now_ms)?; let mut after = part.clone(); after.modified_ms = now_ms; @@ -79,7 +121,7 @@ impl MultipartRepository { .and_then(|bytes| bytes.checked_add(part.length())) .ok_or(ValidationError::Record)?; next.pending = Some(MultipartPartMutation { before, after }); - self.exchange(session, &next).await + Ok(self.exchange(session, &next).await?.then_some(next)) } /// Helps one durable part mutation and then clears its session fence. diff --git a/lib/crowdb-access-iceberg/tests/common/store.rs b/lib/crowdb-access-iceberg/tests/common/store.rs index 87ec5f440..37a81c1ce 100644 --- a/lib/crowdb-access-iceberg/tests/common/store.rs +++ b/lib/crowdb-access-iceberg/tests/common/store.rs @@ -18,6 +18,7 @@ pub struct TestStore { #[allow(dead_code)] pub gc_delete_reply_loss: AtomicBool, pub writes: AtomicUsize, + pub reads: AtomicUsize, pub fencing_delay_ms: AtomicUsize, pub fencing_barrier: Option>, pub fencing_visits: AtomicUsize, @@ -111,6 +112,7 @@ impl TestStore { #[async_trait] impl CatalogStore for TestStore { async fn get(&self, key: &[u8]) -> Result, StoreError> { + self.reads.fetch_add(1, Ordering::SeqCst); Ok(self.values.load().get(key).cloned()) } diff --git a/lib/crowdb-access-iceberg/tests/multipart_publication_test.rs b/lib/crowdb-access-iceberg/tests/multipart_publication_test.rs index 8f88ffd00..527178857 100644 --- a/lib/crowdb-access-iceberg/tests/multipart_publication_test.rs +++ b/lib/crowdb-access-iceberg/tests/multipart_publication_test.rs @@ -93,6 +93,20 @@ async fn load(repository: &MultipartRepository, session: &MultipartSession) -> M .unwrap() } +#[tokio::test] +async fn upload_part_lookup_uses_one_catalog_read_before_the_session_cas() { + let fixture = file::TestFile::new(common::TestStore::default()).await; + let repository = MultipartRepository::new(fixture.store.clone()); + let mut session = fixtures::session(); + session.context = fixture.context; + session.owner.table = fixture.table; + session.location = fixture.table.file("data/part.parquet").unwrap(); + repository.begin(&session, 100).await.unwrap(); + let before = fixture.store.reads.load(Ordering::SeqCst); + assert!(repository.part_for_upload(&session, 1).await.unwrap().is_none()); + assert_eq!(fixture.store.reads.load(Ordering::SeqCst) - before, 1); +} + #[tokio::test] async fn streamed_parts_complete_by_composing_locations_and_saved_md5_only() { let fixture = file::TestFile::new(common::TestStore::default()).await; From 96c081bd9023baf7457377ec05623e2b1ac13e16 Mon Sep 17 00:00:00 2001 From: Gian Date: Mon, 28 Sep 2026 20:55:05 +0800 Subject: [PATCH 04/20] Verify large Iceberg multipart replay after restart --- .../tests/iceberg_file_http_test.rs | 107 +++++++++++++++++- doc/working/plan-access-streaming-io.md | 5 + 2 files changed, 110 insertions(+), 2 deletions(-) diff --git a/app/crowdb-access-server/tests/iceberg_file_http_test.rs b/app/crowdb-access-server/tests/iceberg_file_http_test.rs index 18135b7d1..96e5973cc 100644 --- a/app/crowdb-access-server/tests/iceberg_file_http_test.rs +++ b/app/crowdb-access-server/tests/iceberg_file_http_test.rs @@ -24,6 +24,7 @@ use crowdb_access_iceberg::operation::{ManagementAction, ManagementRequest, Requ use crowdb_access_iceberg::wire::BearerAuthenticator; use md5::{Digest, Md5}; use reqwest::{Client, Method}; +use std::fmt::Write as _; #[path = "common/iceberg_signed_file.rs"] mod signed; @@ -67,6 +68,19 @@ async fn setup_with_bounds( process::TestIcebergProcess, TestFileClient, TableLocation, +) { + setup_with_bounds_and_file_limit(bounds, 16 * 1024 * 1024, 64 * 1024 * 1024).await +} + +async fn setup_with_bounds_and_file_limit( + bounds: ClearBounds, + max_request_bytes: u64, + max_file_bytes: u64, +) -> ( + TestIcebergStack, + process::TestIcebergProcess, + TestFileClient, + TableLocation, ) { let stack = TestIcebergStack::start().await; let repository = CatalogRepository::new(stack.store().await, bounds).unwrap(); @@ -138,8 +152,8 @@ async fn setup_with_bounds( FileOperation::AbortMultipart, ]) .unwrap(), - max_request_bytes: 16 * 1024 * 1024, - max_file_bytes: 64 * 1024 * 1024, + max_request_bytes, + max_file_bytes, }) .unwrap(); let client = TestFileClient { @@ -326,6 +340,95 @@ async fn signed_standard_put_get_and_multipart_publish_unbound_files() { assert_eq!(get.bytes().await.unwrap().as_ref(), document); } +#[tokio::test(flavor = "multi_thread", worker_threads = 4)] +async fn multipart_100_mib_survives_restart_and_complete_replay() { + const PART_BYTES: usize = 5 * 1024 * 1024; + const PART_COUNT: usize = 20; + let (stack, process, mut client, table) = setup_with_bounds_and_file_limit( + ClearBounds { + delegated_access_ms: 900_000, + ..ClearBounds::default() + }, + 128 * 1024 * 1024, + 128 * 1024 * 1024, + ) + .await; + let object = path(table, "data/large-multipart.parquet"); + let created = client.send(Method::POST, &object, "uploads=", b"", false).await; + assert_eq!(created.status(), 200, "{}", created.text().await.unwrap()); + let created = created.text().await.unwrap(); + let upload = created + .split_once("") + .unwrap() + .1 + .split_once("") + .unwrap() + .0; + let mut manifest = String::from( + "", + ); + let mut composite = Md5::new(); + for number in 1..=PART_COUNT { + let bytes = vec![u8::try_from(number).unwrap(); PART_BYTES]; + let query = format!("partNumber={number}&uploadId={upload}"); + let response = client.send(Method::PUT, &object, &query, &bytes, true).await; + assert_eq!(response.status(), 200, "{}", response.text().await.unwrap()); + let etag = response.headers()["etag"].to_str().unwrap(); + composite.update(Md5::digest(&bytes)); + write!( + manifest, + "{etag}{number}" + ) + .unwrap(); + } + manifest.push_str(""); + let mut expected_etag = String::new(); + for byte in composite.finalize() { + write!(expected_etag, "{byte:02x}").unwrap(); + } + expected_etag.push_str("-20"); + let query = format!("uploadId={upload}"); + let completed = client + .send(Method::POST, &object, &query, manifest.as_bytes(), false) + .await; + assert_eq!(completed.status(), 200); + let completed = completed.text().await.unwrap(); + assert!( + completed.contains(""), + "{completed}" + ); + let repository = FileRepository::new(stack.store().await); + let record = repository + .load( + client.credentials.grant().context, + &table.file("data/large-multipart.parquet").unwrap(), + ) + .await + .unwrap() + .unwrap(); + assert_eq!(record.length, (PART_BYTES * PART_COUNT) as u64); + assert_eq!(record.content.etag(), Some(expected_etag.as_str())); + assert_eq!( + record.content.locations(record.length).unwrap().unwrap().len(), + PART_COUNT + ); + + drop(process); + let restarted = process::TestIcebergProcess::start(&stack.cluster.mgmt_endpoints).await; + client.address = restarted.address; + let replay = client + .send(Method::POST, &object, &query, manifest.as_bytes(), false) + .await; + assert_eq!(replay.status(), 200, "{}", replay.text().await.unwrap()); + let response = client.send(Method::GET, &object, "", b"", false).await; + assert_eq!(response.status(), 200, "{}", response.text().await.unwrap()); + let bytes = response.bytes().await.unwrap(); + assert_eq!(bytes.len(), PART_BYTES * PART_COUNT); + for (index, part) in bytes.chunks_exact(PART_BYTES).enumerate() { + assert!(part.iter().all(|byte| *byte == u8::try_from(index + 1).unwrap())); + } +} + #[tokio::test(flavor = "multi_thread", worker_threads = 4)] #[ignore = "requires Maven and the pinned Apache Iceberg Java dependencies"] async fn official_java_s3_fileio_uploads_and_reads_native_files() { diff --git a/doc/working/plan-access-streaming-io.md b/doc/working/plan-access-streaming-io.md index ac054839e..dceeb7f1a 100644 --- a/doc/working/plan-access-streaming-io.md +++ b/doc/working/plan-access-streaming-io.md @@ -175,6 +175,11 @@ while keeping secrets in the environment. reservation/settlement CAS operations still serialize different part numbers; removing them needs a completion snapshot that keeps selected overwritten parts reachable when UploadPart races Complete. +- A native 100 MiB upload with twenty 5 MiB parts completed into one immutable + descriptor with twenty locations and the expected composite MD5 ETag. A new + Iceberg listener accepted Complete replay and returned the full verified + object. The focused end-to-end test passed with a 128 MiB request grant; + the ordinary 16 MiB test grant correctly rejects a 100 MiB full GET. ## Files From b5730e0d25f122b86b69188de315d804f5e0bd5e Mon Sep 17 00:00:00 2001 From: Gian Date: Mon, 28 Sep 2026 21:01:26 +0800 Subject: [PATCH 05/20] Preserve streamed file identity across publication replay --- .../tests/common/iceberg_commit_fault.rs | 5 +++ .../tests/common/iceberg_file_recovery.rs | 2 - doc/working/plan-access-streaming-io.md | 5 +++ .../src/file/repository.rs | 2 +- .../tests/file_repository_test.rs | 41 ++++++++++++++++++- 5 files changed, 51 insertions(+), 4 deletions(-) diff --git a/app/crowdb-access-server/tests/common/iceberg_commit_fault.rs b/app/crowdb-access-server/tests/common/iceberg_commit_fault.rs index 60c8a01c4..3fe5b66e4 100644 --- a/app/crowdb-access-server/tests/common/iceberg_commit_fault.rs +++ b/app/crowdb-access-server/tests/common/iceberg_commit_fault.rs @@ -12,6 +12,7 @@ use crowdb_access_iceberg::{ namespace::{ChildScan, NamespaceStore}, record::StorageRecord, }; +use crowdb_chunk_client::ChunkIoClient; use crowdb_chunk_kv_client::MultiScanPage; use crowdb_protocol::chunk_kv::ClientRequestId; @@ -115,6 +116,10 @@ pub struct TestCommitBlocks { #[async_trait] impl FileBlockStore for TestCommitBlocks { + fn stream_client(&self) -> Option<&ChunkIoClient> { + self.inner.stream_client() + } + async fn put(&self, owner: FileIdentity, height: u8, bytes: &[u8]) -> Result { let index = self.boundary.before("file-block").await; let result = self.inner.put(owner, height, bytes).await; diff --git a/app/crowdb-access-server/tests/common/iceberg_file_recovery.rs b/app/crowdb-access-server/tests/common/iceberg_file_recovery.rs index b73c94e07..82f320257 100644 --- a/app/crowdb-access-server/tests/common/iceberg_file_recovery.rs +++ b/app/crowdb-access-server/tests/common/iceberg_file_recovery.rs @@ -57,8 +57,6 @@ pub async fn run() { assert!(labels.contains("multipart-Completing")); assert!(labels.contains("multipart-Publishing")); assert!(labels.contains("multipart-Published")); - } else { - assert!(labels.contains("file-block")); } } } diff --git a/doc/working/plan-access-streaming-io.md b/doc/working/plan-access-streaming-io.md index dceeb7f1a..9ce8618c6 100644 --- a/doc/working/plan-access-streaming-io.md +++ b/doc/working/plan-access-streaming-io.md @@ -180,6 +180,11 @@ while keeping secrets in the environment. Iceberg listener accepted Complete replay and returned the full verified object. The focused end-to-end test passed with a 128 MiB request grant; the ordinary 16 MiB test grant correctly rejects a 100 MiB full GET. +- The native publication crash matrix now uses the streamed Chunk client through + its fault-injection wrapper. Every PUT and MPU catalog write boundary passed + before/after listener loss and replay. Immutable PUT retries compare the + saved MD5 ETag and length, so a retry that allocated a different chunk still + resolves to the first published file; different ETags remain conflicts. ## Files diff --git a/lib/crowdb-access-iceberg/src/file/repository.rs b/lib/crowdb-access-iceberg/src/file/repository.rs index 99b4c413a..2e549f16c 100644 --- a/lib/crowdb-access-iceberg/src/file/repository.rs +++ b/lib/crowdb-access-iceberg/src/file/repository.rs @@ -202,7 +202,7 @@ fn compatible(existing: FileRecord, candidate: &FileRecord) -> Result Date: Mon, 28 Sep 2026 21:12:28 +0800 Subject: [PATCH 06/20] Snapshot streamed multipart selections at Complete --- .../src/iceberg/file_selection.rs | 37 +++++- doc/working/plan-access-streaming-io.md | 7 ++ lib/crowdb-access-iceberg/src/file.rs | 2 +- .../file/multipart_repository/completion.rs | 19 +++ .../file/multipart_repository/publication.rs | 39 ++++-- .../src/file/multipart_selection.rs | 115 ++++++++++++++++-- .../tests/multipart_publication_test.rs | 48 ++++++-- 7 files changed, 236 insertions(+), 31 deletions(-) diff --git a/app/crowdb-access-server/src/iceberg/file_selection.rs b/app/crowdb-access-server/src/iceberg/file_selection.rs index 51e46e778..2bb6c4cf5 100644 --- a/app/crowdb-access-server/src/iceberg/file_selection.rs +++ b/app/crowdb-access-server/src/iceberg/file_selection.rs @@ -1,5 +1,7 @@ use crowdb_access_iceberg::catalog::CatalogError; -use crowdb_access_iceberg::file::{MultipartRepository, MultipartSelection, MultipartSession, SelectedPart}; +use crowdb_access_iceberg::file::{ + MultipartPhase, MultipartRepository, MultipartSelection, MultipartSession, SelectedPart, +}; use quick_xml::events::Event; use quick_xml::Reader; @@ -134,12 +136,30 @@ impl CompleteSelection { if self.parts.len() > usize::from(session.limits.max_parts) { return Err(CompleteResolveError::InvalidPart); } + if session.completion.is_some() { + let frozen = repository.load_selection(session).await?; + if let Some(snapshots) = frozen.snapshots() { + if self.parts.len() != snapshots.len() + || self.parts.iter().zip(frozen.parts().iter().zip(snapshots)).any( + |(requested, (selected, snapshot))| { + requested.number != selected.number || requested.etag != snapshot.etag + }, + ) + { + return Err(CompleteResolveError::InvalidPart); + } + return Ok(frozen); + } + } let mut selected = Vec::with_capacity(self.parts.len()); + let mut parts = Vec::with_capacity(self.parts.len()); for (index, requested) in self.parts.iter().enumerate() { - let part = repository - .part(session, requested.number) - .await? - .ok_or(CompleteResolveError::InvalidPart)?; + let part = if session.phase == MultipartPhase::Open { + repository.part_for_upload(session, requested.number).await? + } else { + repository.part(session, requested.number).await? + } + .ok_or(CompleteResolveError::InvalidPart)?; if part.etag() != requested.etag { return Err(CompleteResolveError::InvalidPart); } @@ -151,8 +171,13 @@ impl CompleteSelection { revision: part.revision, digest: part.selection_digest(), }); + parts.push(part); + } + if parts.iter().all(|part| part.stream.is_some()) { + MultipartSelection::with_stream_parts(&parts).map_err(|_| CompleteResolveError::InvalidPart) + } else { + MultipartSelection::new(selected).map_err(|_| CompleteResolveError::InvalidPart) } - MultipartSelection::new(selected).map_err(|_| CompleteResolveError::InvalidPart) } } diff --git a/doc/working/plan-access-streaming-io.md b/doc/working/plan-access-streaming-io.md index 9ce8618c6..e01229b39 100644 --- a/doc/working/plan-access-streaming-io.md +++ b/doc/working/plan-access-streaming-io.md @@ -185,6 +185,13 @@ while keeping secrets in the environment. before/after listener loss and replay. Immutable PUT retries compare the saved MD5 ETag and length, so a retry that allocated a different chunk still resolves to the first published file; different ETags remain conflicts. +- New streamed Complete selections now persist each selected part's length, + ETag and exact location bytes in the existing selection payload. Streamed + publication consumes that snapshot without rereading part records. A focused + test replaces a selected part record after freeze and still publishes the + originally selected locations and composite ETag; the 100 MiB replay test + passes with the new selection format. The old selection format remains + readable for legacy multipart recovery. ## Files diff --git a/lib/crowdb-access-iceberg/src/file.rs b/lib/crowdb-access-iceberg/src/file.rs index ef4fd3618..74aa2bd3a 100644 --- a/lib/crowdb-access-iceberg/src/file.rs +++ b/lib/crowdb-access-iceberg/src/file.rs @@ -73,7 +73,7 @@ pub use multipart_recovery::{ MultipartRecoveryStore, }; pub use multipart_repository::{MultipartRepository, MultipartWorkError}; -pub use multipart_selection::{MultipartSelection, SelectedPart}; +pub use multipart_selection::{MultipartSelection, SelectedPart, SelectedStreamPart}; #[cfg(feature = "test-util")] pub use parquet::{ read_parquet_integer_column_for_tests, read_parquet_nullable_integer_column_for_tests, diff --git a/lib/crowdb-access-iceberg/src/file/multipart_repository/completion.rs b/lib/crowdb-access-iceberg/src/file/multipart_repository/completion.rs index ec5034f6a..c001a1b04 100644 --- a/lib/crowdb-access-iceberg/src/file/multipart_repository/completion.rs +++ b/lib/crowdb-access-iceberg/src/file/multipart_repository/completion.rs @@ -21,6 +21,25 @@ pub enum MultipartWorkError { } impl MultipartRepository { + /// Loads the durable Complete selection, including any streamed part snapshots. + /// # Errors + /// Rejects missing, corrupt or foreign selection payloads. + pub async fn load_selection( + &self, + session: &MultipartSession, + ) -> Result { + session.validate()?; + let completion = session.completion.as_ref().ok_or(ValidationError::Record)?; + let bytes = PayloadStore::new(self.store.clone()) + .get(&completion.selection) + .await?; + let selection = MultipartSelection::decode(&bytes)?; + if selection.count() != completion.selected_parts { + return Err(ValidationError::Record.into()); + } + Ok(selection) + } + /// Freezes a caller-selected part revision list; byte work verifies each selected part. /// # Errors /// Rejects expired sessions, unresolved mutations and invalid selection bounds. diff --git a/lib/crowdb-access-iceberg/src/file/multipart_repository/publication.rs b/lib/crowdb-access-iceberg/src/file/multipart_repository/publication.rs index 52440f941..943407a69 100644 --- a/lib/crowdb-access-iceberg/src/file/multipart_repository/publication.rs +++ b/lib/crowdb-access-iceberg/src/file/multipart_repository/publication.rs @@ -2,7 +2,7 @@ use crate::catalog::CatalogError; use crate::error::ValidationError; use crate::file::{ file_key, ContentFormat, FileContent, FileKind, FileRecord, FileRepository, FileTree, MultipartPhase, - MultipartSelection, MultipartSession, + MultipartSelection, MultipartSession, MultipartStreamPart, SelectedPart, SelectedStreamPart, }; use crate::operation::PayloadStore; use crate::record::StorageRecord; @@ -40,15 +40,9 @@ impl MultipartRepository { let mut locations = Vec::::new(); let mut length = 0_u64; let mut md5 = Md5::new(); - for selected in selection.parts() { - let part = self - .part(session, selected.number) - .await? - .ok_or(ValidationError::Record)?; - if part.revision != selected.revision || part.selection_digest() != selected.digest { - return Err(ValidationError::Record.into()); - } - let Some(stream) = &part.stream else { + for (index, selected) in selection.parts().iter().enumerate() { + let snapshot = selection.snapshots().and_then(|snapshots| snapshots.get(index)); + let Some(stream) = self.selected_stream(session, selected, snapshot).await? else { return Ok(None); }; let etag = stream.content.etag().ok_or(ValidationError::Record)?; @@ -117,6 +111,31 @@ impl MultipartRepository { completion.publication = Some(publication); Ok(Some(self.exchange(session, &next).await?)) } + + async fn selected_stream( + &self, + session: &MultipartSession, + selected: &SelectedPart, + snapshot: Option<&SelectedStreamPart>, + ) -> Result, CatalogError> { + if let Some(snapshot) = snapshot { + return Ok(Some(MultipartStreamPart { + length: snapshot.length, + content: FileContent::Locations { + bytes: snapshot.bytes.clone(), + etag: snapshot.etag.clone(), + }, + })); + } + let part = self + .part(session, selected.number) + .await? + .ok_or(ValidationError::Record)?; + if part.revision != selected.revision || part.selection_digest() != selected.digest { + return Err(ValidationError::Record.into()); + } + Ok(part.stream) + } /// Freezes a semantically sealed record; callers must validate its canonical format first. /// # Errors /// Rejects incomplete assembly, changed byte identity, expiry and invalid file records. diff --git a/lib/crowdb-access-iceberg/src/file/multipart_selection.rs b/lib/crowdb-access-iceberg/src/file/multipart_selection.rs index 5d8a6597d..bdddcc311 100644 --- a/lib/crowdb-access-iceberg/src/file/multipart_selection.rs +++ b/lib/crowdb-access-iceberg/src/file/multipart_selection.rs @@ -1,6 +1,11 @@ use crate::error::ValidationError; +use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; -const MAGIC: &[u8; 5] = b"ICMS\x01"; +use super::{FileContent, MultipartPart}; + +const MAGIC_V1: &[u8; 5] = b"ICMS\x01"; +const MAGIC_V2: &[u8; 5] = b"ICMS\x02"; const ENTRY_BYTES: usize = 42; #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -14,6 +19,14 @@ pub struct SelectedPart { pub struct MultipartSelection { parts: Vec, count: u16, + snapshots: Option>, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct SelectedStreamPart { + pub length: u64, + pub bytes: Vec, + pub etag: String, } impl MultipartSelection { @@ -31,7 +44,44 @@ impl MultipartSelection { previous = part.number; } let count = u16::try_from(parts.len()).map_err(|_| ValidationError::Record)?; - Ok(Self { parts, count }) + Ok(Self { + parts, + count, + snapshots: None, + }) + } + + /// Captures complete streamed part locations at Complete so later part + /// replacements cannot change the selected file or its GC references. + /// # Errors + /// Rejects mixed legacy parts, invalid descriptors or incoherent digests. + pub fn with_stream_parts(parts: &[MultipartPart]) -> Result { + let selected = parts + .iter() + .map(|part| SelectedPart { + number: part.number, + revision: part.revision, + digest: part.selection_digest(), + }) + .collect(); + let mut selection = Self::new(selected)?; + let snapshots = parts + .iter() + .map(|part| { + let stream = part.stream.as_ref().ok_or(ValidationError::Record)?; + let FileContent::Locations { bytes, etag } = &stream.content else { + return Err(ValidationError::Record); + }; + Ok(SelectedStreamPart { + length: stream.length, + bytes: bytes.clone(), + etag: etag.clone(), + }) + }) + .collect::, ValidationError>>()?; + validate_snapshots(&selection.parts, &snapshots)?; + selection.snapshots = Some(snapshots); + Ok(selection) } #[must_use] @@ -44,30 +94,50 @@ impl MultipartSelection { self.count } + #[must_use] + pub fn snapshots(&self) -> Option<&[SelectedStreamPart]> { + self.snapshots.as_deref() + } + + /// # Panics + /// Panics if bincode cannot serialize an already validated in-memory snapshot. #[must_use] pub fn encode(&self) -> Vec { let mut bytes = Vec::with_capacity(7 + ENTRY_BYTES * self.parts.len()); - bytes.extend_from_slice(MAGIC); + bytes.extend_from_slice(if self.snapshots.is_some() { + MAGIC_V2 + } else { + MAGIC_V1 + }); bytes.extend_from_slice(&self.count.to_be_bytes()); for part in &self.parts { bytes.extend_from_slice(&part.number.to_be_bytes()); bytes.extend_from_slice(&part.revision.to_be_bytes()); bytes.extend_from_slice(&part.digest); } + if let Some(snapshots) = &self.snapshots { + bytes.extend_from_slice(&bincode::serialize(snapshots).expect("validated snapshots serialize")); + } bytes } /// # Errors /// Rejects unknown versions, invalid framing and noncanonical part sequences. pub fn decode(bytes: &[u8]) -> Result { - if bytes.len() < 7 || bytes.get(..5) != Some(MAGIC) { + let version = bytes.get(..5).ok_or(ValidationError::Record)?; + if bytes.len() < 7 || (version != MAGIC_V1 && version != MAGIC_V2) { return Err(ValidationError::Record); } let count = usize::from(u16::from_be_bytes([bytes[5], bytes[6]])); - if count == 0 || count > 10_000 || bytes.len() != 7 + count * ENTRY_BYTES { + let entries_end = 7 + count * ENTRY_BYTES; + if count == 0 + || count > 10_000 + || bytes.len() < entries_end + || (version == MAGIC_V1 && bytes.len() != entries_end) + { return Err(ValidationError::Record); } - let parts = bytes[7..] + let parts = bytes[7..entries_end] .chunks_exact(ENTRY_BYTES) .map(|entry| { Ok(SelectedPart { @@ -79,6 +149,37 @@ impl MultipartSelection { }) }) .collect::>()?; - Self::new(parts) + let mut selection = Self::new(parts)?; + if version == MAGIC_V2 { + let snapshots: Vec = + bincode::deserialize(&bytes[entries_end..]).map_err(|_| ValidationError::Record)?; + validate_snapshots(&selection.parts, &snapshots)?; + selection.snapshots = Some(snapshots); + } + Ok(selection) + } +} + +fn validate_snapshots( + parts: &[SelectedPart], + snapshots: &[SelectedStreamPart], +) -> Result<(), ValidationError> { + if parts.len() != snapshots.len() { + return Err(ValidationError::Record); + } + for (part, snapshot) in parts.iter().zip(snapshots) { + let content = FileContent::Locations { + bytes: snapshot.bytes.clone(), + etag: snapshot.etag.clone(), + }; + content.validate(snapshot.length, &[0; 32])?; + let mut digest = Sha256::new(); + digest.update(snapshot.length.to_le_bytes()); + digest.update(&snapshot.bytes); + digest.update(snapshot.etag.as_bytes()); + if <[u8; 32]>::from(digest.finalize()) != part.digest { + return Err(ValidationError::Record); + } } + Ok(()) } diff --git a/lib/crowdb-access-iceberg/tests/multipart_publication_test.rs b/lib/crowdb-access-iceberg/tests/multipart_publication_test.rs index 527178857..7d1733bb4 100644 --- a/lib/crowdb-access-iceberg/tests/multipart_publication_test.rs +++ b/lib/crowdb-access-iceberg/tests/multipart_publication_test.rs @@ -20,6 +20,7 @@ use crowdb_access_iceberg::file::{ }; use crowdb_access_iceberg::key::FileId; use crowdb_access_iceberg::operation::mutation_identity; +use crowdb_access_iceberg::record::StorageRecord; use crowdb_protocol::chunkdb::rpc::Location; use crowdb_protocol::common::ChunkId; use md5::{Digest, Md5}; @@ -107,6 +108,41 @@ async fn upload_part_lookup_uses_one_catalog_read_before_the_session_cas() { assert_eq!(fixture.store.reads.load(Ordering::SeqCst) - before, 1); } +async fn replace_selected_part(fixture: &file::TestFile, first: &MultipartPart) { + let mut replacement = first.clone(); + replacement.revision = 2; + replacement.owner.file = FileId::random(); + replacement.stream.as_mut().unwrap().content = FileContent::from_locations( + &[Location { + chunk_id: Some(ChunkId { high: 7, low: 99 }), + offset: 100, + length: 41, + logical_offset: 0, + logical_length: 7, + }], + 7, + "33333333333333333333333333333333".into(), + ) + .unwrap(); + let key = first.key().encode().unwrap(); + let before = StorageRecord::MultipartPart(Box::new(first.clone())) + .encode() + .unwrap(); + let after = StorageRecord::MultipartPart(Box::new(replacement)) + .encode() + .unwrap(); + fixture + .store + .compare_exchange( + &key, + Some(&before), + &after, + mutation_identity(&key, Some(&before), &after), + ) + .await + .unwrap(); +} + #[tokio::test] async fn streamed_parts_complete_by_composing_locations_and_saved_md5_only() { let fixture = file::TestFile::new(common::TestStore::default()).await; @@ -116,7 +152,7 @@ async fn streamed_parts_complete_by_composing_locations_and_saved_md5_only() { session.owner.table = fixture.table; session.location = fixture.table.file("data/composed.parquet").unwrap(); repository.begin(&session, 100).await.unwrap(); - let mut selected = Vec::new(); + let mut parts = Vec::new(); for (number, size, md5) in [ (1_u16, 7_u64, "11111111111111111111111111111111"), (2, 5, "22222222222222222222222222222222"), @@ -150,18 +186,16 @@ async fn streamed_parts_complete_by_composing_locations_and_saved_md5_only() { session = load(&repository, &session).await; repository.settle_part(&session).await.unwrap(); session = load(&repository, &session).await; - selected.push(SelectedPart { - number, - revision: 1, - digest: part.selection_digest(), - }); + parts.push(part); } - let selection = MultipartSelection::new(selected).unwrap(); + let selection = MultipartSelection::with_stream_parts(&parts).unwrap(); repository .freeze_completion(&session, &selection, 102) .await .unwrap(); session = load(&repository, &session).await; + assert_eq!(repository.load_selection(&session).await.unwrap(), selection); + replace_selected_part(&fixture, &parts[0]).await; assert_eq!( repository .prepare_stream_publication(&session, 103) From 9e0e4e2162746cd406e57c4e10af9eb22ab5cd08 Mon Sep 17 00:00:00 2001 From: Gian Date: Mon, 28 Sep 2026 21:24:25 +0800 Subject: [PATCH 07/20] Commit streamed multipart parts independently --- .../src/iceberg/file_admission.rs | 6 +- .../src/iceberg/file_http/multipart.rs | 50 ++++++---- .../tests/iceberg_file_admission_test.rs | 4 + doc/backlog/R92-chunkdb-in-chunk-gc.md | 6 +- doc/working/plan-access-streaming-io.md | 8 ++ .../src/file/multipart.rs | 4 +- .../file/multipart_repository/completion.rs | 2 +- .../src/file/multipart_repository/parts.rs | 66 +++++++++++++ .../tests/multipart_repository_test.rs | 93 ++++++++++++++++++- 9 files changed, 212 insertions(+), 27 deletions(-) diff --git a/app/crowdb-access-server/src/iceberg/file_admission.rs b/app/crowdb-access-server/src/iceberg/file_admission.rs index ae9e48ca1..6a4f67a45 100644 --- a/app/crowdb-access-server/src/iceberg/file_admission.rs +++ b/app/crowdb-access-server/src/iceberg/file_admission.rs @@ -84,9 +84,11 @@ impl FileTransferAdmission { if self.operation != FileOperation::CreateMultipart { return Err(FileAdmissionError::Scope); } + let max_part_bytes = self.request_bytes.min(self.file_bytes); + let max_parts = (self.staged_bytes / max_part_bytes).min(10_000); let limits = MultipartLimits { - max_parts: 10_000, - max_part_bytes: self.request_bytes.min(self.file_bytes), + max_parts: u16::try_from(max_parts).map_err(|_| FileAdmissionError::Bounds)?, + max_part_bytes, max_file_bytes: self.file_bytes, max_staged_bytes: self.staged_bytes, ttl_ms: 24 * 60 * 60 * 1000, diff --git a/app/crowdb-access-server/src/iceberg/file_http/multipart.rs b/app/crowdb-access-server/src/iceberg/file_http/multipart.rs index d8f7512ab..ba1d5a740 100644 --- a/app/crowdb-access-server/src/iceberg/file_http/multipart.rs +++ b/app/crowdb-access-server/src/iceberg/file_http/multipart.rs @@ -228,33 +228,43 @@ impl FileHttp { })?; (Some(tree), None) }; - let before = self - .multipart - .part_for_upload(&session, part_number) - .await - .map_err(catalog_error)?; - let part = MultipartPart { + let mut part = MultipartPart { upload: session.upload, number: part_number, - revision: before.map_or(1, |part| part.revision.checked_add(1).unwrap_or(0)), + revision: 1, modified_ms: now_ms, owner, tree, stream, }; - let pending = self - .multipart - .reserve_part_state(&session, &part, now_ms) - .await - .map_err(catalog_error)? - .ok_or(FileS3ErrorCode::SlowDown)?; - if !self - .multipart - .settle_part(&pending) - .await - .map_err(catalog_error)? - { - return Err(FileS3ErrorCode::SlowDown); + if part.stream.is_some() { + part = self + .multipart + .put_stream_part(&session, &part, now_ms) + .await + .map_err(catalog_error)? + .ok_or(FileS3ErrorCode::SlowDown)?; + } else { + let before = self + .multipart + .part_for_upload(&session, part_number) + .await + .map_err(catalog_error)?; + part.revision = before.map_or(1, |part| part.revision.checked_add(1).unwrap_or(0)); + let pending = self + .multipart + .reserve_part_state(&session, &part, now_ms) + .await + .map_err(catalog_error)? + .ok_or(FileS3ErrorCode::SlowDown)?; + if !self + .multipart + .settle_part(&pending) + .await + .map_err(catalog_error)? + { + return Err(FileS3ErrorCode::SlowDown); + } } MultipartResponses::upload_part(&part) .map(|response| response.map(IcebergBody::new)) diff --git a/app/crowdb-access-server/tests/iceberg_file_admission_test.rs b/app/crowdb-access-server/tests/iceberg_file_admission_test.rs index 5b8da7acc..39c3754e9 100644 --- a/app/crowdb-access-server/tests/iceberg_file_admission_test.rs +++ b/app/crowdb-access-server/tests/iceberg_file_admission_test.rs @@ -226,6 +226,10 @@ fn multipart_session_and_global_credit_intersections_fail_closed() { ]); let create = request(&grant, &Method::POST, "?uploads"); let admitted = FileTransferAdmission::authorize(&grant, &create, service(), None, 101).unwrap(); + let limits = admitted.multipart_limits().unwrap(); + assert_eq!(limits.max_part_bytes, 4); + assert_eq!(limits.max_parts, 7); + assert!(u64::from(limits.max_parts) * limits.max_part_bytes <= limits.max_staged_bytes); let mut session = session(&grant); let mut policy = policy(&grant); assert!(admitted.check_create(&session, &policy).is_ok()); diff --git a/doc/backlog/R92-chunkdb-in-chunk-gc.md b/doc/backlog/R92-chunkdb-in-chunk-gc.md index 7257a945e..933ff05b3 100644 --- a/doc/backlog/R92-chunkdb-in-chunk-gc.md +++ b/doc/backlog/R92-chunkdb-in-chunk-gc.md @@ -19,6 +19,10 @@ references and reader protection before reclaiming, and must not infer orphan status merely from age or a missing intermediate upload record. Account for in-flight writers and delayed publication so physical ranges are never reused while a writer or reader can still own them. Report candidate and reclaimed -bytes separately. +bytes separately. Include Iceberg MPU Complete's frozen selection payload as +an authoritative reference while completion is in progress: it stores the +selected parts' exact chunk locations, which remain live even if a concurrent +UploadPart replaces the same part number before publication. After publication, +the immutable file descriptor is the authoritative reference. **Scope**: Placeholder - detailed design to be refined before implementation. diff --git a/doc/working/plan-access-streaming-io.md b/doc/working/plan-access-streaming-io.md index e01229b39..cab6b9484 100644 --- a/doc/working/plan-access-streaming-io.md +++ b/doc/working/plan-access-streaming-io.md @@ -192,6 +192,14 @@ while keeping secrets in the environment. originally selected locations and composite ETag; the 100 MiB replay test passes with the new selection format. The old selection format remains readable for legacy multipart recovery. +- Streamed UploadPart now performs one CAS on its own part key and no session + reservation/settlement CAS. Distinct part numbers commit independently; a + lost CAS reply is resolved by reading only that part record. Create caps + `max_parts` by the staged-byte reservation divided by the per-part limit, + so concurrent parts cannot exceed reserved capacity without a shared + counter. Complete validates its snapshot against the per-session maxima + rather than mutable staged counters. The focused concurrency, lost-reply, + limits, ordinary HTTP MPU and 100 MiB restart/replay tests pass. ## Files diff --git a/lib/crowdb-access-iceberg/src/file/multipart.rs b/lib/crowdb-access-iceberg/src/file/multipart.rs index 4f13efa3e..0c75546cd 100644 --- a/lib/crowdb-access-iceberg/src/file/multipart.rs +++ b/lib/crowdb-access-iceberg/src/file/multipart.rs @@ -149,10 +149,10 @@ impl MultipartCompletion { || self.selection.length == 0 || self.selection.digest != progress.selection || self.selected_parts == 0 - || self.selected_parts > session.part_count + || self.selected_parts > session.limits.max_parts || progress.next_part > self.selected_parts || progress.completed_bytes > session.limits.max_file_bytes - || progress.completed_bytes > session.staged_bytes + || (session.staged_bytes != 0 && progress.completed_bytes > session.staged_bytes) || progress.part_offset > progress.completed_bytes || progress.active.is_some() != progress.part_digest.is_some() || progress.active.is_some() != (progress.part_offset > 0) diff --git a/lib/crowdb-access-iceberg/src/file/multipart_repository/completion.rs b/lib/crowdb-access-iceberg/src/file/multipart_repository/completion.rs index c001a1b04..9a65b9348 100644 --- a/lib/crowdb-access-iceberg/src/file/multipart_repository/completion.rs +++ b/lib/crowdb-access-iceberg/src/file/multipart_repository/completion.rs @@ -57,7 +57,7 @@ impl MultipartRepository { if session.pending.is_some() { return Err(CatalogError::Busy); } - if selection.parts().len() > usize::from(session.part_count) + if (selection.snapshots().is_none() && selection.parts().len() > usize::from(session.part_count)) || selection .parts() .iter() diff --git a/lib/crowdb-access-iceberg/src/file/multipart_repository/parts.rs b/lib/crowdb-access-iceberg/src/file/multipart_repository/parts.rs index 685b67c40..c473a6560 100644 --- a/lib/crowdb-access-iceberg/src/file/multipart_repository/parts.rs +++ b/lib/crowdb-access-iceberg/src/file/multipart_repository/parts.rs @@ -8,6 +8,72 @@ use crate::record::StorageRecord; use super::{check_live, increment, MultipartRepository}; impl MultipartRepository { + /// Publishes a streamed part with one CAS on that part number. The session + /// is read for admission, but distinct part numbers do not write it. + /// # Errors + /// Rejects closed sessions, invalid parts, stale contexts and storage failures. + pub async fn put_stream_part( + &self, + session: &MultipartSession, + part: &MultipartPart, + now_ms: u64, + ) -> Result, CatalogError> { + session.validate()?; + let current = self + .load(session.context, session.upload) + .await? + .ok_or(CatalogError::Conflict)?; + check_live(¤t, now_ms)?; + if current.phase != MultipartPhase::Open || current.pending.is_some() { + return Err(CatalogError::Conflict); + } + if u64::from(current.limits.max_parts) + .checked_mul(current.limits.max_part_bytes) + .map_or(true, |bytes| bytes > current.limits.max_staged_bytes) + || part.stream.is_none() + || part.tree.is_some() + { + return Err(ValidationError::Record.into()); + } + part.validate_for(¤t)?; + let before = self.read_part(&part.key()).await?; + if let Some(before) = &before { + before.validate_for(¤t)?; + } + let mut after = part.clone(); + after.revision = before + .as_ref() + .map_or(Some(1), |before| before.revision.checked_add(1)) + .ok_or(ValidationError::Record)?; + after.modified_ms = now_ms; + after.validate_for(¤t)?; + let key = after.key().encode()?; + let expected = before.as_ref().map(encode_part).transpose()?; + let value = encode_part(&after)?; + check_context(self.store.as_ref(), current.context).await?; + let outcome = self + .store + .compare_exchange( + &key, + expected.as_deref(), + &value, + mutation_identity(&key, expected.as_deref(), &value), + ) + .await; + match outcome { + Ok(CasOutcome::Applied(_)) => Ok(Some(after)), + Ok(CasOutcome::Conflict(Some(existing))) if existing.bytes == value => Ok(Some(after)), + Ok(CasOutcome::Conflict(_)) => Ok(None), + Err(error) => { + if self.read_part(&after.key()).await?.as_ref() == Some(&after) { + Ok(Some(after)) + } else { + Err(error.into()) + } + } + } + } + /// Reads the current part for an `UploadPart` replacement. The later session /// compare-and-swap rejects a stale snapshot before the part becomes visible. /// # Errors diff --git a/lib/crowdb-access-iceberg/tests/multipart_repository_test.rs b/lib/crowdb-access-iceberg/tests/multipart_repository_test.rs index ce88e51b5..b06e55655 100644 --- a/lib/crowdb-access-iceberg/tests/multipart_repository_test.rs +++ b/lib/crowdb-access-iceberg/tests/multipart_repository_test.rs @@ -11,11 +11,14 @@ use std::sync::{atomic::Ordering, Arc}; use crowdb_access_iceberg::catalog::{CatalogError, CatalogStore, RootState}; use crowdb_access_iceberg::file::{ - FileIdentity, FileTreeWriter, MultipartPart, MultipartPhase, MultipartRepository, MultipartSession, + FileContent, FileIdentity, FileTreeWriter, MultipartPart, MultipartPhase, MultipartRepository, + MultipartSession, MultipartStreamPart, }; use crowdb_access_iceberg::key::FileId; use crowdb_access_iceberg::operation::mutation_identity; use crowdb_access_iceberg::record::StorageRecord; +use crowdb_protocol::chunkdb::rpc::Location; +use crowdb_protocol::common::ChunkId; async fn setup() -> (file::TestFile, MultipartSession) { let fixture = file::TestFile::new(common::TestStore::default()).await; @@ -47,6 +50,94 @@ async fn part(session: &MultipartSession, number: u16, revision: u64, length: us } } +fn stream_part(session: &MultipartSession, number: u16, chunk_low: u64) -> MultipartPart { + MultipartPart { + upload: session.upload, + number, + revision: 1, + modified_ms: 101, + owner: FileIdentity { + file: FileId::random(), + ..session.owner + }, + tree: None, + stream: Some(MultipartStreamPart { + length: 10, + content: FileContent::from_locations( + &[Location { + chunk_id: Some(ChunkId { + high: 7, + low: chunk_low, + }), + offset: 0, + length: 44, + logical_offset: 0, + logical_length: 10, + }], + 10, + "0123456789abcdef0123456789abcdef".into(), + ) + .unwrap(), + }), + } +} + +#[tokio::test] +async fn streamed_parts_commit_independently_without_session_mutations() { + let (fixture, initial) = setup().await; + let repository = MultipartRepository::new(fixture.store.clone()); + repository.begin(&initial, 100).await.unwrap(); + let writes = fixture.store.writes.load(Ordering::SeqCst); + let first_input = stream_part(&initial, 1, 1); + let second_input = stream_part(&initial, 2, 2); + let (first, second) = tokio::join!( + repository.put_stream_part(&initial, &first_input, 101), + repository.put_stream_part(&initial, &second_input, 101), + ); + let first = first.unwrap().unwrap(); + let second = second.unwrap().unwrap(); + assert_eq!(first.revision, 1); + assert_eq!(second.revision, 1); + assert_eq!(fixture.store.writes.load(Ordering::SeqCst) - writes, 2); + assert_eq!(load(&repository, &initial).await, initial); + let replacement = repository + .put_stream_part(&initial, &stream_part(&initial, 1, 3), 102) + .await + .unwrap() + .unwrap(); + assert_eq!(replacement.revision, 2); + assert_eq!( + repository.part_for_upload(&initial, 1).await.unwrap(), + Some(replacement) + ); + assert!(repository.abort(&initial).await.unwrap()); + assert!(repository + .put_stream_part(&initial, &stream_part(&initial, 3, 4), 103) + .await + .is_err()); +} + +#[tokio::test] +async fn streamed_part_lost_cas_reply_resolves_from_the_part_record() { + let (fixture, initial) = setup().await; + let repository = MultipartRepository::new(fixture.store.clone()); + repository.begin(&initial, 100).await.unwrap(); + fixture + .store + .fail_after + .store(fixture.store.writes.load(Ordering::SeqCst) + 1, Ordering::SeqCst); + let published = repository + .put_stream_part(&initial, &stream_part(&initial, 1, 1), 101) + .await + .unwrap() + .unwrap(); + assert_eq!( + repository.part_for_upload(&initial, 1).await.unwrap(), + Some(published) + ); + assert_eq!(load(&repository, &initial).await, initial); +} + #[tokio::test] async fn reservation_stamps_durable_part_time_from_admission_clock() { let (fixture, initial) = setup().await; From bbca8b9218dca3fa9ad00e17f6be6a6f36a4e56b Mon Sep 17 00:00:00 2001 From: Gian Date: Mon, 28 Sep 2026 21:27:57 +0800 Subject: [PATCH 08/20] Verify concurrent Iceberg multipart uploads --- .../tests/iceberg_file_http_test.rs | 35 ++++++++++++------- doc/working/plan-access-streaming-io.md | 3 ++ 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/app/crowdb-access-server/tests/iceberg_file_http_test.rs b/app/crowdb-access-server/tests/iceberg_file_http_test.rs index 96e5973cc..2b762439f 100644 --- a/app/crowdb-access-server/tests/iceberg_file_http_test.rs +++ b/app/crowdb-access-server/tests/iceberg_file_http_test.rs @@ -368,18 +368,29 @@ async fn multipart_100_mib_survives_restart_and_complete_replay() { "", ); let mut composite = Md5::new(); - for number in 1..=PART_COUNT { - let bytes = vec![u8::try_from(number).unwrap(); PART_BYTES]; - let query = format!("partNumber={number}&uploadId={upload}"); - let response = client.send(Method::PUT, &object, &query, &bytes, true).await; - assert_eq!(response.status(), 200, "{}", response.text().await.unwrap()); - let etag = response.headers()["etag"].to_str().unwrap(); - composite.update(Md5::digest(&bytes)); - write!( - manifest, - "{etag}{number}" - ) - .unwrap(); + for first_number in (1..=PART_COUNT).step_by(2) { + let second_number = first_number + 1; + let first_bytes = vec![u8::try_from(first_number).unwrap(); PART_BYTES]; + let second_bytes = vec![u8::try_from(second_number).unwrap(); PART_BYTES]; + let first_query = format!("partNumber={first_number}&uploadId={upload}"); + let second_query = format!("partNumber={second_number}&uploadId={upload}"); + let (first_response, second_response) = tokio::join!( + client.send(Method::PUT, &object, &first_query, &first_bytes, true), + client.send(Method::PUT, &object, &second_query, &second_bytes, true), + ); + for (number, bytes, response) in [ + (first_number, first_bytes, first_response), + (second_number, second_bytes, second_response), + ] { + assert_eq!(response.status(), 200, "{}", response.text().await.unwrap()); + let etag = response.headers()["etag"].to_str().unwrap(); + composite.update(Md5::digest(&bytes)); + write!( + manifest, + "{etag}{number}" + ) + .unwrap(); + } } manifest.push_str(""); let mut expected_etag = String::new(); diff --git a/doc/working/plan-access-streaming-io.md b/doc/working/plan-access-streaming-io.md index cab6b9484..a940baa91 100644 --- a/doc/working/plan-access-streaming-io.md +++ b/doc/working/plan-access-streaming-io.md @@ -200,6 +200,9 @@ while keeping secrets in the environment. counter. Complete validates its snapshot against the per-session maxima rather than mutable staged counters. The focused concurrency, lost-reply, limits, ordinary HTTP MPU and 100 MiB restart/replay tests pass. + The 100 MiB E2E now sends each pair of 5 MiB parts concurrently; its + Complete, listener restart, replay and full GET still pass. The native + publication crash matrix also passes after the direct-CAS change. ## Files From a1b336650bb1e1e90d87ec01c3c09a35a1569f5d Mon Sep 17 00:00:00 2001 From: Gian Date: Mon, 28 Sep 2026 21:39:11 +0800 Subject: [PATCH 09/20] Expose Iceberg catalog operation counters --- app/crowdb-access-server/src/iceberg/http.rs | 1 + .../src/iceberg/metrics.rs | 2 + .../tests/iceberg_file_http_test.rs | 102 ++++++++++++++++++ doc/working/plan-access-streaming-io.md | 10 ++ lib/crowdb-access-iceberg/src/catalog.rs | 4 +- .../src/catalog/repository.rs | 8 +- .../src/catalog/storage.rs | 50 ++++++++- .../src/file/multipart_repository/parts.rs | 17 +-- 8 files changed, 183 insertions(+), 11 deletions(-) diff --git a/app/crowdb-access-server/src/iceberg/http.rs b/app/crowdb-access-server/src/iceberg/http.rs index 2865880c3..26431d4c4 100644 --- a/app/crowdb-access-server/src/iceberg/http.rs +++ b/app/crowdb-access-server/src/iceberg/http.rs @@ -57,6 +57,7 @@ impl IcebergHttpService { #[must_use] pub fn metrics_snapshot(&self) -> IcebergMetricsSnapshot { let mut snapshot = self.metrics.snapshot(); + snapshot.catalog = self.repository.store_operation_counts(); if let Some((read, write)) = self.files.as_ref().and_then(|files| files.chunk_metrics()) { snapshot.chunk_read = Some(read); snapshot.chunk_small_write = Some(write); diff --git a/app/crowdb-access-server/src/iceberg/metrics.rs b/app/crowdb-access-server/src/iceberg/metrics.rs index 73c51d4f1..f538e846e 100644 --- a/app/crowdb-access-server/src/iceberg/metrics.rs +++ b/app/crowdb-access-server/src/iceberg/metrics.rs @@ -51,6 +51,7 @@ pub struct IcebergMetricsSnapshot { pub selected_versions: [u64; 3], pub chunk_read: Option, pub chunk_small_write: Option, + pub catalog: Option, } struct Counters { @@ -109,6 +110,7 @@ impl IcebergMetrics { selected_versions: array::from_fn(|index| self.selected_versions[index].load(Ordering::Relaxed)), chunk_read: None, chunk_small_write: None, + catalog: None, } } } diff --git a/app/crowdb-access-server/tests/iceberg_file_http_test.rs b/app/crowdb-access-server/tests/iceberg_file_http_test.rs index 2b762439f..c467d81f1 100644 --- a/app/crowdb-access-server/tests/iceberg_file_http_test.rs +++ b/app/crowdb-access-server/tests/iceberg_file_http_test.rs @@ -25,6 +25,7 @@ use crowdb_access_iceberg::wire::BearerAuthenticator; use md5::{Digest, Md5}; use reqwest::{Client, Method}; use std::fmt::Write as _; +use std::time::Instant; #[path = "common/iceberg_signed_file.rs"] mod signed; @@ -168,6 +169,107 @@ fn path(table: TableLocation, key: &str) -> String { format!("/{}/{}", table.bucket(), table.file(key).unwrap().object_key()) } +async fn catalog_counts(client: &TestFileClient) -> (u64, u64, u64, u64) { + let response: serde_json::Value = client + .client + .get(format!("http://{}/_crowdb/metrics", client.address)) + .bearer_auth("m".repeat(32)) + .send() + .await + .unwrap() + .json() + .await + .unwrap(); + let catalog = &response["catalog"]; + ( + catalog["get"].as_u64().unwrap(), + catalog["compare_exchange"].as_u64().unwrap(), + catalog["scan"].as_u64().unwrap(), + catalog["conditional_delete"].as_u64().unwrap(), + ) +} + +fn catalog_delta(before: (u64, u64, u64, u64), after: (u64, u64, u64, u64)) -> String { + format!( + "get={} cas={} scan={} delete={}", + after.0 - before.0, + after.1 - before.1, + after.2 - before.2, + after.3 - before.3 + ) +} + +#[tokio::test(flavor = "multi_thread", worker_threads = 4)] +#[ignore = "native null-DiskIO release performance fixture"] +async fn native_file_5_mib_profile() { + let (_stack, _process, client, table) = setup().await; + let bytes = vec![0x5a; 5 * 1024 * 1024]; + let object = path(table, "data/profile-put.bin"); + let before = catalog_counts(&client).await; + let started = Instant::now(); + let put = client.send(Method::PUT, &object, "", &bytes, true).await; + let put_ms = started.elapsed().as_millis(); + assert_eq!(put.status(), 200, "{}", put.text().await.unwrap()); + println!( + "iceberg 5MiB PUT: {put_ms}ms {}", + catalog_delta(before, catalog_counts(&client).await) + ); + + let multipart = path(table, "data/profile-mpu.bin"); + let created = client + .send(Method::POST, &multipart, "uploads=", b"", false) + .await; + assert_eq!(created.status(), 200); + let created = created.text().await.unwrap(); + let upload = created + .split_once("") + .unwrap() + .1 + .split_once("") + .unwrap() + .0; + let query = format!("partNumber=1&uploadId={upload}"); + let before = catalog_counts(&client).await; + let started = Instant::now(); + let part = client.send(Method::PUT, &multipart, &query, &bytes, true).await; + let part_ms = started.elapsed().as_millis(); + assert_eq!(part.status(), 200, "{}", part.text().await.unwrap()); + let etag = part.headers()["etag"].to_str().unwrap(); + println!( + "iceberg 5MiB UploadPart: {part_ms}ms {}", + catalog_delta(before, catalog_counts(&client).await) + ); + let manifest = format!( + "{etag}1" + ); + let complete = client + .send( + Method::POST, + &multipart, + &format!("uploadId={upload}"), + manifest.as_bytes(), + false, + ) + .await; + assert_eq!(complete.status(), 200); + assert!(complete + .text() + .await + .unwrap() + .contains("")); + + let before = catalog_counts(&client).await; + let started = Instant::now(); + let get = client.send(Method::GET, &object, "", b"", false).await; + assert_eq!(get.status(), 200); + assert_eq!(get.bytes().await.unwrap().as_ref(), bytes); + let get_ms = started.elapsed().as_millis(); + println!( + "iceberg 5MiB GET: {get_ms}ms {}", + catalog_delta(before, catalog_counts(&client).await) + ); +} + #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn signed_standard_put_get_and_multipart_publish_unbound_files() { let (stack, _process, client, table) = setup().await; diff --git a/doc/working/plan-access-streaming-io.md b/doc/working/plan-access-streaming-io.md index a940baa91..a951f4af2 100644 --- a/doc/working/plan-access-streaming-io.md +++ b/doc/working/plan-access-streaming-io.md @@ -203,6 +203,16 @@ while keeping secrets in the environment. The 100 MiB E2E now sends each pair of 5 MiB parts concurrently; its Complete, listener restart, replay and full GET still pass. The native publication crash matrix also passes after the direct-CAS change. +- Iceberg `/metrics` now exposes lock-free cumulative catalog get/CAS/scan/delete + counts beside the existing request and Chunk I/O metrics. On three separate + release/null-DiskIO 5 MiB fixtures, PUT took 228/235/233 ms and used 16 + catalog gets plus two CAS; UploadPart took 179/201/176 ms and used ten gets + plus one CAS; full GET took 27/25/16 ms and used eight gets, no CAS. The + earlier release/null-DiskIO baseline was 2682-2791 ms PUT and 2091-2184 ms + UploadPart with the legacy per-leaf path. This shows an order-of-magnitude + improvement in this fixture; the request helper signs and copies the body, + and null-DiskIO is not a production storage latency profile. Remaining + catalog gets are request/session/context checks rather than frame writes. ## Files diff --git a/lib/crowdb-access-iceberg/src/catalog.rs b/lib/crowdb-access-iceberg/src/catalog.rs index 6234d44a9..8fef30d80 100644 --- a/lib/crowdb-access-iceberg/src/catalog.rs +++ b/lib/crowdb-access-iceberg/src/catalog.rs @@ -15,4 +15,6 @@ pub use deadline::ClearBounds; pub use repository::{CatalogError, CatalogRepository, ManagementPrivilege}; pub use root::{ActiveCatalogRecord, RootState}; pub use state::{CatalogAuthority, CatalogContext, CatalogLifecycle, ClearTransition}; -pub use storage::{CasOutcome, CatalogStore, RoutedCatalogStore, StoreError, StoredValue}; +pub use storage::{ + CasOutcome, CatalogStore, CatalogStoreOperationCounts, RoutedCatalogStore, StoreError, StoredValue, +}; diff --git a/lib/crowdb-access-iceberg/src/catalog/repository.rs b/lib/crowdb-access-iceberg/src/catalog/repository.rs index beb7fe4c7..58f177c80 100644 --- a/lib/crowdb-access-iceberg/src/catalog/repository.rs +++ b/lib/crowdb-access-iceberg/src/catalog/repository.rs @@ -10,7 +10,8 @@ use crate::operation::{ use crate::record::StorageRecord; use super::{ - ActiveCatalogRecord, CasOutcome, CatalogAuthority, CatalogStore, ClearBounds, RootState, StoreError, + ActiveCatalogRecord, CasOutcome, CatalogAuthority, CatalogStore, CatalogStoreOperationCounts, + ClearBounds, RootState, StoreError, }; #[derive(Debug, thiserror::Error)] @@ -42,6 +43,11 @@ pub struct CatalogRepository { } impl CatalogRepository { + #[must_use] + pub fn store_operation_counts(&self) -> Option { + self.store.operation_counts() + } + /// # Errors /// Rejects invalid timing limits before any storage access. pub fn new(store: Arc, bounds: ClearBounds) -> Result { diff --git a/lib/crowdb-access-iceberg/src/catalog/storage.rs b/lib/crowdb-access-iceberg/src/catalog/storage.rs index cad893f75..af3613f57 100644 --- a/lib/crowdb-access-iceberg/src/catalog/storage.rs +++ b/lib/crowdb-access-iceberg/src/catalog/storage.rs @@ -1,4 +1,7 @@ -use std::sync::Arc; +use std::sync::{ + atomic::{AtomicU64, Ordering}, + Arc, +}; use async_trait::async_trait; use crowdb_chunk_kv_client::{ChunkKvClient, ClientError, MultiScanPage, MultiScanRequest}; @@ -37,8 +40,39 @@ pub enum CasOutcome { Conflict(Option), } +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Serialize)] +pub struct CatalogStoreOperationCounts { + pub get: u64, + pub compare_exchange: u64, + pub scan: u64, + pub conditional_delete: u64, +} + +#[derive(Default)] +struct OperationCounters { + get: AtomicU64, + compare_exchange: AtomicU64, + scan: AtomicU64, + conditional_delete: AtomicU64, +} + +impl OperationCounters { + fn snapshot(&self) -> CatalogStoreOperationCounts { + CatalogStoreOperationCounts { + get: self.get.load(Ordering::Relaxed), + compare_exchange: self.compare_exchange.load(Ordering::Relaxed), + scan: self.scan.load(Ordering::Relaxed), + conditional_delete: self.conditional_delete.load(Ordering::Relaxed), + } + } +} + #[async_trait] pub trait CatalogStore: Send + Sync { + fn operation_counts(&self) -> Option { + None + } + async fn get(&self, key: &[u8]) -> Result, StoreError>; async fn compare_exchange( &self, @@ -51,12 +85,16 @@ pub trait CatalogStore: Send + Sync { pub struct RoutedCatalogStore { client: Arc, + counters: OperationCounters, } impl RoutedCatalogStore { #[must_use] pub fn new(client: Arc) -> Self { - Self { client } + Self { + client, + counters: OperationCounters::default(), + } } pub(crate) async fn delete_mapping_if( @@ -93,6 +131,7 @@ impl RoutedCatalogStore { return Err(ValidationError::Key.into()); } validate_value(expected)?; + self.counters.conditional_delete.fetch_add(1, Ordering::Relaxed); let response = self .client .execute_with_identity( @@ -143,6 +182,7 @@ impl RoutedCatalogStore { { return Err(StoreError::Response); } + self.counters.scan.fetch_add(1, Ordering::Relaxed); let page = self.client.scan(request).await?; if let Some(failure) = page.terminal_failure { return Err(StoreError::Rejected(failure)); @@ -160,8 +200,13 @@ impl RoutedCatalogStore { #[async_trait] impl CatalogStore for RoutedCatalogStore { + fn operation_counts(&self) -> Option { + Some(self.counters.snapshot()) + } + async fn get(&self, key: &[u8]) -> Result, StoreError> { IcebergKey::decode(key)?; + self.counters.get.fetch_add(1, Ordering::Relaxed); let response = self.client.get(key.to_vec(), None).await?; match response.result.map_err(StoreError::Rejected)? { OperationResult::Value(value) => value.map(|value| stored(key, value)).transpose(), @@ -192,6 +237,7 @@ impl CatalogStore for RoutedCatalogStore { value: value.to_vec(), }, }; + self.counters.compare_exchange.fetch_add(1, Ordering::Relaxed); let response = self .client .execute_with_identity(operation, None, identity) diff --git a/lib/crowdb-access-iceberg/src/file/multipart_repository/parts.rs b/lib/crowdb-access-iceberg/src/file/multipart_repository/parts.rs index c473a6560..178c87ed6 100644 --- a/lib/crowdb-access-iceberg/src/file/multipart_repository/parts.rs +++ b/lib/crowdb-access-iceberg/src/file/multipart_repository/parts.rs @@ -50,7 +50,6 @@ impl MultipartRepository { let key = after.key().encode()?; let expected = before.as_ref().map(encode_part).transpose()?; let value = encode_part(&after)?; - check_context(self.store.as_ref(), current.context).await?; let outcome = self .store .compare_exchange( @@ -60,18 +59,22 @@ impl MultipartRepository { mutation_identity(&key, expected.as_deref(), &value), ) .await; - match outcome { - Ok(CasOutcome::Applied(_)) => Ok(Some(after)), - Ok(CasOutcome::Conflict(Some(existing))) if existing.bytes == value => Ok(Some(after)), - Ok(CasOutcome::Conflict(_)) => Ok(None), + let written = match outcome { + Ok(CasOutcome::Applied(_)) => Some(after), + Ok(CasOutcome::Conflict(Some(existing))) if existing.bytes == value => Some(after), + Ok(CasOutcome::Conflict(_)) => None, Err(error) => { if self.read_part(&after.key()).await?.as_ref() == Some(&after) { - Ok(Some(after)) + Some(after) } else { - Err(error.into()) + return Err(error.into()); } } + }; + if written.is_some() { + check_context(self.store.as_ref(), current.context).await?; } + Ok(written) } /// Reads the current part for an `UploadPart` replacement. The later session From 67776fc755524453b2588b8b400bb09c1337b644 Mon Sep 17 00:00:00 2001 From: Gian Date: Mon, 28 Sep 2026 21:47:26 +0800 Subject: [PATCH 10/20] Bound multipart snapshots and verify streamed GC reclamation --- doc/working/plan-access-streaming-io.md | 18 +++-- .../src/file/multipart_selection.rs | 20 +++++ .../tests/common/gc_blocks.rs | 17 +++++ .../tests/gc_worker_test.rs | 76 +++++++++++++++++++ .../tests/multipart_completion_test.rs | 32 +++++++- 5 files changed, 154 insertions(+), 9 deletions(-) diff --git a/doc/working/plan-access-streaming-io.md b/doc/working/plan-access-streaming-io.md index a951f4af2..75de7a5df 100644 --- a/doc/working/plan-access-streaming-io.md +++ b/doc/working/plan-access-streaming-io.md @@ -10,7 +10,8 @@ and crash recovery while eliminating per-frame catalog operations. Status: Bounded S3 and Iceberg reads, access configuration, conditional S3 SHA-256, shared HTTP receive plumbing, Iceberg pin removal, location descriptors, whole-object uploads and metadata-only MPU completion pass focused tests. -Fault coverage, operation-count measurements and final gates remain. +Catalog operation counts and a release/null-DiskIO 5 MiB comparison are recorded. +Remaining work is full acceptance, flow review and final gates. The access processes now have a shared typed TOML startup schema. The single-node container renders it separately for S3 and Iceberg; it exposes read slots, @@ -25,14 +26,13 @@ while keeping secrets in the environment. flow before implementation; include cancellation and crash boundaries. Record read-window, location-normalization and Chunk layout-query counts through CROWDB metrics, then compare with perf counters before changing read flow. -- [~] **Remove legacy request-level metadata amplification**: removed the +- [x] **Remove legacy request-level metadata amplification**: removed the file-request, file-publication, delegated-credential and table-load GC pin writes; removed the pin record, protocol schema, GC handling and manual commands. No old Iceberg pin data exists, so no compatibility path is needed. - UploadPart still updates its session through multiple serialized CAS - operations, and the old sealer still rereads complete non-JSON files. - Treat new-path bytes as opaque and keep part publication independent across - part numbers. Measure catalog operations per request. + New streamed UploadPart uses one part CAS and no session writes. New-path + bytes remain opaque, and catalog operation counts are exposed in Iceberg + metrics. Legacy tree file records retain their original read behavior. - [x] **Bounded ordered read pipeline**: replace the 64 MiB fetch window with configurable per-stream slots (default three, at most 1 MiB physical data each). Schedule strip reads concurrently, verify complete frames, retain @@ -70,7 +70,7 @@ while keeping secrets in the environment. and durable storage completion; leave client file formats opaque. - [x] **Shared reads**: use Chunk read streams and owner-backed Bytes for full GET and ranges; preserve integrity, the GC grace period and cancellation. -- [~] **Multipart completion**: compose validated completed parts without the +- [x] **Multipart completion**: compose validated completed parts without the old serial per-leaf rewrite/commit path; retain recovery and terminal credits. Save part MD5 values at upload and produce the final ETag from those values. Compute SHA-256 for a part only when explicitly requested; preserve any @@ -213,6 +213,10 @@ while keeping secrets in the environment. improvement in this fixture; the request helper signs and copies the body, and null-DiskIO is not a production storage latency profile. Remaining catalog gets are request/session/context checks rather than frame writes. +- Streamed Complete now bounds the persisted selection payload before copying + part location arrays. The focused test rejects an oversized snapshot and an + oversized decoded payload. Retired-catalog GC reclaims a streamed file's + physical location after worker restarts; all 18 GC worker tests pass. ## Files diff --git a/lib/crowdb-access-iceberg/src/file/multipart_selection.rs b/lib/crowdb-access-iceberg/src/file/multipart_selection.rs index bdddcc311..42ae55e01 100644 --- a/lib/crowdb-access-iceberg/src/file/multipart_selection.rs +++ b/lib/crowdb-access-iceberg/src/file/multipart_selection.rs @@ -1,4 +1,5 @@ use crate::error::ValidationError; +use crate::operation::MAX_PAYLOAD_BYTES; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; @@ -56,6 +57,22 @@ impl MultipartSelection { /// # Errors /// Rejects mixed legacy parts, invalid descriptors or incoherent digests. pub fn with_stream_parts(parts: &[MultipartPart]) -> Result { + let mut estimated_bytes = 7_usize + .checked_add(parts.len().saturating_mul(ENTRY_BYTES + 64)) + .ok_or(ValidationError::RecordTooLarge)?; + for part in parts { + let stream = part.stream.as_ref().ok_or(ValidationError::Record)?; + let FileContent::Locations { bytes, etag } = &stream.content else { + return Err(ValidationError::Record); + }; + estimated_bytes = estimated_bytes + .checked_add(bytes.len()) + .and_then(|size| size.checked_add(etag.len())) + .ok_or(ValidationError::RecordTooLarge)?; + if estimated_bytes > MAX_PAYLOAD_BYTES { + return Err(ValidationError::RecordTooLarge); + } + } let selected = parts .iter() .map(|part| SelectedPart { @@ -124,6 +141,9 @@ impl MultipartSelection { /// # Errors /// Rejects unknown versions, invalid framing and noncanonical part sequences. pub fn decode(bytes: &[u8]) -> Result { + if bytes.len() > MAX_PAYLOAD_BYTES { + return Err(ValidationError::RecordTooLarge); + } let version = bytes.get(..5).ok_or(ValidationError::Record)?; if bytes.len() < 7 || (version != MAGIC_V1 && version != MAGIC_V2) { return Err(ValidationError::Record); diff --git a/lib/crowdb-access-iceberg/tests/common/gc_blocks.rs b/lib/crowdb-access-iceberg/tests/common/gc_blocks.rs index 4e7ead9e9..4984afadb 100644 --- a/lib/crowdb-access-iceberg/tests/common/gc_blocks.rs +++ b/lib/crowdb-access-iceberg/tests/common/gc_blocks.rs @@ -3,12 +3,14 @@ use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use async_trait::async_trait; use crowdb_access_iceberg::file::{ChunkRoot, FileBlockStore, FileIdentity, FileIoError}; use crowdb_chunk_client::ReclaimOutcome; +use crowdb_protocol::chunkdb::rpc::Location; #[derive(Default)] pub struct TestReclaimBlocks { pub blocks: crate::blocks::TestBlocks, pub deferred: AtomicBool, pub deletes: AtomicUsize, + pub location_deletes: AtomicUsize, pub reply_loss: AtomicBool, pub delay_ms: AtomicUsize, } @@ -42,4 +44,19 @@ impl FileBlockStore for TestReclaimBlocks { } Ok(ReclaimOutcome::Reclaimed) } + + async fn reclaim_location(&self, location: &Location) -> Result { + if self.deferred.load(Ordering::Relaxed) { + return Ok(ReclaimOutcome::Deferred); + } + let chunk = location.chunk_id.ok_or(FileIoError::Bounds)?; + self.blocks.values.rcu(|values| { + let mut next = (**values).clone(); + next.remove(&chunk.low); + next + }); + self.deletes.fetch_add(1, Ordering::Relaxed); + self.location_deletes.fetch_add(1, Ordering::Relaxed); + Ok(ReclaimOutcome::Reclaimed) + } } diff --git a/lib/crowdb-access-iceberg/tests/gc_worker_test.rs b/lib/crowdb-access-iceberg/tests/gc_worker_test.rs index cd21dd3ac..cecbf8809 100644 --- a/lib/crowdb-access-iceberg/tests/gc_worker_test.rs +++ b/lib/crowdb-access-iceberg/tests/gc_worker_test.rs @@ -8,6 +8,7 @@ use crowdb_access_iceberg::{ operation::mutation_identity, record::StorageRecord, }; +use crowdb_protocol::{chunkdb::rpc::Location, common::ChunkId}; #[path = "common/gc_adoption.rs"] mod adoption; @@ -123,6 +124,81 @@ async fn retired_file_reclamation_survives_worker_restart_at_every_step() { .is_none()); } +#[tokio::test] +async fn retired_streamed_file_reclaims_location_after_worker_restart() { + let (fixture, blocks, mut task, limits, _) = fixture(true).await; + let chunk = ChunkId { high: 1, low: 999 }; + let location = Location { + chunk_id: Some(chunk), + offset: 0, + length: 128, + logical_offset: 0, + logical_length: 64, + }; + blocks.blocks.values.rcu(|values| { + let mut next = (**values).clone(); + next.insert(chunk.low, Arc::new(vec![7; 64])); + next + }); + let file = crowdb_access_iceberg::file::FileRecord { + file: FileId::random(), + location: fixture.table.file("data/streamed.parquet").unwrap(), + kind: FileKind::Data, + format: ContentFormat::Parquet, + length: 64, + digest: [0; 32], + content: FileContent::from_locations(&[location], 64, "00000000000000000000000000000000".into()) + .unwrap(), + hint: None, + }; + let mapping = crowdb_access_iceberg::file::FileMapping { + file: file.file, + location: file.location.clone(), + }; + for (key, record) in [ + ( + file_key(fixture.context.catalog, file.file), + StorageRecord::File(Box::new(file.clone())), + ), + ( + crowdb_access_iceberg::file::location_key(&file.location), + StorageRecord::FileMapping(mapping), + ), + ] { + let key = key.encode().unwrap(); + let bytes = record.encode().unwrap(); + fixture + .store + .compare_exchange(&key, None, &bytes, mutation_identity(&key, None, &bytes)) + .await + .unwrap(); + } + for _ in 0..300 { + let repository = GcRepository::new(fixture.store.clone()); + let worker = GcWorker::new(repository.clone(), blocks.clone(), limits).unwrap(); + task = worker.step(&task, 2000_u64.max(task.retry_at_ms)).await.unwrap(); + task = repository + .task(task.context.catalog, task.identity) + .await + .unwrap() + .unwrap(); + if task.phase == GcPhase::Complete { + break; + } + } + assert_eq!(task.phase, GcPhase::Complete, "{task:?}"); + assert_eq!(task.deleted, 2); + assert_eq!(task.reclaimed_bytes, 4096 + 64); + assert_eq!(blocks.location_deletes.load(Ordering::Relaxed), 1); + assert!(blocks.blocks.values.load().is_empty()); + assert!(fixture + .store + .get(&file_key(fixture.context.catalog, file.file).encode().unwrap()) + .await + .unwrap() + .is_none()); +} + #[tokio::test] async fn inactive_discovery_preserves_file_when_gc_workspace_is_unavailable() { let (fixture, blocks, task, limits, file) = fixture(true).await; diff --git a/lib/crowdb-access-iceberg/tests/multipart_completion_test.rs b/lib/crowdb-access-iceberg/tests/multipart_completion_test.rs index 13f2e89ca..457234549 100644 --- a/lib/crowdb-access-iceberg/tests/multipart_completion_test.rs +++ b/lib/crowdb-access-iceberg/tests/multipart_completion_test.rs @@ -10,8 +10,8 @@ mod fixtures; use std::sync::{atomic::Ordering, Arc}; use crowdb_access_iceberg::file::{ - FileIdentity, FileReader, FileTreeWriter, MultipartPart, MultipartPhase, MultipartRepository, - MultipartSelection, MultipartSession, SelectedPart, + FileContent, FileIdentity, FileReader, FileTreeWriter, MultipartPart, MultipartPhase, + MultipartRepository, MultipartSelection, MultipartSession, MultipartStreamPart, SelectedPart, }; use crowdb_access_iceberg::key::FileId; @@ -107,6 +107,34 @@ fn frozen_selection_is_ordered_versioned_and_independently_bounded() { .is_err()); } +#[test] +fn streamed_selection_rejects_oversized_snapshot_before_copying_part_locations() { + let session = fixtures::session(); + let part = MultipartPart { + upload: session.upload, + number: 1, + revision: 1, + modified_ms: 100, + owner: session.owner, + tree: None, + stream: Some(MultipartStreamPart { + length: 1, + content: FileContent::Locations { + bytes: vec![0; crowdb_access_iceberg::operation::MAX_PAYLOAD_BYTES], + etag: "00000000000000000000000000000000".into(), + }, + }), + }; + assert!(matches!( + MultipartSelection::with_stream_parts(&[part]), + Err(crowdb_access_iceberg::error::ValidationError::RecordTooLarge) + )); + assert!(matches!( + MultipartSelection::decode(&vec![0; crowdb_access_iceberg::operation::MAX_PAYLOAD_BYTES + 1]), + Err(crowdb_access_iceberg::error::ValidationError::RecordTooLarge) + )); +} + #[tokio::test] async fn lost_selection_and_progress_replies_resume_exact_bytes_on_new_instances() { for lost in 1..=2 { From dea91fbd480d368faae775390afeba6d6d22255a Mon Sep 17 00:00:00 2001 From: Gian Date: Mon, 28 Sep 2026 21:53:59 +0800 Subject: [PATCH 11/20] Document shared Iceberg streaming architecture --- .../iceberge/design-crowdb-iceberg.md | 76 ++++++++++++------- 1 file changed, 50 insertions(+), 26 deletions(-) diff --git a/doc/design/access-server/iceberge/design-crowdb-iceberg.md b/doc/design/access-server/iceberge/design-crowdb-iceberg.md index 9095bbe0a..6af69c9f8 100644 --- a/doc/design/access-server/iceberge/design-crowdb-iceberg.md +++ b/doc/design/access-server/iceberge/design-crowdb-iceberg.md @@ -252,22 +252,27 @@ bytes. Dot traversal, leading slash, backslash, controls, query and fragment delimiters are rejected rather than normalized. HTTP percent decoding belongs only at the transport boundary, not in stored S3-shaped locations. -Native file records bind FileId to exact location, kind, format, canonical length -and SHA-256 digest. Eligible metadata stores at most 16 KiB inline; bounded LZ4 +Native file records bind FileId to exact location, kind, format and canonical length. +Legacy tree and inline records also bind a whole-file SHA-256 digest; streamed +records bind a bounded array of complete Chunk locations and an HTTP ETag, and +rely on verified 64-KiB storage frames rather than a whole-file digest. Eligible +metadata stores at most 16 KiB inline; bounded LZ4 compression considers at most 64 KiB original input, and decoding verifies the -canonical length and digest. Other file kinds retain a fixed-size chunk root, -never a growing location vector. Hints are non-authoritative and out-of-bounds +canonical length and digest. Location vectors are validated for exact logical +coverage and bounded by the record limit. Hints are non-authoritative and out-of-bounds hints are ignored. The publication primitive stages an immutable authority before the exact-location CAS; equal-content retries return the selected FileId, while conflicts retain losing candidates without overwriting or physical deletion. An SDK upload supplies a path and bytes, not the eventual Iceberg data/delete -use. Sealing validates physical container bytes and records ambiguous Avro, -Parquet, ORC and Puffin uses as unbound. Selected metadata and manifests must -validate declared uses against these canonical records before table publication. +use. FileIO treats client-supplied bytes as opaque. It verifies the declared +transport checksum and durable frame writes before publishing the descriptor; +format interpretation belongs to the client or to a CROWDB component that +constructs those bytes. Selected metadata and manifests validate their declared +uses during table publication. The isolated native HTTP surface exposes signed immutable object reads/writes and multipart operations, but no general S3 bucket authority or file DELETE. -Chunk-backed files use bounded leaf blocks and immutable chunk-resident directory +Legacy chunk-backed files use bounded leaf blocks and immutable chunk-resident directory pages, with at most 256 children per page and eight directory levels. Each page binds its catalog, table and file identity, child heights and covered byte count. The writer retains only one partial leaf and bounded per-level frontiers. Native @@ -289,7 +294,7 @@ coverage. SHA-256 compression uses RustCrypto; versioned digest checkpoints reta only chaining state, byte length and a partial block. They are trusted-storage recovery records, not client authentication assertions. Failed checkpoint writes poison the current writer without invalidating earlier durable checkpoints. -Native block writes persist an exact physical-range ownership intent in the +Legacy native block writes persist an exact physical-range ownership intent in the catalog before DiskIO. A shared-writer callback receives the assigned location; uncertain catalog writes are read back before the physical batch proceeds. This ledger also covers process loss before file publication and checkpoints @@ -318,19 +323,29 @@ immutable before/after session references; policy-bound sequence receipts make create and terminal release recoverable without double accounting. Released receipts remain in terminal sessions. These logical credits are not physical disk reclamation or accounting for retained orphan bytes. -The native multipart repository reserves one part mutation in the session before +The legacy tree multipart repository reserves one part mutation in the session before changing its part authority. A bounded before/after snapshot and monotonically increasing revisions make the write and fence release recoverable across servers. Counts and current staged bytes are reserved once at the session CAS. Abort cannot bypass an unresolved mutation; stale helpers cannot restore an older part. Abort retains parts and completion evidence rather than deleting physical storage. -Completion freezes an ordered part-number/revision/digest selection in immutable +Legacy completion freezes an ordered part-number/revision/digest selection in immutable payload pages, then changes the session phase by CAS to fence part replacement. Selections are independently bounded to 10,000 entries and 420,007 encoded bytes. Each completion step verifies that bounded selection and one selected part before copying a bounded byte window and publishing its checkpoint by session CAS. Lost replies reload progress without appending selected bytes twice. Assembled bytes remain unexposed until semantic sealing and immutable location publication. +Streamed UploadPart writes one part key by CAS without changing the session for +each part. Create caps part count by the reserved staged-byte ceiling divided +by the per-part byte ceiling. Complete freezes an ordered selection with each +part's exact location bytes, length and MD5 ETag in bounded immutable payload +pages. A later replacement cannot change that selection. Completion composes +logical offsets across the selected locations and derives the multipart ETag +from the ordered raw part MD5 values; it does not read part data or assemble a +new chunk. Session phase CAS freezes publication against later selections. The +final file descriptor becomes visible through the immutable location publication +protocol. Foreground and recovery drivers use the same native-block-aligned byte window below the one-MiB assembly ceiling. Equal windows prevent systematic CAS losses to a smaller competing recovery step; alignment avoids checkpoint-only tiny leaves. @@ -369,12 +384,15 @@ number. Current-session checks bracket each scan; concurrent mutations invalidat the page rather than mixing pending counters with old part records. Expired or terminal sessions and malformed storage pages are not reported as successful lists. -Native HTTP upload staging holds an independent concurrency -credit, slices each received frame into bounded writes and awaits storage before pulling more -input. Declared/actual byte limits, exact content length and optional signed SHA-256 -are checked before returning a tree. Failed or cancelled uploads retain orphan -blocks without publishing file authority. This transport adapter does not infer -semantic file kind, authorize grants or accept unchecked checksum trailers. +Native HTTP upload staging holds an independent concurrency credit and bounded +1-MiB receive owners. A body below 1 MiB enters its write pipeline in one push; +larger bodies push each filled owner while the next owner receives. The Chunk +writer frames data at 64 KiB and chooses shared chunks only below the configured +fraction of one strip's data capacity. Unknown-length bodies choose a dedicated +chunk. PUT and UploadPart require a verified request checksum or signed payload; +SHA-256 is calculated only when declared. Durable completion and exact length +precede metadata publication. Failed or cancelled uploads leave unpublished +allocations for orphan scanning without exposing partial file authority. Metadata JSON structural validation uses a bounded pull-reader bridge and an ignored-value parser rather than retaining the metadata graph. A separate scanner @@ -542,7 +560,7 @@ Physical reclamation follows a proof that no live metadata, snapshot, reference, lease, or retained operation can reach the file. General S3 deletion and lifecycle rules cannot reclaim Iceberg-owned data. -The reclamation proof binds current and pinned historical metadata to their +The reclamation proof binds current and retained historical metadata to their captured heads. Its immutable traversal stack and compressed binary file-ID index use content-addressed payload pages. A task CAS publishes the pending stack and mark root together; a missing page is an error, including during a nonmembership @@ -564,19 +582,19 @@ authorizes deletion. Committed files remain readable when new chunk allocation fails. Progress resumes after capacity is restored through the normal storage flow. Shared-chunk ranges remain pending while range deletion is unsupported. -Metadata readers, direct FileIO, file publication and both published and staged -credentials persist pins before rechecking their authority. Pin expiry includes -the applicable persisted request and clock-skew bounds. Once a file's canonical +Metadata readers, direct FileIO, file publication and credentials recheck their +authority without writing request-level pins. Physical reclamation observes a +minimum retention interval that covers admitted request lifetimes. Once a file's canonical deletion intent has started, ordinary resolution and publication reject it even if physical range reclamation is deferred. Legacy live tasks are retired without further deletion, releasing an owned table fence. Retained and deferred candidates remain durable work for later inactive passes. -An already authorized FileIO GET or HEAD can pin a tombstoned table while its -credential remains valid. The pin is persisted before the exact head is -rechecked; a concurrent transition to `Reclaiming` rejects admission. Uploads -and new table credentials still require a Ready table. Logical drop therefore -does not invalidate retained file reads or bypass the physical deletion fence. +An already authorized FileIO GET or HEAD can continue within the configured +retention interval after a logical deletion. A concurrent transition to +`Reclaiming` rejects new admission. Uploads and new table credentials require +a Ready table. Logical drop preserves admitted file reads until their bounded +response lifetime ends. Retired catalog recovery scans system retry and management ledgers before file deletion and after the final file rescan. Pending or retained bindings stop the @@ -588,6 +606,12 @@ candidates. Assembly checkpoints have separate claims and a durable frontier-roo index. Abandoned frontiers are authenticated before traversal; a conflicted final tree is traversed once instead of revisiting its shared frontier. Published sessions reclaim only the checkpoint block, preserving the assembled data tree. +Streamed file and part candidates instead reclaim their exact Chunk location +ranges after retention. A published selected part remains owned by the immutable +file descriptor; part cleanup does not reclaim it a second time. Allocations +abandoned before a file or part descriptor is published have no per-chunk catalog +intent and remain for ChunkDB's orphan scanner to discover after checking +published descriptors, frozen selections and active writers. Each physical step rechecks the terminal session and retention. The checkpoint block is deleted after its children, and session cleanup requires its completed claim. Block intents are swept after tree candidates, preserving reachable owners From 82cb67e33580cce7a514287017eb949212a11b3d Mon Sep 17 00:00:00 2001 From: Gian Date: Mon, 28 Sep 2026 21:59:38 +0800 Subject: [PATCH 12/20] Align native Iceberg test budgets with small write limits --- app/crowdb-access-server/tests/iceberg_file_storage_test.rs | 1 + app/crowdb-access-server/tests/iceberg_gc_capacity_test.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/app/crowdb-access-server/tests/iceberg_file_storage_test.rs b/app/crowdb-access-server/tests/iceberg_file_storage_test.rs index 02893266f..32069fc44 100644 --- a/app/crowdb-access-server/tests/iceberg_file_storage_test.rs +++ b/app/crowdb-access-server/tests/iceberg_file_storage_test.rs @@ -23,6 +23,7 @@ async fn chunks(stack: &TestIcebergStack) -> ChunkIoClient { diskio_connections_per_endpoint: 2, diskio_rpc_workers: 1, small_write: SmallWritePolicy { + object_limit: 1024 * 1024, min_pipelines: 1, max_pipelines: 1, memory_budget: 8 * 1024 * 1024, diff --git a/app/crowdb-access-server/tests/iceberg_gc_capacity_test.rs b/app/crowdb-access-server/tests/iceberg_gc_capacity_test.rs index 3a169b60f..aa836cdef 100644 --- a/app/crowdb-access-server/tests/iceberg_gc_capacity_test.rs +++ b/app/crowdb-access-server/tests/iceberg_gc_capacity_test.rs @@ -60,6 +60,7 @@ async fn chunks(stack: &TestIcebergStack) -> ChunkIoClient { diskio_connections_per_endpoint: 1, diskio_rpc_workers: 1, small_write: SmallWritePolicy { + object_limit: 1024 * 1024, min_pipelines: 1, max_pipelines: 1, memory_budget: 8 * 1024 * 1024, From 546a71528afee6dc91ced11e281433d76823238a Mon Sep 17 00:00:00 2001 From: Gian Date: Mon, 28 Sep 2026 22:19:43 +0800 Subject: [PATCH 13/20] Bound frozen multipart decode and report listener exits --- .../tests/common/iceberg_process.rs | 7 +++---- doc/working/plan-access-streaming-io.md | 12 +++++++++++ .../src/file/multipart_selection.rs | 20 +++++++++++++++++-- .../tests/multipart_publication_test.rs | 9 +++++++++ 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/app/crowdb-access-server/tests/common/iceberg_process.rs b/app/crowdb-access-server/tests/common/iceberg_process.rs index 54951fdcb..27a1d103c 100644 --- a/app/crowdb-access-server/tests/common/iceberg_process.rs +++ b/app/crowdb-access-server/tests/common/iceberg_process.rs @@ -41,10 +41,9 @@ impl TestIcebergProcess { let mut process = Self { child, address }; tokio::time::timeout(Duration::from_secs(30), async { loop { - assert!( - process.child.try_wait().unwrap().is_none(), - "Iceberg listener exited" - ); + if let Some(status) = process.child.try_wait().unwrap() { + panic!("Iceberg listener exited before readiness: {status}"); + } if tokio::net::TcpStream::connect(address).await.is_ok() { break; } diff --git a/doc/working/plan-access-streaming-io.md b/doc/working/plan-access-streaming-io.md index 75de7a5df..b072db614 100644 --- a/doc/working/plan-access-streaming-io.md +++ b/doc/working/plan-access-streaming-io.md @@ -217,6 +217,18 @@ while keeping secrets in the environment. part location arrays. The focused test rejects an oversized snapshot and an oversized decoded payload. Retired-catalog GC reclaims a streamed file's physical location after worker restarts; all 18 GC worker tests pass. +- Frozen streamed selections reject a mismatched encoded part count, out-of-bound + deserialization and trailing bytes. The focused publication suite passes all + seven tests. The full access-Iceberg, access-S3 and access-server library + suites pass. +- The native Iceberg release suite exposed two legacy test fixtures whose 8 MiB + small-write budget inherited an 8 MiB object limit plus 1 MiB pipeline shadow. + Both fixtures now set a 1 MiB object limit; their storage-restart and GC + capacity tests pass individually and within the rerun native suite. The + native file publication fault matrix, 100 MiB MPU replay, and 5 MiB profile + also pass. The table-commit crash matrix exited once while starting a recovery + listener at publish-stage 5/31, then passed every boundary on a complete + rerun. Listener startup now reports its exit status if this recurs. ## Files diff --git a/lib/crowdb-access-iceberg/src/file/multipart_selection.rs b/lib/crowdb-access-iceberg/src/file/multipart_selection.rs index 42ae55e01..8ae47ec83 100644 --- a/lib/crowdb-access-iceberg/src/file/multipart_selection.rs +++ b/lib/crowdb-access-iceberg/src/file/multipart_selection.rs @@ -1,5 +1,6 @@ use crate::error::ValidationError; use crate::operation::MAX_PAYLOAD_BYTES; +use bincode::Options; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; @@ -171,8 +172,23 @@ impl MultipartSelection { .collect::>()?; let mut selection = Self::new(parts)?; if version == MAGIC_V2 { - let snapshots: Vec = - bincode::deserialize(&bytes[entries_end..]).map_err(|_| ValidationError::Record)?; + let encoded = &bytes[entries_end..]; + let snapshot_count = u64::from_le_bytes( + encoded + .get(..8) + .ok_or(ValidationError::Record)? + .try_into() + .map_err(|_| ValidationError::Record)?, + ); + if snapshot_count != count as u64 { + return Err(ValidationError::Record); + } + let snapshots: Vec = bincode::DefaultOptions::new() + .with_fixint_encoding() + .with_limit(MAX_PAYLOAD_BYTES as u64) + .reject_trailing_bytes() + .deserialize(encoded) + .map_err(|_| ValidationError::Record)?; validate_snapshots(&selection.parts, &snapshots)?; selection.snapshots = Some(snapshots); } diff --git a/lib/crowdb-access-iceberg/tests/multipart_publication_test.rs b/lib/crowdb-access-iceberg/tests/multipart_publication_test.rs index 7d1733bb4..628271c05 100644 --- a/lib/crowdb-access-iceberg/tests/multipart_publication_test.rs +++ b/lib/crowdb-access-iceberg/tests/multipart_publication_test.rs @@ -189,6 +189,15 @@ async fn streamed_parts_complete_by_composing_locations_and_saved_md5_only() { parts.push(part); } let selection = MultipartSelection::with_stream_parts(&parts).unwrap(); + let encoded = selection.encode(); + assert_eq!(MultipartSelection::decode(&encoded).unwrap(), selection); + let mut oversized_count = encoded.clone(); + let count_offset = 7 + 42 * parts.len(); + oversized_count[count_offset..count_offset + 8].copy_from_slice(&u64::MAX.to_le_bytes()); + assert!(MultipartSelection::decode(&oversized_count).is_err()); + let mut trailing = encoded; + trailing.push(0); + assert!(MultipartSelection::decode(&trailing).is_err()); repository .freeze_completion(&session, &selection, 102) .await From 711b942b495e072bedc55f23520dd6a4666d7d63 Mon Sep 17 00:00:00 2001 From: Gian Date: Mon, 28 Sep 2026 22:51:31 +0800 Subject: [PATCH 14/20] Verify shared object sizes and SDK opaque uploads --- .../src/main/java/TestIcebergFileIO.java | 29 +++++---- .../tests/iceberg_file_http_test.rs | 61 +++++++++++++++++++ .../tests/s3_e2e/basic.py | 45 ++++++++++++-- .../tests/s3_full_stack_test.rs | 8 ++- doc/working/plan-access-streaming-io.md | 31 ++++++++++ lib/crowdb-access-s3/src/metrics.rs | 30 +++++++++ 6 files changed, 183 insertions(+), 21 deletions(-) diff --git a/app/crowdb-access-server/tests/common/iceberg_java/src/main/java/TestIcebergFileIO.java b/app/crowdb-access-server/tests/common/iceberg_java/src/main/java/TestIcebergFileIO.java index 13125128d..54a09e5e8 100644 --- a/app/crowdb-access-server/tests/common/iceberg_java/src/main/java/TestIcebergFileIO.java +++ b/app/crowdb-access-server/tests/common/iceberg_java/src/main/java/TestIcebergFileIO.java @@ -15,7 +15,6 @@ import software.amazon.awssdk.core.sync.RequestBody; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.model.CompletedPart; -import software.amazon.awssdk.services.s3.model.S3Exception; public class TestIcebergFileIO { public static void main(String[] args) throws Exception { @@ -51,6 +50,7 @@ public static void main(String[] args) throws Exception { files.initialize(properties); String prefix = configuration.getProperty("location"); byte[] small = "{\"client\":\"iceberg-java-1.11.0\"}".getBytes(StandardCharsets.UTF_8); + System.out.println("S3FileIO: small PUT/GET"); verify(files, prefix + "metadata/sdk-small.json", small); byte[] large = new byte[6 * 1024 * 1024]; Arrays.fill(large, (byte) 'x'); @@ -58,8 +58,11 @@ public static void main(String[] args) throws Exception { System.arraycopy(start, 0, large, 0, start.length); large[large.length - 2] = '"'; large[large.length - 1] = '}'; + System.out.println("S3FileIO: multipart PUT/GET"); verify(files, prefix + "metadata/sdk-multipart.json", large); - verifyLateError(files.client(), prefix + "metadata/sdk-invalid.json"); + System.out.println("S3FileIO: opaque multipart Complete"); + verifyOpaqueMultipart(files.client(), prefix + "metadata/sdk-opaque.json"); + System.out.println("S3FileIO: file operations"); TestIcebergFileOperations.run(files.client(), prefix); if (credentialRequests.get() != 1) { throw new AssertionError("SDK did not fetch and cache the delegated credential response"); @@ -67,27 +70,23 @@ public static void main(String[] args) throws Exception { } finally { credentials.stop(0); } - System.out.println("Apache Iceberg 1.11.0 S3FileIO PUT, multipart, HEAD, GET, seek and embedded error passed"); + System.out.println("Apache Iceberg 1.11.0 S3FileIO PUT, multipart, HEAD, GET, seek and opaque file passed"); } - private static void verifyLateError(S3Client client, String location) { + private static void verifyOpaqueMultipart(S3Client client, String location) { URI uri = URI.create(location); String bucket = uri.getHost(); String key = uri.getPath().substring(1); String upload = client.createMultipartUpload(request -> request.bucket(bucket).key(key)).uploadId(); + byte[] payload = "{invalid-json".getBytes(StandardCharsets.UTF_8); String etag = client.uploadPart( request -> request.bucket(bucket).key(key).uploadId(upload).partNumber(1), - RequestBody.fromString("{invalid-json")).eTag(); - try { - client.completeMultipartUpload(request -> request.bucket(bucket).key(key).uploadId(upload) - .multipartUpload(parts -> parts.parts(CompletedPart.builder().partNumber(1).eTag(etag).build()))); - throw new AssertionError("SDK accepted an embedded Complete error as success"); - } catch (S3Exception error) { - if (!"InvalidRequest".equals(error.awsErrorDetails().errorCode())) { - throw error; - } - } finally { - client.abortMultipartUpload(request -> request.bucket(bucket).key(key).uploadId(upload)); + RequestBody.fromBytes(payload)).eTag(); + client.completeMultipartUpload(request -> request.bucket(bucket).key(key).uploadId(upload) + .multipartUpload(parts -> parts.parts(CompletedPart.builder().partNumber(1).eTag(etag).build()))); + byte[] stored = client.getObjectAsBytes(request -> request.bucket(bucket).key(key)).asByteArray(); + if (!Arrays.equals(stored, payload)) { + throw new AssertionError("opaque multipart payload changed"); } } diff --git a/app/crowdb-access-server/tests/iceberg_file_http_test.rs b/app/crowdb-access-server/tests/iceberg_file_http_test.rs index c467d81f1..03a70e613 100644 --- a/app/crowdb-access-server/tests/iceberg_file_http_test.rs +++ b/app/crowdb-access-server/tests/iceberg_file_http_test.rs @@ -442,6 +442,67 @@ async fn signed_standard_put_get_and_multipart_publish_unbound_files() { assert_eq!(get.bytes().await.unwrap().as_ref(), document); } +#[tokio::test(flavor = "multi_thread", worker_threads = 4)] +async fn ordinary_put_size_matrix_streams_and_reads_ranges() { + let (stack, _process, client, table) = setup_with_bounds_and_file_limit( + ClearBounds { + delegated_access_ms: 900_000, + ..ClearBounds::default() + }, + 128 * 1024 * 1024, + 128 * 1024 * 1024, + ) + .await; + let repository = FileRepository::new(stack.store().await); + for size in [10 * 1024, 1024 * 1024, 12 * 1024 * 1024, 100 * 1024 * 1024] { + let key = format!("data/size-{size}.parquet"); + let object = path(table, &key); + let bytes = (0..size).map(|offset| (offset % 256) as u8).collect::>(); + let expected = Md5::digest(&bytes); + let started = Instant::now(); + let put = client.send(Method::PUT, &object, "", &bytes, true).await; + assert_eq!(put.status(), 200, "{}", put.text().await.unwrap()); + let put_elapsed = started.elapsed(); + let head = client.send(Method::HEAD, &object, "", b"", false).await; + assert_eq!(head.status(), 200); + assert_eq!(head.headers()["content-length"], size.to_string()); + let record = repository + .load(client.credentials.grant().context, &table.file(&key).unwrap()) + .await + .unwrap() + .unwrap(); + assert_eq!(record.length, size as u64); + assert!(!record + .content + .locations(record.length) + .unwrap() + .unwrap() + .is_empty()); + let get_started = Instant::now(); + let mut response = client.send(Method::GET, &object, "", b"", false).await; + assert_eq!(response.status(), 200); + let mut received = Md5::new(); + let mut received_size = 0; + while let Some(chunk) = response.chunk().await.unwrap() { + received.update(&chunk); + received_size += chunk.len(); + } + assert_eq!(received_size, size); + assert_eq!(received.finalize().as_slice(), expected.as_slice()); + let get_elapsed = get_started.elapsed(); + for start in [0, usize::min(size - 1, 65500), size - 1] { + let end = usize::min(size - 1, start + 127); + let range = format!("bytes={start}-{end}"); + let response = client + .send_range(Method::GET, &object, "", b"", false, Some(&range)) + .await; + assert_eq!(response.status(), 206); + assert_eq!(response.bytes().await.unwrap().as_ref(), &bytes[start..=end]); + } + println!("iceberg ordinary size={size} PUT={put_elapsed:?} GET={get_elapsed:?}"); + } +} + #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn multipart_100_mib_survives_restart_and_complete_replay() { const PART_BYTES: usize = 5 * 1024 * 1024; diff --git a/app/crowdb-access-server/tests/s3_e2e/basic.py b/app/crowdb-access-server/tests/s3_e2e/basic.py index 050502cb2..0b22c586c 100644 --- a/app/crowdb-access-server/tests/s3_e2e/basic.py +++ b/app/crowdb-access-server/tests/s3_e2e/basic.py @@ -240,7 +240,7 @@ def test_slow_signed_upload_releases_native_buffers(self): bucket = f"{self.bucket}-slow" path = f"/{bucket}/slow.bin" second_path = f"/{bucket}/slow-second.bin" - payload = bytes(range(256)) * (4096 + 1) + payload = bytes(range(256)) * (32768 + 1) status, _, _ = self.signed_http("PUT", f"/{bucket}") self.assertEqual(status, 200) barrier = Barrier(2) @@ -259,12 +259,12 @@ def upload(target): status, _, metrics = self.signed_http("GET", "/_crowdb/metrics") self.assertEqual(status, 200) self.assertIn(b"crowdb_s3_native_retained_bytes 0\n", metrics) - backpressure = next( + direct_bytes = next( int(line.split()[-1]) for line in metrics.splitlines() - if line.startswith(b"crowdb_s3_native_backpressure_events_total ") + if line.startswith(b"crowdb_s3_native_direct_bytes_total ") ) - self.assertGreater(backpressure, 0) + self.assertGreater(direct_bytes, 0) self.client.delete_object(Bucket=bucket, Key="slow.bin") self.client.delete_object(Bucket=bucket, Key="slow-second.bin") self.client.delete_bucket(Bucket=bucket) @@ -478,6 +478,43 @@ def test_fragmentation_and_storage_boundaries(self): client.delete_object(Bucket=bucket, Key=key) client.delete_bucket(Bucket=bucket) + def test_ordinary_put_size_matrix(self): + client = self.client + bucket = f"{self.bucket}-sizes" + client.create_bucket(Bucket=bucket) + sizes = [10 * 1024, 1024 * 1024, 12 * 1024 * 1024, 100 * 1024 * 1024] + for size in sizes: + key = f"size-{size}.bin" + payload = bytes(range(256)) * (size // 256) + digest = md5(payload) + started = time.monotonic() + result = client.put_object( + Bucket=bucket, + Key=key, + Body=BytesIO(payload), + ContentLength=size, + ContentMD5=b64encode(digest.digest()).decode("ascii"), + ) + self.assertEqual(result["ETag"], f'"{digest.hexdigest()}"') + put_s = time.monotonic() - started + self.assertEqual(client.head_object(Bucket=bucket, Key=key)["ContentLength"], size) + get_started = time.monotonic() + fetched = client.get_object(Bucket=bucket, Key=key)["Body"] + downloaded = md5() + while block := fetched.read(1024 * 1024): + downloaded.update(block) + self.assertEqual(downloaded.digest(), digest.digest()) + get_s = time.monotonic() - get_started + for start in [0, min(size - 1, 65500), size - 1]: + end = min(size - 1, start + 127) + ranged = client.get_object( + Bucket=bucket, Key=key, Range=f"bytes={start}-{end}" + )["Body"].read() + self.assertEqual(ranged, payload[start : end + 1]) + print(f"S3 ordinary size={size} PUT={put_s:.3f}s GET={get_s:.3f}s") + client.delete_object(Bucket=bucket, Key=key) + client.delete_bucket(Bucket=bucket) + if __name__ == "__main__": unittest.main() diff --git a/app/crowdb-access-server/tests/s3_full_stack_test.rs b/app/crowdb-access-server/tests/s3_full_stack_test.rs index d0d58c9af..77fdbf564 100644 --- a/app/crowdb-access-server/tests/s3_full_stack_test.rs +++ b/app/crowdb-access-server/tests/s3_full_stack_test.rs @@ -32,7 +32,7 @@ use hyper::body::Bytes; use serde_json::json; const MASTER_KEY: &str = "1111111111111111111111111111111111111111111111111111111111111111"; -const TEST_COUNT: usize = 17; +const TEST_COUNT: usize = 18; const BOTO3_CASES: &[&str] = &[ "test_signed_raw_http_wire_contract", "test_independent_frontends_share_one_namespace", @@ -42,6 +42,7 @@ const BOTO3_CASES: &[&str] = &[ "test_slow_response_reader_keeps_full_object_consistent", "test_basic_bucket_object_matrix", "test_fragmentation_and_storage_boundaries", + "test_ordinary_put_size_matrix", ]; struct AccessServerProcess { @@ -454,6 +455,7 @@ fn assert_native_write_metrics(listen: &str) { assert!(native_body_bytes > 0); assert!(metric_value(&exported, "crowdb_s3_large_write_framed_owners_total") > 0); assert!(metric_value(&exported, "crowdb_s3_large_write_framed_views_total") > 0); + assert!(metric_value(&exported, "crowdb_s3_small_write_completed_total") > 0); assert_eq!( metric_value(&exported, "crowdb_s3_large_write_payload_copy_operations_total"), 0 @@ -533,7 +535,6 @@ fn start_access_server( .env("CROWDB_S3_TENANT", "boto3-e2e") .env("CROWDB_S3_MASTER_KEY", MASTER_KEY) .env("CROWDB_S3_REGION", "us-east-1") - .env("CROWDB_S3_SMALL_OBJECT_LIMIT", "0") .env("CROWDB_S3_EC_DATA", "2") .env("CROWDB_S3_EC_CODE", "1") .env("CROWDB_S3_NATIVE_BUDGET_BYTES", (1024 * 1024).to_string()) @@ -571,6 +572,9 @@ fn run_boto3_case(method: &str, context: &Boto3CaseContext<'_>) { context.access_server.log_content(), context.chunk_kv.log_content(), ); + if method == "test_ordinary_put_size_matrix" { + print!("{}", String::from_utf8_lossy(&python.stdout)); + } } fn run_restart_phase(phase: &str, listen: &str, access_key: &str, secret_key: &str) { diff --git a/doc/working/plan-access-streaming-io.md b/doc/working/plan-access-streaming-io.md index b072db614..917fbb6a0 100644 --- a/doc/working/plan-access-streaming-io.md +++ b/doc/working/plan-access-streaming-io.md @@ -229,6 +229,37 @@ while keeping secrets in the environment. also pass. The table-commit crash matrix exited once while starting a recovery listener at publish-stage 5/31, then passed every boundary on a complete rerun. Listener startup now reports its exit status if this recurs. +- The Boto3 S3 full-stack suite passes all 17 cases, including fragmented + signed uploads, backpressured reads, lost PUT replies, object reads after + each service restart, and direct Chunk/S3 request-path benchmarks. Its + existing fixture disables the shared small-object route, so separate native + small-writer acceptance remains necessary. +- Java SDK namespace pagination and offline Maven dependency setup pass. The + FileIO acceptance then exposed an obsolete test that expected server-side + JSON parsing during multipart Complete. New streamed files intentionally + preserve user bytes, so that case now checks opaque Complete and GET. The + focused Java S3FileIO rerun passes, including small PUT/GET, multipart, + range seeks, immutable replay and exact table scope. Ordinary PUT size-matrix + cases for both APIs now report separate PUT and GET times. +- The ordinary Iceberg PUT matrix passed full GET digest, HEAD length and + beginning/frame-boundary/tail ranges at every size. In the release/null-DiskIO + fixture, PUT/GET timings were 10 KiB: 58/8 ms, 1 MiB: 107/16 ms, + 12 MiB: 396/62 ms and 100 MiB: 2716/201 ms. These include the signed + request helper's payload hash and body copy on PUT; no equivalent old-path + size matrix exists, so only the 5 MiB same-fixture comparison is a direct + before/after speedup. +- The S3 full-stack suite now runs with the default small-object threshold + instead of disabling small writes. All 18 cases pass, including the 10 KiB, + 1 MiB, 12 MiB and 100 MiB ordinary PUT/GET/range matrix. The metrics endpoint + exports small write submitted/completed/failure, buffer and wait counters; + the suite asserts actual small pipeline completions. In its debug/null-DiskIO + fixture, PUT/GET timings were 10 KiB: 36/47 ms, 1 MiB: 186/51 ms, + 12 MiB: 1501/322 ms and 100 MiB: 10835/2713 ms. These are not comparable + with the release Iceberg timings. The old slow-upload fixture had only 1 MiB + of payload, which became a small write under the default 8-data-shard + threshold; it now uses just over 8 MiB to exercise native large ingress. + Deterministic native backpressure remains covered by its focused allocator + tests instead of timing-dependent null-DiskIO overlap. ## Files diff --git a/lib/crowdb-access-s3/src/metrics.rs b/lib/crowdb-access-s3/src/metrics.rs index 909247c88..4084ab3ce 100644 --- a/lib/crowdb-access-s3/src/metrics.rs +++ b/lib/crowdb-access-s3/src/metrics.rs @@ -272,6 +272,7 @@ impl S3Metrics { } if let Some(chunks) = chunks { append_chunk_metrics(&mut output, chunks.large_write_buffer_metrics()); + append_small_write_metrics(&mut output, &chunks.small_write_metrics()); append_read_flow_metrics(&mut output, chunks.read_flow_metrics()); } output @@ -343,6 +344,35 @@ fn append_chunk_metrics(output: &mut String, buffers: LargeWriteBufferMetricsSna } } +fn append_small_write_metrics(output: &mut String, small: &SmallWriteMetricsSnapshot) { + for (name, value) in [ + ("crowdb_s3_small_write_submitted_total", small.submitted), + ("crowdb_s3_small_write_completed_total", small.completed), + ("crowdb_s3_small_write_failed_total", small.failed), + ( + "crowdb_s3_small_write_aggregate_buffers_total", + small.aggregate_write_buffers, + ), + ( + "crowdb_s3_small_write_aggregate_payload_bytes_total", + small.aggregate_write_payload_bytes, + ), + ("crowdb_s3_small_write_queue_delay_ns_total", small.queue_delay_ns), + ( + "crowdb_s3_small_write_reservation_wait_ns_total", + small.reservation_wait_ns, + ), + ] { + append_metric(output, name, "", value); + } + append_metric( + output, + "crowdb_s3_small_write_active_pipelines", + "", + small.active_pipelines, + ); +} + fn append_read_flow_metrics(output: &mut String, flow: ReadFlowMetricsSnapshot) { for (name, value) in [ ( From ee3e7e11025a7b6eaa532b58fd1894bfccc813ea Mon Sep 17 00:00:00 2001 From: Gian Date: Mon, 28 Sep 2026 23:03:34 +0800 Subject: [PATCH 15/20] Verify small routing at strip thresholds --- app/crowdb-access-server/tests/config_test.rs | 14 +++++++- .../tests/iceberg_file_http_test.rs | 35 +++++++++++++++++++ doc/working/plan-access-streaming-io.md | 18 ++++++++++ .../src/file/repository.rs | 2 +- 4 files changed, 67 insertions(+), 2 deletions(-) diff --git a/app/crowdb-access-server/tests/config_test.rs b/app/crowdb-access-server/tests/config_test.rs index 2d620bc67..2a7778829 100644 --- a/app/crowdb-access-server/tests/config_test.rs +++ b/app/crowdb-access-server/tests/config_test.rs @@ -3,7 +3,7 @@ use std::path::Path; -use crowdb_access_server::config::{load_args, AccessConfig}; +use crowdb_access_server::config::{load_args, AccessConfig, SmallWriteConfig}; use crowdb_common::config::{load_from_file, BaseConfig}; #[test] @@ -52,3 +52,15 @@ fn invalid_read_budget_is_rejected() { config.small_write.memory_budget_bytes = 1; assert!(config.validate().is_err()); } + +#[test] +fn small_threshold_uses_strip_data_capacity_for_ec_and_mirror() { + let mut config = SmallWriteConfig { + ec_data: 8, + ec_code: 2, + ..SmallWriteConfig::default() + }; + assert_eq!(config.threshold_exclusive(), 7_549_748); + config.conversion_enabled = false; + assert_eq!(config.threshold_exclusive(), 943_719); +} diff --git a/app/crowdb-access-server/tests/iceberg_file_http_test.rs b/app/crowdb-access-server/tests/iceberg_file_http_test.rs index 03a70e613..8e690ff55 100644 --- a/app/crowdb-access-server/tests/iceberg_file_http_test.rs +++ b/app/crowdb-access-server/tests/iceberg_file_http_test.rs @@ -22,6 +22,7 @@ use crowdb_access_iceberg::file::{ use crowdb_access_iceberg::key::OperationId; use crowdb_access_iceberg::operation::{ManagementAction, ManagementRequest, RequestIdentity}; use crowdb_access_iceberg::wire::BearerAuthenticator; +use crowdb_access_server::config::SmallWriteConfig; use md5::{Digest, Md5}; use reqwest::{Client, Method}; use std::fmt::Write as _; @@ -503,6 +504,40 @@ async fn ordinary_put_size_matrix_streams_and_reads_ranges() { } } +#[tokio::test(flavor = "multi_thread", worker_threads = 4)] +async fn small_routing_is_strict_at_the_strip_threshold() { + let (_stack, _process, client, table) = setup().await; + let threshold = SmallWriteConfig::default().threshold_exclusive(); + let completed = async || { + let metrics: serde_json::Value = Client::new() + .get(format!("http://{}/_crowdb/metrics", client.address)) + .bearer_auth("m".repeat(32)) + .send() + .await + .unwrap() + .json() + .await + .unwrap(); + metrics["chunk_small_write"]["completed"].as_u64().unwrap() + }; + let mut previous = completed().await; + for size in [threshold - 1, threshold, threshold + 1] { + let object = path(table, &format!("data/threshold-{size}.parquet")); + let bytes = vec![u8::try_from(size % 251).unwrap(); size]; + let response = client.send(Method::PUT, &object, "", &bytes, true).await; + assert_eq!(response.status(), 200, "{}", response.text().await.unwrap()); + let current = completed().await; + assert_eq!(current - previous, u64::from(size < threshold), "size={size}"); + previous = current; + let last = format!("bytes={}-{}", size - 2, size - 1); + let response = client + .send_range(Method::GET, &object, "", b"", false, Some(&last)) + .await; + assert_eq!(response.status(), 206); + assert_eq!(response.bytes().await.unwrap().as_ref(), &bytes[size - 2..]); + } +} + #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn multipart_100_mib_survives_restart_and_complete_replay() { const PART_BYTES: usize = 5 * 1024 * 1024; diff --git a/doc/working/plan-access-streaming-io.md b/doc/working/plan-access-streaming-io.md index 917fbb6a0..0ee8a3f22 100644 --- a/doc/working/plan-access-streaming-io.md +++ b/doc/working/plan-access-streaming-io.md @@ -260,6 +260,24 @@ while keeping secrets in the environment. threshold; it now uses just over 8 MiB to exercise native large ingress. Deterministic native backpressure remains covered by its focused allocator tests instead of timing-dependent null-DiskIO overlap. +- The threshold contract is verified for 8+2 EC and mirror geometry: + with ratio 0.9 the exclusive upper bounds are 7,549,748 and 943,719 bytes. + A native Iceberg PUT test checks 7,549,747, 7,549,748 and 7,549,749 bytes; + only the first increments the small-write completion counter, and all three + return exact tail ranges. +- The final code-flow review found no new read or write catalog records on the + frame hot path. Streamed PUT stages one immutable file record and CASes its + location mapping (two CAS total in the 5 MiB fixture); UploadPart CASes only + its part key; Complete stores the selected snapshot and final descriptor + without reading part bytes. GET checks authority, then holds at most three + ordered Chunk read slots plus the configured global budget, verifies full + frame CRCs, and hands owner-backed buffers to HTTP. Response cancellation + drops the stream and its retained credits. GC observes retired catalog + authority and the grace deadline before reclaiming location ranges; the + existing 18 GC worker tests cover streamed-file reclamation after restart. + The two publication CAS operations and request-level catalog context checks + remain fixed per object rather than per frame; the 5 MiB counters quantify + their cost. Legacy tree records retain their old intent path for readability. ## Files diff --git a/lib/crowdb-access-iceberg/src/file/repository.rs b/lib/crowdb-access-iceberg/src/file/repository.rs index 2e549f16c..6d348cb74 100644 --- a/lib/crowdb-access-iceberg/src/file/repository.rs +++ b/lib/crowdb-access-iceberg/src/file/repository.rs @@ -31,7 +31,7 @@ impl FileRepository { Ok(result) } - /// Publishes a candidate; callers must verify chunk bytes and format before calling. + /// Publishes a candidate after callers verify transfer integrity and durable storage. /// # Errors /// Rejects invalid records, changed content, retired contexts and uncertain writes. pub async fn publish( From a28af8e9ea9b385bd38c2e9f5a8ef34ffd32f2a3 Mon Sep 17 00:00:00 2001 From: Gian Date: Mon, 28 Sep 2026 23:21:52 +0800 Subject: [PATCH 16/20] Derive small object threshold from configured block capacity --- .../conf/crowdb_access_server_config.toml | 1 + app/crowdb-access-server/src/config.rs | 10 ++++++++-- .../src/iceberg/file_http.rs | 2 +- app/crowdb-access-server/tests/config_test.rs | 16 ++++++++++++++++ .../single-node-container/templates/access.toml | 1 + doc/working/plan-access-streaming-io.md | 5 +++++ 6 files changed, 32 insertions(+), 3 deletions(-) diff --git a/app/crowdb-access-server/conf/crowdb_access_server_config.toml b/app/crowdb-access-server/conf/crowdb_access_server_config.toml index 12b4d04b2..c9204d095 100644 --- a/app/crowdb-access-server/conf/crowdb_access_server_config.toml +++ b/app/crowdb-access-server/conf/crowdb_access_server_config.toml @@ -14,6 +14,7 @@ recovery_memory_bytes = 268435456 [small_write] threshold_ratio = 0.9 +disk_block_bytes = 1048576 conversion_enabled = true ec_data = 8 ec_code = 4 diff --git a/app/crowdb-access-server/src/config.rs b/app/crowdb-access-server/src/config.rs index 24b0f3b2f..907fb03ea 100644 --- a/app/crowdb-access-server/src/config.rs +++ b/app/crowdb-access-server/src/config.rs @@ -74,6 +74,8 @@ impl ReadConfig { #[serde(default)] pub struct SmallWriteConfig { pub threshold_ratio: f64, + /// Data bytes per strip block used for the small-object routing boundary. + pub disk_block_bytes: usize, pub conversion_enabled: bool, pub ec_data: usize, pub ec_code: usize, @@ -89,6 +91,7 @@ impl Default for SmallWriteConfig { let policy = SmallWritePolicy::default(); Self { threshold_ratio: 0.9, + disk_block_bytes: 1024 * 1024, conversion_enabled: policy.conversion_enabled, ec_data: policy.conversion_data_num, ec_code: policy.conversion_code_num, @@ -110,7 +113,7 @@ impl SmallWriteConfig { )] pub fn threshold_exclusive(&self) -> usize { let data_shards = if self.conversion_enabled { self.ec_data } else { 1 }; - (self.threshold_ratio * (data_shards * 1024 * 1024) as f64).ceil() as usize + (self.threshold_ratio * data_shards.saturating_mul(self.disk_block_bytes) as f64).ceil() as usize } #[must_use] @@ -202,10 +205,13 @@ impl BaseConfig for AccessConfig { if !self.small_write.threshold_ratio.is_finite() || self.small_write.threshold_ratio <= 0.0 || self.small_write.threshold_ratio > 1.0 + || self.small_write.disk_block_bytes == 0 + || self.small_write.disk_block_bytes > 1024 * 1024 || self.small_write.ec_data == 0 || self.small_write.ec_data > 32 + || self.small_write.threshold_exclusive() > self.small_write.policy().object_limit { - return Err("small_write threshold ratio and EC data count are invalid".into()); + return Err("small_write strip capacity or threshold is invalid".into()); } if self.s3.ec_data == Some(0) || self.s3.ec_code == Some(0) { return Err("S3 EC data and code counts must be nonzero".into()); diff --git a/app/crowdb-access-server/src/iceberg/file_http.rs b/app/crowdb-access-server/src/iceberg/file_http.rs index 63f783176..7d5d506c0 100644 --- a/app/crowdb-access-server/src/iceberg/file_http.rs +++ b/app/crowdb-access-server/src/iceberg/file_http.rs @@ -78,7 +78,7 @@ impl FileHttp { issuer: FileGrantIssuer::new(secret, 15 * 60 * 1000)?, responses: FileResponseBudget::new(64).map_err(|_| FileGrantError::Invalid)?, uploads: FileUploadBudget::new(64).map_err(|_| FileGrantError::Invalid)?, - small_threshold_exclusive: (9_usize * 8 * 1024 * 1024).div_ceil(10), + small_threshold_exclusive: crate::config::SmallWriteConfig::default().threshold_exclusive(), native_allocator, region, limits: FileServiceLimits { diff --git a/app/crowdb-access-server/tests/config_test.rs b/app/crowdb-access-server/tests/config_test.rs index 2a7778829..1f54fed77 100644 --- a/app/crowdb-access-server/tests/config_test.rs +++ b/app/crowdb-access-server/tests/config_test.rs @@ -20,6 +20,9 @@ fn tracked_access_configs_load_and_set_bounded_read_resources() { assert_eq!(config.read.recovery_memory_bytes, 256 * 1024 * 1024); assert_eq!(config.read.policy().stream_slots, 3); assert_eq!(config.small_write.memory_budget_bytes, 1280 * 1024 * 1024); + assert_eq!(config.small_write.disk_block_bytes, 1024 * 1024); + assert_eq!((config.small_write.ec_data, config.small_write.ec_code), (8, 4)); + assert_eq!(config.small_write.threshold_exclusive(), 7_549_748); assert_eq!(config.small_write.policy().max_pipelines, 32); assert!(config.s3.listen.is_some()); assert!(config.iceberg.listen.is_some()); @@ -61,6 +64,19 @@ fn small_threshold_uses_strip_data_capacity_for_ec_and_mirror() { ..SmallWriteConfig::default() }; assert_eq!(config.threshold_exclusive(), 7_549_748); + config.disk_block_bytes = 512 * 1024; + assert_eq!(config.threshold_exclusive(), 3_774_874); config.conversion_enabled = false; + assert_eq!(config.threshold_exclusive(), 471_860); + config.disk_block_bytes = 1024 * 1024; assert_eq!(config.threshold_exclusive(), 943_719); } + +#[test] +fn unsupported_disk_block_size_is_rejected_before_routing() { + let mut config = AccessConfig::default(); + config.small_write.disk_block_bytes = 0; + assert!(config.validate().is_err()); + config.small_write.disk_block_bytes = 2 * 1024 * 1024; + assert!(config.validate().is_err()); +} diff --git a/container/single-node-container/templates/access.toml b/container/single-node-container/templates/access.toml index 0b30383e5..f64ddc054 100644 --- a/container/single-node-container/templates/access.toml +++ b/container/single-node-container/templates/access.toml @@ -14,6 +14,7 @@ recovery_memory_bytes = 268435456 [small_write] threshold_ratio = 0.9 +disk_block_bytes = 1048576 conversion_enabled = true ec_data = 8 ec_code = 4 diff --git a/doc/working/plan-access-streaming-io.md b/doc/working/plan-access-streaming-io.md index 0ee8a3f22..85b75e7a4 100644 --- a/doc/working/plan-access-streaming-io.md +++ b/doc/working/plan-access-streaming-io.md @@ -103,6 +103,11 @@ while keeping secrets in the environment. - Confirmed 8+2 partial EC with two 1 MiB data shards: incremental parity matches the zero-filled reference and recovers two data failures or mixed data/code failures. The focused EC test is in `lib/crowdb-common/rust/tests/ec_test.rs`. +- The 8+2 layout is only a partial-EC test example. Production defaults remain + 8+4; deployments may choose 4+2 or 2+1. The access configuration records the + current 1 MiB disk block size, default EC data and code counts, and the 0.9 + small-object ratio. The small routing bound is the ratio multiplied by one + strip's data capacity; mirrored strips use one data block. - Chunk read stream normalizes locations once per stream, schedules up to three concurrent physical reads of at most 1 MiB each, and caches valid Chunk layouts across windows. Credits remain reserved while HTTP holds payload views. From 709fc18a227f059a06fd43c60b7d36c31e9fea8d Mon Sep 17 00:00:00 2001 From: Gian Date: Mon, 28 Sep 2026 23:32:59 +0800 Subject: [PATCH 17/20] Align access writers with configured EC layout --- .../src/iceberg/file_http.rs | 19 ++++++++ .../src/iceberg/file_http/multipart.rs | 1 + .../src/iceberg/file_http/stream.rs | 13 ++--- app/crowdb-access-server/src/iceberg/http.rs | 18 +++++++ .../src/iceberg/runtime.rs | 19 +++++++- app/crowdb-access-server/src/main.rs | 47 +++++++++++++++---- app/crowdb-access-server/tests/config_test.rs | 15 ++++-- .../tests/iceberg_file_http_test.rs | 27 ++++++++--- .../templates/access.toml | 4 +- doc/design/config/design-crowdb-config.md | 6 +++ doc/working/plan-access-streaming-io.md | 14 +++++- 11 files changed, 149 insertions(+), 34 deletions(-) diff --git a/app/crowdb-access-server/src/iceberg/file_http.rs b/app/crowdb-access-server/src/iceberg/file_http.rs index 7d5d506c0..02c4d608a 100644 --- a/app/crowdb-access-server/src/iceberg/file_http.rs +++ b/app/crowdb-access-server/src/iceberg/file_http.rs @@ -11,6 +11,8 @@ use crowdb_access_iceberg::file::{ use crowdb_access_iceberg::key::OperationId; use crowdb_access_s3::auth::{RawAuthRequest, StreamingPayloadVerifier}; use crowdb_access_s3::native_buffer::{NativeBodyAllocator, NativeBodyReceiver}; +use crowdb_chunk_client::{ChunkClientConfig, LargeWritePolicy}; +use crowdb_common::ec::EcScheme; use hyper::body::Incoming; use hyper::http::header::{ACCEPT_RANGES, CONTENT_LENGTH, CONTENT_RANGE, ETAG, RANGE}; use hyper::{Method, Request, Response, StatusCode}; @@ -37,6 +39,7 @@ pub(super) struct FileHttp { responses: FileResponseBudget, uploads: FileUploadBudget, small_threshold_exclusive: usize, + large_write: LargeWritePolicy, native_allocator: Option>, region: String, limits: FileServiceLimits, @@ -79,6 +82,10 @@ impl FileHttp { responses: FileResponseBudget::new(64).map_err(|_| FileGrantError::Invalid)?, uploads: FileUploadBudget::new(64).map_err(|_| FileGrantError::Invalid)?, small_threshold_exclusive: crate::config::SmallWriteConfig::default().threshold_exclusive(), + large_write: LargeWritePolicy { + ec_scheme: EcScheme::new(8, 4), + client: Arc::new(ChunkClientConfig::default()), + }, native_allocator, region, limits: FileServiceLimits { @@ -98,6 +105,17 @@ impl FileHttp { Ok(()) } + pub(super) fn set_large_write(&mut self, policy: LargeWritePolicy) -> Result<(), FileGrantError> { + if policy.ec_scheme.data_num == 0 + || policy.ec_scheme.code_num == 0 + || policy.client.read_buffer_size == 0 + { + return Err(FileGrantError::Invalid); + } + self.large_write = policy; + Ok(()) + } + pub(super) async fn dispatch( self: &Arc, catalog: &CatalogRepository, @@ -229,6 +247,7 @@ impl FileHttp { digest, native_receiver, self.small_threshold_exclusive, + &self.large_write, ) .await?; let published = self diff --git a/app/crowdb-access-server/src/iceberg/file_http/multipart.rs b/app/crowdb-access-server/src/iceberg/file_http/multipart.rs index ba1d5a740..e9a9de6a6 100644 --- a/app/crowdb-access-server/src/iceberg/file_http/multipart.rs +++ b/app/crowdb-access-server/src/iceberg/file_http/multipart.rs @@ -202,6 +202,7 @@ impl FileHttp { digest, native_receiver, self.small_threshold_exclusive, + &self.large_write, ) .await?; ( diff --git a/app/crowdb-access-server/src/iceberg/file_http/stream.rs b/app/crowdb-access-server/src/iceberg/file_http/stream.rs index e4459472c..c509c61b5 100644 --- a/app/crowdb-access-server/src/iceberg/file_http/stream.rs +++ b/app/crowdb-access-server/src/iceberg/file_http/stream.rs @@ -1,12 +1,10 @@ use std::fmt::Write; -use std::sync::Arc; use crowdb_access_iceberg::file::{ ContentFormat, FileContent, FileIdentity, FileKind, FileLocation, FileRecord, }; use crowdb_access_s3::native_buffer::NativeBodyReceiver; -use crowdb_chunk_client::{ChunkClientConfig, ChunkIoClient, ChunkIoWriter, LargeWritePolicy}; -use crowdb_common::ec::EcScheme; +use crowdb_chunk_client::{ChunkIoClient, ChunkIoWriter, LargeWritePolicy}; use crowdb_protocol::frame::MAX_FRAME_PAYLOAD_BYTES; use http_body_util::BodyExt; use hyper::body::Bytes; @@ -29,6 +27,7 @@ pub(super) async fn upload( expected_sha256: Option<[u8; 32]>, native_receiver: Option<&NativeBodyReceiver>, small_threshold_exclusive: usize, + large_write: &LargeWritePolicy, ) -> Result { let _permit = budget.acquire().map_err(|_| FileS3ErrorCode::SlowDown)?; let small = declared_length @@ -53,13 +52,7 @@ pub(super) async fn upload( ) } } else { - let mut large = client.prepare_large_write( - declared_length, - LargeWritePolicy { - ec_scheme: EcScheme::new(8, 4), - client: Arc::new(ChunkClientConfig::default()), - }, - ); + let mut large = client.prepare_large_write(declared_length, large_write.clone()); large .wait_until_prepared() .await diff --git a/app/crowdb-access-server/src/iceberg/http.rs b/app/crowdb-access-server/src/iceberg/http.rs index 26431d4c4..fcb2f55a1 100644 --- a/app/crowdb-access-server/src/iceberg/http.rs +++ b/app/crowdb-access-server/src/iceberg/http.rs @@ -14,6 +14,7 @@ use crowdb_access_iceberg::catalog::{ }; use crowdb_access_iceberg::wire::{BearerAuthenticator, CatalogConfig, IcebergErrorResponse}; use crowdb_access_s3::native_buffer::NativeBodyAllocator; +use crowdb_chunk_client::LargeWritePolicy; use hyper::body::Incoming; use hyper::server::conn::http1; use hyper::service::service_fn; @@ -114,6 +115,23 @@ impl IcebergHttpService { Ok(self) } + /// Applies the configured EC layout and data-block size to native large writes. + /// # Errors + /// Rejects an unavailable file service or invalid write policy. + pub fn with_large_write_policy( + mut self, + policy: LargeWritePolicy, + ) -> Result { + Arc::get_mut( + self.files + .as_mut() + .ok_or(crowdb_access_iceberg::file::FileGrantError::Invalid)?, + ) + .ok_or(crowdb_access_iceberg::file::FileGrantError::Invalid)? + .set_large_write(policy)?; + Ok(self) + } + /// # Errors /// Rejects invalid namespace token signing configuration. pub fn with_namespaces( diff --git a/app/crowdb-access-server/src/iceberg/runtime.rs b/app/crowdb-access-server/src/iceberg/runtime.rs index 072c31653..4c143c2fa 100644 --- a/app/crowdb-access-server/src/iceberg/runtime.rs +++ b/app/crowdb-access-server/src/iceberg/runtime.rs @@ -8,10 +8,14 @@ use crowdb_access_iceberg::catalog::{ use crowdb_access_iceberg::operation::{ManagementAction, ManagementRequest, RequestIdentity}; use crowdb_access_iceberg::wire::BearerAuthenticator; use crowdb_access_s3::native_buffer::NativeBodyAllocator; -use crowdb_chunk_client::{ChunkIoClient, ChunkIoClientConfig, ChunkReadPolicy, SmallWritePolicy}; +use crowdb_chunk_client::{ + ChunkClientConfig, ChunkIoClient, ChunkIoClientConfig, ChunkReadPolicy, LargeWritePolicy, + SmallWritePolicy, +}; use crowdb_chunk_kv_client::{ ChunkKvClient, ChunkKvRpcTransport, ClientConfig, Group0ChunkKvRangeCatalogSource, }; +use crowdb_common::ec::EcScheme; use crowdb_kv_client::{ClientConfig as KvConfig, CrowdbKvClient}; use tokio::net::TcpListener; @@ -194,6 +198,16 @@ async fn start_listener( .native_budget_bytes .unwrap_or(256 * 1024 * 1024); let native_allocator = Arc::new(NativeBodyAllocator::new(native_budget, 1024 * 1024)?); + let large_write = LargeWritePolicy { + ec_scheme: EcScheme::new( + access_config.small_write.ec_data, + access_config.small_write.ec_code, + ), + client: Arc::new(ChunkClientConfig { + read_buffer_size: access_config.small_write.disk_block_bytes, + ..ChunkClientConfig::default() + }), + }; let mut service = IcebergHttpService::new(repository.clone(), authentication, timeout) .with_namespaces(store.clone())? .with_fileio_native( @@ -202,7 +216,8 @@ async fn start_listener( "us-east-1".into(), Some(native_allocator), )? - .with_small_object_threshold(access_config.small_write.threshold_exclusive())?; + .with_small_object_threshold(access_config.small_write.threshold_exclusive())? + .with_large_write_policy(large_write)?; if authority.admission_bounds.delegated_access_ms >= 900_000 { let endpoint = std::env::var("CROWDB_ICEBERG_PUBLIC_URI").unwrap_or_else(|_| format!("http://{address}")); diff --git a/app/crowdb-access-server/src/main.rs b/app/crowdb-access-server/src/main.rs index 91b7b0c54..dfe8eb8f4 100644 --- a/app/crowdb-access-server/src/main.rs +++ b/app/crowdb-access-server/src/main.rs @@ -26,6 +26,8 @@ use crowdb_access_server::s3::{serve, ProductionS3Operations, S3Dispatcher, S3Se #[cfg(feature = "s3")] use crowdb_access_server::storage::S3StorageClients; #[cfg(feature = "s3")] +use crowdb_chunk_client::SmallWritePolicy; +#[cfg(feature = "s3")] #[cfg(feature = "s3")] use crowdb_common::ec::EcScheme; #[cfg(feature = "s3")] @@ -75,7 +77,7 @@ async fn run_s3(access_config: &AccessConfig) -> Result<(), Box Result<(), Box, small_write_limit: usize, + ec_scheme: EcScheme, ) -> Result> { let mut config = S3ServiceConfig::basic(tenant, continuation_key, small_write_limit); + config.large_write.ec_scheme = ec_scheme; if let Some(limit) = access.s3.list_scan_items { config.list_scan_items = limit; } @@ -226,14 +231,7 @@ fn configure_large_write( config: &mut S3ServiceConfig, access: &AccessConfig, ) -> Result<(), Box> { - let ec_data = configured_usize(access.s3.ec_data, "CROWDB_S3_EC_DATA")? - .unwrap_or(config.large_write.ec_scheme.data_num); - let ec_code = configured_usize(access.s3.ec_code, "CROWDB_S3_EC_CODE")? - .unwrap_or(config.large_write.ec_scheme.code_num); - if ec_data == 0 || ec_code == 0 { - return Err("CROWDB S3 EC data and code counts must be nonzero".into()); - } - config.large_write.ec_scheme = EcScheme::new(ec_data, ec_code); + Arc::make_mut(&mut config.large_write.client).read_buffer_size = access.small_write.disk_block_bytes; if let Some(max_chunk_size) = configured_u64(access.s3.max_chunk_size, "CROWDB_S3_MAX_CHUNK_SIZE")? { if max_chunk_size == 0 { return Err("CROWDB S3 max chunk size must be nonzero".into()); @@ -243,6 +241,35 @@ fn configure_large_write( Ok(()) } +#[cfg(feature = "s3")] +fn s3_ec_scheme(access: &AccessConfig) -> Result> { + let ec_data = + configured_usize(access.s3.ec_data, "CROWDB_S3_EC_DATA")?.unwrap_or(access.small_write.ec_data); + let ec_code = + configured_usize(access.s3.ec_code, "CROWDB_S3_EC_CODE")?.unwrap_or(access.small_write.ec_code); + if ec_data == 0 || ec_data > 32 || ec_code == 0 { + return Err("CROWDB S3 EC data and code counts are invalid".into()); + } + Ok(EcScheme::new(ec_data, ec_code)) +} + +#[cfg(feature = "s3")] +fn s3_write_routing( + access: &AccessConfig, +) -> Result<(EcScheme, SmallWritePolicy, usize), Box> { + let ec_scheme = s3_ec_scheme(access)?; + let mut config = access.small_write.clone(); + config.ec_data = ec_scheme.data_num; + config.ec_code = ec_scheme.code_num; + let policy = config.policy(); + policy.validate()?; + let threshold = config.threshold_exclusive(); + if threshold > policy.object_limit { + return Err("S3 small-object threshold exceeds the shared writer limit".into()); + } + Ok((ec_scheme, policy, threshold)) +} + #[cfg(feature = "s3")] async fn issue_user(args: &[String], access: &AccessConfig) -> Result<(), Box> { let command = args.first().ok_or("missing S3 user command")?; diff --git a/app/crowdb-access-server/tests/config_test.rs b/app/crowdb-access-server/tests/config_test.rs index 1f54fed77..57e23e853 100644 --- a/app/crowdb-access-server/tests/config_test.rs +++ b/app/crowdb-access-server/tests/config_test.rs @@ -13,7 +13,9 @@ fn tracked_access_configs_load_and_set_bounded_read_resources() { load_from_file(&root.join("conf/crowdb_access_server_config.toml")).unwrap(); let container: AccessConfig = load_from_file(&root.join("../../container/single-node-container/templates/access.toml")).unwrap(); - for config in [canonical, container] { + for (config, expected_ec, expected_threshold) in + [(canonical, (8, 4), 7_549_748), (container, (2, 1), 1_887_437)] + { assert_eq!(config.read.stream_slots, 3); assert_eq!(config.read.stream_window_bytes, 1024 * 1024); assert_eq!(config.read.global_stream_bytes, 256 * 1024 * 1024); @@ -21,8 +23,15 @@ fn tracked_access_configs_load_and_set_bounded_read_resources() { assert_eq!(config.read.policy().stream_slots, 3); assert_eq!(config.small_write.memory_budget_bytes, 1280 * 1024 * 1024); assert_eq!(config.small_write.disk_block_bytes, 1024 * 1024); - assert_eq!((config.small_write.ec_data, config.small_write.ec_code), (8, 4)); - assert_eq!(config.small_write.threshold_exclusive(), 7_549_748); + assert_eq!( + (config.small_write.ec_data, config.small_write.ec_code), + expected_ec + ); + assert_eq!( + (config.s3.ec_data, config.s3.ec_code), + (Some(expected_ec.0), Some(expected_ec.1)) + ); + assert_eq!(config.small_write.threshold_exclusive(), expected_threshold); assert_eq!(config.small_write.policy().max_pipelines, 32); assert!(config.s3.listen.is_some()); assert!(config.iceberg.listen.is_some()); diff --git a/app/crowdb-access-server/tests/iceberg_file_http_test.rs b/app/crowdb-access-server/tests/iceberg_file_http_test.rs index 8e690ff55..e53b4d4c0 100644 --- a/app/crowdb-access-server/tests/iceberg_file_http_test.rs +++ b/app/crowdb-access-server/tests/iceberg_file_http_test.rs @@ -23,6 +23,8 @@ use crowdb_access_iceberg::key::OperationId; use crowdb_access_iceberg::operation::{ManagementAction, ManagementRequest, RequestIdentity}; use crowdb_access_iceberg::wire::BearerAuthenticator; use crowdb_access_server::config::SmallWriteConfig; +use crowdb_protocol::chunkdb::rpc::{QueryChunkRequest, Strip}; +use crowdb_test_harness::chunkdb::make_client as make_chunkdb_client; use md5::{Digest, Md5}; use reqwest::{Client, Method}; use std::fmt::Write as _; @@ -473,12 +475,25 @@ async fn ordinary_put_size_matrix_streams_and_reads_ranges() { .unwrap() .unwrap(); assert_eq!(record.length, size as u64); - assert!(!record - .content - .locations(record.length) - .unwrap() - .unwrap() - .is_empty()); + let locations = record.content.locations(record.length).unwrap().unwrap(); + assert!(!locations.is_empty()); + if size == 12 * 1024 * 1024 { + let chunkdb = make_chunkdb_client(stack.cluster.make_service_registry_client()); + let chunk = chunkdb + .query_chunk(QueryChunkRequest { + chunk_id: locations[0].chunk_id, + }) + .await + .unwrap() + .chunk + .unwrap(); + let ec = chunk.strips.iter().find_map(|strip| match strip.strip.as_ref() { + Some(Strip::EcStrip(ec)) => Some(ec), + _ => None, + }); + let ec = ec.expect("large Iceberg write has an EC strip"); + assert_eq!((ec.data_num, ec.code_num), (8, 4)); + } let get_started = Instant::now(); let mut response = client.send(Method::GET, &object, "", b"", false).await; assert_eq!(response.status(), 200); diff --git a/container/single-node-container/templates/access.toml b/container/single-node-container/templates/access.toml index f64ddc054..4eb6885f9 100644 --- a/container/single-node-container/templates/access.toml +++ b/container/single-node-container/templates/access.toml @@ -16,8 +16,8 @@ recovery_memory_bytes = 268435456 threshold_ratio = 0.9 disk_block_bytes = 1048576 conversion_enabled = true -ec_data = 8 -ec_code = 4 +ec_data = 2 +ec_code = 1 memory_budget_bytes = 1342177280 queue_capacity = 1024 min_pipelines = 1 diff --git a/doc/design/config/design-crowdb-config.md b/doc/design/config/design-crowdb-config.md index 84aee0207..e3fc8145e 100644 --- a/doc/design/config/design-crowdb-config.md +++ b/doc/design/config/design-crowdb-config.md @@ -97,6 +97,12 @@ and bearer tokens remain in the environment. For access settings that predate th file, an explicitly populated TOML value takes precedence over the legacy environment variable; an omitted TOML value keeps the legacy behavior. +The small-object routing bound is `threshold_ratio × disk_block_bytes × EC data +shards`; mirrored strips use one data shard. `[small_write]` supplies the default +EC layout for both access writers, and S3's EC override changes its small and +large writers together. The default is 8+4 with 1 MiB data blocks and ratio +0.9. The single-node container profile uses 2+1. + All schemas tolerate unknown keys so a newer file can be staged before a binary upgrade. A known key with the wrong type or invalid value rejects the candidate. Omitted fields take their typed defaults. diff --git a/doc/working/plan-access-streaming-io.md b/doc/working/plan-access-streaming-io.md index 85b75e7a4..3a491f80a 100644 --- a/doc/working/plan-access-streaming-io.md +++ b/doc/working/plan-access-streaming-io.md @@ -107,7 +107,19 @@ while keeping secrets in the environment. 8+4; deployments may choose 4+2 or 2+1. The access configuration records the current 1 MiB disk block size, default EC data and code counts, and the 0.9 small-object ratio. The small routing bound is the ratio multiplied by one - strip's data capacity; mirrored strips use one data block. + strip's data capacity; mirrored strips use one data block. The single-node + container selects 2+1 for both access protocols and their small-write pool. +- Official Rust Iceberg SDK integration passed all five cases across its two + test binaries. Apache REST Compatibility Kit passed against the native + listener. A release/null-DiskIO ordinary PUT/GET matrix with default 8+4 + passed, including stored EC layout verification: 10 KiB 36/3 ms, 1 MiB + 76/13 ms, 12 MiB 334/60 ms, 100 MiB 2787/215 ms. These are API times, + not physical NVMe throughput. +- The Boto3 full-stack suite passed 18/18 with 2+1 EC overrides, including + small routing, lost replies, every service restart and benchmarks. Its + debug/null-DiskIO ordinary PUT/GET matrix was 10 KiB 37/48 ms, 1 MiB + 183/48 ms, 12 MiB 1303/296 ms, 100 MiB 10669/2612 ms. Different build + profiles make this matrix unsuitable for direct S3/Iceberg comparison. - Chunk read stream normalizes locations once per stream, schedules up to three concurrent physical reads of at most 1 MiB each, and caches valid Chunk layouts across windows. Credits remain reserved while HTTP holds payload views. From 19bcfae14fc5f9596b06cca59eefdc2f4edecbf6 Mon Sep 17 00:00:00 2001 From: Gian Date: Mon, 28 Sep 2026 23:35:26 +0800 Subject: [PATCH 18/20] Validate supported access data block sizes --- app/crowdb-access-server/src/config.rs | 3 ++- app/crowdb-access-server/tests/config_test.rs | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/crowdb-access-server/src/config.rs b/app/crowdb-access-server/src/config.rs index 907fb03ea..b607fea83 100644 --- a/app/crowdb-access-server/src/config.rs +++ b/app/crowdb-access-server/src/config.rs @@ -205,8 +205,9 @@ impl BaseConfig for AccessConfig { if !self.small_write.threshold_ratio.is_finite() || self.small_write.threshold_ratio <= 0.0 || self.small_write.threshold_ratio > 1.0 - || self.small_write.disk_block_bytes == 0 + || self.small_write.disk_block_bytes < 128 * 1024 || self.small_write.disk_block_bytes > 1024 * 1024 + || !self.small_write.disk_block_bytes.is_power_of_two() || self.small_write.ec_data == 0 || self.small_write.ec_data > 32 || self.small_write.threshold_exclusive() > self.small_write.policy().object_limit diff --git a/app/crowdb-access-server/tests/config_test.rs b/app/crowdb-access-server/tests/config_test.rs index 57e23e853..028f46251 100644 --- a/app/crowdb-access-server/tests/config_test.rs +++ b/app/crowdb-access-server/tests/config_test.rs @@ -88,4 +88,8 @@ fn unsupported_disk_block_size_is_rejected_before_routing() { assert!(config.validate().is_err()); config.small_write.disk_block_bytes = 2 * 1024 * 1024; assert!(config.validate().is_err()); + config.small_write.disk_block_bytes = 3 * 1024; + assert!(config.validate().is_err()); + config.small_write.disk_block_bytes = 768 * 1024; + assert!(config.validate().is_err()); } From 9c899bbda89844913530ba47bba9edc9ab25e0e6 Mon Sep 17 00:00:00 2001 From: Gian Date: Tue, 29 Sep 2026 00:11:34 +0800 Subject: [PATCH 19/20] Close shared access streaming requirement --- ...R190-access-iceberg-shared-streaming-io.md | 183 ---------- doc/backlog/backlog.md | 7 - doc/working/plan-access-streaming-io.md | 313 ------------------ doc/working/test.md | 5 +- 4 files changed, 3 insertions(+), 505 deletions(-) delete mode 100644 doc/backlog/R190-access-iceberg-shared-streaming-io.md delete mode 100644 doc/working/plan-access-streaming-io.md diff --git a/doc/backlog/R190-access-iceberg-shared-streaming-io.md b/doc/backlog/R190-access-iceberg-shared-streaming-io.md deleted file mode 100644 index 848ad88b7..000000000 --- a/doc/backlog/R190-access-iceberg-shared-streaming-io.md +++ /dev/null @@ -1,183 +0,0 @@ - - - -### R190: access — Shared S3 and Iceberg streaming data path - -Status: Ready after R187 completion, at the user's request. Begin with a -complete read/write/delete/GC flow review before implementation. - -## Problem - -Iceberg FileIO treats each roughly 64 KiB leaf as a separately durable small -object. Each leaf registers physical ownership through six catalog reads and -one conditional write, then waits for the Chunk readable cursor. Receiving the -next leaf waits for that entire chain. Reads fetch individual leaves and copy -returned bytes. S3 already receives into 1 MiB owners, frames at 64 KiB, and uses -whole-object Chunk writers and lazy read streams. - -A measured 5 MiB release-build upload on the native null-DiskIO stack took -2.68–2.79 seconds for ordinary PUT and 2.09–2.18 seconds for UploadPart, excluding -multipart completion. These are API measurements, not NVMe throughput. - -Root designs: [Iceberg](../design/access-server/iceberge/design-crowdb-iceberg.md), -[S3](../design/access-server/s3/design-crowdb-access-s3.md), and -[Chunk I/O](../design/chunkio/design-crowdb-chunkio.md). - -## Solution - -The user-selected data path is shared streaming infrastructure with independent -S3 and Iceberg metadata semantics: - - HTTP receive owner (1 MiB) -> Chunk writer (64 KiB frames) -> DiskIO - durable complete locations -> one atomic file/part publication point - published locations -> Chunk read stream -> owner-backed HTTP response - -1. Share deferred HTTP receive-provider installation and bounded native owner - allocation. Authenticate and admit before reading bodies. For objects under - 1 MiB, receive the complete body and submit it to the selected write pipeline - once. For larger bodies, submit each ready 1 MiB receive owner to the write - pipeline while reception continues, then submit the final partial owner. - This receive cadence is independent of the small-object routing threshold - and the 64 KiB integrity frame. Preserve signed AWS-chunked decoding, - checksums and unknown-length bounded streaming. -2. Write a whole file or multipart part through the Chunk writer selected by - object size. Do not register catalog intents or await a durable cursor per - frame. A frame is transport/integrity granularity, not a catalog transaction. - Select the shared small-object pipeline when logical bytes are strictly below - a configurable ratio, initially 0.9, of one strip's data capacity. Larger - objects and uploads whose total length is unknown at admission own their - chunks. Unknown-length uploads stream into that dedicated writer without - buffering to the small-object threshold. EC tails encode only present data - shards; absent data positions contribute zero without writing empty blocks - to DiskIO. -3. Publish complete immutable file or part metadata only after data completion, - length validation and HTTP transfer checksum verification. Require at least - one verified request integrity declaration for Iceberg PUT and UploadPart: - `Content-MD5`, an S3 checksum header/trailer, or a signed payload. Reject a - request with none before reading its body. Treat - content as opaque bytes; do not parse JSON, Avro, Parquet, ORC or Puffin on - the file IO path. A client that supplied a format validates it after reading; - CROWDB modules that generate format files validate their own output. Keep - fencing, conflicting-path rejection and ambiguous-result resolution. - Readers cannot observe partial data. -4. Stream GET and Range through the same Chunk read machinery as S3, retaining - owner-backed buffers and bounded backpressure. Read complete overlapping - 64 KiB frames and verify their CRC before exposing requested bytes. Keep - Iceberg credentials, generation checks and GC retention protection. - Use the same 64 KiB frame for Iceberg in this requirement. A possible larger - Iceberg frame remains a later measured choice; do not assume Parquet or - Iceberg checksums replace Chunk frame validation. - Schedule selected strip blocks as an ordered producer-consumer pipeline. - Each read stream has a configurable number of slots, initially three; each - slot holds at most 1 MiB of physical read data and its verified frame views. - Read slots concurrently, but deliver frames in logical order. If the first - slot waits for recovery, completed later slots wait within the fixed budget; - do not schedule a replacement until the consumer releases a slot. Keep the - slot charged while its owner-backed payload remains in the HTTP body, so - socket backpressure bounds retained RPC buffers as well as queued reads. - Also bound total retained read bytes across concurrent streams; a per-stream - slot count alone cannot bound server memory. Account for recovery scratch - space separately from the normal read slots. - Verify each complete 64 KiB frame before exposing its requested payload. - Normal reads must not copy payload in the RPC, DiskIO, EC, Chunk or HTTP - layers; recovery may allocate the reconstructed bytes. Ensure the RPC pool - outlives every buffer retained by a response, including cancellation. -5. Make multipart completion concatenate the selected parts' chunk-location - arrays with adjusted logical offsets. It does not read part bytes or restore - the per-leaf write/commit path. Save each part's raw MD5 on upload and compute - the multipart ETag from the MD5 of the ordered raw MD5 values, followed by - `-`. Share protocol-neutral session, part and completion logic - with the later S3 MPU implementation, while keeping API-specific publication. - Preserve ordering, replay and atomic final-file visibility. - New file records do not require a whole-file SHA-256. Preserve the old - SHA-256 record variant and its full-read verification for existing files; - use a versioned descriptor for new files and verify complete 64 KiB frames - on reads. Calculate a payload SHA-256 only when the request explicitly - declares one, including signed payload verification or an optional SHA-256 - checksum. Multipart SHA-256, when requested, is a composite of part checksums, - not the SHA-256 of concatenated object bytes. Do not reread parts at Complete. - Keep the HTTP ETag separate from storage integrity metadata. -6. Publish one complete descriptor only after durable data completion. A crash - before publication may leave orphan chunks or shared ranges; do not add - per-chunk catalog intents or upload-owner records to the write hot path. - R92's future ChunkDB scanner will identify and reclaim these orphans after - proving they are absent from every published access descriptor and active - writer. Current delete/GC must never free published data before the - configured grace period, and - submitted writes must drain before any physical reuse. -7. Review delete and GC end to end alongside reads and writes: logical - invisibility, reference discovery, grace periods, cancelled writes, - shared ranges, compaction and physical reuse must form one coherent model. -8. Keep existing stored file descriptors readable, or implement an explicit - migration within this work; do not silently invalidate persisted volumes. - -## Dependencies - -- Existing native receive owners, prepared Chunk writers and Chunk read streams. -- Existing Iceberg file, multipart and GC contracts remain acceptance obligations. -- R92 will handle scanner discovery and reclamation of allocations abandoned - before publication; R168/R169/R147 contain other deferred shared-storage - reclamation work. R190 may leave unpublished allocations as orphans until - those requirements are implemented, but must preserve all published data. -- R188 remains a separate console-authority follow-up. - -## Acceptance - -- Given ordinary PUT bodies of 10 KiB, 1 MiB, 12 MiB and 100 MiB, upload - through S3 and Iceberg -> both use bounded - 1 MiB owners and 64 KiB frames; no catalog operation is issued per frame. - **Bounded shared ingress. Integration test.** -- Given a configured 0.9 ratio and 8+2 or mirror strip geometry, upload bodies - immediately below, at and above the derived data-capacity threshold -> only - below-threshold objects use a shared chunk. **Configurable routing. Integration test.** -- Given an 8+2 tail containing two data shards, seal and lose two real data - segments or one data and one parity segment -> recover exact bytes without - writing six empty data segments. **Partial EC recovery. Integration test.** -- Given signed chunks, corrupted signatures/checksums, short bodies and cancelled - requests, upload -> reject without publishing metadata; release owner credits - and drain in-flight writes. **No partial visibility. Integration test.** -- Given completed bytes, publish with a conflicting path or a lost reply -> keep - one complete authoritative outcome without overwriting different content. - **Atomic publication. Integration test.** -- Given full, cross-frame and cross-chunk ranges, read -> exact bytes, bounded - retained buffers, integrity checks and cancellation propagation. - **Shared bounded reads. Integration test.** -- Given a 100 MiB multipart upload (twenty 5 MiB parts), complete/replay/restart - -> one correct immutable file, no per-leaf rewrite/commit loop and no - premature part reclamation. - **Multipart correctness. E2E test.** -- Given saved MD5 values for selected MPU parts, Complete in a reordered subset - -> return the MD5 of their ordered raw digests with the selected part-count - suffix without a part read. **Metadata-only ETag. Integration test.** -- Given an unsigned upload without a requested SHA-256 checksum, write without - hashing the payload with SHA-256; given a declared signed payload or SHA-256 - checksum, validate it before publication. Existing SHA-256 file records remain - readable. **Conditional SHA-256. Integration test.** -- Given process loss before/after data completion and metadata publication, - recover -> no partial file is visible; unpublished allocations may remain - orphaned for R92; published data remain readable through the grace period. - **Crash publication boundary. E2E test.** -- Given published and unpublished data, delete and run GC across restart - -> logical deletion precedes physical reclamation; retained reads remain valid; - freed ranges are not reused before writes drain; unpublished orphan discovery - is deferred to R92. - **Delete/GC consistency. E2E test.** -- Given existing file descriptors, restart and read -> preserve bytes and ranges. - **Persisted-data readability. Integration test.** -- Given the same 5 MiB fixtures and build/storage profile, measure PUT, UploadPart - and GET -> record elapsed time and dependency-operation counts against the - baseline without raising deadlines or weakening assertions. - **Measured operation reduction. E2E test.** - -Commands: - -```sh -pixi run test-access-iceberg -pixi run test-access-s3 -pixi run test-access-server -pixi run -e s3-e2e test-boto3-e2e -pixi run -e iceberg-e2e test-iceberg-native -pixi run -e iceberg-e2e test-iceberg-sdk -pixi run rs-fmt-check -pixi run rs-lint -``` diff --git a/doc/backlog/backlog.md b/doc/backlog/backlog.md index 6b28d7658..4ff88de49 100644 --- a/doc/backlog/backlog.md +++ b/doc/backlog/backlog.md @@ -13,13 +13,6 @@ complexity, and dependency. Before implementation, follow the **Next R number: R191** — Bump this line in the same commit when adding a new item. -### Planned — Shared access streaming - -- **[R190](R190-access-iceberg-shared-streaming-io.md)** — align Iceberg PUT, - multipart and GET/Range with S3's bounded native receive and Chunk data path; - remove per-frame catalog transactions while preserving publication and recovery. - Ready for the full read, write, delete and GC review before implementation. - ### Next Milestone — Chunk-backed range KV R144 is a deferred merge follow-up to the completed chunk-KV server and routed diff --git a/doc/working/plan-access-streaming-io.md b/doc/working/plan-access-streaming-io.md deleted file mode 100644 index 3a491f80a..000000000 --- a/doc/working/plan-access-streaming-io.md +++ /dev/null @@ -1,313 +0,0 @@ - - - -# Shared Access Streaming I/O Plan - -Implements [R190](../backlog/R190-access-iceberg-shared-streaming-io.md). -Goal: share S3's whole-object write/read path with Iceberg, preserving authority -and crash recovery while eliminating per-frame catalog operations. - -Status: Bounded S3 and Iceberg reads, access configuration, conditional S3 -SHA-256, shared HTTP receive plumbing, Iceberg pin removal, location descriptors, -whole-object uploads and metadata-only MPU completion pass focused tests. -Catalog operation counts and a release/null-DiskIO 5 MiB comparison are recorded. -Remaining work is full acceptance, flow review and final gates. - -The access processes now have a shared typed TOML startup schema. The single-node -container renders it separately for S3 and Iceberg; it exposes read slots, -window size, retained bytes, recovery memory, small-write memory and pipeline -limits, S3 resources and Iceberg GC limits -while keeping secrets in the environment. - -## Execution - -- [~] **Complete flow review and counters**: map read/write/delete/GC, ownership, buffer - lifetime, publication and physical reuse for S3 and Iceberg. Review the whole - flow before implementation; include cancellation and crash boundaries. Record - read-window, location-normalization and Chunk layout-query counts through - CROWDB metrics, then compare with perf counters before changing read flow. -- [x] **Remove legacy request-level metadata amplification**: removed the - file-request, file-publication, delegated-credential and table-load GC pin writes; - removed the pin record, protocol schema, GC handling and manual commands. - No old Iceberg pin data exists, so no compatibility path is needed. - New streamed UploadPart uses one part CAS and no session writes. New-path - bytes remain opaque, and catalog operation counts are exposed in Iceberg - metrics. Legacy tree file records retain their original read behavior. -- [x] **Bounded ordered read pipeline**: replace the 64 MiB fetch window with - configurable per-stream slots (default three, at most 1 MiB physical data - each). Schedule strip reads concurrently, verify complete frames, retain - out-of-order results within those slots, and hand owner-backed payload views - to HTTP in order. Add a global retained-byte budget across streams and a - separate recovery scratch budget. Hold credits until the HTTP body releases the buffers; - cancel outstanding work when the response ends. Remove copies in normal EC - reads and in the RPC-to-DiskIO response handoff. Before retaining pooled RPC - buffers in HTTP, prove or extend pool lifetime through their final release. -- [x] **Remove repeated read-stream location work**: normalize a descriptor - once per stream, select only locations overlapping each window, and export - lock-free normalization, range-scan, window and layout-query counters from - Chunk I/O through S3 `/metrics`. Files: `lib/crowdb-chunk-client/src/chunk/chunk_reader.rs`, - `lib/crowdb-chunk-client/src/metrics.rs`, `lib/crowdb-chunk-client/src/client.rs`, - `lib/crowdb-access-s3/src/metrics.rs`. Focused Chunk reader and S3 metrics - tests pass; perf attribution remains under the active review task. -- [x] **Conditional S3 payload SHA-256**: maintain MD5 for the existing ETag, - and initialize payload SHA-256 only when a request declares one. Both generic - and native-owner upload paths select the digest before receiving body bytes. -- [x] **Shared receive plumbing**: extract deferred native HTTP receive-provider - installation from the S3 facade into a protocol-neutral access-server module; - install bounded native owners for admitted Iceberg PUT and UploadPart requests. - Keep S3 receive-provider behavior covered and verify Iceberg signed-body - decoding before handing native owners directly to the new Chunk writer. -- [x] **Complete stream descriptors and ownership**: define bounded durable - locations for complete files/parts. Preserve old SHA-256 descriptors and their verification; new descriptors - default to frame CRC and carry optional requested checksums separately from - the HTTP ETag. Published references remain protected through the GC grace period; the - user assigned unpublished orphan discovery to R92's future ChunkDB scanner, - so do not add upload-owner or per-chunk intent writes to the data path. -- [x] **Whole-object uploads**: reuse prepared Chunk writers, 1 MiB native owners - and 64 KiB frames; remove per-leaf catalog intent/durable-completion waits. - Reject Iceberg PUT/UploadPart lacking Content-MD5, an S3 checksum or a signed - payload before reading the body. Publish only after declared transfer checksum - and durable storage completion; leave client file formats opaque. -- [x] **Shared reads**: use Chunk read streams and owner-backed Bytes for full - GET and ranges; preserve integrity, the GC grace period and cancellation. -- [x] **Multipart completion**: compose validated completed parts without the - old serial per-leaf rewrite/commit path; retain recovery and terminal credits. - Save part MD5 values at upload and produce the final ETag from those values. - Compute SHA-256 for a part only when explicitly requested; preserve any - requested composite checksum as separate metadata without rereading parts. -- [ ] **Faults and measurements**: test cancellation, lost replies, crash points, - ownership reclamation, stale grants and existing records; compare identical - 5 MiB baseline plus ordinary 10 KiB/1 MiB/12 MiB/100 MiB PUTs and a - 100 MiB multipart upload (twenty 5 MiB parts), with matching build profiles. -- [ ] **Final gates and cleanup**: run affected S3/Iceberg suites, fmt/clippy, - update current architecture, close this requirement after the full reviewed flow passes. - -## Evidence - -- Existing native Iceberg writer uses 65,502-byte leaves. A 5 MiB upload has - 81 leaves plus a directory block. Each registers ownership with six reads - and one conditional write, then forces readable-cursor completion. -- Existing S3 selects the prepared large writer for this size and publishes - object metadata after `on_finish`; receive owners are configured at 1 MiB. -- Confirmed write receive cadence: an object below 1 MiB is received in full - before one push to the selected pipeline; larger objects hand off each ready - 1 MiB owner as reception continues, plus a final partial owner. This is - separate from both the 0.9 × strip-capacity routing threshold and 64 KiB - frame validation. Current S3 native owner handoff applies to known-length - large writers; R190 must extend the shared path accordingly. -- Release/null-DiskIO measurements: ordinary PUT 2791/2682/2752 ms; UploadPart - 2091/2184/2159 ms. All HTTP 200. Temporary probe was removed. -- Debug multipart intermittently exceeds its existing 10 s deadline even after - streaming MD5 verification. This remains unresolved; do not hide it by only - changing test profiles or raising the timeout. -- Confirmed 8+2 partial EC with two 1 MiB data shards: incremental parity matches - the zero-filled reference and recovers two data failures or mixed data/code - failures. The focused EC test is in `lib/crowdb-common/rust/tests/ec_test.rs`. -- The 8+2 layout is only a partial-EC test example. Production defaults remain - 8+4; deployments may choose 4+2 or 2+1. The access configuration records the - current 1 MiB disk block size, default EC data and code counts, and the 0.9 - small-object ratio. The small routing bound is the ratio multiplied by one - strip's data capacity; mirrored strips use one data block. The single-node - container selects 2+1 for both access protocols and their small-write pool. -- Official Rust Iceberg SDK integration passed all five cases across its two - test binaries. Apache REST Compatibility Kit passed against the native - listener. A release/null-DiskIO ordinary PUT/GET matrix with default 8+4 - passed, including stored EC layout verification: 10 KiB 36/3 ms, 1 MiB - 76/13 ms, 12 MiB 334/60 ms, 100 MiB 2787/215 ms. These are API times, - not physical NVMe throughput. -- The Boto3 full-stack suite passed 18/18 with 2+1 EC overrides, including - small routing, lost replies, every service restart and benchmarks. Its - debug/null-DiskIO ordinary PUT/GET matrix was 10 KiB 37/48 ms, 1 MiB - 183/48 ms, 12 MiB 1303/296 ms, 100 MiB 10669/2612 ms. Different build - profiles make this matrix unsuitable for direct S3/Iceberg comparison. -- Chunk read stream normalizes locations once per stream, schedules up to three - concurrent physical reads of at most 1 MiB each, and caches valid Chunk layouts - across windows. Credits remain reserved while HTTP holds payload views. -- The user confirmed 64 KiB write frames for both S3 and Iceberg for now. - Revisit a larger Iceberg frame only with evidence about actual read units - and end-to-end integrity coverage. -- Legacy Iceberg `FileHttp::execute` persisted one `GcPin` per file request, - `FileRepository::publish` added a publication pin, and the table loader and - credential endpoint also created pins; those writes have been removed. - `MultipartRepository::part` still reloads the session before and after - reading a part. `reserve_part` and `settle_part` serialize part uploads through - session revisions. `FileSealer::seal` reads complete non-JSON files before - format validation. These are separate from the per-leaf write intents and - must be removed or amortized in the R190 path. -- Frame decode previously copied each physical frame and payload, then copied - the growing output again for every adjacent range (quadratic total copying). - Extraction and payload slices now reuse `Bytes` ownership. All Chunk read - APIs return verified buffer arrays; streams send each buffer without joining. - `frame_decode_wait_ns` measures the decoder and `frame_parse_wait_ns` - measures parsing plus CRC. The removed range-merging counter previously - measured 5.9–7.5 ms on a 4 MiB release run; that merge no longer executes. -- The original bitwise CRC32C and byte-table replacement were too slow on the - 4 MiB mixed-read release run: `frame_parse_wait_ns` summed to 17.5 ms with - the table implementation. Hardware-dispatched CRC32C retains the seed-zero, - no-final-XOR wire checksum and reduced that sum to 1.49 ms. The independent - bitwise CRC reference and existing cross-language frame vector pass. On two - otherwise identical four-request release runs, measured read throughput was - 94.9 then 165.2 MiB/s; RPC and host noise remain, so this is directional. -- After removing payload range merging, the same four-request release fixture - passed at 173.8 MiB/s with 2.27 ms cumulative frame parse time. This is a - small concurrency-2 null-DiskIO sample, not a throughput target. Whole-object - and range reads now return arrays of verified `Bytes`; HTTP streams emit each - frame buffer without an object-sized copy. Callers that require contiguous - bytes, such as legacy Iceberg block parsing, materialize them explicitly. -- The normal TCP RPC receive path now detaches the buffer owner from its pool, - so DiskIO and the ordered S3 body retain the original payload allocation. - Split frames are CRC checked across buffer views without joining the payload. - The RDMA/non-system-pool fallback still copies once and needs a separate review. -- Legacy Iceberg tree records still use their per-leaf reader and full-file - digest. New location descriptors use the shared Chunk read stream and emit - verified owner-backed buffers without joining the object. -- The bounded Chunk read tests pass all 11 focused cases, the Access Server - target suite passes, and the S3 full-stack suite passes all 17 cases after - configuration wiring. These checks establish correctness of this slice; - they do not establish an Iceberg throughput improvement or complete R190. -- S3 single-part integrity now initializes payload SHA-256 only when a signed - payload digest is declared. Focused integrity and streaming tests, S3 - Clippy, Rust formatting, and all 17 S3 full-stack cases passed before the - streamed-writer change; the full stack is being rerun. The old Iceberg tree - descriptor retains its whole-file SHA-256, while new location descriptors - rely on frame checksums and verified request integrity. -- Shared objects larger than one frame enter the pipeline with the first 1 MiB - receive buffer. A bounded two-buffer channel feeds subsequent data while the - pipeline writes the current buffer. Cancellation seals the abandoned chunk - and opens another chunk for queued objects. S3 and Iceberg wait for the - readable cursor before publishing even a sub-64 KiB object. The focused - shared-object and cancellation E2E cases pass; the full suite is running. -- After rebasing onto `origin/main` at `a59d24fb`, the Java acceptance setup - prefetches Maven runtime dependencies before its offline SDK invocations. - Offline `exec:java` reached the Java program with all dependencies present. - The ChunkDB restart fixture now waits for a usable registry read instead of - sleeping for three seconds; its focused and full reader E2E suites pass. -- The Iceberg UploadPart response can use the part value just successfully - settled; reloading the session and part added four catalog reads. A separate - upload lookup now performs one part read and leaves the session CAS to reject - stale snapshots, removing two more catalog reads. The reserve CAS now - returns its pending session to settlement, removing another reload. The - focused repository test confirms exactly one read for the part lookup. - The remaining session - reservation/settlement CAS operations still serialize different part numbers; - removing them needs a completion snapshot that keeps selected overwritten - parts reachable when UploadPart races Complete. -- A native 100 MiB upload with twenty 5 MiB parts completed into one immutable - descriptor with twenty locations and the expected composite MD5 ETag. A new - Iceberg listener accepted Complete replay and returned the full verified - object. The focused end-to-end test passed with a 128 MiB request grant; - the ordinary 16 MiB test grant correctly rejects a 100 MiB full GET. -- The native publication crash matrix now uses the streamed Chunk client through - its fault-injection wrapper. Every PUT and MPU catalog write boundary passed - before/after listener loss and replay. Immutable PUT retries compare the - saved MD5 ETag and length, so a retry that allocated a different chunk still - resolves to the first published file; different ETags remain conflicts. -- New streamed Complete selections now persist each selected part's length, - ETag and exact location bytes in the existing selection payload. Streamed - publication consumes that snapshot without rereading part records. A focused - test replaces a selected part record after freeze and still publishes the - originally selected locations and composite ETag; the 100 MiB replay test - passes with the new selection format. The old selection format remains - readable for legacy multipart recovery. -- Streamed UploadPart now performs one CAS on its own part key and no session - reservation/settlement CAS. Distinct part numbers commit independently; a - lost CAS reply is resolved by reading only that part record. Create caps - `max_parts` by the staged-byte reservation divided by the per-part limit, - so concurrent parts cannot exceed reserved capacity without a shared - counter. Complete validates its snapshot against the per-session maxima - rather than mutable staged counters. The focused concurrency, lost-reply, - limits, ordinary HTTP MPU and 100 MiB restart/replay tests pass. - The 100 MiB E2E now sends each pair of 5 MiB parts concurrently; its - Complete, listener restart, replay and full GET still pass. The native - publication crash matrix also passes after the direct-CAS change. -- Iceberg `/metrics` now exposes lock-free cumulative catalog get/CAS/scan/delete - counts beside the existing request and Chunk I/O metrics. On three separate - release/null-DiskIO 5 MiB fixtures, PUT took 228/235/233 ms and used 16 - catalog gets plus two CAS; UploadPart took 179/201/176 ms and used ten gets - plus one CAS; full GET took 27/25/16 ms and used eight gets, no CAS. The - earlier release/null-DiskIO baseline was 2682-2791 ms PUT and 2091-2184 ms - UploadPart with the legacy per-leaf path. This shows an order-of-magnitude - improvement in this fixture; the request helper signs and copies the body, - and null-DiskIO is not a production storage latency profile. Remaining - catalog gets are request/session/context checks rather than frame writes. -- Streamed Complete now bounds the persisted selection payload before copying - part location arrays. The focused test rejects an oversized snapshot and an - oversized decoded payload. Retired-catalog GC reclaims a streamed file's - physical location after worker restarts; all 18 GC worker tests pass. -- Frozen streamed selections reject a mismatched encoded part count, out-of-bound - deserialization and trailing bytes. The focused publication suite passes all - seven tests. The full access-Iceberg, access-S3 and access-server library - suites pass. -- The native Iceberg release suite exposed two legacy test fixtures whose 8 MiB - small-write budget inherited an 8 MiB object limit plus 1 MiB pipeline shadow. - Both fixtures now set a 1 MiB object limit; their storage-restart and GC - capacity tests pass individually and within the rerun native suite. The - native file publication fault matrix, 100 MiB MPU replay, and 5 MiB profile - also pass. The table-commit crash matrix exited once while starting a recovery - listener at publish-stage 5/31, then passed every boundary on a complete - rerun. Listener startup now reports its exit status if this recurs. -- The Boto3 S3 full-stack suite passes all 17 cases, including fragmented - signed uploads, backpressured reads, lost PUT replies, object reads after - each service restart, and direct Chunk/S3 request-path benchmarks. Its - existing fixture disables the shared small-object route, so separate native - small-writer acceptance remains necessary. -- Java SDK namespace pagination and offline Maven dependency setup pass. The - FileIO acceptance then exposed an obsolete test that expected server-side - JSON parsing during multipart Complete. New streamed files intentionally - preserve user bytes, so that case now checks opaque Complete and GET. The - focused Java S3FileIO rerun passes, including small PUT/GET, multipart, - range seeks, immutable replay and exact table scope. Ordinary PUT size-matrix - cases for both APIs now report separate PUT and GET times. -- The ordinary Iceberg PUT matrix passed full GET digest, HEAD length and - beginning/frame-boundary/tail ranges at every size. In the release/null-DiskIO - fixture, PUT/GET timings were 10 KiB: 58/8 ms, 1 MiB: 107/16 ms, - 12 MiB: 396/62 ms and 100 MiB: 2716/201 ms. These include the signed - request helper's payload hash and body copy on PUT; no equivalent old-path - size matrix exists, so only the 5 MiB same-fixture comparison is a direct - before/after speedup. -- The S3 full-stack suite now runs with the default small-object threshold - instead of disabling small writes. All 18 cases pass, including the 10 KiB, - 1 MiB, 12 MiB and 100 MiB ordinary PUT/GET/range matrix. The metrics endpoint - exports small write submitted/completed/failure, buffer and wait counters; - the suite asserts actual small pipeline completions. In its debug/null-DiskIO - fixture, PUT/GET timings were 10 KiB: 36/47 ms, 1 MiB: 186/51 ms, - 12 MiB: 1501/322 ms and 100 MiB: 10835/2713 ms. These are not comparable - with the release Iceberg timings. The old slow-upload fixture had only 1 MiB - of payload, which became a small write under the default 8-data-shard - threshold; it now uses just over 8 MiB to exercise native large ingress. - Deterministic native backpressure remains covered by its focused allocator - tests instead of timing-dependent null-DiskIO overlap. -- The threshold contract is verified for 8+2 EC and mirror geometry: - with ratio 0.9 the exclusive upper bounds are 7,549,748 and 943,719 bytes. - A native Iceberg PUT test checks 7,549,747, 7,549,748 and 7,549,749 bytes; - only the first increments the small-write completion counter, and all three - return exact tail ranges. -- The final code-flow review found no new read or write catalog records on the - frame hot path. Streamed PUT stages one immutable file record and CASes its - location mapping (two CAS total in the 5 MiB fixture); UploadPart CASes only - its part key; Complete stores the selected snapshot and final descriptor - without reading part bytes. GET checks authority, then holds at most three - ordered Chunk read slots plus the configured global budget, verifies full - frame CRCs, and hands owner-backed buffers to HTTP. Response cancellation - drops the stream and its retained credits. GC observes retired catalog - authority and the grace deadline before reclaiming location ranges; the - existing 18 GC worker tests cover streamed-file reclamation after restart. - The two publication CAS operations and request-level catalog context checks - remain fixed per object rather than per frame; the 5 MiB counters quantify - their cost. Legacy tree records retain their old intent path for readability. - -## Files - -- Access server: shared body receive module, S3 dispatcher/operations, Iceberg - HTTP, body decoding, uploads, reads and runtime wiring. -- Access libraries: native buffers, streaming, file descriptors, multipart, - file validation, GC and record codecs. -- Chunk client/protocol: existing large writer, read streams and durable - allocation ownership; extend only where the shared path requires it. - -## Tests - -- Unit: checksums/framing, descriptor validation, bounded reads and lifecycle. -- Integration: native receive-provider tests, S3/Iceberg upload/read tests, - counted catalog calls and backwards-compatible descriptors. -- E2E: native file/multipart crash tests, official SDKs, container acceptance. diff --git a/doc/working/test.md b/doc/working/test.md index f50a528d8..196374ff6 100644 --- a/doc/working/test.md +++ b/doc/working/test.md @@ -77,8 +77,9 @@ and subprocess startup/shutdown, so feature changes and cold builds affect it. Counts are runner-reported cases, not assertions; ignored cases are excluded. Native Iceberg and Java/Rust/RCK SDK acceptance use release binaries, matching the published container profile. Component suites retain their default test -profile. Debug native upload deadline failures are tracked for the R190 I/O -review; successful release acceptance does not resolve that issue. +profile. The focused debug native 100 MiB multipart upload, completion, +restart, replay, and full read passed on 2026-09-29 in 54.40 s. Its previous +10 s completion deadline failure did not recur after the streaming I/O changes. Status icons: ✅ = PASS, ⚠️ = PASS with ignored tests, ❌ = FAIL, ⏳ = measurement pending. From eb0596eceeaaeb6c96155e6c2e67f44944e11e4f Mon Sep 17 00:00:00 2001 From: Gian Date: Tue, 29 Sep 2026 00:13:21 +0800 Subject: [PATCH 20/20] Consolidate repository documentation links --- .agents/skills/doc-design/SKILL.md | 2 +- .agents/skills/doc/SKILL.md | 5 +- AGENTS.md | 2 +- CHANGELOG.md | 4 +- CONTRIBUTING.md | 7 +- README.md | 182 +-- container/single-node-container/Dockerfile | 1 - container/single-node-container/README.md | 4 +- doc/assets/demo-cluster.mp4 | Bin 343573 -> 0 bytes doc/assets/demo-failover.mp4 | Bin 464566 -> 0 bytes doc/assets/demo-kv.mp4 | Bin 321595 -> 0 bytes doc/backlog/R188-console-group0-authority.md | 10 +- ...R189-access-iceberg-container-ecosystem.md | 4 +- .../iceberge/design-crowdb-iceberg.md | 2 +- doc/design/config/design-crowdb-config.md | 5 +- doc/doc_index.md | 2 - doc/user-manual/build_html.py | 585 --------- .../docker-single-node-user-guide.html | 486 ------- .../docker-single-node-user-guide.md | 169 --- doc/user-manual/user-guide.html | 1124 ----------------- doc/user-manual/user-guide.md | 1023 --------------- doc/working/plan-console-authority.md | 9 +- pixi.toml | 2 +- 23 files changed, 64 insertions(+), 3564 deletions(-) delete mode 100644 doc/assets/demo-cluster.mp4 delete mode 100644 doc/assets/demo-failover.mp4 delete mode 100644 doc/assets/demo-kv.mp4 delete mode 100644 doc/user-manual/build_html.py delete mode 100644 doc/user-manual/docker-single-node-user-guide.html delete mode 100644 doc/user-manual/docker-single-node-user-guide.md delete mode 100644 doc/user-manual/user-guide.html delete mode 100644 doc/user-manual/user-guide.md diff --git a/.agents/skills/doc-design/SKILL.md b/.agents/skills/doc-design/SKILL.md index a2b4f0438..0f3162d31 100644 --- a/.agents/skills/doc-design/SKILL.md +++ b/.agents/skills/doc-design/SKILL.md @@ -21,7 +21,7 @@ stable IDs such as `I1`. Describe current state only. Omit requirement numbers, change history, before/after prose, file paths, and line numbers. Refer to searchable symbols. Keep architecture in root docs, detail in sub-designs, and operations in the -user guide; link rather than repeat. +relevant design or component README; link rather than repeat. When explicitly promoting a working draft, remove temporary scaffolding and requirement references, rewrite as current state, update `doc/doc_index.md`, diff --git a/.agents/skills/doc/SKILL.md b/.agents/skills/doc/SKILL.md index 0da06acb2..1f2f0ace0 100644 --- a/.agents/skills/doc/SKILL.md +++ b/.agents/skills/doc/SKILL.md @@ -15,7 +15,7 @@ Start at `doc/doc_index.md`; open only the row and section matching the task. - `doc/design//design-crowdb-.md`: architecture and rationale. - `doc/design//design-crowdb--.md`: permanent topic detail. - `doc/design/kv/kv-*-flow-analysis.md`: permanent KV path analysis. -- `doc/user-manual/user-guide.md`: operations; generated HTML is not hand-edited. +- `doc/design/`: permanent architecture and operational behavior. - `doc/backlog/`: requirement index and analysis. - `doc/working/`: implementation plans and explicitly requested design drafts. @@ -29,9 +29,6 @@ Start at `doc/doc_index.md`; open only the row and section matching the task. - Split independent topics; delete working files when their work completes. - Prefer concrete, tight prose and raw-readable bullets. Remove filler, repetition, rhetorical openings, adjective lists, and excessive em dashes. -- Rebuild `user-guide.html` with - `pixi run -- python doc/user-manual/build_html.py` whenever its Markdown - source changes. When the target is a design, backlog requirement, design draft, or working plan, use only its matching `/doc-*` guide instead of this router. diff --git a/AGENTS.md b/AGENTS.md index a72e8e9ea..4a16f3f23 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -45,4 +45,4 @@ and transport exposed through FFI. the backlog index only for selection, ordering, or status. - Pre-push or explicitly requested code review: `/review`. - Design questions: one section selected through `doc/doc_index.md`. -- Operations/user behavior: `doc/user-manual/user-guide.md`. +- Operations/user behavior: the relevant `doc/design/` or component README. diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ff11d724..78fabdb2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,5 +35,5 @@ The intended image is `crowdb/crowdb-iceberg:v0.1.0-dev`; no published digest is recorded yet. The GUI is not ready for this container. Multi-node deployment, production hardening and data-format upgrades are outside this release. -See the [container guide](doc/user-manual/docker-single-node-user-guide.md) for -supported startup, persistence, credentials and recovery behavior. +See the container deployment files for supported startup, persistence, +credentials and recovery behavior. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 06131b708..403f07876 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -76,9 +76,10 @@ pixi run test-single-node-container - Add dependencies through the owning package manager and avoid newly published versions until they have had time for ecosystem review. -Start documentation work at `doc/doc_index.md`. Permanent architecture belongs -under `doc/design/`, user behavior in `doc/user-manual/user-guide.md`, future -contracts in `doc/backlog/`, and temporary execution plans in `doc/working/`. +Start documentation work at `doc/doc_index.md`. Permanent architecture and +operational behavior belong under `doc/design/` or the relevant component +README, future contracts in `doc/backlog/`, and temporary execution plans in +`doc/working/`. ## Code and tests diff --git a/README.md b/README.md index 79d5ec707..093732d29 100644 --- a/README.md +++ b/README.md @@ -6,161 +6,55 @@ [![CI](https://github.com/buzzcrow/crowdb/actions/workflows/ci.yml/badge.svg)](https://github.com/buzzcrow/crowdb/actions/workflows/ci.yml) [![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE) -CROWDB is a distributed storage platform for objects, tables, and AI datasets. -It owns the data path from S3, Iceberg, and native Dataset access through -distributed metadata and chunk storage to disk—and eventually GPU memory. - -Version `0.1.0-dev` is the first development release being prepared for public -evaluation. Use disposable data; production use and on-disk upgrade compatibility -are not supported. Dataset and direct GPU delivery remain planned work. - -- Use **S3** for familiar object access. -- Use **Iceberg** for native catalogs, tables, snapshots, and immutable files. -- **Dataset** is planned for samples, shards, tensors, batches, and direct data access. - -## Three Layers, One Data Path - -```text - Applications - S3 tools Table engines AI runtimes - | | | | - | HTTP | HTTP | | native - v v v v -+--------------------------------------------------------------------------+ -| LAYER 3 — ACCESS | -| | -| S3 Iceberg Dataset | -| [implemented] [implemented] [design] | -| HTTP objects HTTP tables HTTP + native client | -| | -| Access Server serves HTTP. Dataset native access can bypass it. | -+------------------------------------+-------------------------------------+ - | - v -+--------------------------------------------------------------------------+ -| LAYER 2 — CHUNK | -| | -| Distributed structures: Chunk Stream Chunk-KV | -| | | | -| Data path: chunk client -> Chunk I/O -> ChunkDB -> DiskIO -> DiskDB | -| | | -| Accelerated path: DiskIO buffer -- RDMA / GDS -------> GPU memory | -+------------------------------------+-------------------------------------+ - | - v -+--------------------------------------------------------------------------+ -| LAYER 1 — REUSABLE KV | -| | -| crowdb-kv multi-group Paxos WAL Group 0 crowdb-tree RPC | -| | -| A standalone distributed layer—not the product-level data model. | -+--------------------------------------------------------------------------+ -``` +CROWDB is a distributed storage platform for Iceberg tables, AI datasets, and +S3 objects. Each access model keeps its own semantics while sharing one storage +core for distributed state, placement, protection, streaming, and recovery. -## Why CROWDB? - -Storage systems are usually assembled by stacking one system on another: table -metadata over object storage, dataset libraries over table or object APIs, and -new accelerators behind paths designed for disks and CPUs. Every boundary adds -another namespace, lifecycle, RPC, copy, and recovery model. When that boundary -becomes the bottleneck, the layers above it can only work around it. - -CROWDB exists to own the complete data path. S3 objects, Iceberg tables, and AI -datasets are intended as native access models over the same distributed storage core. They -share durability, placement, protection, and reclamation without pretending -that one model is merely a convention inside another. - -That control matters because both hardware and workloads keep changing. NVMe, -RDMA, GPUDirect Storage, and accelerator offload reshape more than one isolated -module. AI training and inference also need data to reach GPU memory without an -HTTP gateway or client CPU becoming the permanent middleman. Supporting those -changes cleanly requires control from protocol semantics down to buffers and -disk layout. - -The goal is a storage foundation that can evolve as one system: simple enough -to reason about, fast enough to justify owning the stack, and composed of -layers that remain useful independently. - -## Technical Foundation - -- **Parallel consensus:** crowdb-kv runs multiple independent Multi-Paxos slots - concurrently, with WAL durability, lease reads, and pluggable engines. -- **One protected chunk layer:** bounded streaming, mirrored small data, - strip-level erasure coding, shard repair, placement, and reclamation serve - every access model. -- **Chunk-based distributed structures:** Chunk Stream provides durable ordered - append. Chunk-KV uses range partitions that split and rebalance online while - reads and writes continue. -- **Native access models:** S3, Iceberg, and Dataset share the core without - being wrappers around one another. The planned Dataset model targets direct - topology access, RDMA and GPU delivery. - -## Where It Stands - -| Access model | Status | What it means | -| ------------ | ----------- | ---------------------------------------------------- | -| S3 | Implemented | Core HTTP object operations and bounded streaming | -| Iceberg | Implemented | Native catalog, FileIO and core v1/v2/v3 semantics | -| Dataset | Design | HTTP, topology-aware native client, and GPU delivery | +- **Iceberg:** native catalog and FileIO, implemented. +- **S3:** core HTTP object operations, implemented. +- **Dataset:** native access and direct GPU delivery, in design. -The KV, tree, DiskDB, ChunkDB, chunk I/O, Chunk Stream, Chunk-KV, RPC, -operations console, and core S3 foundation have working implementations. See -the [backlog](doc/backlog/backlog.md) for current delivery scope. - -## Quick Start - -The first Linux amd64 image is being prepared for manual publication. Once -`v0.1.0-dev` is published, start the Iceberg catalog and storage with Docker: - -```sh -docker run -d --name crowdb-iceberg \ - -p 127.0.0.1:80:80 \ - crowdb/crowdb-iceberg:v0.1.0-dev -``` - -Follow the [single-node Docker guide](doc/user-manual/docker-single-node-user-guide.md) -for startup checks, client credentials, persistent volumes and recovery. -For source builds and development with Pixi, see -[CONTRIBUTING.md](CONTRIBUTING.md). - -## Explore - -- [Access architecture](doc/design/access-server/design-crowdb-access-server.md) - — S3, Iceberg, Dataset, native access, and GPU delivery. -- [Documentation index](doc/doc_index.md) — every permanent architecture and - subsystem design. -- [User guide](doc/user-manual/user-guide.md) — setup, console, CLI, and - supported operations. -- [Single-node Docker guide](doc/user-manual/docker-single-node-user-guide.md) - — preview image, volume, credentials, clients, and recovery. -- [Backlog](doc/backlog/backlog.md) — what is implemented, in progress, and - planned. - -
-Current cluster demos - -### Cluster lifecycle +## Why CROWDB? -Bootstrap a cluster, register physical topology, create stores and Paxos groups, -and watch replicas elect a leader. +Storage bottlenecks move—from disks to CPUs, networks, and data movement—but +system boundaries tend to stay. A change that crosses a metadata service, an +object gateway, and a separate storage engine can become a negotiation between +systems rather than an improvement to one data path. - +CROWDB owns enough of that path to change it when workloads and hardware change. +S3 objects, Iceberg tables, and planned AI datasets are native access models +built over shared infrastructure, not conventions layered on top of one +another. The goal is not to claim novelty for Paxos, WALs, trees, or erasure +coding; it is to make their contracts agree on durability, placement, bounded +buffers, and recovery. -### KV operations +Read the full motivation in +[Why we’re building CROWDB](https://buzzcrow.github.io/blog/why-we-are-building-crowdb/). -Put, get, scan, and delete through a selected distributed group. +## Development preview - +Version `0.1.0-dev` is being prepared for public evaluation on Linux amd64. +Use disposable data. Production use and on-disk upgrade compatibility are not +supported, and Dataset and direct GPU delivery are not available yet. -### Failover and replica management +- [Project homepage](https://crowdb.dev/) +- [Quick start](https://crowdb.dev/docs/quickstart/) +- [Documentation](https://crowdb.dev/docs/) +- [Architecture](https://crowdb.dev/docs/architecture/) +- [Demos](https://crowdb.dev/demo/) -Expand a group, remove its leader, and continue operations after re-election. +## Repository - +The repository contains the storage implementation, tests, and source design +documents. Start with: -
+- [Contributing](CONTRIBUTING.md) for the development environment and workflow. +- [Documentation index](doc/doc_index.md) for subsystem designs. +- [Backlog](doc/backlog/backlog.md) for current delivery scope. +- [Single-node container](container/single-node-container/README.md) for image + development and packaging. -## Notes on AI-Assisted Development +## AI-assisted development The code in this project was written with AI assistance. The architecture, naming, module boundaries, and trade-offs remain human choices. AI is the @@ -168,4 +62,4 @@ compiler. The intent is mine. ## License -See [LICENSE](LICENSE). +Licensed under the [Apache License 2.0](LICENSE). diff --git a/container/single-node-container/Dockerfile b/container/single-node-container/Dockerfile index 682b73374..db18caefb 100644 --- a/container/single-node-container/Dockerfile +++ b/container/single-node-container/Dockerfile @@ -29,7 +29,6 @@ LABEL org.opencontainers.image.title="CROWDB Iceberg" \ org.opencontainers.image.description="Apache Iceberg REST catalog with native storage, in one container for development and testing." \ org.opencontainers.image.url="https://crowdb.dev/" \ org.opencontainers.image.source="https://github.com/buzzcrow/crowdb" \ - org.opencontainers.image.documentation="https://github.com/buzzcrow/crowdb/blob/v${PREVIEW_VERSION}/doc/user-manual/docker-single-node-user-guide.md" \ org.opencontainers.image.authors="Gian " \ org.opencontainers.image.licenses="Apache-2.0" \ org.opencontainers.image.revision="$SOURCE_REVISION" \ diff --git a/container/single-node-container/README.md b/container/single-node-container/README.md index facd8f9d2..e8b5b3dbd 100644 --- a/container/single-node-container/README.md +++ b/container/single-node-container/README.md @@ -3,8 +3,8 @@ # Single-node container development -For running CROWDB, see the [Docker user guide](../../doc/user-manual/docker-single-node-user-guide.md). -This page describes building from source on a Linux amd64 development or CI host. +This page describes building and running CROWDB from source on a Linux amd64 +development or CI host. ```sh pixi run build-single-node-container diff --git a/doc/assets/demo-cluster.mp4 b/doc/assets/demo-cluster.mp4 deleted file mode 100644 index 86ef09f76952caa963f73610b1244f0e14e86608..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 343573 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 JrA75;_+#~V@(|x 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

eEdO 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�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 rNGibHTWtiR*qYS-w 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

<2<*27j>yjQ>Iw7u|qAn*FFC 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ξ`7EiPLfj&HUuXlb{ zNi8B3=s!q&JE_3h0ISSTGFyse2Qdg53o0i(?!`u`MDWA-T0e__s5z6NQzdgdr(0bc zv=i(p90~T+ia|!~IebBx0>3r9(xQrKpxN(Ui`0Hvw4~Fatrk4B46m~I65iD5wOFNm zjPJOX6*@{=gKxFP6f$ol{4z8^T#kPD5J7wzJZH8}SyPoT+ewOI%D1!4NVS#XNP|%b z1B5#_ClzxmwTSw`4QE+P416kOtT#zC=imSU0{|4AQSq#|L=YcbM(HJW$knBWV#Yz-^ z#d9(uRq}j@+vb?mW!0S?^TL5WJKAHLq>|^FH{dO_2u)DJjtVl@8o!tV;Q_8Jtd0n- zgwqkxK&qY48qqT{GlqAI7+t!={%oXQLo-5q!FrE>Tq^ug%$>Amj%MIM=cTa8sRfjH zDat}>U*%4EJ6mJ}lZrW*Q zGWcMp!FQ#}*4~cC({rhUnYr8eME1#(!`57W3qL&C*JQ;IFfZr0A=FI+dx_*REGg3_UaF zN!w)>Jpc@Z&sq|^lY>TG;0gZ6#0_7^<(I(@O2F;w36wh56MH=}uexIVrG{(S#SUp= zE5%zgyI_oU3-;LvpIqb{|BS*V7Gb}LIpG(-og{j}g%R?|Wfj}+e>rKA+fkm8^-cyB zlomD?sosKiErY9yn)EyQ!QXnFSQ5Gl(D0#fgAINA=FhG%DR-VHn`|tis{b9>Ii_r%$4KuyY+muTC z(F5ZO3K*eJn}y#(xDBIK`S#`dh0{jh_OcI8rpe?PMKpy)R)CFB{QJ%2Gx6ljppfc` zt(J1peKfkcTC)yZX%hxis4`Sow{=`l zTD*^C4XGR3e0vTn?2e)dpy3&t{H>2lsd5r`Hy?Zj!lQ$Det$bIIwV4mvGME1=liHB zllR7hJR~7veRek`_sgQMh``T|i6Y(@j!gaRCVyk+G6fprqxFuzI|7$@HxQLTTU^j_ zQm>f2>QcR5-px2WPe}E6n|vC^u0J80SSC7Y#%z$xt#ht_O}p>XC^S!!Fi8y&_Nwy? zWciDD|NH_mi2}&y`K0)Wt0QGIut=6(;;4SX0z;jMF$x(Yjzs@f6xUJaELco?za9Gd zE6xk%g<^R;0OQI(f_#b2_^YF6h_>rhW!Dl-%g-#=5OG>l8Zsa$WePC?iE`1|XnZtj zBq4%hXUFKWLPr!|EqvP*C6HGx@p12Hl++~WwP7IB_vZpyHZkBsE3*V>cWXB$r2)}p zU*~nJgt$akB?b3wu&i?!NV7OjOQ!T6?#$PO+uU5b+X$tXv{{oq#-!eiirqKQgDKMuHQ$mYsN2)T+@c2x(7ZR zvW)7lPwm85RR-iIzv=Wg@z>B>CWp}%Oen(Cr1Mxe_riN$&Vw2S?23nmKEoU3wKoQ< z&kKpc+=aRPmqOf_@506ybIp5Twqdul+$hipA8nZaI87nB30PnUKeVRF{YPepbEb8) zpgAs|vdW%18~4Jg%BT!Zqfg#3K1{BCY<6lj7SvC_x&nIO+zH9@EZND&-F3(Blo=r& zn0z3N;`@4QurjU0Eis6lr)?%ED|9=(`a|WJIC85MGt9?b6^0d^#@Xd-V219^zs)Ko z(BrB4hY&^G`v0I8E2E-F*S6YH_2uLPbhq>ws|(e%$M7m2s-BO7j#W{aX?28R6qu*d z&z%?fi9T;0GHpYT@`Lh6OIf zbm_z-QbrD^7;xh|{rS!l^?wpamoh;SMhhJy?%RxK-n^u0wCl4AFrW48z4+*o+%^d> z7jUnzV0`66!X%+q9x(~2IU-PPGc*#b-7RD%ii4Cnf?AniJjsqoIYC?c5c5Q@e*inD zZ=CDADw%|0`gJ)@UZ87;00093UB|ljmei^MY344U&Kv+B;Q#;w(m|gtWRz6!L;nB( z1lNm-QrywXBecw+5i1S%zV|sM+`j4c%AvmIg(cGZ?p>|S>HZDEaq6$gr zPOu22Mf|n4JMK(mP8l$?mqVx~1ui(&PYP^byrUmu^?oBqikfP>F}kiz$#PDdizVp& zUbjVx9w0v{`;efC*=HF{I`8XM;k|YM4VVENrQbp<9*oe8P9qu-j0eoQ#>VkXxg05l zujj1A(1q1)k;D25x{g&sG(ihC3REO^Sh4n~i_1CQcP0PbQ5!?tPM z0HDAa>^p?3B>;pX#@hz8Rs<;JFd4{00?5z+00RT*|NWp)2V3_4%m76ihXGqz-dIn$ z>p6I(dQ@DhX7zN0R^FG$wHNeD?nE5j6w|M&px34^4YN#L_AU$CP*Si(HmA3oKBG2u z69B2R)e@5YK`rvCArT(_r2Z^BSiyKhyJsL{*k5);HUEMy`pH#}MQYPqMfpF;(dmpc zU2^uxu6FDjsBKP?kkai!x_lq`1FEkpNrylm)%Wb!L6#0sarP0K+im=8klX?t>a})N z#*s002H7H*JLX1oW!Bb(Lf{d|_Gb z({CYgZc#cr0cwNm?t@&-AV+Q&55P*->NlAg@UXC4iD$g!^@N&{HtcqvnPK(E+P-@3 z5W|XcPQz#oq#f1&WSU`eS_(NZ}_8dW@3sfg^xLdI`qIx zj1r1GnJAXHsU)M8z>abH-L9PlWBc>KoB&#P{CaW|ang2?dST=5ip`t;JE?u-cH`_ znc&rld#ny_F?Z&BDWh2dbbjUL9WTrKp_HuiC^Q_jcS%b%8sN1ENNJLOqQ+Y4t~@=@ z3mPAv&wVKWh+$?bBm>z_SUL`r_>op2q;-I>B5U^PACM%Pa+^A|BE)&kQZ+BuDw!z8mwFF0U_0bqSprRbKAVpS zKiO;T_C3i`erv~t$uFdy0^%sEZgrOUpvmCf3R0v_8#)=!6%xl*tXaeB7 zG$!hb=!{8ZfPItn0jBeo^ZSg$Le&AeNkDK5UwWgp@6_b%?k*DWUnBQT2_=?_c^esN1$vf) zO@HFw*EneiT%W)AlU0|T@qz9DvNK9rqzW+IZv?7$Awu)L~XS#Ci(zh+1^Tv0DGQ&PnV|m`j_RI z@45goC@*VaENlJDzQn}aHH-+!d zE4l(0Joq)yio1!=%19uSV&{8L<5eVwsrQ$^`VP~!GtMqSkGh@KIgmVL5J%Az7{l1l z2JzaZ)!SQCG>=3bA;O<3hQvoRA}r53|I_3q#=P@<9FCidS(N+uG`drxxqzmwJ|#ze z6dm3}uLdf@p<@vy-%UChuH8cpMB%K^U;sn9^t1)JN3C1!DR|uOJ5=z101HU~00J8U zpHFT>{{R36nbI$S3d$d6{Hc2%Q*gUh7%fHaQ8J0zSu1CM>i>>fo>}g@)tOvHlfA_( zqd;&f-U?^g-{gtA6Rm4*=7!)_2-{PQxc58u9g>!t1OfmN5^-XPV6c2V2eJi?UHXWb zBNf@GzU>XRu-9o11zOw*qFx)~0;1trQ8uC0GC6Rwn1%!x2K#tK6UiK-wm|t7O?9tv zkpm3u$m}7rpW$#=)DzJ2*GqIt;A?qc1zuqo?g2c zD^B;*QvKOi3P(9GQ|S4lz`TJOKVhuAZyBbhIYh0d*|&PW)a|ELb70+9TBwp6f*won z*$?}i5eOdDD8L@HRf$;w6cdi$3G#uR+Rs)1P%R4A3(%9>W{i~*CeB2$mb9_6%8FS& z*x2kLJQM5DO8x9*fqr4i(K6HqH%n}3y@D0;HjP6z<9bwIrC0Jg-QKyJI*MnZ# z?Km$dSU15k0ze#Pu zhy3xo3H3FD{`w8=ZIw8}5XZ6uk1BuC>M&#L*Nolb7|xM4KD!n5B+Y=UO44S$fhQZv zCM!$F`puAf!c6cqC{6O!!Ad?~9s_`bgW6^(Ss+LuwGEzd00LTfk?_MhWxxR3VpkdR zy%`4p00RMF%X>(gQ!D@gN5B985`RIPR5b`KQ8JhW6#xFImH-R4J@5m(cTDdD zFbm^X9eYVX_p1y8taK%Yl2stkw#G&_eZki<7}!XfJ-EjA_q^uI@jb%F#1lqxGs$zu zX9J(Zt!~o*a4|Vx1_W+=3`j#LP556P_6;Yo8shDjm3~$ig$>2wukZ7Ks(Q+uePd+E zpsINae(L!H-i%RIIa@fRo^f3-HsI}ShR%L0g2$O-fivCmP!esQ8#zqnw;cl^U7l#e zlU6%KSC@!Cx6OycTKF^HZ2C&~?SfFdjDh5@k6Q zY97o!=PiDFe1jQKGHY8KnBxG#;<8X0Ho#`9!+C>--Q0Kre`6kkS0($k_3ErT7yN=S z4(j%*<$MC0F1Z8_BKs!MOdTAw0@PqN_HL|MgkJT2Y$vA)3Tig|=SO7VhP4qTK#hCr zY*aSUfu^3K*^vosz9B0Cd%s9ZRM2i1iUbM9GE;~)1^^sI-%=>V>`StG0IL{_2W_+n zK~9`3=HXy~DnKI6o=WoEmUegY$lo2dw z$r8UNT+97<8l*(-&2z<(Y3x#lYz%WE4vgEDpGiH(d}K?gk$=vqk)79a0PqP5fuahS z2uk_06EkR7rors#PnnqFipLBorgm?TYkkcey}nkn3Tam8Jk$5O(l!T)_8XF=E>OM5ay4e#(U4XoJqINTC{U(qgF1tTBpOO<^)-GbL4F zAO^ftb#A#g-iTB2;*qXVy~RXh@>yg)K*kcVL)9h)X2L$AF6uQA>4ax8c_n5=)KC56H@NHS|w<;U_Uo4v`qHe$UPyiE&JN_qful|$Dfs+yA0m5g0@Bm zb(X$>6eg+ILQx1Rum_!U4|aYm811h+n@@X{$Y2$))fATTdWK##EV z9ove1H|1XVu7Cirvp~__Gmlzrq+?@LZ2+75(n`N+83Sg#6#>Y>k+@RHwd9q-j!S>g zbYGe`xdnucd@lWK)qcGXTbf}l+UJ`1KbJR7tCvulLcblq?mRZ(m)RblrJ+>C;OG`s ze!Te|cx>)@JQA8~jyo5<05uIDFQW9~Zb5|~#&Hbm+2dG41XfsC8!ykfV&g*X}H>gtZ>5q0-x-VLF7MOOoYt z{0+BLftlOkz^i}(>KG}zeo)*hv2Gr(S}fhv0TdS?ZN`b`Vb2Ww)m4DKIC1g2_w(PM z=UARhgF5Ra&ET(2N=%>I)U(U-_2wnSMD{NAJqswJi=)eepy#7c{rqt^x|n9BWNO7+ z_JNv+s!)QT-QExlipA<1W0tRX!KplBnu-1^OWHdSgnhxd3*p~m?IBbY+PONq0?f64B{NEw8Bgb-#O!3m~rr}Lub(cp&IG*AGM=tI|k`T~bql`y8$-~6tG z(Km@M&TUZjas;oqi#h0+r!|CgiRZ!r+nftUX!a zhE&C0{u;S#`C(!t^E@VQ__LG#a2gk-FBykS4$m)UmUuD)_WDilQ+UJODztoxHtXnh zsITksgHesqRP^tb!pD;rckngiu-A?ZVjDB-6)GU4$_b&YLjN&9S}C?$V_VmRHhEKH zl9m{kXJ@24w|Nt8e#Oa#>-$L4;;p7KWOwiBJmdq{C@&`ocP+F45JFfd-~KSw#NU-B z@x<4#Eg0JLwEJ2d5cea78fbZ@!>r>f_^mvf+TXe z_ah8tyASaAS(o_SEPE>B>E?W``$HCT0-XRaaAaLfr3&3arv6t_Ep8#go#%TYwkc^L zyj;^LI%$1}GkP7`hmQ%Od?7PN<1bDPWQ%ZelY(I9xr2%(EIoutv6v%N zv#J`S@$4Sn66T$UsM%S|R!tS;sQRksaiZ)G6$ari!13h0Yt3>)9_vgY+5I(}JVwY+ zoOtJ(pbgpB0zq0#WFw2ss{==2Gd)3vES_aq#;^2Qm^T-BnAv@rOysrN3c=IXS1ofd zpGjpz8XpCVb-A^^pN|vV-8d0~BB^0c#zIJHt-$604L&k{t68$(%s`NB+f9g5BNZz3 z)Hbc%0o;U1l~q56Ci-XBqslAYR|No5WtJWo{1YsyNc_t3E_d3jQTI|BSGIta zdCO2YJ!;}ghQ;7zWCKK9IVI`CIk!ny(y0+e3n5Fjfc?LKzGIALIRq{`M5;Ic# zXW)(tsDTaie)w{*L{lU8;BrX%tje!l@5xZk?hW>$y5!V;RUUzLrI#)T9&o8XwS$Aj zb%8J1+QhWIABSosQCM-$+M{Cn?Gs>jCW2>^0DEVcS)(IOfL}QGC&*#Vvad`Ue_=aV z!Ao(lB0mrHhSjgv8}V5HmSn;h^liYFRPhjF#o}$likw%5QT=3r7iD41h%*I$AwmQ} z;U()`U3uKMDJq(Sc``m2g7teeXkc%SZjHYiE=6-w^J>nPb=7>%KL<5~Zs==7>i6~S z3vgq$V595hCcpk`lv6!Ihqns0R_O0NW)#INA0Y@wpG(GR*bYwt#Mq|a7frig0<>4N z#P#UP#yv{^Y@Ke>ST&?WO+7e#r%eBR@=s_@UQ=z&et}Cw+XKt|LnUwhaA!{ba4^U$ z9{nrr^kw@@_fjwZB!5bGOfDISMD%Y1=2>{U>ppQU`y%t6Yn^ZN04*o>^k>xF>_YQ! z(J=LAC7jT1pqjBJR^LCL*urK@=lUi?U3vzaeUj4YS)`!$Ap~bJdN z7&KLQ>DdfU^gEy8CKzK$_0)guH2}8;BFNAK1k4A+_7pJ7lyUURm{TM)z3$5X%1W@m z`;mj@qd}}{JI~M&L*tfN!Y#ip$4XP&(P7(s6);mUbQZh-;*t}s_e1&PM2Pm&CY@%v zlOFE{Q#+^gy6^sQ4iaqyTn{}k-%BN4>s!PO6&$Vhz;a(?q$oh#d^vQ_Q=hZaIzNMc zj7y;XKl({Z%y&PTcU#pyykOU5C!kf^c&?fptvE`*w;H{ zqUi-CHS(67WU%x|fu75P_I>7UA#)!Yw@+`oMQ%R9ime zlodjH`9NED+)3;-==`DumxDIPBt-f*i+{t^=~lB0!DFrHQLjsC*R!Pr+$!sRcwHsVA343>}CRmtb0h2L=D;vX3E zOdD@=?Ol1`^=S1RtkawH$DpB1XCnSG!UN9R?JfQIcth3-^u$7!pt3hxKutC+^iu(7 zh5!u!btWu+m6tpO%Fz$7G>2 zphu23b^6_B$;CS`QrC~+&5Ul61*g3*0ROw{6;pIq9k(&c*tBrcf}~?RAu?Y*K73bb zx0+4~vH?C&W};IOb|@pvc%xd+7r}0+IW~-s1tfH&jSBJN?{2S24->TQsG4)63*riz z5VT8IiphO!@TmonN2|iwJ`i-O-WaP|nr{8Fh9!YlBg7c|0^jgcdzu56iSq-m?>x7c z7)G9W0bHM1eZQym7W$4!o2m;H$9Sa_E^h=q^p0$l2| zQZJk~ZY(cvxZUGFjg8=A>c(>~Uqq9!k$*ZZmfAMA4PL~Z!y0s1sx`Y@q*u%mL1fH@ z_P-~I9gxT9-R9337|>qK2bduB)FvfAmBiYQY=q2vZR~NfbG{_Iq11FfxH;?|5plqj z#FtxcmTfc?^#JeDupu#!#4DOMfv%*iU=KYCJsJ8pjBPP;=$kSx-H1h3oaR4CXWC~E z&}HIgmz&dUrgtg%`ymp}tH>IRJs^M-1708So?Bell@6E$WwBg(N|Vd^xi|LC#TdPS zocdQj!3vW#toe&_0tKxEfVv-^bz~;#kb~Xi7b6@ddQ}ZlKc`E8)5|y)VuQPEC!C$6 zD{1w-{mlP5bFJlsfui|El#!{ewm7Z4nDT7p>l9q&rCuDwohFp@7Pl-8>H1wLlcs{F z+_DAU-;iP7imm+UmZupBj)PFrBXm{iec=VEgGTcQ{#@HXk;|E@w_C2Xqhogk8`fEY z3j=VU_DEmgj5Jjz%uo3W#23ui007@pbs>H(sFT_pMnHp$Qp-cMXMUG(Fh6Y*U{e zMCPHbhCB#xn4E1nh;7MxzBaoQ8T-~ood(17`Fi$=KdcKJK2QO1tsEc)t|{VIjIo?) zffe$ek*?+!!n_6Wfa&L_<3wNjL!qnT{bWjiL>d$f>Z~uU9z`8nA5C@{^5n`uE+fM^ zod-;9QG`uMNjkK7A`ef2{m3@m7| zx3A(z$`}Dp{@JKStiDhH{63+P4{K{xM2=Er!;;9795}W1#I+B8nSt#bKWlu!!H@t6 z=^dR$mK8t=jv5`dheWnr6?*l+W=4eHWoH?@?FE0W=l3~To5X(9BlB43g&%8*p`}o# zYr`t-+&YC5!i5Nm%%Tvw=t^|v1DrpJE&MTm1q$Z3{l5Trlq>wu$)yVNyLp5g<)x-ICpae&&Sxi)5Zp3`5I(fB*1bjp8fmMOrn+G8R7n{%kj<(2D z%r8%1xP#{N-)jfUV1#LFzw<-}p%*8@TBH!sDf5?|+^h{QIlnIAEh!c)PFEeoFZ68T zSnM>~vk0ATzQ+Cs6$*mW8wF?53+_^6rA;jPKP*TM5PWAb0UO##WP%V+rJRNbcWbOB z8{o8%u6MUk`wx*i9X9JbTc1z4q@vz9C{tnjdic7xm)f@zW&4n1LYJF^TR2jm9W{nJ zJ<)@GlILVqxwqbh3XREV{<`NiV6>v|zw8$cvms-8^EfQ`J=PvmqAoiI!hw?L0EApw zX;FSWeQ&xCFh={i`z`@s015dWPx^@f02&!7mH+{P0002F0iTm@L;nB(2AR??fB~ia zj*Ewqfa{C!8~34(<&Xkfi0S7&603s6!vt7))(7io@VLfTT&g$q=OaCBLiG^yN7=G| z$&#L#96#WHqt|&Rwxr(p*x#KRh$3pWwlV-4eqd4fzy(lKU7osvl5&wDyo#dQHHLXt zTxypB8D*gi>2o@cs2?&bNnLxmKJ?>}cJTvi2)o#Mp*d)C?3WV7DH+ z1W1?ie?!*v%i;3o*9(R1rF^`PRje|#>q}e zMV0k?-Es4kis;>-wdsWrZX$8)GFkof${ehn(_!%$v_Sn4x$if-HuDKp0pfE!)wxyE zCXm-Lk)#t+7^K7|LE>&$+<%=;7*d%UhF6HqV|g_HAsd#HDRM} zPJA#p)%N81@Hq0(@@@(W%FQq^)3!C=gBmZe74_|lc2kNF&z7QL{GcIeJw%^eUI%k3}Mj1#TF-^i{{T^ zu7K2^Dbhuv!DO?75XY$C_6W0iiwz%U3LZE)im~(jW=YheKZVZZv$Cs9D5*vq8y`w6 zHQZ15kfv^{22oA8z@T?++b>2}8LKGZsnFIbAw^&4Nep-DCQ7E-N?p=BG%zcT)<5Ud zu4@o{(b_Ymtx&s2?yFmyNp2%$^YBi-?~lZ}o43x!HcSJ9OE}&QJmQ4iDbIY0L6SSf zLK@C5z-dYrs2;O>@W0$oPsac6%!syI^542|3TX&{{y=>+Lz{t3EUUxb>oeb+N{X%l z)fR}b#M`&|kHH;?X6U=j5i-H5S4DTyw#oRf_5Oe?NZ}%ky#tUf-Lft^+csC*Ty5L# zUTxd9ZQHhO+wRrw)mUxY#_fNfeec^R?t3R*L`B7{npHI_V~ooDGDnUtXZ=l+Ay!AD zeYvy?LgdnFS&uVuW)Ey#8>ZTC@xfL{QR~FEB}<{Pw}!%E=8HO14PGjt>I@cxRHT5^ zU;Mny&SaAlDV*2_b5vdP!F8Oa13BP27X- z&yScW6J#|yPyhqR8TcY% zlo*1~VigWkfy@uu0^PuuKSay5^QH!Y%r6L7W0^i2;UtcGM|bhsZRgg&>7c#2I!bE$Hu<;`qYzr(JeNDunIm; z;J9@dD)aUw^%ng|tQU0SP>IIf)M#U$;JD0damojFOW)hDqonTaV9KZ$hoHuPCTd}M z~;M=VKUgLKcB^cQO1_fI6-^Bm=+222$q^Cm?4*nUwGg~kYXK;l=& z+wvf_1I~@u+1&MCn`R1DLiq}Vmv>865p38m@Il*kHl0#g7@v;Q-gtn^>_Gji<`hm^ z^*G7cw(E*gwaHTnzVt{nU~pKh2R2qtjAd6>p6?x6b0a{_5AZtq#f=)SMx+H)_&#pv zG@SrAmuyI}VRy&}r%mUIc)DcQr~s>M!PX%(EJln7h;&}5wzjzXOC#3z;ZsLV+1XZ}(jTQ-$3~ZzS&v%@x?&x{P*|Uny@Ef) zg%pL&r&rd&5;eTrJ0VgN4pvGqa5Ul$iITTG>6o3ifgtXi%F;;YiV-T}B21fQF<0wu zeWa>y+;;FC*cK!gfo~B{=>#v4J#Ewx6Sp(ZyOA4SG^a%{ND zzp3-`Z~jp}QyM?HPwpf)8DM=A3moS$q_wm%mfXvLL!zYJo=(<8PY=IbSE79|wHfvb zFv*5;Z&n=d-JAI_Gn!{=%0~GR8b$O@R_|G+jHCG@JbmrY8K#@ljOdI> zzGM>}Jvz?m`Zzeq=kaN6IW&kj)3Ppa)`&|N{Uiok@7I3S=z(T#bD(xW>ZoNv40x6X*!vG}gf+3usL zD<`*6L=c?ky31|s$Tu>M9XM_4mRs<~<;t>Suk0o?DDqQ33%+TiK~^75pu9t4;;m}l z2{*HDl+<<|65@uXqtA2lZp348jj$0!TrB9&rl^X~P{x_mCgng(Ho|ugoy5X|71mfu zjw#cgKfyQuOjec?XxNxL1&%iA+yz%AO;d)BY=SrBg$*oim{fr+M+znDJ(w9`8ZaEK z$gpxuv_%N;%W0id(38cuLoVm>wVb(YR%_CY1cn@FhT?6XdBV;aYus4S6h8I9L)Q3W zkupC?dRq~F!Jg6Zt633PPBX(QNfT>bil z7?aKwZXwm$1Y172{k@{b6mBhg@f(y{o14g{B~zpBp`gx%=dpc6C!c!D(s-UI{?9(P-f)#!%Sedj5 zGurXqI5FyRckwn{kN7-R3C!pVBev5a(AzTKOVJ7J0tD~C5tBi7O%vzVvfuUWVQQm3#BXe>ecV>{F*iK!n6uF;VOkLonay zkm4sA7Add9$FFvkqmnw{_vn)w8rMa*f?OU^HF`?t!SEho^e}@NH=)kq3QEP;1yqO4;?IqyqpFmhU;q- zR${pIL?tXaLQmd*%?$TpJtlYh55{#N3Jf!~|VU-l&i$3b2Cwj|Bgg zC9sM+Y2#}OH=L!P4A*sko=jgpdhml88KH7cn1C2~?Z>tkIhNr3u@;wb#3c~r$hQ}l z{TTO~d#9qKhr&{%RYm4T*CM<p#mTb1WQSbPktZVJfWW&N zQSIRm$sbT6tXZ1TJzVQ(DFZ^}c?7}#v4&hiH&6V{CllvUIe-y@@Hq+Y*r(xGY!ljL zq2)2=WKij85^X*g96`y!>;4glXli2Kz_0V@M3G%-V;o3!`qFr)>NK1`7PSw8$unNC zV7$B)H@(+JHQjh$D1sns5c_`Z1U%J1WA~||#s@EgJLRFVo8aE(xh@QCg+F`fNb9n) z;?e5g7-DB2fXmqnD`Es?cU%EBnUj|}Y#|xr0qc=qZXa1cEyTl=>TH>@V;U*1tq}=` zJftXjmdav;LT-nKXR{RneZ$7x^TG^8OnSXB9dR?J52@VyNe2gg9;9njb z@%Q!Cv?4`+OKj_#Rv@5g=BMbET51$#9D3xlpARhExR-`7?~nHhfu;8pSb9C={aQ1m zyX5_9qI@{mog5!TR&q;Vh4ms}WMjgp*aZfxd4kk78FM-wQA7}*zH~4r#gr=>7pPDZ z0Cw_>^JR5!AIW?8YBL8Ck+anRB@lK7_X7o<(1^v(a&mLzM)~--uemJ+=X~tI#I+Mi zPqo^5Q~TZ!t|d?OC@$&^YGa*k$uhN$T&OGXt=Jtp>PW}-YCS9nB?rk5TC1%6b_b$C z#rNF!116&lE19QGSc3K&A=+$v4%Umn#*N2!<9!C=VBIEPyp0U?taxcGOV%`9xMpK{ zre42?UoDiSO{#W#XU%c8W;*#nH*yqR9t^MvQo*QI=0NE{zVCPn9G%6)$?AJ1ThfniX*+qyZLrvdu_xLz z+V~lfa#tdJ6_gy*_84f{u|*BVazX{gVa{Fsl#FJ$MGg&EnZW+M2pm1G>4m_zf`q7; z!f}2sMzYvv+h13%7FQRLb~^}dGFdoXNqZo6tO$8VHXO*Pa3#}vbj|5QM#mzI$TKct zOv36he5SEdDmcg(6$*YuT}WgX1AqEBY!a{zBKyzHMkHpylA$f7Dfqo=)u_J2?qJnk z5C`IZX=9Ah(PF>(HhizpAwOm=xfpnPzHh%B*kvPam}$Wy|FTVMfQ{KzhsKsX_Kk)>AR$(iw*9Bh>op@XBG!>DoT0jVe5~Fee7Q89O6b$% z`~%l72=xNf2X_JW2Kk-C=9xB_moez4oKkU=PqL%9EdLBH!t0_8kR&oufYkK`@ zfT!0E`>J>AcGi1xrYOwM7m~h68lOZY0b0U*J(DDgHa#^5bfk+@C+y& za7av(o;7+Oeir7uU)S=ih^j)z86U1*7X3)qyi~UAlHs)5!OuyqMMF$rfL_ep6?RhM5AjZm*u0 zM7sw$Xq;Zlvc%NW2}K58Vu zV~iNdhFnX!i|+NdKlbta{iJUD0c{$zCs=e`2{9()1Z3(LMw)9e3X0a-N!?p$(c9v{ zFLVM=FAWtZpZo()wl0^F{#wM3W9C0$ojgSObrJI)3FOt6Jew=#1>XscC)!$4{Y)AW z-_ewSEDs+&K>QVSw3nPD=j6w$T4e?hh|b6jO$9%)pV<)W()PfZQn+zO*kjuf^gC<% z0Y6(79H|k{GGzhTan&ULMHjN`fjFi2O0rjkJ-KUrYD`N-hT8d1BkYRoUzx8n6nCYI zX*?8j#B4`~Hju6jBL`(+)Bba5u$8S_!QO`^e6pddpMY1+Hm2t*-E9ea0%__}qUdei zy8}jE@Ft91kfP-gf;em$wO`6AgS*q1^X4VNYk!4FImZN$<-147D~LDs@w(w2lg5G3ar(!QoA=>K z-QA_FtC{Fys7YnDPs=47aqmwRxUzezplo*Db(}K5NLv_8_@mb;-?mp*Y9oEbX@oP< z$UQQi1`ltSKNd8Wj1%Uj_%TpCofR*@DYbq&q2uCs{vIch6OsRl+Q>m0RDvFWkELN+D|}4g2XFWq48I9q0{E#lB%%8< zfCl_f(EWoi*$SxvD99y61iP`KyNW>20#m0<8Vm3kl=e)lpJ0Stw4A@|yBYoq(keGI zt*%}<&pwD;O>3c=>fe7MZAm% zaOMAwgwhJ+zW$F@k>y{p0f<{r2i>X8y$sw_Hbnw~i4Bn6TTqi+AGXFqk3EQlN|!ho zY4D#X%#g43XhB*Felo`=DNmSv{iTuzzskK3z2CHadhcI>%8k5)h^VAnw!$JCs1D9O zoo{cl;D4-Jr4Az_X#7^QM9-sdhA0tVcc6Amdo!Or;>Hb%+DEp55(fYdu-`3_BW#WR;3iuKWEqy(voDlH+@Anx_D)zjuj|31RgXrr& zb(SIB-3wjuh{PeV45x;rKJF_D(=(FHC7ocopnq9}sRkioB`o36uniP0$D=5EgO3{a6tqrOL! zmmYg$audlXhm7+B72*KD1Ievx>H*Av`B8pc_|Y3`bJc`l9B1Q6Ocntnp0+vw4Lmk< zw9Q3qQB*R~F7jDQsTJQ0iWy!H5Lrp5$efK$bj;;ucq5Gl>g%}#R4hFF5xqema5g_O zB9gX~Mhfr?MH3R;4e|^3VQl1YB!Jmb7x0^2re0#kIj!I*Oe-EG-TH&g+XsN`H6ho2 z_g^k+HSC}AgJk~}%t`X7I54KwD*ZgSG@hmP*_jo?(|{{6l@eIcIj{6?n5zXbZ@OfN|s z!Yg7Ano=5QWXAY`!Vc!Q@Y)B%NQcUTt0%f+@7j`UnSG;+3Con4^(uhk6$^~|=*;GK zyafQ8knuwAdqPKY@doD+ZXm=e+pYpqM>z!vJe*vI)02NZGl+rJ{atL?li2j=&|Hz6 z#uSq^1s0!Ms+b(;=Sqf!yOAC74sEKarym>^s75B`9hr@7%3*&^PM9P(V<4+tJ}tnY z|GWOnr9cMY-!X{)$Y5^rfpUYN>iH*wIdsPPX1uLP5TBAiG;hto5`m1slm@x=MKHpZ zwa~Elc|}32cLVfD6-WHi-A!s*z(gx)H?O#H;qeTmqP6`EVc6c1?Fwrqn_k_oYB}dV zXM*tE#<;l!33GFZ=y#vgN__QhI#c)0cl-CEz`1+>3R*fgohygnzTeGaaeZ=moxF zp}&1m-cj;5{j4_P5)V$e#DIZGvM*a_qyoAfi>z)zfy$`my9Hi6H*0Hb%Hy_TuQT@; zXq{|2)FuC(DI$y_Xh^KRD-p5Qtm{WUy?ip`IXcHXwf+^~%a%tS%=F_HvpcxuU-<@~ zmhj?l=O$>X)Gv1QyUqo(_V{L}?wzt-xjM`QrRrvA;Kd4mYIvicgC?kTD<9YzJu&?b z_`4=!P!AGMy=4@^uszVgwDe20#oDGRE9B(Uh#V&&HicO=&6SG2cVB^jgk&&}cye_z z)NO)8NHbU|`(?G?bfq83`IX@HOUtw|?U%Uj*_T9ok4YujVKCmfmh^6m7Jk2c+!sN} zCKF9K7v$rRR|uG`I4W1*c5&$~p&KzKR_A^i7NzP~dsxaO8WgXp15zfR#NWNjJrE6F z;Z99|a>O#faNkou@gLdrKzjT-Dw@AY;&g>5WY5^r@A&3qMF)*2VV(RlIpLFvuzh8;>WZ@Al@kZ?u;My>e*u9Tx8+{3tl8a9f>XR*=toEbF~risCd2#{N8mE+hH zTwzIvxmYnK9zg$aUEJ#n<&pG0pF!oqU1ksejS{uIo6@V>#d;gcQN($k$VlLjMKQ1i z2ZekDU6BS)#x5zEo9R+U>qResD<(NqG8^mM+B_28XzwfUETngu8Bejci_5l3!+pq~ zFELXY7HjNJ_f35tY6_${*bsT=S9}%o+xy!;DC_=a#37KLYMyQe>Nv@g%&6%#oNp$-}14K0>;x!lbRP{BC{Mk6UFe+%uYf6zUtEy1gF$1oW8!)vAmPHH3 zj()myV7I#48rh1=bm+EyvT z+X}@keX)^P%0EP=n%u7Gy>7jvXB#~sufb!J^fol#N>e15JH>0udji4duDk-kb#CT_ zYLaMc@6!r?rdiAehO>=BYSd_gX8=n!cXMVRQ&?LM-hFV22>C876H0;%qLK=;KQcS5 zx3dYlhsKY+kbAndy3M;N=)gEQPCuq*T~)%14}K)`V=2J0z4o4kDiLcs+|{b^%#q96;_7*U4a_pF5~IY7eRl3+G2H-PrEW|4fG z63kASs?d9;EMt0T7Ugeo~hW?;C2++Ij?j&H?vs0~YaCVJen@tgfwNNoZd- zP7q!>Ta@*NrlNS}zkTos?8Hb$Ja+dYI~;3r8nEf9p6;(3=`2H}t#x5n3{mnax`tm( z(LvzV4LY_JyP%!K+a!fLs0Q=viu;S7MEC74lt6@?2ZL@#;*po-j~C;C&JeE%+wsD z;lDy2MjuHkt6};rHS`cbKMm~&_WiKwzfngLP>%M00H}9kk_?8>GcJKFhb-%6th2cw zDtK`qNW=IK^W3{9Uk@#y2@ii$BO4b_E6m>#JjD-v003px!e-*!tvDKuVFx@av$juU z^TFS(lJPP;B&7E5dKO=YaEhugUb5hj+mzo622Bo(ll`7+wgF7$ZQ~-*=Kh25y~v@W zx@-EcH{=Z7ApDa@!cl4msFwk0SkwdFsqm{p4-x@Kiem zi|vCzKeMY0F2mV{w&wzbq2s+1idklB`T12IONXMK%bG!+Qt*F2rHSIcR^|-uqd>pm zF8d16*e$$l-@l9p{X;fwG4zXBv4mOlN(Y9C#9X`mcyGH3@kBkf+(D*~^3zhO*Ov*$ zD>}#kp=MlzIP;gPtGUV#Y({;mf^UQ$~cicil+GWms31W7PhZbf(9&2Y(p z;bqp&y~oM%S_c4&;`~nR&%Z;{a-7S5jJe#Sd+((*$%4T2eL3i_e|P6ff7gHS2T}=p z!6zm6_=-o!N+&_0T#sYme4wm|Gt4beq|%wj+tG1E3t%$P*uGi|A!=`;VCnEuOY@C{ zAo5H}hXGDbO9;r|TGJaJBu0zm#W!~e+-yumB4X(?C=L=Y9RXn6kV$ANvj^^t$decO z7R@u8?-jpy0p&?M@N(FJ(9jd2&@LAWh2oJwT}D*6_qI784x)G^us)B>k;x<%iQAmg zo2>G%YQfd`yhn@3{w)_1%~QAhc{Zalzf#AhcLcGP?8KqN_&I9o&g-g9Aozql#^oV# zrCo(*PzapOiDQtY>lpM{)0$plz1>!DXN^}nnRF>R*%M1w4;#LYgMGsga}&R#3cb%G zQ=Vve6j)HU6OuE4?TN{9_{X)^#g>}0f+1A`$q8kGKzc*YI6x>RA5JBe%YxltV0u2Q zh!dUEhO_r#6Fl&b7m5Ag?2`xntg6MJXF4wSRl_W+mmpod7}t%Mhvo>$SC^^4=4>7) zR45*Z-W;17y+jhIOvcpAA&#tAHG(sM;C zF>T)=XfiQ_QYOfWqDUYbO`O7OwcEC)>3cnD{p2_=bua^`SiGJ3*xYsDI5vyTuDOXq zE3*x>|db@!r* ztk251%xqPen?oHonvq5+>3rB#9o?`nm zj=>%&cHuk*z!oIGoP5e$KX>OSA>gd3YR7C97o`kCuo7gjUV)W45PxsCe_1f~^A@B# z>G@h);!;!dxHeD?z}Z`#o1(DQIs=2z{cB>mv>Hn?;0Tr``8s~d=(N3>j+lk7zA zO&U!mlk>im45l!;BtehT-=6N2)zJ=YIZ#(Qi0~ql;+!?o%vgtTA5W0^jl|ch>r~u} zK65T$bi|!2&u(XI;1c>1(%~LT01yHygtxk)?UNfZmB2lys!R>ANId``s5O;5VY+~&e*}y4yf#%?=C^qN~Fan;)lk_ zX^Y#Ep(F*j?ho+o(dx}Sy-D+%qkL!0OmO8xS5thGB=aq9&7yo`=%mx2M9#-SAr#DE zjU&TA422+M#y(kpYMQb%-k2P*S zJvW$79Kp3&LG-5DAyX^FIg&mdOnGAc}P6SiXj80xo- zdq6pX|Ka0j*%(|TbmQUxq8(fPxq+^O)Eccc6I&7ma*UpY0oh&0+px8RcC&lZtt@q9 zq4!ZF6a^HYPnt)jtpfB-Vgo7*G7MgK{C$KX1>WzqJQYn?+TKwe3l(tL7+IA+IYvKz zd6l(MM0bE=?!1sHE@Dfo_CZQwg$(8%rIXHSo3{}Vcf|Z-m!QzL+zS2FAUYM+=VO<< z4`@7VHI+tUN9Wqv&wl5~3pMP|Afeg^UVf<`(CSCZUyvfBAgiUEW9Y-~{e+`CVcBkV zbU1vX2V8AH_Ce5m5A-#GjIjUa$p6du{nHeTFN~DQq{qA-Scv3ZMQ52ibkxEqg7I+( ze%em`pg*ufT@ejCH(#Xq0`GCqCkQdOQ< z*PY#!8XyAu8K0l==={Ogjc^?eL_NmMaAq5a9?i$c5QrGuH7_0?r3EVuM)o_x@DIo7 z;er?t^eNNWEA|4GN-RPD*bUi%zBIVArzpMp)fHJudzxF^A<>%4L?sMvFK{603lETF z)*tQHqp!R0+f3Xqiw~21PAuUIj8L#3^Zs;$&{1nSN~AQNVe2ufX%)JxihS7yJ$9O>hmnxBqxq z>k&;?;_g6NMc1HsJD!-xAEo( zz$kD;cK5t`^|8jHr-A!Z`9c1quLJ=6sD}WgySrxv&WYF3Qr~et*OT$gt_(K-03#`m zgP>js0D$`O-Mc~pIVJyp)EE?w3{oYO1UG!{<9uCUCA?TPfK8_hJ*f5x=SSf_72f-p zd=|WZf6#lMzqiyHzQM8FSW=-Yw+OP&$>WPCq~d^QKL#u1_9X zAg4!nj{PsY!Fxnks3ceGvbOM zZB}OSr{XiifE$e?WLm#ro-HaS9AjE8uf1o9ds)llV`$+kE&lMh3IL!>2IWm7JpF*4 zgko|0Sf8|OD0HycA6vH{B7lZ{e&))uCF!^8tg3LxkRrvL2R!j?wfBjlFr){4M=$w6 z0L&chEK44UM`pe`p`hB`gWfZEa=vbadf9YE!OomOj-eRdG!m#QZ1kw*Yki(TDDL5YT3IY?ea*Lllm%3P(mlMoxcf?I;w{ma1j{=*|kp3?C0s z3Yl)$U_{j4D`f&O4vj^RaHhs%2Ef7g13-TF?svM9En#5 z?aQXiTqzvA@nJwLZqq>wnxcijmyYQfa2TGI3W77etkfn$O9knz8zIT|4x^|@7-n)L zFgdAPzRmhj>3FZ#T>e>!1POO6?RKzGXsD?JV9;Rt#(bsMChzcz6y`$l5z^A)mg)!7 zL&8a|K|wYQB`O!eEO-}tq5aE#f&#WAA^^ha8_r7$WK8{A!ytcsmVTFq{1?aV_a2>T zeLbn|`{iy}ogG3Y*%>Dv#S@W;lOh(L`94bz-J8s?mr=ri+^$B7cuyIW=^fAPL|)AV zxVr$Y3nF*^~mTuPc1?!DO~x7WY@D8BBd)t;%F! zZ(^Nt9H`$s)u89BwCm^=rwSq{{MiK)bVP=zi%Qblt!PH}@iSwGqyKaF>y`#;J)h9D zBj%4vG!+z-6Bxt-U?xU!Wc-aAIO#6zf)Y7)!e~PWBQ@xVohtP2g#85QpB*^Ss2XRI zx7#Iz1rGz505<2R{Jz$mDx-CJqBNV^u4MQhdb`4eD>#}#LH{-U?HL@!?WW2Rd@8`~79gq?iH&Oz-kPJ#mt9mS zIAj&|Zn;X-liaacCXzvK&%M~`sKMoO@R$1u|>2}lUOZkiPI2@7Cy@Pp8g1Lj;zB0SnEZ_WA0NE+ z#+anz*O@&@{)wzUl>?fy{^(YUn+=otsLO$VJ0+|YlQ=P$8tT~XCo4V;h;(?UvZ#{5 zy0fcAs}U5;6xf1gS+$MjW|!9Ylr*@%yRfFLJ?csUW;P4h2Hk;Sf5{6AZ6vl}n0=&`HBCL!g@T)tB5Ttp zLN{K{Us#Q+lO|DJC_2xe$00XbjQoIXAVsr@#~k=x)QTRc>Xdhv$c&aNQc3@#fSgVW!0L6};cRb&ac{1AJyevgX8bl*F zM53m(H+qT|qtB%OqQ~lL6b_h^5|_ zLzhcN{S4@8ASbL);|?L?Z*&3xK)}GIgw=+M@4I>r8^ z^Gi}+GaRlAhl^%#yg6ea>nE8sL@KQNtw-~(X|_OI@j8dt>g{@5*$iWmED?U-Ez)nC z{QV?=D^M=X|0`IIKgoRIlb4A$8wJ{D9~gRv z_U-ScOTIbO|7~87zCLsQaR>gr$$G0*?CcV?0SKkVarf*~>2`I`j$U2fYCV@OYW`-_ z%Oy>PAj!8cV#vIktwBpG8GN3x*gUuAfsPGfm3tCmad8lR=W53xs+978J+}AW(l?3x zO<>^2MnF&)&#O$$BHv{T!z(^-Ov^!evd{I0pf4q6?fZAzx?h+7&x?FJD5KwC4EJ9& zh{9U`pG&UNeDL&H=aJM*yl@$g_T#(K59b<}sR{@XE-Rv_iIF*b#?$MfZvf?7H&v96{;)q9*9S z!R><#L7o}_*}ccPRz!EZ{uT=)ag?#s#k<~tG22;|?F28B{8=E+!|x+8KYnNT*pR=L z1y}okm|h{HIS&uKcIUYR9?E+3Q_kz*FzteUEXGq0bkX+5QTC%{M7`%q0svp*7yvQ- z-IcBXjZqFjP%dmPXk5VCuKcy4rVqfXF02n54AFWJYB+)oa-3g+u}SDr?rhPM&eol3 zT06r+BIWzxTIK#gh=WB+;;?-#0Xa0f?9`iKW$ijzSt$4-BxWQqIH^WRE&^QEN*j2p z1AqK23=jh}iu!;SYqb)bH3%J15an|q+ITVE71q*P><`^eXIpae`Q-FpO&k;tQN#`^ zxHhwXRFD{X(ZzbP{D6hIDFF00;TNFgzY%@`u)&X^3p0LU*obvph7KR`b1H8)A_B3l zC-Enx8RtHx=DGUHmMtlHO{|FX^R|NK^hk*-%;I-HhZ+X|g&bl{*`g6IPHi1|YA>5Q{xPJvz zD4kVmVa}eq^GS$z3vf^*^5<7bjH(&VA}+IFlIVL8&;Bzyap?g7L^6R)d6_anL`?-q zX7GkU0y{W=VelqE0Dh!WrNrHvDX1hOC<05~KiB`C4LX$R)b3x01livBt3jwFWB(k1 zkYj#I9fPk+QWN7~|5e{4ds5R&r)daX)LIZx}c=j2Zws%k(mhRz>>^Hle*Z0;9&b627d1627Whg zHFX7gu;K*(KO5(AOUUb;Bg*!K5A8rO1+FZ%r%<#E2{Yn~IG|lV%wJ>Kc z?5|tk*r=rAZTk}6K>)(w3^q50H*!p`1I(Xx!*39azAZ&^J9#QW6U%-6mzSPIRjufQ zt_SXuS*08|hCiuC50IDEj+-Ed0Q-}!(t#Eq+Xvx4hp+;5w><)>~=f?=qzUb(f6 zKN|Zt*>FO<{*jKq%tyHb;au@W@1Jf#6{da36puZxR%%cZ;5=6brU1=I%*SKp2i)hK zyVYL?aqJb#zgHSjRAg4|g@_{5eXm=@lELw&jRDHZ0DRvyw#IM0fJ95 zho-eNh`)W_?>}KF)pS!&i={XRMUx)fpJLzD_{-Uu?E?P}9NhP+!U|==Hh2LAWYYqL z8p7CDw!M9;q-b1K$KJXq?de@1N$KdUkQ7vIBeo|I$(L$=$5~=V2Ny}(@T=Y+0tPj6 ze+J)@W^46%sN%1Q(dU;Ehdxy%3>4K(jQg0B)6)+YjPlq&+?9_p$5Bqg+$r{}4k~*u z{WoPN+xJ$&u_K*P$c1{4UyyUu&z?x3Z>`>Dpx?+NK-HoPc8{MWF?*7u&O(GO9BL23 z*Db@=&5|E1VZ^(zzBA5l<{|3>_Rk+h+l0T}5iUh6jkny^7U;{>%1FsOX4A@&&HY=e zJIuLY*yKJP9zhW5=2)r&{BFBVC%JLx?(TS1mNY?_ZI5W6P_@5wNT~X)6yN-XsR|LHC$7h72PKIAPL( zCth)DY3UBE9p9T5x8}N-AjIgqdL~20kT?1q%#u6+Jt`rno6Ni@Iv=EO9o**Vg})vv zV)4WWA#PdSXOEf?-{ID>w_I37wcSBVj|u4)VV-&BUN<=E2z7AB($MV(Anp_do;($v#cL)bvkE#(OE2;V%xB$kx3X&|2ph9l&k_x!-maRk$cIUKK+Atk5c?| zZ^pt!=01vQh!geXcTDNmuN&mu)ACWC+fW4Ty7@{uzO83IjuFqOC0MSM*~ZjS@q&fr z4}RS}i7K+5j@C;+_fP;r`muTqmNPwjA@o~L|Az67{bD+vN(3gKN?Z9l}CbD4&M7$ZM4aKeB_WJW4Au&GB%4S7W<(la#G z)D{oY&k3&XRD7<0>k2xtz18!P+-L4tUecKMw$%k=kq<`T47#0)kIlI`a)j#|tuU{5 zto?lx|2#hZO-5(5?8Tv7SsKF%Kn;jpIZ(R~2{~I+fFAlb5C68-xYA@x2vF~)R5l8G zRF5dH1!4JG*plp(oEI@9#w;M)({G;F?h9q45yN!ihh~_bn6QA zkS*O*8fUSxCHR?xC46A2MjTnmlZP3dY7@3FVm?Gnm1ifyQ(iY~5;qFPqQEgt@_MEw zyGQ2lSie)JFxR6%-3cV`xj%OiM8sg2VO4D#s%C|Cypu`M1m`!~xL7yfFamu0A<9W` zpa?`AkydxmWST{_0wBT-o@BLM&-0|eB}F4b^Nt=^Y#XOYC_09SlJGB}Rm5-;#E{}p zJ2Ey7g~46M!3F#ehfzXdlfifQgkb$AB&?LF!&WDb>twD@p#L6$*KzRFsgl}=fk<2b zEqT~H&E=Lt;b@G0M|1%E?iJlal%d@u6#>uiP@R$*d@V$r4L-% znyqF?HiW#54V` z^q`t;sw_cL_|prZ6_n!&-PBUrjV@3H0wcm+)Ez{}`X^>ANi?pOt5%S{XHreB_Uv-C zoOru%9;hTP|3iuiVauPtnki%M{$uujxifmVZgD~EI_Oe>&8v3ZYurXP?ZigZI923B z36bAYUm^7m7uNZ_hwaQ+us_|861O78)ka(da?2;Ugb1}7-UaO_b+(DbohaLA@Q}-Q zWDYkJO_8U#9fR z?%Y=f7u;A~2&OH29eA*5wx9m7Gh%?m-$eLLF4mRALoC9cx^-yL0jFBTtZNYdb;YiT zIAC+QX`$Sk-0qZdXHzU<)-%;d5|{Vbr*|{st(0@x0PrLW4>chv27s8`g5nhgp9`!ZtknO!XANP*}CYjgd3i1p25UQ zA~`Dg$W8t9iPptnc4xG-&Kw9dE(pzwUZ1DuOEoQ;#v$?OAoA<@c431tNz{6NUV1cW z7G8PnKJb|jqw9Th9Y$n0nXUIA$OKX30-Lxe20pR2quCVZA=t%HH=C8NGvHbe9fzsS zH@z#BOr3P9W1AoT>W|Kc7_0BqG8^*pNPQO>L-zt7+$0PoHe8Y4T1nrriKFiBZX)PS#%Rq>5GMDN|bfxt99%EJf8yUE9CaKkIAv zqN*9Rz*R?}8TMF_qrt9TrJM5&M6-XXA+~&HhB+V*OosjCrXJ4XQ7;Axegm~>Y~y_; z)nhoXN1-SqduTX69IV~oi7QAfj{xf*l(L`H5)5O=cE^!$GgS&o&E|IOH;d?7u4xz! zdfA+$&_(uOj;d978{oTp`O`7-Cq>+p+*No??K>fzA|M;qt5{+;-9R4d(&;Ja(dBei zBI&+s_jDsP4pp)ArFRg4ZQ1;fs*R0kGFIARyb4>W;7{Z-NflG_eKwJSa2^^uM95W+ zrODoYjZ)M+ENw{f_iuO&7X34j9eHm3=i=flii8l){BTh~rg4A3?vb0!UI*qD`4!DI zRjs+J|K+ZEbXrBlCG4B#JQyoF{)EkeN3aU;UEAdTeGf{X#qvNB-TT*`+o!!@ywWE` z#bPu<);@4JR9*d983yi$b35R7gv6o+=Fl`zeF6>=`_CKtzijbo?s zBSOfPJ@d@Zu3efJ=hysGb_gAwE%o&Nii$z`U~oRL$|>Xdsi+!_UR@Yba4F&Io zm^5XvBR)eZ=AZ9AaJxY>NjvUz)|Auq!R-s+Ldp=L!e&0ABzPZUe-2(-`5Cm`N@Pd6 z+!EkbKFyc(yeoK0_rxi*&3^3Zmgefjn42Uhn;f4eGvzd~($YQ%OTUD{` zRl+25-wHgk_lo9rQ_SC_z7Z(JYde4#wSR%WPV{Ajlj?P7D3~4QcJ*nq$=-u!5oSM* zVyZ%i^CH|LhrHBSjT2Xsj3h`B<(ku#2b`vyR4^#Eqtcnjc4W}CNeKP{zHKsIomnj> z=ifE23vjm;49?z7QQ6}ppN9sGNdAop%nJ^}?)Zh1lLHm12Gag!0c45m;eRo|jqc;2 z#wmJi4+)t*3-ud~ose7vS4jGktkgTELT4{=H>9Vmyjg%_9L<9b1ihJ;% zc*Js4SqJ<`W?&P=mi)cw!l3&RRUCbJ`%4U~e-V~R;eny*N(U?fUN`hY^FN44aY~E4 zwUeVjZ8XsCCu0W9nNGNM(-5!f6+1$z5QM#)egGY1@fQ09=s5Oi(Hj&~D z2T2ZuWUS|#z$x!m(RoT`hSqkk<0o-(8VRIpw-Xqq?IM@RW`7MP&G}R_E+H`Hf9Jx8 zs@SiUf()NYtoC+F>$hwx`ptUaZj!5{8m6oO6yex^CCLu3^Sv}u%mB`>)TH9c_qCl9 zqb+?`;Ak6(M=WDn2?@40;E;AsUqdeovTIEZeV}?d4>oJUtaU7D*kzhWZ5I^WPKq69 zqEKoXR~*nK+q)UgMi?eI;l=NYU+)pSXas9L39P{k6w2@j0D*gcWq5Xr{Uz)KfLE0a zLz~ug@Vx`g+2dW7Ki@=2(uR=PXI}^wO6tPk>~N|Q{`PaZs}<=29&==>%Wb1_QU@hk zKj5OhsTdzq^#rAmb3mztq;eNa<3=!<6#Vg7)+Dqa|LQI@qX}NI`ELs72PRP)c0u^9 zaA6GqurRo6s$onEKA3n`yYs7=36czH4zmA!4^T?E;-rg}fjw!+f1Lpa9)qC- z>B=V=@8<`A=R&~V-!3J29spFa4M1;Y4iL?N zuK)UMba@*qe$jwH`dN)#wG}tg0w_(_Kx&DQs^MRSAf482DoW(D9jbuBxWunM#33(T zRSyCyMGk|UOnU&S28@a|?Gq|!1bUreNnTL%2z@wo6Y^o%yJkI{nUA4QU;cBPem{I zVQ7Xzk!Q~Ot5TG^*3*Jicqs9(j{AI>xD z9!)Rh*34>J-^Ak}K&iUBVxdV;_ApzlJsQ5Hp@w1p`qY z)}|*)o0o4@>UHEqQ_&Hv(dg@ZCy=edKO{F9&Dr^33KqAgIkc_+*5L*oc({F5ucy?^ zio~*_#^5!Xvf`kk(~1%U*UX{|7r(Yrh}iC;+o#Y8y#6T`TX70*@} zPc)WRRjnY+-ktaoUjs0q1=n_IzJPa2^xjthN+N(jO^cCTrhT>t1=CY?Ia6HWOLfDq zxI;0iDfO=qSM@li74a*<5TPVV)xncYZj;j3An#8P(POA>`$|Rt1d-?k<39h>HNc%1 z%_Nh<16~j&PM4(Yu^yB^CYWd+&Ur$<%|T>#x1bj3aD`1NK997U-KJQx(jz@}ICQ2Y zlun)WU1#lj5w6%k>od^8N@i{1aq6_+9rWp|FEKkd(hdV0_gAOU8nCMR(Rm4 z-De+aR;yz+o}84S)~Ip-9TsZVw*I90o<=Ti&fr_b&oe(%=P4&-tQmuG&c3(pF! z@t4OO{zfD(9)pG%Ua9qiMv@c&OYcr>1CSjkTJ>oWpOGTa07mq3879ps6#zHWyX%ib z>)KIgxSg`k22-5FhAPsvh7hGtP%&TbkId)@O8j_(b?LH>L*QBBebOyPx{Drad zJV<+g<~L?Pu0W5|_pSg_DOjxL(2 zFj01F;RPGmsDHnA22!eSxi`anO%t}e2;~~fucpIPs%p;vKJ&W>xY0v)?Ps>{IFt== zTrX57KnLqWtk|sMa^CysKIlx-kJIuqd~xK6S|*3+=XYjRuVbv?5C)ygwj*)s8RY(q zXmZgQ89wGDOn{LGIqV?EiYRH+J&{7Bo_pK#uDP=Q8@M6X4mBCY{?CZofkz4RYy?vs z91OE5X|ADNba2_a%1W?iMgU<0uLO>Glo;HF*HVBuId9yKoPX+%pS4kP>3n0#S%thO zHqYi%^mgLsEu7_qE;`iD>R0`%b^}LfEZA|!9iFft!UzA-Z6FhWWF#z{SHu*$eWPPq zg8Axm9Te2yF%Wj(7#=ezFz4=&Wc^fG9i0VYngEURtNxj7drTQ_yvYr7TAJdMGAt|w zAC<;qRc|))3WA$8DX;Uzsf?Dyh(NjG2GUp zi!DjXoV}4&BZ@q5W|EwC?@NG(IEud_x{z1CvRRv*59}^5O ze$CJo!ylk*&w7ZLYx7EnJX?R)hijdz+i<9s5li4bP2dkB@a6nev~G#_F2sP^(8;v^o;dqW(MbAo4m z(%Z+3cL4d{?38ttOyG&EX5mxF(i9bao4Sh=ZZ(EGFJDdt5t6qbgW~9tvz$Zz-RB_D zWe-7OjJgrc_QQwwP}~Z5J#xS!+>Ti9XhgFsHV^Bk4~}fs8}zx6zvj_9n*wvd1Om#Z zhCK)1N&FlCJE_upVn5Cd*1Mk{>zz|-Dorl}W-(v)4E2CTj!m%iR`UH7e1gPcv0S>7 zJ8QK>&sh9>KWR83?W7z@WXn+gaMxL?BV{>oE?rS2~Zv5!EC>QId z2(eN`C=VW%SI#bEtdTY;-9{~$avQ9 z65QBg`Ahj5Hj`@z?_Elr!MLP@g&_FVbl38@u^pg!R4}669yGHoat<5yi8aF`NFbKA zX74p;V3Jyl2xOuOf%CvwDtA$qYB56v7Ev4R15${cLH={~zp537xtUNHDphtP!3Ta9MP;>H{mwpVl+V}wVSNynFb{ZUVDPaRi@^$u z*dDReBQmVG@zQ8F-=DL~85%+xbQUeV+{D>*fHA_fqlI)4>Kpi8TKh3(+f1hyS!XY$ z@Y+y~m(Im?f(67;P?HTi9bsvAKL)RR>?x|{*i)wT&*eRNOoGVCdxp{+qi;kE!VLzq zeXHl~OW5Vw3!NHt`zrWEd#O%uf%1E3)bB#GLUc>py8$G&8Lu7y00RI3d)$>sV1o+> zGM>e?S52}oO3L%d9yUB%QM#kZ4F`s<6*H|>QtUxa4+92pkPPmM8XVl2?$)wK>l`pbFod+O=}1O9gq z2Xl7d4|^&_GAGb^Jws0m@l&7wX{UvKy}+@9`ptS}{BLM@K6Wpun}Bxi1+lybt-^HU z9+$1G=@3lr9i_eI^_7s`PM=1uC>UYx?a>_*66>mIPag_^M2=wVpwQ5_0dVl+(hl7z ztX$PkLYNs0^1Zr=_XTjsvUdssm35NoM^=O*Q^|)dbw*gXsYj(2!6cf&Q&ELGriYDH z@!)ja#R)(sF}Gpo>&};1x@6%5qzU$LW$man*b)~`joQR#ghu;LBL%DqHWGtesBqJs zceJm)sp4Ppv#m8xI(slCdbAe<$_g;`JTmpGv|(z_M@uA8Z(OS>R=|^cwZ6cy9V#&P zVfaI3UE&6MIBqljAZ1w-0Y4a~_Y!Zy-;DcApWcuu+>$NkC&n^@w4D!2^rJVGB9R=u zkTN;(-9c9A97S3P5q7y4g^RZWwQ)`(9l_!fk@fl4LwAt{O_NR*_X1lD+oFQ|@v*%S z$iEqLTK}%t9MRinb+K!TP~b3$NI-_%?6z|HEM@}h}PtEQU^3`)wHh4{V$Ekq4U^9kPQVdR%oJ8i5+uiMfD`097k#r4T8lj zPn#~$VSerzT@Ja-d;$11RT26JaoflLnNOnh$TjY`yPLN!A~6I>L5J`m5gZ%I2u6Fi zgz;~Wa=;WXT+E&5&~6a=(ag4Gp_|OWp%h8Gsnl5r4X|Po) z(K+!J1y!4YLn~?5ctnw7oBO1Z6bU0qP$Pf<04I_@SrVp83;|0MxQ20Po4O>+;F5wE z)9YJeywBdE1J>b`;h|P-U|hH^^jfoshx{47|Fc2>?&M!uZk-x9!;DpoV;yOZD4S^) z_M|HPF>wyqmSzHa3ca)wv8m3q%b2i+ovP2zbrQuRP3#@A}XS! zzydt7Fx4l)r^f)|g-ST3CYVy6g3ua4x>%$A6c6wHV2gXLo9UJA@EsTPQw$CtkJ{ot z4iiD~B6emr=B{%YH*_GCS?wkum;Ob*)z!O7eiA7Gf{y;MQjKS7d4-X9+Jl;7H$}tD zoW?gacbv^+0|8gyw>KAa&KVM~VDRrLqXs~Hx zR&lT1ffl6{0Z4wu9{g_bWxKbk^I>7r3nnBhaa}u?R6$K;o5{knwbMVsI?RFrZr*iV z#0ww@F*l!=DrX~gLsJ;c->TN@q7$tDbeY)bESL7^H{2rVY;{z<|D<2?F~*lWt83qy zkYlySqQf}AvQ!efd6z@}AntQ3`-h?(r*!xr00Dci()XUA3iynm`G*F1nR?(VB{?Pp zS`l{X3l`;k!6YZ9%Y>A*WNPX~#@Jq`dequWoPJ<63l6jv%<`zp4h1g$n^=6&y)FlH z{}ExYhJDW>1h1eey1&PuR#h;r1d!7Rw1WZ7uQ1riq#Npgq`u}_rN>)%7Q!EJCPn6U zw`nXk$t*hWc019SR$TH|fOsPd&eH{(0gO`47ts#j-C!Iqmr8j7PCV0cuj2V#=7bN- z3K1?V-r{nD8@&|8PZ>~EZG}|*haywZ+n_)s;_slW=t<3GG$IAWhF8CY=w$+G^Z!}^ z1f}j3Hrj0cep{JTq+R{Yohi>yb<`Zt6hZ2eebWdL1&D>%l-{3OD||L`z`OiiB=n4? z?mbq&Zi$X_(J%AR$I%p+hu>jV%Gxvd14GO()ia28lTcGH3niC_40uLKv~bm@N0 zNHw|f#)sr3!@OBs3KJC*3G42)qwfgwx~s{NFmlq0FLL}Gz6cEav$Q-_zRyJFhV@{z z0!UFK{(`|ZV0~GE#S+mQ8z&s39v-?4R6{(7!|DCvX)j!68H};$8P5tR9aQ}#F z5{TMh^R428s~;x#FjR@+bQk-eIhh=kVEd1}eGz;Q#HQx$#g9d;f^X=X377Q~gave8 zXXsYagFF0rWyjWCcC|iaK#-Vb{Vjgt(fiR#1JUp{s9cUFG5{~W!A>-3JAXR42m{3h zeX4{6^*Y6DYNS?Jp)YM&#q_0PUsx2cH<X4y(=B{6L-$!W2D1^zQmwVWSxtu9yUR)v9IeHo<5Xihri z4QK>u6;0CV-_P_#xdas7xZlR1PhHnOkAH+8K9(5(F_e3@e#Q>;?;dc9Zj9b6JP3(( zxWX7|?L?unL7!C7jc6RicWhm2|FSCiF^o>Gy40$iUx6xd?wRo(xWa>8hNWr-sXs8M zHk)7SSSEWtmZ5>Xl;#sDZ>IK3=r=ZQKccZFBj8>+=6Gd6KCZISliEE zyOybOR@dtRy3&4$KL`-feC>T+DY52tm7CzO<$qWufIJ!~iQ}UbzTLH7+q9`2)S#PX z%~!^ddIQ5*Dh(}(QGEJ4`=e*Ytt13hEnXqx?uqpf=_4!elwQD`o9)c&8Jx@|V0X8R zdz1Vv&`lDjo?3EG2`6Di^vY45(oSabx{PS5_5|ZA;~!(mv|%|SEpVGb)rdgK4toP< zW7xXHDocFDMd$1jI#gq-Ff%Hl!Ar_E%pQXU@iOM;3FDXL!}`Y98jiF$Q4IZoqUZazNT6=WF5QZrTlX zrsU%q@R3$(R|PsY0KnWC3U)~y!+sNb5MUb;4+nw2o4;ASjBDwaFc+OYB!#O4>L z0U?Wj*zH^3@CMneY#{s(+nTE{kPICqB2Fn=HMvdJ`Xmy*R}!BTqWlbsZb!DixB%OD z;_z&l1}Vsc<%00TE6b%|z&fj+95PIXSc}|(*-Lc&%Pbh!t?$yJv08m9&PmRGfyT`Q zr&wrrh>~J=s|uTjd~Kz`{6GvE6|CiFdq=(4$kRIL(6{~+zpBjeln%7pMAqVBA`&2? zq0p%q>b;dS_MYa_tWvqS^HMmtGh;*ql;!rUihNAWVS5tSq45`q;xpGvES z)bzh(+$OPC4tuBiuQz=i#hF;4z4sT4*e6TJ5-VR(d!BWpc){oJ?nwH#ldO-H3ZJCC zDMy$`%>(&~?|iW%sTS>dUE8_S_w$bi*0L2UHxJF0zOb*PUCob)!~5>Vhmd>e_hBkh zYp^2e;6iS4oAz!|&GB1P0F^gvP z&+iG#cF&AvPT2hz-E7udGEG$B++F7L%1_t|mRG^cejB@_;`OImMQ7~4?ai4%ZZ=%m z#MxQlP8wJ-XyJmzAJLKfb$({&^z#Sp7B#}4D9ePQ?U1DGz=uUlvmY6J!Iyk~{k~km5RSET;Bc(J-cYSC;_k9b}VKXMB&O$l{p2;w(p;We_BN zs1JHe+5ms?%AQ}Mwu5N5e6KaiB)8goT6g}Z(cExNf3E~zW^=^vI&TRwq7KU^qFE$v zPwnCt-iZK9x!ls46?!c5PElamEWJ~wJ}K)AQw|N$CQ@DcKVz!1f$Sn$!Ry?=P&jlS zzZGuZwtKVeB%` zMewHLS{h=LGUPkSAGC|ezq}A3Bu)ZZ^9NwODAIMDG&z0Xwii6B{@FBP(JFx+o`)~` z6~76G!h&!K$$@LKn=F3m?o*q<89_xQ8EVi=E$_B zMSsWKaCf2FVu#X2no}|}tWg`SucUO##F#EDb08r`fbQ=#*E2_{Qh-(i)9MOPhxJLh_- zA@LS~nzpRmEn=E2?IL|of!oN$bgEW6UvbU2yc}(LZ6~CSomwm6HM#{S=%H5QygK;W zEvBDe=urm;-o9(wU;o5Q*NC3S#AevxcG77Wy`m!tOu~3&b$ZDr`QGd^052hbM&R&q z#;q&am8G4S?qz%`%U1G}pRE(Xr=N#H^TEgptcqXPd=WzojDhP3&5UDn*HajBnrUqm zFpsVuGrd4olX}!y7k!Y^H6!<5D}6QzA7#zR{}*1WNXk5?**E}x(u+lBdAfp_=7h=% z9$9ybbxDL&XQc!S|1D|^)4RTpLOf5{U_CqK_f*Iu4!2eMjFO$f$9{?^O^H=e2++VT zuw#_CE^>-{n;@ru3VXS{>C5F4t37h$^cKpbFQr`ruPYXFM}{scMH>*pe9__MBJ{vU zYeE|q3HQRBdHnkV7>*nQNrS$Wur{a7d>If8AZx4t9mbubi5mkbdO{&fAN z4DlTmx&)C5HXOH71z!JCU1zszAJb z9<=U8VzH$8rrzw9HjfX`)EgBYOQ)h?2$&SJ--0t!HWVAAwUF(Kcnzfx?hyja?wuA-5|EerleS6tW$u!;R3Y)D0;!%eL56ssH8cIJC0?NxA6-Cai ztmJmbD!Blw@?V8^%#E(@GbUdN-L*ki5c*KuR#@c!XnP}gW8|aBiRivv?IIH;Mjeg^ zVx<#}RR@!d^Y3jK(m^NCQa3r6=!|7NT(VC~AHoiY@D9g~55IXfI!SwF*AlFTB0@C% z!cYTqSF5Nv9DMWkJV~i>#>fBR%()zwr%IRG$B8PKPjVv*l#0R``yOhSo#<2S!vm2T zp;3L&3hDWjh&@0QllhA+OFeOHNb5=-l?X>=Oa%l-bP`-(IxTq)4Fk07vwcuftB$Eh zRbz)+yw5$X@MW8v$m*FYn+Bd@MAXFgBpXQ^-0XjMO9VMPa@4d>^vHZ_VX zg^HBZW}j)=Cc_WiX5-$&GBfxzO%ZIxIW?d^9E%n;pU3M!{F?doR7$!jLB_S_tdlAw zt@y)nRykpEJcLQ%$jSy7?~s4IYMfqAFIT>#+XQlr|0;fhjR2KrpwmR*XE?eqYNc{w z6qE}%Ft2Y0a8stKj_8)I24ExTHz(FFS(aBwPm|0>uj-8aDkRkjhAIgLx|T@|D6J2e zCx|gyz{a(H`<_(z51;3)R+JM3vrFA9u>8SNQaMe80+SUfq->89J3|1&zLB z2MA>pUXYVaJEc@WZ`GnRW zS=&TToaa8)hf^(yduX(fD?sLD|+w2>3_G1zaa- zVT>SugVO=GaU-QOTCdW|kzoD4KjAY^O_HjBdnI#rAWfAgJ;*^Y9BMCrJK@Qi9M?Yb zudxviK2B~Bef_+xUximBHuSLa8aU})Gd9)E2>0jG%&XJYyeba-}6e4>?B+~z(wQupL65p6JBC8 zVLGX}h7_A?HV!?u<#vo@|BwrZ3DiGWoz29KnhK(^ZO%CRA`lP%9veHgGz3c93FWZ- z(YJ6%I*K))0kGHjOt|0J5{xob5&*)?(ijd?cK4FsOUs-ETclkR4nyOq>5#^>4zy8< z16EYO5@TiwH_s;tCg%W!Zh%(xRqeH$0^CLPGoMQzwk-3zEwLvwY7OjMql!C#Py`wm z9Y*t6uMx)&(zJ0f&B?%Y9xji`79{lo?>~g~>5YNNdZ}zr9RDOj!Wh6k@Bj+?$cddq z$}q9SE3kUc(A2__gAEs~ASciCHsG+WQI%Y;JtYopWZQznn#CYg-F@HSsF^lyPN1SA zhdQJoB;YTzWp!Czo^4ASD>oPMsSkDSk9w{~8b5t6J)hJ?(v7dT3_2J)I)i;AgnDbdXpP3Hrbn?q=$m zxoFjZ-zl>_?9xN$DIzEy;(rslHdu;K2Q{ACG{nW{*{d?$%|^LNd1;il-*;uOD^b)1 z2`wh5>&oMw+=tg)3~kyBZ^v3|qxLFAp}}>`_jdW>9btia*5OeWLCn(vTOgItHrw^scuuwYl7T3X3`d3IiurgfzcahE?OBF4Pp~8`bP5$J9 z>#ZgbbVtQjXC0>zay7Xg>G@FmjB#FzwYAtupocAotFyAvbIw#y9UB9g$S-(4#f&ppXK8+ru9C3Xc=09Be^Ry{QN(PArOaz z-hv=;>E=X|R8opSbY!gMn>n`!*x;iN(|%Z0<2$KQSoTA!?yJq#H-H_p{%H@L_zaj;3~Frsr|&$%Zk9j%g+gX5vJ*(BS~YJ<>>l7Vg=4IvfZ(>P;x zF5w@zi`?YS%*&YNr%9rM?A5!hU^+WVJs0z^Get205`3=+F&1C48&mDAK;F!&V>T-m z&a}yf3;|1_39{`^>OrC9&wyMiGn^R#O>3jRNdlokd8+3X8qjNdFL)#+&=&sBYRjp? z5FtRbz4^IKHfp%S`2x!P9TA6`beCevKrT*`*}73QLWb+G zAOi8yKb1d;hUX4tuebJv9o*4~e8s4b-`xPzbg1FbU2{jvdNdUaY>i;s?UG!MpmOs+ zN|m?X=KHEecSc{#P!;ozMppK$R%Cn=VSFIEiCw(-2N==sqJTDG^rhbV0h_(-UBsGC zzvvi3lhj=W%VP`5#~D;!L9Y*;g{rM3rRh~QP4iTx5Sfr(!=2I67ulA&Ncp04i+Rp# zq5rLiC(O(a`~g-5oX-S93XD4fP0!(R6pa95opv#ZhAQE+Bz=o`?3Q?w3)XT}&Dd^W zSaf<39KFyM;FpEDIG=#?M#9aYO2D$^fitLHQfrYd`9!}+;5K>%;R<|1L_FeXqKhiU zFFQEzraDZkMVDJglOPN#B(+wV^`uko zl2PE&t)3gb&qViq>Og%H!^doW^3C&#!J<1}VrB4nGeom-VhF$UTs!U!Qf)Qe!2!vh zV+}lJUdcJ&Bv?_LINZ)9RUA_H*kK3qgZR8Fg2d~;{M0icTWtVgIHgU;?6;DH>a3@_ zZ;}!(n(l>mq5l9xMFTOGY=S9uh-7!l_zVE=z7(~Un|pePZ)8elb7f$bnCKd`4{hKh zW~2I~i^<2x#KRGkWfAiOc4Dl!z)!qF=Kv(r(A)JDJ#Kpf15;BPD7!*!a$EZJh~5Ax zfgyuWvR7aYEr7d%Xpz*o@-yV?g&VIL-6#R2Ac*+K8-odtFVK`jJ%q~;L{{{Vn3YL^ ziZ-lk0-Xzlx1LD|0YOs(MjO#%xKSB{d|#8t;Y%=le+-a%C0Wdl?aaCoBHc%w23qwS zkv2IcAH8)-E0T^TDthgkFDoFXtHHefDZ9YDOzcDkE+j`?uR10eG>nciFkqI>Kgl*G zR|Sso?#J8i|5Eg`5>4u(GUP^rHQUY3dW>5}xm3c|f}W2;W$F%w^Ae{lSS)k99)rn8is(eum0|Ti zEvfVY-m^f1Mc}z?39`?-bupO8r?BGgeH0UlP!W_0+++-vmYYDAKljdqny*XFwrnPl!Q!DcWsFz6y)F*iSam45ra z9IyKwJ*X&ngz6RATMToy)TNlC+VNj+^f~L+Vup&LC5qBkgB;Zo$ddp8qf1xcW`pB0 zY3x}^g|f{c_lgCv))#B+x2*v*oryrao$aEWeLL*&#oJJ)U2qfmx3f@Xi- zuqk+CdOqkU(rCdYXxhmZA);Nd9@E_7n!5(Fgnl8{2@476L*KA^*UaUOEm!H8;^~Ey zVt)i?U3(o+Cz;n)V4rLSI*qf{E~JPgOi>5o)E55usap^e+r6y|XlO^ziZbXl?;32x zj5Z#t*ELTEr^fG|($IkFdpc+fnqKz@+Sd)Hig*q>=~q{+x51DiXC!^%$I__BJ74oS zWb*0R@O`ROcJUjc;ZM~S2SSwha9Q|#6Pw8S-UmuDC|DRAQvqM^Dp%T)rCq(W?*qs` zRRZJl$#Phteo~t3XhvB{sD|NmsgO4xvz61sY|w@m4h-yc2YYBgiq1LVZ-P_4^ISOC zoZ+vD?QSAV)>-L$k^;qg9kk+?nr@A_z$i+!pEP)?ZJA~KkycE9+1No+YDOE3w? z6Sa;P!Mk6W|HFzU|ANXEODdxMxfCq1A)evQi)BfpIkXNXu@_?_a8W<2@CC${iaOc9 zvOT~DVr&rA3UtGumjg@agQR3=P*{t<{Yx2!d*J0{&_t>aUzbD=zLl!8SBf@;fbbTq z_4d!yQcJkaOUalZgCfrVM-T#PfynTi{;AX|1#^ny;HQbvGt)!^Knd$wI|h!fD%U1eiFCiF!e zJcq(`o?q-esxZ6MRGsb@zSWwweLC{=KSOU#%H5Qax7XywGSAna%YW7+ip`eQR3Jm!e9N&;s32ILDrO-uP>Vm zTg|T&4J0^It4R^#G=*lR6@ca*WOuL$^$D8LwgN_C;_B~So~gnw-tYiFuq1CUpG^a= z89m;Tmsvko*ZwEwqqnwLCY1dx`}PpKS_(3P>n`2IsO>oisCSO;HQ1M)<6s()Y8i7 zLY+<&MX4R9U;iW_sBmIqGt_Le*zH4ZvWI{&j?$eku7>6A zIFo}-=xM>lWMFR!!&R3E49nj@&mQrZwPhPKgw%2H zrq${GqqpTY?cg8!?QT*o(5B@)J7f9Oty}Jsa=juirwhLmV6(tKmoNVlK>E& zyxtJ(-5nUdv8lG&z7YjHiU`QER0i8uoa@eAD7(jo19v}8VJvr#$SCv#8KnR8wDx7& zwp&C;FjAQ^(7n=51GGtmWOR{8yeDeE^`z_mw?C@XqqT3Nq&F=JSRtKJWlhR?E5|A&SOG@3iug$)Q#55NKhq-@L9LmJL(F+`7HmFknGYZMLcz;4N*+&j- z;7DREu6(K{y6&z$YK96}Z8ePyO5Xw9ht`lAaIls&Yuq%-d{0E4*Hn{jQ+bX+TXl14BE}JT z$&nb%XUJu}vm&LL*2y#DeN-%BU5eBJ@qq@0KD9ZleA&{feN8%B!_2!P_5(37P)rS; zNms9Hhe4x22n{aWk7vn=i-=L+o;XeSYzrjvnqUNEe*K)mJhp1nqL$|jLP#L=yi~}h z&mXj-d=QHcVJ=)e^|L@xjn3-1DYZCDUO@ndW&Cy3GM)OkaqUoBVT>ZNiWQw|-APs! z#)NW1A%NT3(3Z_^v=FHY7+TGO()Eu$0suGGt!pH)7X4%5!y>no!u1d88Trs*b`@J5 zpkL~A^0q7O=${$sO`bmP^)PN=gbcjgW7;D&Zh0mRq0eMvnD*?n5i!kxp`7d6fzh`; z!4D~{)F!rK>U1PN2p9?aPC@R9yhw9y9;U<0dPWWZs|=nfM$41yqK|tCbyw9C^@>pr zZV>IXua$c&MRlI5+^^eWL@TfA=anw^oU&fAls;#E-jHZ+xv>e;v`~m0d5=@CPxzR@ zPImjHGJF7}*gT#DdN-JU0=&n>3;fuMF#bbYYQD`Z$@KMjrW>K|kyW%9mIzA})UM_B zDs62elkE*EPrAtuqB?y@X1Cd=pe|V2MF1ukVZa{*nhiI4QZwJsuLhU}M@Wo^6$j5z=MWx4yX43%cWtXh zGdsXTp569vxwsuM{}S)oNGkbTy`WxfM6;Dn5jlLdud;$iYHxlQ!a1}DpSGYf_;fZ1)Yy8zIbeV#ZMYChpEjawN9%t%%{y5 zt!RCnq|_u@O4RSsaz$tNhBe@}55w$EH*5=nCOW>)b9kZ6#40)?3`!Acq> z2xrW0z`Xo@9SN|hEW>VKa6HGvkzHEU0TNxPe79Ly=sk>1ao+npsl~70 z5Z+**wNvWv$1a6e4nYP+fW^oHtpCscwTq1q@|9^cWKnu9g<3%4}x=LWA13wNuSq{XKp}qlK zjXb7HhRmh70`i&W`j)w2`LvEw>=w+h>FG@Ml6(C!WxH6KOY|=hCK;#n%GH*3Y5pOr zDVzy$QmNvn4g;V`$DzB;W^>@s>_XzN9kK>IBW&vy zHfTb4*Ez7vpDh?Z+S&w37n>YnM5z`R!iNL&w_m%H@c;muJt%GxkC{@OfR&fOeM;rW zh;kbFO%L^sgS4YWU+bsKR2BY=OeSC|Ck6`u|KT8uTM&rTENNjO%L->(H_)!`ls8oYI`KcsBH^~)&^PLyk_Zh^77;w!aWqQguL_gGk4-(<{AVmn*hV^8+{_~QIQvNfYy#PdwJjq)k(eJT zu1F6$C%%cMGzF&0c&$UOx6aSrjARzE~LG+gCHI0CXI2N!nlwV-z- zwLm}z9ZyF77@^FfZhmDaYV`rsIO|0I;N1|MJg_B8jdC~QeNk*-#1OAELZZNxCG%fu+920P-!*_QeQbzlJCFleI?kGq@W({dPvqpwW;t{qfbHk5FaULYL zXffUtPZAB)Q~ehYKn9%%96|tIjVn(wNR+#0nFa2$#FWAe?B3EuGjixB8|sPw(nAfu zy-sIfV&|-KLEj*o;7Ei2&+beGtjSRseNoZ}=lK{qUEykPWibmeyeuDJ^)wd|bi&^F zvY_$2Rk2bJzDL+>buady778d1pmK!nJ^>9bbBT51>pZGtUm5x!*cD@nHHfVB2>O3^ zaE0P%tOI7_>DtD+tUt`juz5H|7?f-t6r!^#-;=gpM-E_80N!<_K$?zp+TWj=9NDPZ0|-SLOc#d?ZVR(-hW zpwGeaTe=rS;8*{7$FtbMEf+Cr?#XGix0+jB`tQxHlimx2}=0nCsQjPTUDQZF!w>d3NrljJ56wSv7 zmX=o*qK}%>>^Za^W&);TE5kiDg5nN{iJm6kg$7hc_DjzRYgg3oBNPmuq8L@}X2%HQ z|wDRik1^uByqVTLp(LSfhmu@5#D+hV%17WVmS=% z{Oz6ml~&CPBpx(;~sZ^kIw$=PxAEEyzKtF9)P_4|Mt?21%>afJHFNOd6A>atvJx3oQED7*M7kLAxwn9mi`u(G ziOVAGYwacZ*Hi1A=mgb}u*o7Th7bao}BF@x1qX!G)2{4RIpY6!6hZF1=iGy9Q z23>M>PX%86n=X=4s2tBamR1H5-uZyuO-y!odUtz-9pIE`8>5C^QcW!lp+*LRP-2?j z+6g29rRIv(hfnfoqCFkAD(j2)Y0d&~t9M>-M(4yw0N`NKX^B+S%WCXgG_C5}s5 ze@b9&%sUxHaIa8>*Cq>aeqjiZdKcAM;*ZWM4UY$sqFrl7wU_NmrY5W29Mb|Ex|3g< z@KQVgt(LYu1??NuQxt=ZTv;yA3Lqafgbbo&XSU6VX;TF)tuYo(Q`>+mhWP#wkSMQD z{r$@-lanfNju$K%c7WQDs8-tC^J?Mz3e#n#KaT$k%)3rOh6BjWIY}+_%x`g~=2k8J zZQm|-g}ocx1920|Yif_8oi53gFEoZt1Nk0jIz^DEcg`@uvO~`*nUK+2>Q|RII-fY?}<0!c+_Du(uAD2Jdk`qqNc?erDu!nx}wR-m`7cIH!rWl!pzGjxjX`KH!-~ zXgAVw<38`NvxCdh?OhjTw2L)T2KX6|QuLqZV^Kf{YpEp%VcfUnG)%C}dkX7-+DG{Y zrpTcQa&Wot0Dd4^mE6bAeX{la)PsE5VuBxDHtZ58Nz;>vx0j?No}w%Po#xWyO2`^>A%` zG>~tG@mcM`b}*{b)UA}42^w3lS$Jr$OOXaX8MzsNTF_LQ$m3@K{UT?{>bszdCLyZ3 zK%#u1W6~bK6b}=HF9{Vo#Jw8fpY?6+!J^uYb~zT{KI}?6Fa&;F(CD z(2L;p%}oeQ{@us}e3A*GfR7&|6bttsqm01#s}6%AE1^u(5rg;hR9 zwR(1lS(Oa5LuHtLvq>@bL=n?*_NbSX>ti@F1?AcTZ&06;Pd}sv{+rG+PWWY~bCA{_ zl!m-XQU6W^%$F&5fO_ovxvN@4w^Z7h{&Z zg#B$KzLcF1kR4S$dH_Q}yuUdNs2fdiG*u`7ZIo)o%Qt0=Dx?4J5;{$14xJsWR(#-Y zeNm44ZSl!Y2>;umn|GDf=TDq1q;`|1M=c#uWCvxqxl!BE>h0+h0V90?&yXMAh?Hds zn~Cu4_u*aZHWn9}UJc$-PaCUBUB?x@LOWLi_?zcGT-rPdrGGHd{P#ArF76l%%=BYu6Yc55fU$=s z`e2&()NA&tRX_cB)v{4%sLQ#(84k!v#e8wi)Z4`6ruBnJpq}m?jj6$8ijq*!a5{A_x!G z#pPBsOn%SCxz{RYRfq{EJ1TR=kd8bimHe;ae@M;HAs8w*1Sj#W zj=v&)!Yn|Rmfy=>(1m;XjhsRi{=3~Oh{H4Y$h(`7!qJLky!K2={F}U1`Av^%G*ugY zOAVm3u=iI4aJ!7ywDB&mv98(h>aJz49b`D=Mo-RIUN<%w#Z{e%!_ta3cV5dGYH?pd- z4uB0C7vE>4|MHf)KaV~dE?BGCDbv8DrdzuHAsv=V~h%|{u3BqevXc#-gGF98LRk^1FZeF zZ^CFFf>383|CFj94&pJ%sc?kkD?@>PTR8bxs|$ScGRR{hcZ>hfE6@LALC#y0aNC;I za_zVegIR2N|EV6!CZUB@`5^-4Q&#U}^YfJ{#s0n>lPJV^t(H3o^=xPYZN~G%8F+PD;zkQ3Sb(n9uVX2>-gAi4Ib3n4^vqB+)m}l>>U> zvfp|m!C3g5&=1WQzq{9qLjxACcaPKT>D2hYuTmF|AbLE!W1NnN^@3fS`tz%ZexJ&s^W@`PIAp;-B>fLjcq>GDsNnSkWwttIup#q=>!+0GUlzRXGmu1WcGndapx;PNS1kpQix<8;8$uVH{&wF7+q#R zU38(A@pLuBa73ZAX?@8LoH?PLC)!;b@B4-7#GBHPrvhxjatBY&2b}~)uIwRvrN5pS z(2LTXKx13f4dU7PQKAaaGV;3MPfV8?x<#)a?m^3-zO7k&AK*%NZ*`XU@XW4KF0w=Z z@AMKdP>wv#N4jZDF8RY8pMU@W0{|8@GL!6Cn|Jnax?w9YJME$ zMN(aFCY725z}!l@BudG?Jx^Cz{c#Fv0PTMRL|JH7D#FN+RpwWj8@z?7>Q~r?V9C*P zeEx+`_&^e&3M7wt{#m-=4A~>b9Q-V`jreGDE^+zHGtE^k+19g!~k zu0O*1E~{1ag5~H@it9cBW}E$)3*wk^v+UFIS!`R5M3^VakSyeF6zmxLnCXao#N~Zi zrshaa3oyy|@c-z0EJv}6RF?M6KyjSAN-N16THa|r7x4H+=S{a$I~5)}nYG?Paf%vC zRV>P-sHO2c!(BbE zqXKLREN9B(ZK1XSYMN7C;cvkK$jt^bz=e{HJ${m*1VU39vTcAR5V=NP=l(Bu9Gx

a(s};&wFLTn`-)aRK9U3iyX8zZ)hCoYKSX6NdajHtFO|Xiq zR@PJn+V(NT6CL6RRi&nHUKBrB>Qj&*n`EZBVQPjt{AX?+d%0m83v+es%6-TDmFhnW zgpQ8+9(BaMBq1 zle#A<(SGsF^_?x3<9KQX@W|sNZ1P%LZZ+~1jT#c}ewB|)9> z#_NV$knF)1!yzIx0cdzWN zA44mFMMUZpH^Ykw4$Y>3HI1!qaNL>aaC_}YBqC4VtW!d`n zjD=fuz6kd@+~DeTWHN8V)_d3!mr|?X16FXA!%j(NLJp87OHi-c2;PFPyc8XoEA}V? z3neD9Nqu}!9!~;7fcm7=tb~H&U&|-6tf~7H!r8%h{f$GHo>t$HdYDM6jHX2=r7uCu7$&C;SxR+_1QY4PKnu&aANLB$C^8|AxFFcG-{$t{0&N=I3On zo)JkSVd1`0OynHti>vBZ?q3Bq5vgOyCG61%9nOS>tJ7GQjc4)$EI9Z)p!MUxm}L?O zd10bs3;o?;#AQXOK%GIZ(H9uw#ut4qB8k+jfl!vJbzf70a?8P( z-#sJ+W=rH&o^cp?iX(!UWI?f+`oV7GeW#;%n+QA(d6i>-8{uTMQa5(j`ZiX|`!mI+ z#{;UvU=-W~d_DUk;ZjK}S~my`jI*PVE1mB507_){Z$lUMp?@*hSX6R9Lhw0nBnCKw ziG0T-Fa~Yp@9vgp3{ek#Z5fvg3;h9Q51~h3yGjm&6w+fW^0ywxynt)lHOf-%(9CS! z7V~Jl2Fx42*QZsJ|3$2h2DRkX(^}m0slusD*Wwq^8NqE_{6c`pJ9y`IijY@=h>G)R zWoVDxk<{O-jA-zs{BERVx70o^@^t$Q65(fLCQ^U=6ZT7!!X{qIkrhp$W13MzvQJ z&Plu=glD%Mc$CTR&aT?bIm#j1Dk-M0s#JIV9Div|I0Z)nI=xj`@0Tpf5=6lbwn8=O zFB!l;Z=mrup}y)AY3C!#e{c2Nb_4A1EpIGQsv1p*Xu9iIGtK=c>c=o`22*U8ZN)aS zvf>jqY&A4t26UNM65p_9YBwAhmPJR2Dk1%!XacnzqwI5U)Ljgb>VV0RSH>AIAZf}5 zh#vD8daIB9zc&Fo$n^W*Fi0G>Wi0WBkjiNiVOi3y$Glql^~JJjYR>$v^>XQm$^5~v z1mlu{i0Jez0+T_hJn69CXVZeRR_|nqj!HquOEBGOtb1viWdvj>}lHFCURt>vGiPI~w%fq=qkHtUUIs7R9Q zG8=Gl-y;+4ZIGfx>k$gP%`}tk%Gb~f# zC{AlT$X-kGi0KvkX)iZs(RDEP72|T`wBp4nmnHEW2N#_vs7WMXm;a^4)k7K*02M@1-gc<0*01E<+|wamBpk%Ka~}}+8o9A?$3t* zjq(|A0m#=lHjEdd7iyT{o?#2B@tiz@yu>Y2vKeIVnYtx~W~oR@-%ThI2_>)jVwXxX z(bYy}(EPr1!8eqOpphekxm-e4VT$4(5JqY3_>+uZNuGXU)7;xcM6a_z@)UqEIJ`}9(RdpX*{}s00095o4KFL z5zz^5V`nML*z(l1Er&5BdY`jG3t%yW^@(MN6ysPh~cNXqYm<=*$hoFC64U<>! z_~-7}QAPgs5yRD?!&n4wj%HF)oHm5b*GhYfnLnne)*FnZx1|Y;cdw8oYqE1Lp*NPiM4`+a6i@a)Z|CAE zj3Hvgh$J_Q?>2lgeEs)q>?Ohz08+$@ws zi-dS@mHx5T}lRj(y*nPxA z8Iks$K+KNZd$O+JhA!=Pt|f8MvB<{Y%(pgBmm_wn>cBh-k4}}gb8?J0QMTO_BWzlP zbc}%og>CLMDjYV+)48C*(VUX-EkxAVhQh!F_2Quwrmu`TWL`6dTVV|lLIOuOs!jhL z^l7O7=pK0d#$xU545xwZ(;EY?P@?5U zrC84DEFfi_5_a(mWZ7R2VSbO9Xk&&N-OlEuUDuKNh|#wu@Y7gD)v@JTcckn0cV1t~ zMV14FqGtk71PD%Vk7|kqwN8%R#j39St^`<~FGdnauD~$8#O}%?Mos3c_OPL5hhNf+ zBH$N?1Il!JpO2J@O*{n$SJZcdulUHD1OI0RM8u6lLmm1J92c8~QQxbjM z7kC~dw&w8xZM#k4QU^&Du}(^tQ&Doo;Q^`?&s|N-UhBwvI0134tzscUo0wE`-1oqw z-BP)Ze!is2f#e;;k{2A*24tMDcc1_O6Gm))QKh90&pk4jWhainMVxv)iV8}ktVQq- zwVV8e+g^@EQp%?%x`40v;j@zb;SnVNd(dd`T38am~r@S!wPMJU){dokHkRC`B@3uUALHhNm<5!td=B}tu>+|w8J zPGXs3n}&yQ35KHwmB0>xWpjN2Cb_5>)c7mMFp~J;Lh`Xq%YxcGpn;6d3bs{Dc}b1S zpu3)zImA^7{@p*W8k>Iu4h;fsizJx`wF2mdlo@{3WH?&R|P(AY9V$Y%I8R*o3 zZB0`u;?Ge6vml8~tRUeH2NNtnB2Rh(V$1)$OcEXPqWv4*c)~XdX3}$%wV+>I zI^oDGDfdX9mNbvdyP~HET#_PR6YMxKyuomf%a1e!m}X?+N=?f8AGHt*_@O=X#*>fh zZDRm8Hvz_WdC=Rqo)Z_nsY_nJavs6%y1@Yei&C47!rsA2!|cBF_{a+eXqAWkQU|#S z-sWAUF*K}pN?a)2TbM)!Tl96tk0BzXAs$`zQr^0hTFjbd1!~Ir$u43w=*V!zu{MFj ze1ToyqNh9C{g}O>>~b9_*~Kb@I1{hAGGFI~cC>Gqy-oQa)?c9sDNSB;a+$MtNmORSZJn;0UeV(*^qOXez_2K~4pl zEIebTtNObr!h$jU53qNo>Mc0gKmToN!S+c^FXw8Q5HKxp>*H;NT!=OY$Q@jILzOKq zY-lIGCD(^omt`lIz1a1Vv%;c5t9-_^42 zQ+~RD-G~Q3?`iqPvkhH#r_#hjkqT)N5d^0Ff9~zG%)jZXM4a)^UR7>Ga9yWfs#0BS z@>}l065p#lFm~Hmc>%Lfo#l`TVmR#jgse>Q zI5qesGlm8dEH`CoC7a}QV-Op_UC`cTMkKth+`k~IU3=8%CA$=&VdFE-WKKoj>)YL4 zScVs$n0TuXB=Y(sc(5AO000{qlecbX+XJV#4v$qYEG)l$1cM}mxM@RYGPz3-_8uXK zAMi&^6OWn7qCG-jzyd?gzyY9uDmx{O4Jc^285h+YXj&`Jpx!eOEj=RhMPtj0hp0Ff zgtdaCAlm)rrK7wGH}@tSFYenG-qMq5H%5g*ZP0PYji3iwsW_9q7PF!B&2DC6;nJoh zw(Sz-A#I#@cDWHC{U0QBm(iL^oK|`iyG0R+G3F_|mZPIYuaq+1lhO{}(%_(bYyNMu zb|Ih3bqTr1)#ZD1gLAjbZahrgCWcHA$2h#|YTaFhOTgM>kSZ8rk%XS|Ft^9(Q-~gO zghaQH>M#hp9=2<|#LvWa@Mur21(Y(OwjAWQvHn^l4BPj&bB#)uYwj_vdPV1Jwi|Ci zBT%8t9fq3ApO1#cU|h?|G@Wv?XO(lBUCm%{9Z-B(#WkFYS6XlW{e{%k^H5`fovW0H zgL|8@SFG~XWgqi5)N&TM(OU$pIq8!-kFTyrY2{+BR> zq7!<|j4ph+QRku;(90oE1GbSXlSH*mWZCEoZ%{JVl)o7lx!$5#_53-go=u|BW+36l zIfFh@pqQVCLbXaa_*4}IE3y{^K(w{E4>?pmGyiiXmxm*`mz7}np}%70==?NQ-L$&h zcbt7RmRtprJ36h^23Mai?6^^}k6ogE6PO#ukdoE%#W5MCn?p3TNJuIhmoOU|a^G6C zigIIezIRy*Y7p*5q&wuB#^MEW?)r*TmooYD@Q6!#NW&>=N&Ibvsc%To&`i*-(BoW6Vs32Xb^Q#ks|FD?Z5?mGp8Q{SOxq5mv$E|&${c8 zl;rh-f7G2d2}ixl?Es)B>%bd7;Oqh4Ff6-nmTd(HjflkN6{`?mFcXF5^#y7Od@ zZ}2z8vRC~(W;z6fq9TgV%-jxmVYl_3V)1Om20nb5{Q?cdX% z&edc|$oLSbdb88J`K#WI+ei!l^8cD1{tS>0>0IL@PIwWz^QF++VC4?h%B3D|7L*)V z8%~W+d1Rg}p^N7l?@=#Z>Ki9Pzt4Ad%Yii(U{3aWcvEB9{Ld+IlD?=X!`fO;Qb`=T3^H<3tD3ta&PSLs zdZ{(;6Zh#rGIyp=Jh4t1!*H)CEyh!KS6oM9RU541--qE_H_q{gNX*>GByQX z)Ptz-uT4GOO)#&^T&QE=_&Eo%7z9yxgxkS&L*BZva<59@T%BO6A0WGGzB9$Yr=2A- zJoXwoAfH^Z88dqMX17=+w*E&SZN2Pd0x4YMUt=GYLRx|eMBV+m2g&))-8CXmo~Km4 z!)@Y=^Tsxi_VrtuN+UwHB+dQfnTeV;k7}rK>X+A>l72XJH}pKL0W z(+JH~kuK9tRg6AV`5vmnN{~i)h^<@p$W+_QtZ_&=X_=RegMlNlw~_7rvZ40MU#Kby zT*aUQ;4D|_XmVWe8Mz-@qR~O|ih*}fzCfqJ_K?JtBT_pXB!32Dk;!XkbJDU++Mj;W zFhoWQsT7W^R5LZlh`{#bOVlSx0e7aP`jbYO4?)Cg>$|-6vRpEi;vowZVI%;gbfmV9 zd11}rK-%;Rn#w&rEf_kmOk+*Y;SH-?O&oSErq9nIcHeDhI&V_JT$eAIlO8xS4>BI4 z_n8yzifwq^00BUHrfJ1_YFTLNB)~R(A!LMp@)?^q zbfX@bT)~X|et}FFh*;AK`DYohkj;N<#d~LVO7D}_s@>k1#Uo~ukiv#^{d#kUS?3Nw zTeLNnI5aFA+eX1s`0obMl8*^@sJ2C2#nzeY zxqjXnx2OmxvLp3FBd}IoL-65?kxEx`*q3#_a{Ip!@tat|F5v6}ZpQ(2O%ZhF< zk>OhawR05L2modVn+B(=L81CxS5r=c^#ubecCHNs3OlHWz@dyHbXwCX zH+pigdzME$A8nbigi8T*D>r!mbUlR<)YZ(wpUzXaE~YkkT-Uy_1LUls5KpF(+L(ZI ztyI%D6@XsNTls0T1W7IZ-o5pQ)d%D%AwUyuFxS`L5_f0-mBtVo4T9k5T%q;J^x`@7 zX>pnk8|D&S{(c(^nBinlW9&lQ{KUsj(hO_Kd8~A~AU1(^76$GK6k;B2(SI8YS6iI2Ykf>Y!R0@r!7Vij&7D*Pic?J^Q)3v}Q4Bh)pkZwwO zn7i!(kE21L)70t{uncQjZ-=%+d z??l$(7G!0T=b4Au5d%-gl7u;$WjUUFlGA}>&WrkLa%T~eNs*28=syHdUcCJ<)yw>( zj_HThu#&r6c(VZmG%ISf;PYx^cyz34DNpc$zqobp;s)+z@fnYRR`mC85!;YEJmFJB zo5vTf4I@=xOdNMO_ZB%;mH(_PBL9Djd?wm};wB*gvbXQi$O37Z|H@KOKhGpK}}TI8PZ^-MoXL2M0t*ti_6>Rl~Y-;JHI zHE1S8OB5eHd(eqWXU%r^$rwdK*~~NK!w{EGX?Z(7{n5(Z`a}}>%bF>I2b7~S9gt+% zw4V7vySzZ09JjnowwT{S2c+B?JvgXwfjp9(^|Su#EBTHe2E`^8lIfdZQGd)J-%DZo zH+R*CrT9@7ivih?za>y=!41FfK^#6l!d?a2+~+A6s# zOx>>vg1q^z1t9ZJAHSHygM5d+Rk@AOcMvu>I~jW6nmOox(F9V1prr!#Eh`iJ=n`pV zc#w-qfrx=1tHI7Y4(B+qDIxhG3AaV_YyY7E?S9!hP$>3QQ zuCUF`G6mcSAU#vTc(vryz-;A`sL@l|mLdVkRgP2~xLLHqasibyg;}2TeGo2TDFI&I zg#=aae4Qp1%At7XWIv|{th}t=S2%Z0gZ%}E)ngqHsJzwEW|!CcC!@*BDpuZNOe0t@#=J& znIaBK;1|{<@j7SXE<}d_Z@_dElIgecTghWh%Gnf>;Ljq9)Wfp6+!ycZ@Gne>N*3VI zd()H?OFulHmzC9Zr|SP!hC99Nf5&|FQwhNi7meYlE@3xyWnDTM$-9o8_VV`({vduDmf{~#ye{_N_z3YguA;}#R&X#1YH8X zoNV$WiprsOIL z=p{4hS5(Uv53y4ECrQhnpb2*r2%D8}7Hz~7Ag zH56S+(51WtP`D~hX`4IFPMTH$R94`wtYyL7?D@(f zS5LJokTa7sUwbK3RTH3E3UM3t-?g4@Et8oJEolRYjg0BaM)N zmH`0gE*Z`M-Ui3+w|Q3wI+N8g1n>cu#XJjuluce|od&<8NKkt=aRQfF1hu_5PU8Oc zjn-6%<0_?f!?{v!W1XwC=E0thBdKSxnKkx|eRbJE{V(Fni#uR`XtJfPBARRXJuQ%& zL{hti^*%p~r2ge+fat*~8^9Lbn5PW-4(0{{^utiSSQWO!=b!exeXHn=d!#Do;ffMx zZ!JgqI{r7HjSxLk2yUe|wa7JFG!Q0K)DJXEfJ2+Gj9jtH5K0Y$F>rY|jldHA7v}=ljLuMlJJQqU(r94BWW<)ystf zb)UM_cg?`Tjx6jXX87^T_VnYNdV7f!)Hf5L$=y`;}W?d=kKFZoJfU+Q2n3UGs=NxC6DgqUhU3yXWP=cWjm#9BgV+d zdn)lri&#G4kBI*}vQ65py}&h|u4+arNDYP{)+dJ;nU+ATAR**w{H;I$Z^F%8gM^Qm-?oZI{ckpUZZ2_CJFd3PM@7Bs^Syc4pUjPi=OT73P zPkPhU!T#oRipJeakWMvGuw(Kv`B7VksxtyKit=3Oyv0HguS!-6 zLwH_1F#mL^2qGh`3Lj77CSQ}WLL<8T+!G|5zU=?qOuCq^-Ae$FeN)Ni*IoX(6O{wI zge>{s3*qiI3mn@s6%) zw|g4j-(t*ys2okS<+`aYiA)RxYst`I!~6niX&_}760!B*#9{yBOERojWHD}6Xkw;W z#Ec)#A**l2i_p^XFW(H{z(JOhRFKkC9l(> z&;qhxkAgEN_w6}2*;Ir_H@(Xr8-21;)QRhm{n6q!tpW0;5QF$eZXzSN(v4 zyt=W}B}nGOYq2)Y0sB>k&|V2S%oo+<5Jovro1p)%^u&ekQy!uWh`QW6b!gFyIFz`~ z&Moa$y8qto4)6vYv^X0@hy}dNMIfgkRR7q{rK_LDD02dVY4Zz$%A2sDH_NmZSxFT5 z{FQ{JQQR{*f*;HOX{b(3Dn)N5u)jR@o=vxl;_z8M-niWU=OLpAs?GU6n$uYL?gJRQ zN7}@6kko@gLZ(n-)rwca)IV_3Wu+%K}lvCFtQ9MO?c*t)AJrfu>cGq zD{UWxE$jrbO+hx%NKr)*2Vihm&YEinl`X98+^D2|Ye8Ii)qssul{A^-*WW!JE&}59 zL=5Id=1wbWAp!t4``PATdKD!mAcgpkZYnFTC#KD_Fkhbmi1I?aTF3E|Ja`7>XB{EzVa4p*Q##BhZi__*Anr-v~VAiqk0lCtJC~=OK~CKZnR9E{;K@yP>-mQCbL2D~I1G*mmAWA1Ke@RHk~orzlmk zU3-oO^y=79UHXw;U$LaLYkGx1=1o>c)Oz@+Vad`E0Fg9@*FFTGuhrTkDU-eC*bMDu zxnNU8<&kwolAO0c>16?xu-)qC{E(K~8CrWbA|lEkk|Iz^np>-@c@0BfmN}x3T0u9U;`7W~aY!YGnQnFx(od z)xYYM0MNwzH*blGZlBbkc>ueh#Pf3GC<)n$F1CIR_Hcl1%1gyiUSL$FX7IDA*aBQR zGfZq`d_99B#=Itj;BRK**+OX4A(TrfPY%@2UHD6O-%raQ(~e(Q6z)S`i15mXa)Fu+ zKN;xo(;KN&F@m$AQn&;U2=l+^c0`GaDs zxzxI9E+|!4q=R}d*a}e*%#~?V8%c}VgO>^g6T-_NmtOottp4Vqy2ueLB~8TnCk787bug_W;>mv0+oYDhzqE zgZb$K^QR4A>9MyU2q;Bc2OwtIl||t{$g@BSxAN(zky zy_U=_j$?<(LO7Mz#Ar5|?o>zO&})V(Thh>9gn>#oZ?*?SPf$c~XMrd%9CCP6;m>^F z!6vffaJP(S5O{misdy$g5V-{!8^iXQ54d z^zjA{&TTEyEw%->*Loo;ek43Y-Y|C^>Vi>54%GS*-XIrwdevE`jTyZz-_u(I-8w?( z8($Bn4Hvr6)*thq_LAlZ!KeDTCeFlf_Y~m<4?mBSU%l9(SAmqA4Lr*X5YNTSE3>c3J}-2o zPC;8L1a=w+9uc0!dz0sdVeub-cZ8wa%F_=t&k8(C;D+^w^F6!Ivkh@KKip-nxT?^L zi>QuUk&OeU5%R0bo}O}Q6>|@3XXgNi^pG6m2l*Xl1Fh}HB;mToxlBTgYH?OK1YS>N zaZdH<%VB!af_tO^;txGddY^Ei4r#;`+U^Z7JN`^s2)%qHU(z;rx_OurP78*y7Ux+B zP%?`E+FC$R@(J}A@^B6P5=Kd&>@S2R`LfKX9L+&`y8f%np< z^ifIMw8CPl=YlATf#GM%2XHDXM9W|1DPB8+^N^%pzl+f&IT8J>HDk`yLkE+HN25<8 zKxM&RPkGFyiNhoq^1R_;cV$f;`^JfDnc@?&F^U~Ks(+QCSyEo%r7GE_SPlHFQD0aI zHqo&6{u7F0*bc?E-D73m&jp>fu}ZEaYEg zKAA>fi9WR|swO$wiqasb+fbb0OMZP3-`fPW2&0NYUNG&tMCT8>5$|LM!I`j+jnO1A z`BB|6;;^!T^^VkdqX&2Fg>&uDl$7C%Dsv*7xDBT@9Q31OhL^9yh4h}zGu0c5lhFA^ z5CJ;Nn>^TE)S;oqkwcK$^`~8oj#iadS3=jbkD)*-wC;ILfSVbo=lVT8rsQZbDHQR; z*^P%v(Z&2#cyqNW$jS#!o)V$xlHjssUallJ*HA9Y|59@mTOPu(m!c$IO-Ii<%Vl7E z54db~K1{nQY#vo~Zx;A{u%!t?yh6+4o5ai`2cl8M0ND>_Y4_s^q}^vr=%2HAAuvwR zq^lrBdQKB7sU_vsQ7Li}-Ti%`#UK(=MUs!I(nBw+eQWSE_C1KlDEV%gj*|&I$ zru)yhDffxmpZ@vb>+@C$i=Uwd^Xg8)0Bk;8{mGwpr&GuXQzDNAInl}j<;sHdaNxirpXO@HrUEHiSasNm2^T&TWx35sEzxgk^{YV&{DAPazoVZ2G=AW_|jbr zR)HaPW3Kd}fmU>Fnl9Srp*WHtax7J8j3@4EqU6Vf9QD=QG22gryP_ z-#1>c#gFsNw19u)TmydGi*-IAQ`kqURoY1nE?dy?b;V=X>DQON-=idP9gj`L?;&d1 zDLuwHo+!!X8b$uNwGQ?Eh@C{Rf@y3VCQa=gO$9n~-Qh@H0uC?AxJq{Z3AFiADui~y z6NC@3be=q|nDROKF_5ogzR^TdS}eZDqt~Cl{x8FXFnW0TL|6*X3IHYxO{^#;d_xKBoI4E(tuL(ijA2;vxdS<{!K!)8ONl>O>9eeJU#mg47CCdc^ z8il-@08J}V09g-lc`avM1$du_u40}oulcF1Nnvmps1z|?h{DAaNeB^s8zB6#a+o!0 z#Vpa{dQTt4)5BX-uZq9wYUInPm~_1_Nw}`{2rmupl2I8?EO7KRgQ-sIRvFbGFIka- z?kjs`D8z3}8B0B)mv4QSGVhkK^{mnEBp^Uu>2Kr9dV2AIx8mejn(thogQ%}V?)zrZ zxg|YEF0ahWg$*w@xj^&T?-Gjn%YD8d$bQW5ry**=z-kk4(yg8e#>9Zo3w+=Q1mG5P zMJ};afd(XB0do-z9UoTXk887t#8UkvZjwk%b{4~5xVjKy??s%eAUru;G#B)l`r~9y zDkrzhXqKb!(5T!?#%atY56Zi}K?SCbN&-(nc{{h+1r4s)R$Sk+EEOCBWPbKm>UXAR zzq6+XDRUyOeGz_iN2mH)#i%=Shi?)abNqc}5(UQ}Y#MKb+EZWUT)sSK;!Ts17?WjS z3b7?^!{mEEIl)#PpnyUgzj{)tFR|33q`M0D%wIJ+DqL$H5oT}alOj{}mKwPvR+8i> z3T37M#yH+t!!yP?pW?=eMGqt_CTm`lv8=J9b6ph^@xuWQj~f-X1I$j*3ysN74?tq|v$JhvJ|xiru*fj5 zv%tAY(9mNTjE*#LCXbzps*x}M)mpgQ8sPlYfdB2a{fTZYX(<#Ih-R$q;DV>%1vu{l zel?6({b44Ah^9ml%it7ia?9h5=1EGj1CZDSQ z01l@y+gB$$6XKgkC=}pS7PdriD?Ucs^*8DK@VrwtwGv;GIwVr7oV8^2TQ75CvzrQN}+1Vtd4_n7tSBumL z0>nMFIS;K>n2Pu*WGXZ1aVMTFVq%VC_n3>H_^u{RzUR8WtA3v%PXNTaF+O zwxW}Ihw-Xq$nDQ+*O4z@$oXfm&WLBX-ts{TPxh$#n;(-Z-M^J=#F%Oah~RkY4Lz|x zphp%}&zRkLQp-*3D1XvtrBghU7K3#5%+rOQ@nUS;%Ro)S#no9B(k>yK*b7?AF*A-< z@^ecag<6K^(*4OG3wX;Ci{QR9y;y;nsfDR$v}W!ev;mz^=xybiEs{Eeok=lc)69Ff z#d-}r-?&HxiNnK(RWBsq_GfY*96-Vy&(AS>zot?xdd}J9O*|q|$n|!gVcUy4xo?BkbZ6x2}H$)emQ+Ln)5)N{d_WG zhN_{2NK)ZgdYMS88Jm)O(^gO)fncvdP@ev1NdAPe9RW@(!Cp}7*LvSj`$0w`W%-js zWL=C?-S)ne`WF-s@@|5brOGZ5f@GapusMaOUj5X(APc|-VQz>7Y-3(bvS9ntd7*{h8OsbF*Ou1?G& zEYV7NQ&#+h#azt25rNur1Rg&GPKZ0eq}VXzf!rH4>od9n+R(h@+FwkF%tM2XbHkhN zkXOtXmIocCi(RUbrD2@IuL3f=5ZTvM(MpNLnPZ`PZGYa3hG_WFRObCzpPf*3f+^w| z_4F|qkjee=^fypWU}{D2XNovvXk3)Wc5wcrw&hw+Y^w@PEtlb^ZeT@zBPBsQh~JZ| zwDGzovT34ieJwVL!4#$E&?lB z=q|tIp0cqR-j#wE-kM zmifwxNSp@UzB~H=&z#^hnZA*d+|SsxYQ#b{%5zEtry|M`9tnuZ&^PQwNnR3Itm_yG zzReqY7wC-y4iH#i0$=%J!tlHb2`^(=kd#?~>71?n)(Oi>3N4SI*Oin#!^5eF?r&a( zwt=!?J%6V6P2Nh?B|VVOBX;B|$kL$HO##kFn)rtdds>+1qg#*q-T@ z3scePw0!HQ6nsAt7ws{CMwZkm9(*!Aft~aw<|`IA?Coaw3#r(HoQCC`H#UKq6gty{ zH_UC{*l!?*PK{&ss5eBb_8HJPN-Vn6`!bf$klv~iQ=p7|N&uQbWxssMw3J-z#Xl_+ z@h4Gh(w$`fjuV}N9G%DcE93}1>fKIlLr}^%pu~-T9q1pKj(7)eU~Cf&W{ehEmJ<7` z_7-=ANFdI|6>{Xhh_~%Sd$6NN;BmlptbdEin@Yz%Hgkv*XG|uhrEWSM$MrtmI$L-fHz!^$AXkjfwcj={r)CO zG)523+X=dX>DRexZMrz_eWFqw(WHK11q}3bJ?7%@S*M4Rk{i&-F)1%O*C^rq>_}g8 zU)k$U6dkIhZy#~#3Ylk}7`hbEdeZ>x!wSjmQ0S8b5!Ht8b=wwMT;&Hfi=RIBA$&3@ zR&ri!)_*Qhb>b1%uSsU=m6C17P+o_2)eGjYlxg)#?ET}#OGwKs6UKqi;9PK{d&>*P zNrMs{CoNwvTBM{vDq8y61lfuM>Y5z=`(K{-zjcOHpPv42=A%>g(*SL z78w=7;}Zf-3nwUO@==sYvzG0=FE*9G>9frkI&mFjQvOf3!n#`WhB8aC)+o`Rt|htg zLbrPbM&%;F^)9U|xvr$s3SblAzG+JMr*8QdMS*QZ)f-15Hlx`2=GSy|41>VEK^Rmf zC8|3>9nVf$T>$ALTNa+B2pHBiAds=e9)y>tXe<-$H0cOuf3^wZa5>@c`Rk%b+<*W8 z&Y_kCzJB1A(N}}r?@?Up!4d%N?B!8^b{<<804Z7k00RI30|6)SROUcJE$=UhM()-enYQef zBppU#y^V&@MPU}$*{`&m$~hw^;@!J=!M_I@5*h_81{(=TT@vcLS zmHLx2LWm<-xhPeAs10T%KaHs6&KdRY%x{aF61vlj12Yc@zZ<*={4`>DRf}oGov2jA z-F2;68^J|C=WY@tZO*8Z=DYry?ZJ>_j{nYPh^Zt!g+>LqcVUgK36h$xtXSRXPN`tl z)Zgg_%dnm`QS(wc4Ek3AYaakx^>m9@&WeXAc-0+WPRfP*{~W|Mem-=ORRmJxmn7-3Hyele zYaCn-spDd`m!8>&7aXA%l^8g79x9SGtt0Y8+S}DZlb;Y?r)Db*^d;uKH${RvkSLJu z?ZbZJN?4b*%m`r6x?8XMYw;U>L%T=XRLDMP-jY1$Li-%)ATaB%1Opp=2~yUZvW#6i zDqrdCYge`eZvdE&$XRZ`k@vx04-q?@y4CoI@bON>>&NWq8PlCF*H8Bj&ihTe&td*d zjABf(-*Y5bU{hdBrU8H9^`T>V-4*k{cKVqnd~s|nX-vRWwna@(Y1ohY!q6yN0xF_w zkx|25`L)LMq!CcYbcxii|5g+gc9wj72M@ernq>!vK6t0gMyP`^J6`}!==-aIG(#U* zV||GqcbVNmj0DI<*69>#5j-h<;r4iZRLI z<|CyY9$j8>|7@L*c)MufS{Sga5X1He3b-e4V(ymYUt>3${r*2IBpdt_d!Wf~#l5tO z*-R6eyzHfCwa(!hmd%(m)RKOdO^VcRf6%49YL__(`8l|AHUILfC*dunA`vBm?b@OZ zG1m|`kfPafIn()U)#bC6?qLuu>>2U0NHk+G0xL50o+p<^+I2sJ&JhymMHbN%ij*f~ zwYdj)#FCtnL{qcK5=l7sPhS_Tx|(hV8I9n-z*Vk4+49t%>&$WUdyJ+g564t#>+Wn% z2M3``dBBRQZZ2c%Z>F*T%tyLa3}x!@s<`COetc}SR#CD%?xt1+tGHMm2Ex4H2Qw1E z#(`o-$XPdwYdE$*AOD6YcmA(_VeJ8&k7n|nA<$ZQ$I4Hd)V9526U%v9)R#_*2!uteoKH+@(EN_ z1O&QvLJzy2O(d|-C{SO?B>jgZJ&esxb4q8+4w^_rT98{kXTRowMhg!7@T`IWybu^< z!BphV*l~vksGh)G`MCZi3CB3Fdu6+y2SS55(_VJLBok7fRQrxfKu&ikLSF5-_gq~W z2q{sT?=$U7zXUWv7>;s-|3{aK58w8BqGEqxz9+K{@HHdE_zp_Y59oLk(#3w9qYXW> z<|+D;x-^!1b^7v=`p}}ju8Xnvt)8NImkAA$mCE!j+IAuZ6QC;h9onGdaI|5)C9vHc zw~l37I6#lfvUo5VPrtWIr;yY@+HM+C1Ys1XA2OV2V1H@RPbLdbReZ~-v8AQ8^*YB^rIZWkF`N_ zCJJ5=6Z1XY6$f4WA{=YUpPa&f$qm_HneS?{Wji2(AobeO*vgPa65K^n12bm||F26R zU;nTo=?me&VjVyYnFz7WNF-1Jy#N3J@ByE)X%PPa00x)(6bcDB?kfK&!e{hC00093 z00RIHDgXcj0?vBvs%4M%m+8C?u_{6LZt&w_G`IvCWS42v>Kah7;cXno%%WDA0YtqR>l3(*baKoD*^EP zMXUYJiy9jCp4T%QHYBOR<9k416=2U+5 zVE&1do_~X4<|>rs{iVv8mbIR$5-ziCYiuGcNDP&=F0hcI3E-bKQtlI$Z4tRNl?;P~lmcTBzxp+G-fg(0c5}wToeD3 zjc0iYHR_ui2i_ALlaK%a0{{R60009300RI3m66vm&;S4f009*1u&)oA+vWp`xBvhH z00#5_r~DlQl$lcWw7&JpDP~070009300RIaUhS*4s4xHk0{{Sh=7@dXBeqm^tyrPJl-JS?FpKCtWeXihr4(j*@{52vRuL^w>R_ZT9b#tO&dsxz- zzy|yE$xlx1FlfP;`mqNzI?)&8l3ZlEt) z6l;&~c2Cz&j+Y1Q96SQda(%W0Hd-EP&;3rPI+x9SUtKMlYgN5-^kq9l5y#iLBiTVw z3^I53i?u5kWi9m5f8y51^tXjl^h0>QmUB{h4L7rux9)UTGn|3B2;G+3J@8v=hCN#L zDJK6>7b^53hpKBXe+Q$IdvK4vd2Q5QHer{w?>l1u|L9Y1=6mVscXN@`2A(}_ zmgmRfwmXuNUEWR4jW5V3C6k)+_Cu%@n$g!96x$fIGE4~FnQ>t?SdC<=CX>FLh;pC| z4Tk*o-d+o6L}$2YD44VQxJm2XtB~}y)x}YR>CEy13);uwA(kY|tWG~1xtbHuXu6rh zqa2Q?26>(-$G%T33m`zD-KW(6$NNOYs(MdOt_j4U8Ot6DGap1%EDIGPy`PNDzEVL5 z3eYm#qT_e6h9n)`?mtbZQV(0QP75;iw-~O1uM?o-m{M=f_gX8q@VzYLY>Kyj!h1kO zO;WjLBX9pAnav`KF{)B5A1C1^jcR=JQDI9I_4}k8b^hZgDxjdWmQ3o)|2juxYK2^qM!F-T zHoWpV@&2i@csi zbjwS9nexmP7M3kJavmnzBsMWw?|8(wfph3K6xt!axZWRTS${(UNP_Xdo_vS#Q#l8x zPq65KTSXnP{9t3g~ISqT@LjvTmJ#lBoq>&xyu}b$uVl9Y9$ZBQ{J@qW9t(AZ2 z6~9CL1OZ58RphO-)O}&-0GFvCt*ZLpVW(^U3gTW&=3*u{xsWiMX{W+cd|!iuF}(a& zv_t<2h}=hcFBSNsmlE5cp<2=HZps?o z&ac8k77@`DV)Spi>NIJ8j?v`1LYZp;s4nI(yMb?Vu7$W;6isc9@S*>>Ob&BQZN3&d z_h9sY*y_7@9rJ?}!;oB^M_iM56yw$=RB|jJISFq<9d0Nh}UO15QZ)N^JbtvAV zC2yh_Sf{*&7+NllJih4#TD$|i5EeakA3I2dpk6NIkh+1yk2>0Rb^^G;q6{9) zb^*WC@q${4+j+ckV_j8Ms(I21ANn~F)s>sbH3W?zb&&<)UC)=d_F)3LfdnjaBXr>^ zpBS5n04ro2M>Ru`32SWLx6v2yP8~r77$THOM`xfvbiQis0q$lG&%~^q>F|2x3lFr; z+W*`fBq#bDPkvtve^%IZ$T>SsLEThr60#y|7q?dA!SnZxVUcpHsKKl*-z$&+XfM|Msa_UIB41zJ7J01 z`QN|6J|yrw!LVA6bzD_2YW#8IGx3ySNssAUWwP^sT42O%-Ebgk=GalN8glb?zhT-^ z(&dPHi_4-!{fg|J^u5V59vq)}h~H5mXy_9dtYDD(VsORjcUvv_%q$D*=9|t5WR<9S zZXeevyM>X{1n3Y7ygFJ`5X6c41c3IKi?LU8RtWH@nmq9&KjG^C9a-8=AdZz;-K&`Q z`4DOfd|>=3&L|^wE~86Ga+Ko*C8on8x*k|@ae8tt>V&Jn_RRxa*Yvs=8l(lq)JcDP zK%0}Zxwun)0)FVXU%zbM6Ov30SK$WVD^!l+uF1U^-LhQjb(BQnY zr&nBC#XA3eS=14+QVaD|v;8>-0lRUoWl)GVvnX*~b-BlO*td9FRI?fC%@Z-_l0|7I zvUg&^ljUH&6!(k0>2L9yQX|`mUU?t^`p#JTNDb=k&zZ`Zq#g&5Gc{ojX#?Wrn_4fm z=RXI+g}yoswG-5E7Yt31?WDi_RE5H{*4Reb*J2s zp4B(VJnx6x&0EjFnxvag$p;@x8lTOKz>lkSy;{jLi@*@(@y`Dkb>R59pvHGyFGjwP zdYHg)B0=}W>V(YZ-+30?kr0j-^eADCnxrvHeqz zovV{Q?nNVhVWgG>+#dlyFFi`r_Xk`Tt7ct5L=ayy%Kj&R;@*$s$f}OpYxqYW zYzImMdYKoTC_Lw6F)gWRh^kX>)SjnBYF(+8L0PgjS+x}+X#B+~=dEXGkS&9|K=CVi z^_Ho27vQQl{x!hHnv{l<9>?`>NZt=EJ?QnEy6{9##vi}%Z9gl7aYi4t026P|Ks4>B z-xKm&-{~dJ_P|2!>iSbSnUN9=d)p!9X)6Qmg>>J(=b#DlJAx?U-UL`OVX&PK$HNugGIA%t^N z%?P)8UY^rmJvxQ9B@VsODaqGyTu(QKnK3X;vII=?XaroG14mUSfFF9{=)P|@8@&A@ z2>R3?4=JjXBoO-JJh=Vou#M95V=?!2JBZR3$i9wsRHAV(w0e2}k zO!%0}j5Ag>(D{P~2QSwUYWu~yPNU=vYtKu>;#!7(Aft2(V(`#PWnDUaY>ZepD?`nD z%LL#@xAXLrw2ThXIvY!9on?)s7<>~xXQ1d?`+5+7+6!!78~(b32Av>eh(tQs4TLKA zZ0HX&B3*`}8zY~g;a)b^vl9*;oQ-=+IK@XXbT>K0y(NobzVCfA+*OvfSxHhoEZzWC zwY`QIneu1NdJsX!9q~CwFhlSY6%0Rss3eME4C%%7bqcN@J;(7z*k4ct7ek~rA~os- z2CZ-6nSRr$b8fX~pXUWlpt7Up(60BkJeDYcf!+{lf;Ik0@g;1-M$*Gl_CgsF1ar+h zTWJc%7*AwA8z+XvvwX5@uizy6GCQMVU1LYGshyvz4A zBlF>s+TeZs+#H^L>~ylg>6t|g?KJR{U>Z13Y$B+vWdT{Pe3WsQfm(U-27S<7aT?Mr zN?;r4ZzQ#tTQqcQXX%yK<$z$BEfdsxW_3X9rN3+aPB!Mv>Zn9pjCX-aCuW0jY zkcGLL8fQc=15N#~w8iX2!uwX-3L=IBY70)ZukIepF+J1uMJmpuStZyLNeQJBoQX*~ zv9CqV)!!bQ8siigf&%pYWoD?1pv#4`HqPrX5qgE1K2ZwC z%p)~Iz7^mfPKTT1x!KV=@9PyRsXT9<_`EF+04%I))=W&Q_w0}S+4H&FS6{kIwZ8VB zretCanS(*_``%dhGivoQJz$Cbi>o-w3-IGz)h>_D+zwanQnbTR{1lO}-o{M+$Px?J zR+I@B`_cKgHJb@v@<}Xfp8$pN{$WfwC#WG?_&yNUg5C2NXD+F&=t}ytcTutuhFpGQ zX?v=}4&BtS-eNWT#A#!NzK4R0uN zClg=ke z=|8EHll8C*W`z6b{f^(f!X!sdFZo`Pail18SK$lqDz&*j>7F+eA=qcRa=^e>4KvTc z@bV!|rL+QhX^)Is$G;9+{r*EfT4~V>_D6P za>{Aj_tY?ej2m5B)Wgvn!Ts2dMaJy}&E1Lah<}SKO7h5j((i~zp3}l9HDs8GVVr_~ zF3Dqq#il;jk;}sK`wA;7tDC^jCJo#0ko`a=yUWbH_mMK0i}1nH04@Vo&)JQ0Y=ZD@ zYt#vL6=zpYqM75&_>D36(VMbPnK|FxWeF(~1UT6v(9z8Rpow<9s`d`>@kcj8^M9ds zi5Zh#Qo2?fmC2uct0rf>J_6+}8Vqe4rXzCcS|8flD?+!gD{dz&b*k(=zQ9*Ec|nRU z@|%ptbhSoyG+gy*GU-TrTPhz9D=%=eD^~#4c9Z)!2bVQfzby@&;V_)Qwb_AvISWN3 z9&G3PB+bZbF^%dAAWXUA6YLrptd#^f$y#&!qc+68Ryqe8%iXzQyN?3QBuW;_mXVO4 z;c;~-hxeRI+dI)W1>^F@@VGlf#6Oa%qZrB%*mS*e3m$1xBOMy-5_PqozzgeaCD2c zR!g?3Q;q9|9`~-??Gp6T4FaR_vwst*chcpw?-J;|5DfII9y7z}MhRjfjl&9j4 zDMcD{a4GkOvn7+FD~P$Q_%6zxmpAPJS`@vNR6i%G>NH@gB^Hd{qVFEFqeY7Bh+XTz zb#-`D@P@V-jry3`l*nsmvc9i$>F_cZueC-z0nXyvZ=3*x`9NOAn2<-$@8Y~dSo$!# zR%hmMH}mO`_faA^e?(n{2=9Hxn1l*73JRJL^;?L6-PNGV;c&+}r=pZ$+7XE{UaQEi znn!0jpg_7``oI3AnJq>i+NK?yioREK_2}k{|CjWZj)Bgl3@C7Zx0vxy{$CQd1GZ`XmNhNWDRkXxyTG76 z%;P8>M+-6ce2oBpViZOxP!adXu)h#SZyjMfAox+;z&$hvtsgP}gGy;BlLJ<7s*tyL z5yp0xlNMhvSOU$L-~{4XF<8xi2Mij$(7#`lQ`Vz^Vlsg~%V0_A#zg}ihls#0rf}E_hFNv_Er%j{3NM;-A4_lX}N*vy6@>!7a zuCj(4&RvwZ4uh z1O3kYTbu07=Lj%`5_ud;(TDZHX?(bF!r2z`iavl+h6w7+218wpeV%dP@5AS7?eeKzUx0jT2{exItvQd0Ds zwEdUeUv9-jPUDy&n)A>XJFkkWtm>wf%>dgLsB~R2TS1ahqpZIn9(UED`TX&WigHvF zC}sFj1nOexC}+!l)_{-sg7M)sT-CW5bwl!^(uQ z8IlOm!bNAcfLmu|W=S{*dk2H6T#L%{!I37g@tz}II)^eA5fP~p?Ip%E7U=qDHaFpj z*j}iVsu@}DpM3ntM2&Y%sTk-%0cPQS&l~vkDy42kS4Z?J+@K93+bb&NHBlxBc= z<6Byy=VOcd2<3`4bQ%y7CKt3_%pj&*YaAD0pAoHLNz2{%79vfapAt94e25PDUyT)< z5z5?i8@tpT&p?Qg0c2fH_CP+yc(a==V|=3@#p_-wVP5@QwEz3(6{=)_+sfJ)HSHG~ z5I99w+vnx91jNP!s;f8f&_S`Hb&@q3!iHpJFP$InKGD}W!GAG^Z9^xYn|=LOGkZb71?d0EpT^(W?<-O{u=P}&$ZS2! z5z)JSa@}VY?=H%}Qv0)P5!T^+-1&9^B-6r;W-&=MmB7n9UjeNlZasC&;fjs7mtAq* z;`~UB9#}qQKh_-nkfBZ7nP7c0BK@Q-FL3m}tS`c3Y3Ho9(Kdk8~4-jgv zMCEH`CINw2TsWBYv~!BJF9yvCSuV+2c?^|C#-CjA+6PGLS?^H33S$50fc7L(g2s4R zsG|Kt62K8?{BB0kQ6;}Dkqrg+NAX|FL4w0Sl9`4kU$Qr6BMuI@-YFCDv|=ljh5_;* z#d*6*hfXcmivY8feI-LEQyFPwEqkYu0z|FCzzN%CgNJDUuz_p zJJa0^lA?=wEWmt?$9#4$A!pLcUSv0C=2aK5Z~d4Es$^m<6!#Q|+b(T-Ij$o+HqPI! z)^zJ5fQVc%tcJ<-pkt8m+Ic~$>+Xp5D>ml=C-*-#0S3v5+8w)i0Li`TT5=?8 zjM0s8Bd%z`24l+dbvG7i)xXWeD&LO=L0P>99P{(l1yjwWCFWY{i* zq+KiQSi9DI$VwbT&T_lpej~M6NIOx;=sV{&n$noKZWeSoDAnSN?&%5a&_g-A4g3OI z7BFI*;?!HwQcfCIH%xlo@mON5WojsU|9DhSic@~Q0uaq!mrsG7y^`e-1sCj}gO``{ z`^>MQDNe--1GBB6$lbN?1$)j{$lc{qUyaE?gV_PDqROX#DB1>q|BoejmX5C>0?0xp>$tQv)jGzYl z1S1aCF4Q-7_`;v^3S8s#DK6iQT3w_-)Kv!mw3L$qL!c!*ksXK<(JD}CL~<8!!bmJ7 zPX$~zJse0d<9l0xFn|EALjtV|(z6WxLa)rEx>FAQZ7r>h;lmmEhkDRlhz(ggV+l#O3|3u0zD? z4DDl%(RT@JV_9wxTg7ZULnJ>ITXOYGfss!t8y<&GyMCu{cPld6XKq!C>J&{{tuNV@ zE$xQ2)~b?wUZ4l;E29Q9Og_TBb8G*JiT!dQX^}ptzmMl#nEjgx>cc=r84OXxSs%_l zn%L*5*m$0RVzf({fTxv^T0yZ~elq%mP}*yVJs^o0{2RT4vK>u}ymI$Pc`jw7>Xu$+q$uF#IL;cQJxBrBY zYuGXG{vsBLVMFabDxTIHv_RjFme|Sgpzn6X2^TwUg=Wjv^zS*gl2-G3Jhh(9PQ zEeJ3a8FVe9sb#*iM6KCj0_0j^27gxa?l)g(0I7u{P^%BxJ3126WkiXFplNrHTGefA zN&phio7CP5Zv|5Rc33FJ z`?Nji1Rb4xvkS z^5p#*oL}+pCN7x6JTEMetIbg4$6OW`QcchRa*r=omkiL)4)zUQ`IZ*nUh)X25EGZ z<`cUcs7nH|7NXPIyjcWf-3C)r8@&6(%PFy&Vg>YDTjEdFeAnOzs1LO--S3Ol%iw){ zijh9ABACEn4p{PZZ%W^?h%S4dZY%GkgdFo`wVzuG;?hQVjlN;SKwmSlYaXV1S9v%l z#AU2&sY@KpBG$!ToCX4cUl1eD=?|Pb+XuH>^Ca+eEKJ6%NdUvR64I;T&v1o+e$wRz zZI++(@Ku3WZki!N3-4JIp#Nbo`)7Gb9bS8AUbek2Htmeet8v?;z~&sX+Wj=7vN&bk zsXBvll|HER%jSB zIk`x7kLv+6_n8R7MBbKJKvwZ5mPkRWdt|;cl+qi7IT3|X2-r_!V7JuCFTDU3@9Q@< zZdwRTqq1od)dky>xgnJkOe_D-wBZ-=48LL}Ws|iM#4)XsE>(}SyvWyV5@gLtvnL&+9fS=2zlDfY=F-tW+eUUIuRPjo8(z}Bf5e15*;9EW zaDB@Z{|J@?Ag=#2&oNmRiXaO=+mITCv{d`J>n}_jBvX~dGEX6KI(oJy z0{D7nTX8&|(9mcm!kWF-P1)i13t9X&19}fi2;6E`4{<4Zy&v5SY87U ztREcNBts3P>wR&JO?pQppkNym(R<4^40U`;Ug$C3 zf8t`FTsEw*W*UiFIvc31MGhga5*%}h^PIZF+V-{q6PhSF5(kZh)sC#enzfnxCDph3 zy3MD!TbeCM7-;>?!;Cnb{PqP=`s1H^p|SUDsp>^@>lKQ-CixTV z56*slRv$g-G;|d`@HWEh1mqF`I(nrmeVO3;w^QG#02VYY6IiJk2+KwCQ)04OZjEX? zS8#p1gOR?!yU>=J8nMj5sbij@#B%od^~H7wj@25nmC3c9SS<4&$|qO-OX<`N9xs3Q zJ8yz~611yhUuAmTb!`-G^*lRs@n1|A=BZb;Io=5Os3^cv;!Luh%b3b_E<|X<`T&H4 zy`3#Wsw8%5GQIf8IoLL5A$8dfM=C=IhR$I8)AG7)FRB(GEm|V5Dg!8_-N9w3G zU$H`B#D52Rn&z42kS~gl$H9K2s&3mu4FJc<$HcDPZ@5lun(^kl!?W03VCve=YK;Q06P+b_AAe78`JNJEPxBuVHsvk-XQl5huA^% zVNF@VA{KX_ZD&|%E6@g`-TI@|CKiPl(4*A7ljyyfww4S@g`!v$huL3+usn2ySUJMsx;NYCWJs@k91W3%pD+3$79_12(CB<{F@J%c z{>&Z)w`tYf?0KFGwQq=ZR&tfBi7})TSvDI!2E-`4j+Ux`@b%4(nZx`zklD8W-kKd` zGp7C8*sv<0=0bq~>j+{-m*3V|7uB#jS&DOTn{BDUDyVByTMOeXW`9q$P$ctJ&IML+<+jN-lgv6lb+E9j`sJt2BPFk(QI==lHW#>%d*tPW+d&|NC<`Ed3m znXE;%bPZT_GNRT=z!mN)(hV{7kD=r}LgM216=7D$fY^j>|JQyA57<^oOkO2^{f!8D zYzDRklNJi=N|W>(AlM+hYmgzdS_!a5?X=pag*D!b`xTymt#j`0+_V7QbQ*(V&EG2; z^z*jDVv=h^{nz^o;%p0c{mUl5z;@p8V6Zbt=WiXVMFb734}mpO@j(ARtv4cpKlQz> zg(nF+G)#1-V*-o80jKE)PQ+AM|AZGFkS-bK(`+j>QpW~etxI$lY}a12>5QS?1mHED zbfu<>Aipw#LU>V zddVW=d`|T(P*o2R*MB7LPs1^gv!CM-Mt$Kw6BeKNh-Z$4kZ|Hj1){;thXTY>AMx~ z&`iSuhibx0pMQNss5weopaAgr{1KjvY`K;}%PE#uduj;{X7d81LW!P@IC}I6S{nq< zEUav)p7@`_@twEPu%TI>UeU%c@w5D-3Me;&g5#HwGbNkMRnx&WjylWkY{V_--D4D^J6`&T>~xhu;ok`E z{GolLiQ5lGY0=>3d7coTpUX@X1%=&;r-CjE5}8G-=3DSEzIVdDfH6|}p1~G|nakSw<&0LKx#3AXI3Su>ExptIeZsU7 zmNy)fJRlKyC}h1!ARkJ6m1#4rUsP5G5xfT_;gUcz^ntyOK(@8LGsHHaK5EV%pchIGkz#S7AYO^f}P?T|BB!M96cMI}I;BCiY z*C$moUhil5x7O-<`PH1|H%&v}ruEy@oDutEVo_%tRG#>reK#g~pF7y`#6am`KA@V+ zD_B9qL$B?TUJM$0H2*HR%II6uwl>TI44a;g9<+MU`(27TYbwJvEHi?|@EsYNtN3fT zTo6H6zNJUQaB>fJ$|e)C>M#cHSbo`A*+O-^&9^2(Hp4Py`H)-~x`F+anIO1rE}2~B zzKdeOi^$6Bk7K9>Cl3{5^J)eE4@TQ^%|*8|<_jcZfuG}6#V_z5o48G!Ef)jrbw+qQ zcnu%@;IHlQkSbOp{h7p-Rvx=JPH7$d!bYMgzLoW7VkkTq<2e_!VAk$57dne()c_4~ zCwFY5R)X;|vY(PJJq@oHFo|mk0Dk4JE>Vp0C4FP=Sd$qZ_=UryLw0wmFsJ$OSI`Q~ zZ}4-_r>X?78p5NCKmiy46UB5r1kiU&3GGCIkoxTwaxd7%zA6tYD_YhZ8> zzr!1mgq!KU$S9_lA>pdaMODV4Gj{IGE6IW$z~!0~nGg3&4u z%j#qnar$6w(Q@PiF7(E=YiF(3mnlUbea%AbnD0eoBN7%1ZziUclF^BP5l`smpsw6=uiM5m;eEMtD=25-Wofj9|CabSfqmddF>$< zHk;?b=gz;Lc=FLTC^Hx>>Kw;4%yH*1dHsL}CV&7Qn$YmMM$2rBL<0%zP)~tEkJfbs zUY4^!rq95)$j#K}RRB-^*#igCkywr~A`~AKpy1v13VH6ip++XANQI~lx!-C`JoZg6 z*ixbg7UWsPhphfg+>A?+zL1m5_qs!*?4{5W^CRD#E8oK5|DbbzvYERs(_j1~q1y$_ zR1iaJ!^za!z9hAp`~Q=vTZV0J8t5QAejL6w!m6to=2?{}*$;hK2Lc4z3+N86zB$>_ z3GCWSB3)UEcgd4_S!M~^QlXf*99S}@u+>R1|S~1yL_?37o_;OaP8b{wCiHq)p=Q4bkDec3Z zlW8d9{ERNv!+)NRB$(&1n2JErtxGCxK;bc+Q^4-GxKCTCEH*CbGI-D>hT4u!JIa65 z`$~y)QxeZ=htqJpL=Lt9qO)2kMdyQVpG_QZ%SB82;aKksb~}G`qwJGCbtGBJsHKo4 z#brQ5cBPSlkQ;i@J4ycL+>6ULQq!!aC?UP}8&RMGxWvjfkO+Qt42yF9z`K#;ENBN0 zS6jMOpwMneij2+VGw*te%{kdJp4K5qz!)y5sWRim&%Qt_K1ro4AOueMfB>|B02-31 zGG>%9N>W3d1i2MU7AqwXy}KzWC01=>HLM!=BJwdSgR7)1Gd{0iiUgq$E!UL-e`XRi zW}OS$5DyL#1nA>O4&+@->KxU@Y7SyjygV3zT8cu-pU1L5n<`#3wNqayx{wG00GmztydQo#l~x;-m}Y-nH;bI zQ*}`$GM}zdkZKdXUeoQp*xU5M|rfwDV6p8Tq^t!U*k!uYL*yFP% z*(LdP{Sp(ny#&2j8g6xk#L9d+Dh|sEy#pFnD8u-5TRll!_Aj7lRJ0td%p3|~7iXi=C4!tk@HOFE zP;Qoza=7W#Aly9ZR49jTzfN{4lNZU6V#UeG0d!96_kygp&{hFR+Q#V^YLs?kYi*zI z^4@8KmW5F*Ny;deI4KFy_PJ=7wX(dgIz6; z-dp-SIJKi3@635VhVV2rl9)F>%0RIXv|g;{uMa4cps8$5=q;(!Emb;rO&J?^;T)cm z1JKv5=!7&V;&a5<8Y?L&;%cuZ!e_f(AEqNPcBavXim3h^nlhIl4$7`90009300RJU zIfqeB7`Uha00RI30{{R60009377zdc2a^Gx)^0=p002HLpoUR1W>IO+nbB{A9RL6W z0009300RI37>&0f+3!@#06Bv}K3{Cg4fhDVKB{FC*Y47{xaH0k+@5RpN4`m8_n4{o zj_ES(e!dY#yf#QR-0~e0dG;MZ2;n;yyE7JApJTAPg&(gFUhrJ1G-PA$2@-YWc4R}p zCc;3!v1oHAB|&K~5iV@|u7Ze3nzw|JT1Ar%g26W!} zBhJH+gKjfwL+&?`8~^|=Bd*b_hy1&1L{9#)9${OhS|~##++ai;L!UIYhqbYqEZR7* zxOr>Qu|W%q%yV~#yq*i0ppIiBoT@L ztCa?i{#b7Fgf}O471;? z0{DN4E0*(tz?NPNff8In&Jz;nBE{g45mRa!GtXgtw)`m^Q8UVS-xli0f3htsbYdcg z5hkCRvuLvAY3UF4_FP3uOea`J9FaA3IjkG7UU7DF9dmgr(K%ewO>keN0FOX$zi}Os z7evajQctpM3Tmh=hpSF@N}NN5<}vou@{n{@r1H{f?yl~_!x5Ez#mZpW^4%fv{5bjb z*zjdZo&!&%oPc;P!zC=nVDC^k}cYuMwH;&U_)`cPW@~w=(sep$r6m~ouI_e)A z`f_{F`9Y}XUo%cJ4&90r!W-&OY*tN{XdYR!p^*4+vruN|CmDo`8Ms4xQ@%`QEhG3-?oZ=Kkn;`EaUr<8EOlO55=rxP!I@sp`5wrNpexxDv*6<@VnS6cXh#9}LQA;+h_B7H^-miE9 zK+2Y}RoLS)7a9rkjDFrAGNAw*U;qFE0RBrpIRF9NGUFt+{oM`1mdIE`Tz14vLphN! zCzi$+hczx-Zia!h1zph8R&pl`UB|217n79%AB<^?N1GN1bMK`YuJU0thppo@{6k@$r|4a6a-DsI$a* zq7(eLPcVOo*!EKZeyw`oy8Tw1v<0-bD)9c=P!IqB^6y{6IsgOYKjSS3--7si);#m< zK^HWbWWQOo`60w2kKL9_a<^ER*1ozv5dr3C@G%)oo2jwFaJw#DUNL5c!$_=WSjqCx&Q~6W$J7z}X z4J%E{T1=Z(H&HRc4UaZj1k__*OL57~HX(YG$;7u!hQKwF$)GtpCyw%>B)Rhqks$Ll zUSc|E?>;>#$yEb_FzWVf|ES%ks7_;RR{uzH&5%fTG#NHzVTF{#V@OP+UXr%d&hcDl zX8}FbSU{H%Lb1jlLT62iOkc`qSQ(HeRQ(i5Hzh2uF^_oD6u%lcwcBPkY_8D+4(y{- z9L{%-j;XW8+~?BZ;uI2Xg(anW?+Z^WXSvTIxw&%0xV;TXgG8NW2>8&e- z8Q-E6Y!{TVG}+z+JQ}A4cTHxjEAje`L12pQYVcJ*%t{F4&__DOz79XBS(U>g_`n>l9~GD&hvc%fFW@XPW>L zJ6Q0bhG*dhkUd)7x_osI`D(UXEKKR(XxM_*FBs=I*W3lR3}^|9WZMrQ?4*f3 zXk`s@m|!~N7vKC-W+2jO7Oe)1)?8*I=S3mHVyXJ}jBT$XK*R)S!E&W!iAu2eXxtQ? zIi|pOWJAv{Jb;~+e#qW_G~LV4FGm-c)YexGSr^z{lOCRp)ZLs_A|T|iXUg=iVVP?DSJI>1dhzj=_RWHVdS2>}kM>4k|F-21#LOEfljqm;brg(B6%k z1aJNnqHLWf9CT=5CO}Y(5v?c*f5N=XR_L@K&Rp0R(tyJ==i`Z2~r=J`ZdOrKNMF1*AHd&qguFKZ)lval#U63mIYP$2lFc;0`OyciGVoEWvf*q zt0xGze535OE>D4n2rKo{(R{ckeiZTg0Kv#&oGC zSKv6(kU>|ng1$)AGS?05$2&)=jOwQ^;bX}b?<$8f+je2Kl~;Qvwvs=pwJXCXGr;;) zt?fsh$$`mzuyvh*g@!?u_zrH}Q2D89kp=jNj4DOK$vfE~M}G^On}-745{LJt04jg< zm+d#X;xS;W$DC!8Ce8ufVN#=n*GHKpTW~*i#yfn&W?8tV4mb2_%-Phla{xfu^U>zv z);S4A?a(N!cc-@Fr-uq7=b1^4xrU_|BnR%;M=9xdnSmpDE=n_>3=~BD=VEQaUd4H$ zGjJjaBQBhehqZR-Ty271fJ@NIqoC?Vb1!rJ-_rt~Es=(^m11wt(-;Cb4x>4UiKgT& z$z7R?jo&C-M6+s+1fTG%Gi_K(%2LWGLuq3i6idyRD(rSmexxifx<{jxSr|f8?A{5a z)2$DEJY|GUx9i}eNTR0ZDu2dq(@YbX8seP|SbEwOE>a6ybv`sgjp-P{8PZ6?p=U z;0?O7{koq1dQ&mb4(?pekb#6~4Caq?=-cjrVaBSDf#8e{G@Vzm$*HIg=tl)Gm(rI z`B|?tJi~}~Cm7Ayxig3v^@c@LI8&_wnL@LF1JfDz#}Knq9XHKuirVjQO^EPR(UmvAO*$@c(ZRlhr!cv z>AvV_uoCVh_uaQ z{AJLIPK&j;XM!ME3XG7k!#>+soHNL~Rid>pI?9UnlG{q|0TlRuC33?;dLP7gXx_}SY& zZz;YE0K&wA0(T0Ez4>J!AOLw>;@;f8F$B|(F9B0IfrJuQhh`=YYDw|iXDRLp;Ea)*ZC^FK%m7eSG$tAQ zm=^!LqQa=mLBDsr+i;9;eVzyA5!;_ti#q2mA@l#V=d@3j=nC1@pO{?8CJ`i%;+V{( zP67lkx98F>;$>e50@s@oBiN86gtwSN;Or#*RGrko@3Fnv*Tq^gs$?8kz9}=L@6S2` z)6krj7Yo$A8kMokp)N~q<0e#JNPt&uU-hKuLVgB0^ie(~XO3jiVL7sd9V^h(1(ibv zD;<3@J0=Ff_YpMwsFqn%T5k?>(T-36mlDjVdK>i=9o`-3367uM4UG?jQfzyqc5*jjqp{|G;lBY^%%~z36eLLwr&P2^#LrZvCF= zkR>QvnxxxOJxRV5rySO#^bf)>-9YPSVC}tb?1NmF-izj z`tRI%g|lBF<+1LLCEZOXHc6Uz0Nr}K9aqm}%ycTah?<6#@E)adyYzhyvgCk`%YF==~qO>ckH~ER1(~d zaZ!a5AOvHao0sG(YB$|9z95a6*3C08s={=Pd%U2G2kYUF8lr4vkTW2T^l70%JJ!G%$~%6 zA?+!j+LBCrU0i=vxgm9-3^+jWCz_ekm#uJCj zhPb~Cehh+{t}`{$k+u5wAOp=`57$aX0wtE52VCwG3mnHt{{lq_ct@GT4dq`E#~bUf5txw~q?R|o@%PSS|jf9dC z#b%>2)~gQ{`WPH{dQTi+sv(bM@EJm&QAc#P{TNW~VYP*^?XT)}AOvK29iQ}pULrzaO8UpVD4YUrf#t~)@w z9ojpdPWMJ6E-865(Q%Qj9*a8|dL&L~WHy-Nj| ze;v|@oCh~!sVmw{U1-CTl3#Vj1=Zx#UqTYWxFyWZB~m~D0LjYn^18k1VhZyIqsuYI zUFug3kNtQ~ri|zbDY>{-Sky7m0BvYP%B1W{(kTA`8<|7OzxjHX%tgd#Ns9#bfZGA$ z@hG*y%aS2oe(372!{R}Z5}BcW>mL{-!2C$bc5VEn|l;VPw$JR&nTG~>#~PV)eF zs4io61SNop5bTlj-HFSUuDO$#NOPR817kGo_OR`RZxbJ4e8eShIVFekTc-j2_V|By zfD*`{004WjeYWylbmCvPAa**Ieni|1aU~7FgbzRHnM|pwv)Xz5R%Ahx|BmsfPjB5V z3$?4>lCFpqJ`#~Z(+tJ=d;MVx@T+wCbK4+|$V~9UVGt=vN1w}9L}$A=S2ocXOv;Bq zFuE=@=iCpsxk zVcwk-37r5YHn&hiHR;rU*|WQ5#HZ|vnoQ`vr`bXt3Ho^J0)Y7Q2Uy|UO!Eryb`C}W zZ;v`J`eryUjQhSxyxfT`ZMXmBXqQ%ns33$;+MfuyMaVXI(PUm~_>!P*lQk-pn|&~m z&5IEz0{XAPIo&~6$aYw}I=~1eH~C0~?ujt8A>qom4e(Dlqe3ZNkz5EekE%^|F+}fK zLd&)U!}VXF<-bq~J&Z?mUwng9US0ur0%mF~qfj#G(LZr6`m6NpTk+_-8;NY(pxrD% zO16fHBLb{km&!!RbV~9^8}0RZIfg)s?)G}9iZ&DnRKaB>zT|3iiPap} zQ+#xP&H@lmb_Oc8!a!>d(G)9r0foGr(fh28x!OnWhe9$JIV1XV0v_)q48rp2)5zAv zi^IW0ZhN(8y}Mn77&v2n+^vkSpL}BBNHW;o|B$hQAhZZpF%n_&wYN|Gu||{oy-P`I zE)L*sNqub8q1Z>M(!;iVK$fuCLp7Cis)GQ@<*V=Pp`VpnAZ^`C``YL+p|htEdMnXk z#6#wc`E;(~q)lxQc}W???-K0)TOE_H@hX$i2t>DtDgMjpNzf~M`&W6nM8gN!33ynw z^-6Pf$ORISL@PCe@n0u+st$aRpO}F4RBD9q`Miz&?M2v%8A~13#pXmTMwnZ8)4^c^ zy5uDuFS~byc*E7lJ+Jrs50(0Nc(Z(UM5JJoKq);VuQ2zo&&eD!qc3~MSyv)VleIn$L3T~(dSC#9wBrO5F|TCnwLWe=cD1N-<`H*68S(*uV~pq zY&}=pd@4P(ob#@_K?`Hi@N&q6mamBXxK*Rr#usx8Yj12#GK2ybQLPiU>`D5yOg7;0 zMhri4bFK4vQ-1~At(KkR?V~BDZ_A$iSs@O_dT*MU@aq*`@CsV75-i+t#IUwlh8>EI zD%8vUY9sOlCG7EGKD}u|OhU7ucAV2PVE`L^XGKaCHigR~e^t1k`8@LWhG`YV!_2{VZp>C&AAq^qE{k|1E4bLmtJ8T-vh zis9om(rBN4UkoKJoxyZvxd!Q&Si071Iwr*2L*q8DMX*AE3>^)J@VT2XFLciGDc#(! z{NvRK2#FCz{+1IpRbN4XW|dLg7?C`CM`&yA;6e>Sl0a85!wwGxCC1H_JBxq*@xiO( zL9Xl!=(75Aa7Uh#OVBA`B+K_Mhs}VCSE0L-oEh|-l_oY5+T!4fOJL*MlAfizz+RIM z1)2KPEr+T1VCu7-cxqU9sy)&5KtIyhXM#<-*&pKd6ig^=x$aWR67TMR?tGOwU(Yk* z{@J%#1uBsmHb%c9Y5hij{YF35?MKvqBbo{WsHLfx7rD`u?{J9K%=$UvzjSUzKh*1Z zvc=-8Z$xMM@V$1EpYlaTO>PBSyesaFQ&3RYufsv1R1;J?vb6-tLqjp*xQlK zkD+w`s_ps*(F@xVs~_oVPDewBPi*jPk6sxKbAN3}4wKVBG2(RA@Gx95z{Q)G9$BYm zWr9URRNSHfL0LGjq|%e+oU(GUm@na$+=$;D>f`{rz;vjjIiLeD+>LE9Ly_dF(97S0 zJR??EoCR_@v>i*Cp@=z55jBFF?-IFJrQ2KKkkqTleBWRF(C^&9wr6VM)p|$N5#lV? z8ETZ6;i&mK9uNv*<#^;(vFSMj|B_3I83kJ%M(tkD{@(2yrhet)-NtTJ9^)}BRCnsJ z=%Zl{G{1)&0Z-Nomz)!1^kB(*%b7K2vZ7i+l)&wAoE1yvP;6{o8D68WCu9 z3Qq#cI|}PfFM&M^e#KZIjk%}-3x=ZzWXeYRPO2~L%Iavs{oFR|=sBtyk^Jq9f_m@)yOB_I86v#>WUbvK;`5aytO7`PqonX1$e$ zC#0~fMk2{)o?c~xEg6v)Aq_zH#&71LcX@)xEl>f`6!#W`FUL{5FrsFdx&!^+sYCgg zv1XPx27PBLa1^3HnCiC=lK1}>n7K%}Ba-pmUyJQ2S@qzKZfxi-NtLI=6Gp%O25@K9 zDbofcga9RAjL0P6ASbEtk(2_(x=&ek0C0sldM-BE!b>Lph^?Zun~VAOAoTRYLPb?) zwl!7f4B+@&{V3qEy?AcrEc*F;Q1p-5dCjz^lhysfJDld^B<OmvO;e%5zI?})v(`Q=?HVU&*dC-+5BIq*Zx(B`0&7Vs z`?ILld!q40dS~;Jk^javi(+UNj6#kZ9+Z#$b!UxrDFu13J=6A_-W-v7t7#4+!48%= z3Pd9oG@BKcsVRs;Sc&^Db~~mY2*jZ6Xoa?fc`T$$X5Y-w!sTPAFDXj0T)l*3qvlXJ zTr21?&Ia?#c>@iMsl!TMqcb-rM%o6%1#AvxavDPq;0CVi-2@c27dmYEtd0X18Kn>| z==WSOX&n++oBHh4;0JX6qY2Zu0gj!b$i-89lo52$>_BB!%uOWZvb2yDqf^X%D}S#a zboBUE+D5>}^@+dCc;wQ=2R%uk9jR>6^L$GGdQ?<0389yxOa((V9SEZb1$cGp!}?|C zr+nI4P!;O3s-_=veu?<)KpAshWuya|L6+KSndW-NGYVQ~^!=~^H0SmWW~3U7phHV$yg4FL2U{(w9>&W@_UE)Z64!N-@;UJsOgW4)Hgvkkj;w#6sHb}IJ-Iv0b5%w< zn1I(R$1sw>z113~htsmU#EtPt4bDPv+BTdbo{=VTvwf2g>$vW3gCO)49g-cV2!{Nv z0hKr*a=hTDU6ayq`p+!p3-K~ZkqLl17Kg`(zlOQrNZ1MTJ0}xhk}5RxHh`8Y$=6%=;j6eQwt*n} z7e0UG!MEYmK-6GLmL7$KsE?ixYSq^oZ-jwiQHs3Ypy0JgT6h?=KsX+fV>v{Q(3fGb z2P};#Ngq?r{#T(eUjID; z#%|-!PwxHd5GD5+iAHR8+(9?s{-Kd70q{Mk1hC5(7OkSm`r3^5+5>P0PU3&-YAFpG z_bVCI2kha1g=%040h*dZyS3jf{h&(&G3bbxi0X(Q*Nf0;TL1nz9saVGD=u!{lj8WA z3!8M zCim??D=WDEui3Y?>NEEER3w#0k`-jIIG|xqe@snv2f-mC+;sI$ZW8RtP`Q*ru&^Do zA(Cs=m>~IY8X@^8OjCY|=`1ZE zh|YXjwr8g9<=-KLv=wdZ4j3@9m0&+f2$Y&-y4?fFQ+zvCfI|XsFMzi zlZ!og`@mmTS>wS>tpr)-S3vtn(efc@iz#)uX9XLNwARF{W^;3%#|v1iZ{oq8TP_+1 z2)czCN(0jG6v$%>;Pr%f$br?~(DS4$fTyXN`u5&<)W_5uGErn?cB`A>GHB;Q38N8SjIC;1Br>=e55E$Z0id=}! zJ$FbcEBbAn%Gl;iv_SquZ~z(DhFjNh4sXjqG-*5VJM+OMBqUCbPRUX~^pSQF=L|36 z$%X;!13B*nKh?u z0|KRfsxZbACog`Kc)HM<~}GufG9Pg2JL!ns*Ttp2rPo{4zG{i zK@CDJ4w~uzzakeXKjn52>HLWYo}l_i^|yNLL*w5Dp3mLYgcXU#Iq;D(00p%G00Cw} zpZR2zRPaOp001tT7QIQDNkMzUQ^#Tpr5X7UjN*BGB!C6azMuhR$@H+#2Z_cxGD#IT^ST(IX!Ioj5}P#{`pc!;OFU^uupd_(sz#cscL45a(51k7Mgyyo{qE|W z&O^ZmCWPBoFAyBE%T!dI+h(b7DRzY22)}F?LvC_LjT!a~Irnf01TI?Ro3__2b@x8Cfe}XtN;bdN%LFm-mc3Jj5xhcf$XU!mVZ+a zYLTT=^h*S}yZ_G`tqQ~egGLsm?cTHk99FB6`0z*q_^W!;)jk0kg}LlIfW@qPMG(hc z`nok0{Z2yORI9-dHd)3<8Dh@w3@o@91W}TZq}_}vi*$cyYa4*0=QDYzkvUPn@--|h zo;{ZFvhB&KLFr-)Na!i2OMfKp(>S#+5Ys@bG15Zvhk?t11EkGQ9ZlJzOYCcYi;s}o zbwTscHY(wOv{wNdM~V##Rn8vw;etM!;C?_sMF0phM~z8?gV{8Ib9rRYktz zq9iAw$vj|4c-2r+7xJ<~TEV%|3k!KAGGLy0oq{iTWJE@_r%#qr@R~I<;451jc-gvw z=aA@agMRUNsga?-X7|lW3-}ZwoLIY`@EbyKNu`l4SnvR4K>z>%p#h#6ZbSb706r|B zhEX$SQCxll!+ExYDLvBH$iPe06guan?OJXdIPcCRJ_b(bj>Tu$OTjhP(c&kdcWPF-&ZAr4mn z00RI4dpvXNtD>?x$HM8nCukMcBJLNDW;O5UfPF&3lS!a)iQx!AkvBG_YEF8(F!0@V zb?S)XN^Vy&V=(9l*x^%at&ECK_&asGvLy-Vul40z{M5mU-#6mRR6rC6LOD>W{W*XY zw%iupm+00B`TOkY0UpG%TBNF?^g9R4vF6}C7{uLrqwmL!%`;(`0`vzt)~NdQU2)X;Bs=B&#Sq;HV0g^MOSJ}hW%az-Qz z)B~reMt6lH$rqW(o^|$zgDDU#0P{MCjOY}Q1E4JmvseRm^9By0zt1qhVhDLRVZ4qX zrox@E)DH2W-wddcQV;IQh1CfPzX?y17}-z`aB7tT_br0ole33kxn$+qwQZ4;UTTG_ z{r^FG+ZT6@SKQ#*Lw})2(Bl~Lcb=#jKFeglvH^|UtA-3ziUO`)QM>77DLH5t!71w) zE~2?NJhPy!78>`MTC`MOAm;rl9nTW2LV7!Li>29Wl9zG7+CK+jvn>jWttnci%r5@7 zV{(xO4r+OpQLQSbyXX&5td>Q>D>w=;aN z^u0!(#kiiVMgX1h=-fY7F>Q;D3ya|RtvQ+UkrZ5`rxB+-zysnXZp>&@+=2e5z%CxF z5MegnBW1vv+@i3QLXr6j%5K+Ie?vPzw?s<@DLw1$MfCp5RT-~@()o<@Cy5JeAa0j_ z)dmJzZJ1Q{De~bfJ&3~Mme^^4!0avk16dr|n^;)i0KYebi0kX-2}=f{w?DlTm%#~SGqN=`u`D3xt57zhD@d^E`_NM9iqy{ z+OCWm<-2q-s{?@h&c2&MYp|=q0V68T97vo=!sIqh;fvwn2XaPZ^ zxMNV~rxNH)uFcg(j5JPJuC^t|fG+z+j4&fblzW@PY(TaTe=l0Gk;v}^KodHuXeJ`L zkCJ2Kd=%Q3>kp==v1Q>n+p^>YP_}X~LAzmPtFYLY{J5|75f;bsjX^v>1b>(Y9Phz> zk;LIpP|b1&PG4)(GFiNwP1O%A;8#@~ZLrZYMrNOI7Cur>{PBnt3jNx*kKFOUoa-2J z0>sm#qVCQK?_7f{(t7;>s!?S!( z_g*f2g{teif=4r@iLu3q?~T_orr6Z(2a`iXa%h!UExMiLt|T2eu1KQ+P=9T8Y{L5g zwCNboId285J|d^E1RaS~a2=gu^hc!SI@mCi@9GUaGyfnt(w;7M14k!(v8Z|qy7Yc&;yZyr&- zQMP?iELzH2+(ai%WwAWi!+J05IWwjH=SCB?#vFu;`&hRdrf$z0J{BM=iu4u?fFoG@ zpMPW#Gmp}1Za5uCn&*5?LS&5f+BCNC_yKj#E95JaHe)*y!9WHeqrs0C{rji!zJ8{s zrUi0+owKG(7IUIMSYyFnE!aJDOr`v!gDTlE0INM|8mVJh*_DToiO;B2uPuk+ z1NkK%8~Y6;{(jIbB49}f#-X~Cd*C~lqi4lg5^1?|ndiuC@ zXaIe547tD*6#Bf(G zh;667|J@fV&(`C=?BOUg&yeX;gUv0!z8mL57Yrk2ylUhbS*UGrQ483~JC%{cars5B z(g8050=Sy|uqFQ25$WEww*S_7b}I3HJ&GVjX=OzSkCKBVJq{K9uyTOm!}4W0yYaGS zJS;(XsU%nNl})xvTI{1EC2DqMh1wx6LC4<+y2bz$Co^R0dmgoX9s5M(dIPS8t`)Uc zSR-oIXA=nctyxbn`tg!eOGF#Z`fJDfx_IXil*fLTu?*~tJAKP*k9RG84t zhxD-}k$d>eFWd!Ztgtz=V7(;{i6O`fcR%Ya+Q(o;=K*=n{eT(x?bXIMv zt)o2tf{M2zJ#W32B+E+ulB`0y7jN1fN&yB{X>t>X7B3J4jyAuH4(55u3Pm0Ji*z#9 zySTDrR|But%Yz3pMu$o~_nfKRy8N5B5sj9!3({WCh(o%+B2O_i+cRY}{(FeQc5EgI$8=tdGI*Liur=n^HsRARcB?oS`=+Q9+HNZtADHTMu zkrD^v9$M>`tG^(*@RA<%JBt{?ZZLcEZLr`8n;{6#;}5G3>cfpGDIg%L?Ed-ib64C& zR!MU!gnHTyOJ{BW{8QFN>tF!ga8fZ%&S%9Gg!UFy4eIS+g#6voBD!! zyA*h!iJu^P5W&rS`;X{Q=KxA3e}~Qrf6@4h50{RYVJQR_O}K}`)0J#}*t9!*{_s=) z8bs88z3$5+rV&g$G}_*|U>XbIRMaY^vMVxn`sW0;YTv@ht&wPdux|rdtAV{Q>|X;g zH%>;wHuY(1N)pUtBPl@9k&gBmGs5Zcr&M>f+ZN2DSYO@%+%%o}S4lm`(Q96G){_T2 zkth2)UG|rsXEA679cOTt@Vvb_MA^%LwjvJ@jHP*ugfc2qoCfq*NH0!tjllh8g#x&D zv20_PNwc*}L82M9;{d2T&KMEXsY9NUV$`zfdY{?tA?aK6sr#(eRADufl)-i)#jj!W z`0Ru3!Scx3Q{rJ#RvIrlCe=f?p>P<9 z?A?&)GsM~XAGRNyJ|dZZxCES%bEW(~vdSCagPh}1kv80Li+VxW*Rp0&)WuB?de1zV zQ76*82aiDgS0;DtSz#f(Ik#${Xk$lz|JWZbVH=TAqr6--BJoI}P+9KT$MOgvP`{~9 zg=L{7?NGi57l{W*)Ogci=^x@Tg8k8*7y8lA9*LuF$f*O#MgyCC_n1yp2+ujh-s7q+ zb5QSvP@Hnh*CAJ7)-w7R{M5ta%*ebwHc?dr%5i%|4;q!WKrCen-J6 z=?Np|U@bD6 z!E4Yn2?SyL63B6K8Nmd^XQ~P6m83;?a^Xxn zjEsXXP|v${>apf@6Kn3wv|T(LD-O%7P1usxFRYO1Wc9E$iNdbTxOhiuiTg+Vg07sE zmd{1gZ+6fe%q_t6ETC!PM4ay3>lSZnAAT2$#Jc*s8miHR<~c@ABCEZgMjF113~5d> zMz(o^Uspt(ppX@4CNl7tM_B(H_$$Td-@iJ<`puy%8|g@P+?A7Jm?6A`_#A2g;(FIO-&tty z%);!{S+7KI$5DA=1{dD;bO)|!EhGaNm0$Ln)vv`{(#;G^A}G{52)>|CtHv5%uKT!K zL4=-KuS{e$&!;B$*1z@N;sbeVh;@>t{o@OG_;6ufXq=u=0w@9xOWR^%xz>wnV;6bD z#M+C+OYqdryC+^*m6if|SnusF*g28bFyb=7fB91-Og(@cRY+|fh0`lEjf$?glkq}T zrfUNTUPqEg-@X0@18MD`q9r2x5xl55LtdM$w)x~*FNl|)ilEub(83-g62I`NtLrDh zaK^_>_H)rd{e?1QSZ2ZD6iWH0q4d@~&hc)h8^ZY4EY{vZAy%(94<8p?0i2Wo9RSNF z58l_CWqHF2uS>qQ!ylS-PLH?7F#`zFr<=MrBp?qaV2QRWZI~MZ99= z5bW|P>MbBeP)dhiOes%pO7y>uV9QvRc38|AdPv zX=A1j*TFP)w1BPv+wu1&s~y4Hi(5|W^za7MJl;X2z%FXBrSt!A^Q_+dB4zu#`*%Tn zm-1$~7&>~rDd>Ok`O6Q>P!`ouBSSbcW0Nf)HOt`*B|+_gRcyTJk{>ur@_6*D)&8#;LxgK?!C8j zla>=3V99tNE~9^=3M5sPnq4b@^9bR-A*{g~CegBm|F&)0 z)3$AM+O~Vzn6_=(wr$(CZR5^45%=xhh?CC(3rYcf^Q3!5P2 zymCQLSR6BHPiX+*w}G=ZF(FNP|Mx#*p*HkG;J<^Q%`?L`p`+&St9&G2?wjMPd|f5i zO170+-xPyRRt-{^Tg1_BuENKCrueA=?`twWd^159vLh#;rlJxu?Ty^Uy532M+UMDp zaOJ&9+V`yr=b}u+vR}c?LDz%GW9U+^GL|Ua%`f&^85vd?@tjU-WRPGp8Xw+CHIu$$ zNGO%+D6@!?W^D={#{o*Cv-mk8`}wHFE}ol|s$~aHBj%=p^zRr9pymu%t-i?mU1^0{sMJ9M4x@>xn6b^G#TNyi7@R9T#;9f z8jS+<_O!;fQC3611WWaN5D}*YCUtSI_{F3V zjoK9hx}KLvObh43FaOJDD`sqdo?O9ewWM8P2ycp5qDWsCfJhvwSUd+>1 z-wAX}H6e^=m=ibQ1klp26s;DPaC>)z*IIuZz)|3&Gf+PudEZdB|NMAZ0UAq4W!>qkDH$H8X`foWEx%vgX%nX^=q2X|)4-kjfe+zt$idubqv(rg=V zno;7uCmJ`^f@MX@?)h%It_lf&vb#n(tXqTk?5jX0UoWM^aWk(~QllS1NE8pL(qJ-4u8BD)UF;NQnStb{$mT3|qvA_M$D+BMiJsG2`~w8ph0+#NV) zQ@seUU`!d;c#~f4m;0~^#N!}$#7SEETgvl_Is_;w&|g$bFxyGaW% zFWO0SM?#dD^j=$hhpZq`P_;}xQ<+PdwiYb~?gini*gZPPADs&yBA`$!esPA{;p8VIRQC&YU zxcO0-C9MrBayN0m7f+(ur4K35a&~zv`a+=1$=~P&fSPbw5Ih8R0YL2kB-I6=Y~BA; z9s`}NJ#Gbnr=~YqpLhHLFn1mY_a&gu#01wB;^mySj<5o-69m?ZIxo!wR)EP2kORJc zUt*YN(W3=0ETAuNej4wq%YHwpIe}YV4XQt0nNCbr`$1@M`r{`dG;>`C&^@_5jh}sc z;bKDzk_g4?h8?elY z|6GhBLE{N{uJl{d(KP%ExR(lS1Py7Xlv==jI)hM*9X3)k~mC;Qv)gX7hPb7{K2 z-yx|vz+{efO%hWI8fn#}p`pB(_$Tw)iucq ztijB-a&W9%5R*FUjJX3qCt7>b=}+jg`I>v@pR~dC#iI=OrMcjFtwIQIby9O2{nMsj*K{e-NT5AmIw8_@o|W@vcoA)In}^scrJ8 zwfckl7+idTyTei@A5=A%07g>9B`5YE|4`nbg@WnA{{+>te}XFV&v!}Sf41%aT+Fsr zgr`3femEw7=v@<>3*eakZgsKNBnd=n?~hpta(%eA6kGk*xdeS9Ri22uw>nYptfSFi>Y52+v*nwmV& zdI(|`h4{7CQ&EZ0r!uGrSSWB;HVm(3?d2U?+!aJyy+5u~bxrEN&5=*vL78&@Y~DC1 z6>mqXYv^8hVOX~J2x60aPUYEV?)ekgHs(0-Vy6teZ&7Htsk8a_Kt#qg-mNMTk}lPM z!s|j9QyZS>umE#TVyHa!b)v6$-QDE#Dk#lnSR|C4I|qK6f0Ow5^32SG;)maI7)Nd4 z!}pusq&`iAJ6Iy%;0q>!LC-4tgIdcFFFrw9;2oreoy$7WwVm}z&Bg6%HgWp)q`MDpsRE03%^fdK9d&(9|% zDIl-GRr;FuNsl3XbeQjBVV#Q3+;tFTTBm8MKFf<|T888HNGouKnH~pvqY8}^sk`Qc zgLl>@ds|Pvo*}0#80i;kfe#;r$?4zPk3_BnaP{>=yQ#>{C1WK1(rEUlNB0EGw%ULLiZ~L^(qFcJH zhoy<>9e&lHe9n?_)3eC9c2~XL0+6Q(1jKn==WPuMByMSfaeb!hu`T>oGFJ!ZKj>vjV_~xb2=Sd{_32B?_UMp3b(! z>?7U1*bdRzQO3282c9;_On3}uQhSf=Q{&{_t{M&_y>12%Y5RKTkw0Mt#MDLEO~@!d zHEv?&XP;X^Nn~)`VNeM-(=_;Qxcw++iEoHM5NKc0$KhO?uXlv@9*HiavoTDOQ>BjVvVR&V+Qpoxp zwXN}NfhyF_@Wp;cN%Hn2zGbb$LnOw8)~^jpc?33#u+IqyF&s0jVO@pXy~0~f5IrO3 zQG!@)z;7Qk04RU2m}tc2U*4HSJQa8|R3D*mn~Wx82Gf5LK)MC$+P#?iF4P+4Qqr<- zA+K%TT0`&|-l%f8=X*BRV@IVl+ zK5)8{+;R%gQy59uVi}wa{}3eH&zui>B!K+RO(jOe#`pt4Xh^rL4c@@K1t2fJ?;9ui z7QwMpQXEbkeiwNJK>INO&iwwJ9h(u%HZV>U(f_38hDcxbVQ)efMg;S* z(5Gr=J?74wyG?+w7p3$m^H^@JScji~Ze6nR7Vq4eky(PdEpWl~9=*5f3ELahcmI$r zZJa0`O8mmh(i40QLfk?n`gRPx0V4KOzV*H>=QOZ1y^jsZCUgC$<@?cm&Np;+%4a-C z%#Z=HA-l@2f%ovb0jD9`i zwbIL?K4(bSGV{l|oc#{X`n2(_tjm$8I3Ne`+K?5$v9*U;CKQwkt7lt(=tBg-A|l8X z2eBbx*Fx+m98e1Aqmz)_%7uX_ov*5yk_uqq>_rR_CaOChW@BMmbs3R>^I5hoe3}~h zEj_#SDNnXuMv-Uu9EZ=P+z_bx8ZINPJj}I;B&8IjU)s&ue-1>RFeZnMssI3z{WqkS zK-s?kSHb~65gLrO2ZCFnYheUnFU|4*qO6cQgr#AM-Z+MdP-e&%q4(}DE^K|(!W#E; zOs0T~G!Tn*D|m{0qJH{gFw+aBgS$`n4a3f%9yC>Vc8Pmti7=kY`b>uQw=RtD2SLa zY(xC3@kG&hc~pU=JFL#kQ*Yr z!F}{cVNoU}IzV1@Ea|~_S(;66^xKx?T2pU&aSSkrzhkKxTg*ieGykYD zy07DyI@Vhwc__wRwh!3J-}Ci@#T8DMH;=qV{`aq6uJXT^AB#0fFM1OWLzrTDYgWX? zI*tw~s|4Hq=>$=ydx6x|6J4$vnDpTzaX|(;%^ZGLU9~}mw}>$(i?d$#S^zKHg3P4~ zfwVkSF+isM8*gx+Z2$i+f(B5w9?=Fp-JvrPMAQP+d31~p(Eq7na=M<%rUbu_% zf6+K4bGH{H@8-|Czw*7L5eFw-RCEB~4N9~)X(kQ6HI@Da5U>~EhE<4?nB4Q?nl}0`68t8%k@jFpO_j;af!S!zr*M^M7q%$#n0vO+6_8`#hUAWaS}}XI}sSKp?RAs1W_Z zUNiLXEkFQzL~~3ld;nnQu5Bez07yXn{8CX+HwK0!oG1qr&vlFwBH&H^OxtKzm*#K&&dM({|%9AHHPD*U68|J&n5x#hDc~vM*d4=cH zq5I+CE&<8OANi3xKdVgvx(}w%MU7+CCjJ-U0m{ zuvx|a5}8UcvIoF_xuiV_8Z`C)V%%YH#s&R#_SpLYqX2J-a5a1CepyLZW=uru5$NpG zQc9p7DM(GVZvI|BGIDjN-ZrWdwj~ae=7p$iH$`TQ^xVL1I;U7B%WAuD`<;elNmn4& z$Zl^He}K?rPFDN!kpD6`JxK6qlke{!=u8qC3FdDbhPT{0H-On4#h4YWTSGV!7Vf?w zz~qD~)xU{7q_VcUfpJ8acdGMpQt_td=cXvDg=A#>=sGWHzduS)UDIPddxZJl}NlpMk{YmjXKugTHW72Qa&!&62pNNa5YB1-I7=C-F(nNrcz4$UoA? z);>)_Qspn*^SA}{t=`oR1)2hy*hJ3V#crg42X)!-t=e{Hc>AScCfP;`p#50X(7U;P!kiR2qBayUqntzns>ZIq&7-rx%#Qf!0;#I#@ zWJ_nxlxq`5(A|7rBLB0J7xY}G{C z%u%(5pkf$rPv5l144$~-M{^y&a zb&Lw9>_qX9vtybX8hLl36He_cxfMF$4dbdq<#NLGu+aBSa>5kiFV>Q=Yo?-6qgNI1 zB8#q)L}AqKQiW`owrXAl+UyBfwYb&mt*y11$pxMiEcq?8wrMmM=vnbQhjaT(y!7L}AG8UHdBhF%FJVXi_uzxY>YAfmQyJt*uUIZM3y z>?-aQFhz^W3`t_oXo_rHDm|OmJ}*Q4f(wke4Y1{+{%RH}ciL%1xMy*dg>! zw<+*${M1Jv!I%?M6>py5<&itYn=<~%KsFFlG)_=CJydq~bxB+cy{Y3F&wI@SZgwPd z90yT+>4g4S%qX)IU)y-h}_El z_|-!_N%y5T!Lp+zyrKWfVC)!J21VR7omMMxmpuqu!8|r9b1CwWN95#5bD&x67rFdv zP`fl$r5s|q$`jYiwQ|w~{O75@J2{5VfH*gcnQJC3@AR@wen!}-jcu0wO4PDOezGv@ zS_DQ0Gnb3-Y2QH6n$nvoGp^wE(-zt+t@G4+^uqA(&<$lzDBcKfygy{F?BLv{O~ zX$qNj4Xe-h`Oab^bX12lK4PF-3PJ_S)p|ulFF}Mgw3iAG{6P|wP+X8`y&h<;mwsI- zvf6hE7|6?;k1DrzGZH>(Db4WV0G;&x+>@o)w>BoUQi;qS8Pr}txv@-)QEDV#SNe>- z{tObV8ipnj7~j)kXC9H?8AJ#%x1rcq13Aq&#kjoLjn(Ek7urdlfHfr9L9F&+`suzV zmXAV)mV-s1LU@YUld$nAX-F7E2qP4x8YRVZ{V_d#!|4f8>(kR@xt=a_?BN%Bqt@w91=FgQF)P`q0lpwiJGT%*v#zLecD9?wZN{ve zMVA0_ghBhN`N<4AV3V_R7QWaSMFIY<54XQNU@i?{G3H(}Ziq{_YdmrGOrM5yNgRvU zVT%lot5#$Yt^eWJB6#R6D-S}D?3XCPp1}%fNvVy|Ua9@VFxW0sBay;h)L!W~(~MWL zd@4Y$*o*0N;Yhlo&`2V%s-IPa_M^n~>w##Hy6qYmPny;JHebf4C)@>Dn)Oa=HDhb# zoJ)912=Y3^nBy2%*jq2l;E&mty>CAgpq%8bZ0EElojwad`9e&W{U@7=!M`(B)(^d96>NL*>T1~um=q_*kp1pBQ47G#Yxq!bPce21sf z=L=?)0$PX!^!B&I0u``SzLO4(=ME;SVTSLg?D*^*kMS25kLfaPO>_-+c2&{zgP~kQ zATW^(I`Qqpr~poV=lK(FZD{`x6nU~k`x2O!cDWDBhi{g$BD#}*GfGN(Cjp<-0Znr< zW1vrVB$$>cnVLf}yh;yq=Jf$o71^+z-?l^uWFyc8Dj*&+2S%~W zWEBBd0(v4Cz>WAQO-5G@<~w#!SxFgvB?MEy_WYal&LLxa3eG!f~Q2uwhSa?BN}E%>v;mJ>nE zaso!YNjouEF^J+ga^OqK1>N~pRQ_R02CSbIL?DO(g7bm)-FXNhDW7O{%_^mF8doy7 z4L3{`kLt7$ASzarRigt`kul94oow}wv}iR8y#;5)NMiD-@5+|jDUf*61Lzq+#sefW z*+0*@$IZzOI>8ztcFgr;$_v@6S|nn6zFv`7x|8#u)4LoSHDAW8L1oqm8jZ-KJ{|P> zv~#mpzmAVNULJ!?RDO@^i}5ZZ5JWKS0XZGY*NK3vunCdaH7jH=uBq$%E#pIaF7GO| zcm@)mKrf;7Gzy9=d?~*%*GLVZQ2~=~gKl1^M#TLv7*}zWKQC#ePPg@lY^yTumy_I! z7i-ML5*Q>OfrGS#nQf2ED@m`Z^BTJeaxp$k#}_DP`8*!Wa5aIAEWT_r8%wp%1%TS~GmWXFBC9>dr$_VV!yWFsY786enkdoq5em@4JiA{IGWlS{Hmk zF7jIiT7juMZqj7ar64FTm`&i$GRKc%^0=Zfq+o6m;JIR7y|Td78Bd-sxsa)f8Rdk& zIK__ryyNtH$ACKlHbSkX>qaIzk+h}Lncg$2p3)o#Up2fTZCm>qvhz^9TAw zY=Re!C(ejcsjSe8W5TcgAy*OXNTRM`?&m}4kZhYKXe%$S%TVq140Tn;KJVTNF|04c zNk)G@f#O?;%CG#Ob?IL>CevY&oO?8xeof26mz%qh1V!I2>Ne6!=JQ!jVr=&vF86e$ z-q*6%n>^#>((8MIWotvz=t(4MeqU$Bd&DGmA ziw}=(`>Gz_(+X`1$Dg_hkmawLL8jqzz9mleWnXM0q8Zn8tOLn%G%T!4eKu`R@GDuA5r z_HZR2b`t)Eo;b9;g^iWVyMqbK`Lq7vtyI3m<2oHN0Yxcwn|u-8vhc|6thM6yclk%S z#O9=>YQR;mxezOhF89^S?PQd&M+HG@ymW%OCIJI7)4Pv-g4B%5B$#>XEYDJ>>!8kO zm2ORi`cota3l;V9n}-_C^%Jr~e(16^qj${ST7Wc`;S_v_RCJ{O&iVacE2U`3AoOTE z`>tZMM;hvt4YYte^93aa&RH7pI({gANT zzp%#wTvt`2eugn}xBK|)>gi*$=m7-tTkV&li0>SywD%Q8#ttl8ATayoumb4rK%jn zj;6QoaH73t3+8U~t|8agy7{0HyYPhrNVF8-c%4;pw2!>5krMOPLXm-QFsgi&G<~FA=r(?x2#am3Lwl?MeWG0%&ZntraIbP8fW- z;yMj)E~FC4z}>G$ViGzzOzxj^BmEmjAe7B;9#rrQfxi5*yGnuXmaC|&x1lEViUb|~ z+FmJV(bf#5e+qXL#7Y#h8*A`rlh!eAKS8iPURf6%ebu~wO^C}4VrO0~3dY6v2k{;Z zszmF^~d$HM`S%lGvnz2W~@WG$Ksh;gxg?Vs)cl%7IXdT$j-wJ2n;U@(Th^6*PdQJ*h6l8n{sy$+D3B1Z3la)G zQY1zBBXW;_hh0IjGOX4W;m%!wwt=aLoOug%u9Q?B)-X;!c0hXYx&lhY)(T7{54z zw1nfHaE2Aac%ByZH`t4)T>c65#JJw0k(5uo%BSTAnq!h45}l)t$SPwP@uoClac+ReqQUY;|SUul3l}HvA zK2&auqr<{9rKMxDYFnwULK?U%wf#s;gKg?pkr3G;@Xat9_XBIyoF_FCHRu!0p=-EJ zprc|lL#Ui->u;zm734Aoi+1a`GR=;A-SvUaEdA+cmtb<`+3w5JZ^o)A4zN(FMCQvB z(m;Li5MY6UV`_VVv-rq$a&#(llqmXUl4_{OnleOT@wP*G z;UUnAf;(hp7jepW1tM;A6&Onf7Zqcs(jb1hhC7&^?1%i3x2VB^+Qsb<(m!ilP}vcZ zv9z>J_EqzR5n2g1oL_%jRqwlD?|kA}9#0VIz10)lqx)c*qiAfgd2f4}buU#gxdwTy zB`WBq;5=QUFH)j`moJ!HN4umIl`V9!Qa|buI5nvHzVwb!&Gb6fcE=%u1uj0$W^9I8 z`%_G|CbWt^=j_@aIs{5}IVptOwfbEhlXRjw#d;xJz}Wk?vqbU87W~cpq@o;9k+py- zoIh!RcD$eaSt#8g`CE-M)}f6K56QEZQ)rGE#f9I;NR~!H9TeW#?S(d&xlMurxz^=D zP;^F)Y}Zu|<+v{%cBOmcm(3x-)(;eZtrW?Ss~7|Q4z4eK66U)IxUIW-I{+fAQ9-7V z+H)a}!`t?zbXBx@y{6ZV7W-}5&`eU{%#)%RbNt|1UIBk+7FL4eG_1I88fY0!+<`K<7onrWWuH3 zG<6d>?}wQha4!4D=|&ehyv^lm(O?nJ)so{E{HE}!RsH{9Cc7|5y`l5kw?WgXb=uFMhY=2FIu zm39xqc^MeM>KzbANN$nKUBBcWv~9IOO-;81MEX}k^ge%(!pkJ2l@2MDhWD|g-A-j8@mOKxX`oS)9_=yqG;t~cy1}d}fKDN`+4+g-gLh_(ARKEJ`_Gf?`o~s|g zwz-ow7YHs-vc5019E8^KPf(LO!R{Z#kRR0!`_@M7s6 znB;BOV>k>J-2HN?b^f4~NqDyVET%g^wamKkq=XsQMnwAZ*+_MjQSiFMkPf&ul|FZ@ zBs(w*iC4?cQ6BHKxO^n{V+N`weAQ9a@y~L=0Nv|M)f)f2CspVPk z5_cDZ9c8D#u*h+zRW^Vm7jnRVMUr~Q|C~{B7<+HqKKq7vbULh6DvQ%#qshJc3-+rY zXm%YY!t|aVVw?I0anBnt>(Y-_w(b7(vL|kkNt?-1giZ5nPeNy3-lX(Th)1n)g{)m= zY6UVz9_2xOTKZ@>>Hz~o<(_PzP%Hw)#4*~%XAFTkC>*qyfqNnu5v%tHP>A; zw!HCMt)D-VNl4`In{!;i=={yC(riq(^5c^~go8In=I`{)@L&)xSI1HK?Y{tHE(EVD z_Ra1YF&W|3FSldg7^ZkHUP@W@(;yb^0(=vFhu*_U1qu36dI~zR1x3k!D?Eqg*aYitUV6 z@S&*@I$8`lN=-C`Z&YpC%*A3)oukH%j!A|_&b*hZKLsGq#H(gqw&VO$Y+8i4PJ zQ=zp474I_fUqgU=4*7p#kOMgBH_X%+qYYjfK95X%k2OXIYelxz8UALEGkn}js%5pu zI-o$Ig33^5F7EwQkuZB!&c7KhcVIuU8C?@M!?2{5vHMN$)aIzF!&*7YhJadtG`R=_ zsVKOshU=yDTtb?VbZ$bHgpkhzJ zBAoRDQjQ77`ED}lY~*w4DxlS3V*9y%Qykf*;Q{q1fE&edI&L!#;EbM3$@dBuX0aN+ z)4HfJMFFj;x`4iyacphvBP%=P&(5@KdE@mS!0&9kC14t|mgm+=81;L-y_&z?1Qh`o<1E^#7%QO54? znnzhuLAaz9Ab*cbhPf8l_Z1%IuOy!`XNJ-#P_h$RA5%xzIRX!cbg5Wo?!E%XHed|F zpW^-8cMQ$Yhs2;q#Cn3@SF8BEDbtyslwdYJ*36f&6Dy#;>{cyvCQyvWc+x%v!e+X1 za6^7As5s{9k4!(Au8VUIDBjxtne5&}Zus_3Afzo>d?8A$DPO>x)IkC`DMf%$O&`JA z(r(p|C?_>LuDwtn&Ysm`-&ize110%ub5MGDxUYRKTO5OdcMORWATYab&l*5yuvRu% zp{=F@Y&uefLs^etPUpb7sGz(ri7utH*CEBh(#8BMQFS<#d#q11UP8|dvtEW2qSha z()uQ6bEN}Wp}u6&>xxx<`-@KKqP?A6lsD%cn1ZuCN`zkK<%Khhf%Mi9NO3z7S0(r4 zgW3XsI$qs^ruTOa_cpdqP9IdQ*l8T8wLgwvEhKCJ4YGtVFgl z?9WCc9Ip9%eiHC7fG1kh?--Ud)7hfZPYI*OIBf89$=4djzABK63q?Smr$}+Y23dRw z#*?pWEHFIzGigDY8M~WUKWgg zjqF-6CF#df=+L+eVC+yCbR2e0sB>%TfO$vck-2CP`hf28+pqX#wx>F9zA5|cLOO~R zVF$W6e>p!w-a{L_iQQSbHp3GArlUf+-QtkInc7UQw|;~FC}dLzy{xZR<GRBJ89mG}`_!0Df#Wif_~# zmJnsD`A8@Hdz3%@pSz`Jg@ZTT&nT+Z(|vKpY-N1>x?Q+?2*EJ8ozhBv;LGa23m%6J zuYaH1!MO3wu_=Al4?oT?_z1&gAQ(>u{V-B^NP|P;`X>&lL8%ScLm7*PDs8gHFW!nb zJy5W1b46%rhl9^c6T`XtTGfKf$C`dijdX$0-JnDn7_OJv&+z#srDcBEsjz`o`b_6g z$>Y`irv1})Cx>z*YMG`=bV5eBVUrU~2fRPgSdBDv}=pukGp>DQ8#n6Ws}9hlKry8jRJxf#BusA zrGygt0Uty(oTI=nVRsBCg@2Z>;6+t|9yRlM&X^YB&z6aiP1H2BdVoAJcdQGwveg^L zl3h0|>T_X%(T3|#aDzR>fLtc;7c;*aw;GX;x@5cdH2v7@k(CPS3NpbPW z&>V>|kdo+ho~gfBP?aoM^K5kJ=bv)pYzEdlj{Q5nEBzvfijzP)2I`soMoQ0O^9OB^ z?ggj*0;XSt-gZ0TpdieyS*3vY2m*@MlMD=`m<4j<@>~`M2fy2Txf-_D90HxLK3e~M zq&g;C$FsQBQ+~RN`am^)(WrZqUI$?0fEP;f`=A?<&184FoLWBwH{|yl!IDLYgl0_ zG`%GBS=1w>r9Vr-7CTL>>BoxArVj!NlG7pEm46=|Q)FM?)M-)-fBB zF_Q>AOgE%ub792vZLX!m1v8v~L{w>;C*+<)Wgyhvd zEIMVkhnKPYo)-bR43Wb7(jXdr@YusST87pWr}y9&-4JmisfG6P6T}yCX}{wIEu+hS zcyGQ`=F;B}u!16Xv}=&}=lp_<72DmX@m+9Sxc=&I1`{Hfh_jy4@^3U}D=VIBBAEE^6NQuNLoq2hWrtYuBf zNeI*wGhNx$HGveyz&sz_9-Z;j6&Da;-0Z;l4ogSYW{J*rCJkf*v3;@ki2qOd2M-P~ zV^u+wogyW_@UJ?!ezVa)sC{Iu_Bg!le@$kgpivB@U3f$Ep&!C|U9M^POJn!SDz6fm zB+onbMjU>fI3~d`{gY_;c8Q08IWw3})YQ ztGzKPtboE64|l!(R^%20OFQVjgee}?gJCRu6k?sHJ# zj=YVxC>d_10~dcc=7^GdOUUDTuXUwnPO*(7CX7;U3yQHr!c)#@Qc#CEv26mwx$BPs z&Rk?CbHk)Mf9#M*0uY(7(JX6~wo=fjSH8#W;JZ7GUCYVAU%qV#7$F?6u;CKE+3No` zcD%i^;lQiP(tG}j&J|Rq5hfgy|5V;(`Ghp#e)>lY@SjtPSOz?i!5@>I? z?lx&(@#l-q3k#z8LH-x<(!vPK7V;atVJ6#YP-HSfR;L36Fb`7BM66><2Ct7hB(UR` z=QuYh^8I?lJ_&0GB866{H-hfS%qSvnf&U>T!!_){{^1H^2`=VQy zeaVj9SznC%SeyDnCMD-FN0q5Ja+^NdCxpd{u4qB$f-E(^;w(Eb9ew&oH!DT-3_hJ* z8+e@xQZZ=vW-9OE0mDFL8xb;VS24aqunmWu4UjRMlctNy6xe*-=*ir)eDJEf=uOza zht5Y!-ZM2NWj=5o-Xb3gmDRr@mI7|f)fq-&Tyo#~yrI5$Iq}R#;rcG3VLRo6$^GRQ z;>|O~oNKh-Bh%nHS9X#~l%_)Pg=1};cH>M@`7ojfeamPdwd}z!4}oCYxKPu(2sFrC4D%hao)yWT}m zC~ymCz~~Q9du)3`y9!>1ozgZ<9geM9Nhd+-6-rtZJx%V zG*vkC_r=E8CHB6Jd^dN?)Ne#usL)>?!p8n}$%(0NOFWH)qG2oORh7zM3)2JVqLz37=3V-dAHy0YY~&X{mLC;#h{ z>Tcz#He6FYNpo$jkyv$WgG2^Z({s^SrYAYU>L-PIIY|H+VCcy{fR7`){)%%d?ONe! z*YM=WHk)Tma_V{W#UN?{>2}@|Kv!eokl=2TddbZ;5ensrOp{$-X|oYA);TnVc~~@J zMG)nxW5DP0=WI40lrK2i(wT}Je-uGwXW=phA#~GXTcY=f zx@;7|!nq!Q{OKcdcS>Nn+E*sCQTAUKBU!RVh-5$LhEMJB4(vk<6Q;(GkNZ4tQa)QP zZn;w4^YWtj!X=866LS3&qOv(pfqqk;GZU8Slp`hLND@BFj(Xe+1AT$ZLibD4K*iF3 z`|-Hr2b9&y-EwTn0NKr(Ro8+^frt}PFOFm0In(GqfwjrZyW?9W?UN_dYX7N8#-@}0 zq|SD9Jc#b!5e9TFcgFEoWy2o)Og*;=q))g;H7(M`bxm(QZ>kk}&9iFBX)Vya>b-y2+gidPq`&CH$4zPuTrnZ*$p&461be;TcBoXO8>Mt5v#g_Tz zM4UiS7tqu=+GKmlRE-ME5pUDKU&aJtXRc%8Fz`5i%#Ry#wit)BRhSlyQc3S@y2I)@ z)f`lD^TFxjBP7wZ=XyC3t49aJyj_^`{=)hiRfr3y_#g7M`bP;Dv4-pN?GlEM{EEYN zfqTU9*!Mxtr{Wp~R8WbL(U8gNA5xLnO;GwEz4a*CP;`#tR3@iKh57AzCzPk_k5&l9 zJ=u$ggCG5@)1yKOW2E)0SM(W7@1QB{f)N}L#`c6*6YWPRqI#<-r(b^Jhq<;3bRM*lRNby! zk31=aX1a0MJ9HG(&Ghs5jniMvW+y9tAbWd22wJ3?2UC9C# z4s5gHVRBD*|Bsz#ksY-zlFkT?U9R}|vei*;#%CfEc*}&TE|N^6blhu_cuWXte*u=? z2*I-puX%|uek5F>k@J}!KCr8J$H)l0f-hINqG{cG2mgrWU+{+k#?)>76Y3GT(J;?u zafy;A!?^*M;0lqpEsy$`F=*oO;!&59>H?tDRW~e!-3=6xg?*h0KL=oA_;)h?d4A>l zN?wJFbWA7VO->}P!!bZ4UIp7T0tFVSx!zm5#l)p<9PgZm!Sj}@hmhV(;kI%uLrZ>9 zR@3yj$KJ4UR!|c9b%|KC98;S`Twa-5A3rjk87=xYN59(ZU>>9JsNcPU zF7@Yn95OR!>dmK&)(g{x`!*`OQ=I&MVJU*=y?7s0(=+ROqjlsOcc8XXu(N)MzZOKk zeZtdo^rc+Q=8@ADHNvt%HRma#oKP0x_@EN&--wA{O`crQemav9R5D<^#T(Be-H67q zz*`>%goF0|azxNafX2nu0%fFM6zj!dLlS9~4S#f_M3eF?E8oRHng(hF#C%dbBoV$O zK{+sgH7POn0G;?d-uYEMfVhTwN6SY|PHsdEx&hZx;#J1pE1&s0o|>tyJfU%WUT579 zd%p6Z&M0&RE(tm3GFe{dXjqw0Jt){%h?xl5IRw93NvEhG{s^a&_G@+l6DKPL~9P3EH$FM*|rO;OsF9gm`ey}{x+=IU>pr_*3ep|)sr5Qkaw^&xb@hJ<7`H95R>9A&Of@7ckd4L$Cs+U9_;(&?zYiOymr`0jAy=i zLY%IBYR4}j^kkF_z6fECizNI&%o^V3_AdM!GbcOAnFq&YkPp~t$^$Sq z*ZSVGO0*T;3-2KxXD zHE?>UKi7DxfzLjG=@N{dN66PxB$TXwRQrKJ;y1;JCoh9GY-#xIX1;_Hul)*`aP~C6 zf-Zca5INPHbybCp&FTZ65A-pLw<`xs3Q2aFplj}830SEv=#rEV7za5Q#_yB`J!F_F zWj1mnZ2nn>gja+~Sb%IY$zgGFYhF{8hO%m{i{28zdlP!1h!9+kwQjc(o)mLzMkPk9 zL53DGX6Gn0ws>Wx3ua^?e+kPhJ5cX)PeQPW6n>7o?_krkkpFNel@%R!rXxQw+y)4| z%jfcg6o%1xW&lx~j%L3fQ7oksB-b61(N#kzlf>s=GKG^6X@T`F`@0fq&FU5vyAOR( zJ6`S-CzO1{`J5oqGe#EWMpCTT(1tn1Zl= z=r6<4SFP-Q9A=GFiBXRFz>5en<&xwckxRPSs|pNU6^sBCG*RRrKGxNH(-0}>bLGDC z6mYG!Y}Suc!|P2%k#jxWKieEwO~!|$I96hue)^Y}<)n z@ij5NBOK=pMaO|-4#N(d6!_*psaR{GT%*e5duJ+bZe?Z^ufj6jpw`dK@k6JafmGD2jVycV9fY+hcr3@#;PTOL>hBc;qB~#&h5loS(Hx9K}D zdIxn@BiqKTJXx`%KFoeux@$s@Zw>IF-QHetO4l&vhAYS%fs8#0y|R16%6a=U%=IL9 z*RLD*TV%ls%xb^m1JYr^-phX;2zbV-bv$*1qmlTt6Ioy6VSjqjme{LGWl~YxG*)Cd z9<=)XogpEApX~V?s9BR&6%DSLXg~wmu;h?I>&hpXL@XFw=z^9JGj+PBfEHee!uG}e zpU-2ye=dwS{`4u-M~TFM_DT@8`CU}S)n3AVS|wSCWq-`-ZRdZ~o**-N1tD zwyaf7f}pbNx(Gl`kL*QHpNDl-ya1Fy>g&p>TFZkD;y-dgiyCZlzkAYY5{+A;7y=wh zq}B13AwM(YTm-K!`f>LlSP%pvi`25nSiRdX{Qg`zvBO7DF-Zii^pdoLP>bxy3rKvo zYjdmFPYuu_T@k+J!zlygT0Z44h)d7^d1VT0IX{in(8rmrN;J#L2?>L^4i?16dI-?{ zh#YvpKCUF=y$Cn}7P$P3Vx^F-y|6hXrBli72#AvN#uUF~xK47YCDZfok|oD#cT5xo z53sWPPv?BS^uWH3-p{D_qC#<|66Q>8QT=d4k4a~o#wX_mC@>4_(@Qmw?`o>m{B2$Y zXHiMEX#~@rtE)sGfb2Us&oR_~78M2Z(L$~l_qZBQ-2U+;1NS{%^9W z5+5~tyaKM7hUVD&x9zt!0l0$<&e#9kj%N&(#hI41vgBn#=>01m30b#Ye?E z+i~cG*-B@v!QKz9q{AxNLtPeYAv+%y?@hT7C6k_?anx0Ou$YC?TwhyoRBIY$2{Sw3 zO?gtRU9UgSrF{$6YKC6l(79X8@V{h}r&`Qyy>MPjkL!26Uzr2m%hg8ZTOV0SM;5 zHRD>LJ-a@J5DA;J>-4fE+e~or6Eb7%?*fN0lg9U9h{Fn?qdx8?9^M0Y9sw+l?JA?a zW^EvVUOndk`a|Tln=U^YybVI{Lmm*5qMByEQ5&q#o4vv{mb{4c8cj#Jm40{pJH$au zQcuXUra^DdrAtiPJCIjj`lpZU$x2wU4R*rcBiqld`AqKe>}Q(av1EmUhn_S719EpX zv%s6RpK1MNNzsZFj@<>u-DnASg#fqvF$mLA)Gv4lbPrNjQkTj~_C~KRyJ(eXHF42k z(vi}+MmPzMJ4uvCbC^w6A;BvfDSbCDXV|(cJN;jJk@oWm>&VRNhBXWdEu<2gd zEiP>qi{s1OT~T+&dR^=Yu1_A50VoRj+?82Qxy&F z?$F~9g1J7CqJI+Q;XEFoi*KeXkPtfCA2MkX@eYt=PfD4&XYIXVYhU zlx;@)W*N-!2P;PWv`(zr!?buiPm86ihQqeH!iwY89apc-X@lSvBthw-Y_wnN6g2FKH{cr5FOke7~i_;XzQo)R#7) zd-^)>XF|lQ5YOnXudL*F&1RLW3GeLfB#wq$ENZy~SZ)!?PMUy$2KOoGkb>UUgxB(32*cM_0XyB1C9v;P0|xxreCOdLRjc@CP20KfK>g+`d}+zPSN7v z7Ocz1gcdRv$7xak)DCZSDwISFUSmgJ68RI>Jo~U{dF)&&^?>m`=s{{0D3jTj$|tfJ z9}Hv|4MlFU9calQb^yAP^P{`*vci|U1|r?Qu1GG5sP2tV_py}@8u44eMxR6HKuB^i zK-gOI+yL?CETz;p6CT9Q57KNU$|LRIAD2@I`VSIK8eZjd`<=)c2nQdt_I~ShZoOr} zR-=NjyZ+m(;qp?;DCQzrjbnWge-vv+OPAipUd#-YF2~1fHUcOzapqFro$bL-DiD@t zJS;~Dqb16B44oPZb+f<~C5a}E=Ts5F!C&~tVhBUgX|bp-v#O4K!h6fYiZ&(_RKFC( zHD@amTuY`nJKtNgBewiFh*{|-#rVI=l+!CrdjKDQVxFe8+F7o*0+%;46;YEtuio#% ztB{?mV{oH*@AT84x%8GEE?!6e60WkB%58L9N5eZQmBl0*XKk|Zm$H$3im_R7ehfCM z=-EVGR$i1yV~5_?m2Gk&!`TWg*!aKD^J|vlUK3sxB#@sQv_oTS_xC%lQi){7HlAbp zN7h4rcL(tZp6G{lW)}gy3iAci-w3o*Qn-Ck4_3*kK(jwgM`&XR3-Yhep!uPRUb>8M z!4Bz63{X(HrMHn(sGxa444+mf4{z-pSdbYTl4kn_qY9gZx|j=kqa>@9(!a+{V}52Z zbUxS;)znqRol9~1heko8Kn)EGcEYi?-AAg$Flmqh7J}*2GZE|VNwr5;ftYc!a?d() z(TexBEDV{*Hxyu$aC~p=)V+8Z2qyCP<>Y`+mVijtO^(|==gTRYF!*`JyK`4?7UKQg zxw)v{>8~^7Wdjl-N-28oy1|GDwzb+--qfx=@^v?7E`68<9_7Jyt$C@RZ(O|56OFt= zbyYI4Hg-owdMw(KUCY!j0X8SJDzk>62F6xr7+Dir{Fs!g$b-_R8%s4t zpLzyP`%sO3rU0r>e6~a$Y8-6|KmDrs5GSO$5n0Ybl?VUKhw$Eu5~u0`jBI!u4h8$2 zwvt;ddKjHP#g->CJyhMM>@{5g0NbJihevKx^<VP?bsu|kL#&}L%(vOYd!($80aukFxuL2b%)d;jBZQfTNpaL5oQ*mKJP4OBgzF5A4YI(+~qr zqsWV3sF67vMTd`>OOJx#F)yh!a*DJ`3d_*`F^a#tLv*Jq8_7PR#7?~w)Qz8T&SG%p z&;nZsX_BhEWdTcJH?$N-|4Rgd00(1!TQ-F#|w!# ziQ8eH^RT+3j>PVF*p{QBmXS2;`zs8g@rqC=EAH3vY}B!;hE~m%0$?&b!f;%-$UVl= zr8m2F#+S1(Bw>&Y60$3Eu877&Fd{2Au$5b(-Cbn{Ftn248}St{EHybiDf?RbU-i}Y zYwaiJoozfpmHpz<0@%syvuIHAiO79qBk_ysV(40W+IzHL`ssU^sfo`fDl68o_ZFc{ z)yNtr1O$uBpfUiVL=A^6X9cA_h=E`RrWcK2_yg%0q9wuni9Znuy%JRJF!3!0iWkFo zNy-OMGJlMc19Rx<3hl2|b?-I)KrL`rOzBq)}rel6WsT6P?#{4QGgIVzj3(B3ONK6WIy zRF@PRCj+^9N1`yjYX$B2-bKq{HDu1OjPkcU_gF=jz|`*cj75eu?BEy~lXn}Oi^StU zX8Y8?e_T4PCoKmHptz*e04NG>KD0$iYMlNwWFNbQ5#E=)KeIOHvrg#ewZMJTl`-nW zFZ5Q+=h~;$2O;G3yh1<$3COki&4&J=;Pid*Iwn9FW@;)4nm<880%{BaH&K!ZO$7%D z772y8|G-N+FJZ-4AEOLq)fAeaPI#M$INreL{z67^#p^->P%uDSAaBw!HuW{Ukg;ES zWYD7S?Zd{?rqOOURE+MN#_W zReMU=xLMqxC7Zub2q9TW%)=lh#aY{+%WeG}^qo|}_I`5d{Es=SRXn}H zcYJ9-l>ehR54dBD<8p@nRh0om9d3f2f3Lkepr&cn{A{$vnrZb&yf+e`xZ=1JNKFn3 zn!<4aFFDf!Gia*RzDVuq zm^9D2K+?YHIqZ@e>amEWI>L3#S7dpcm_n~w?SiM&%p+Gd`gg~!WhPaGaZY_FD_S63 z>udxZ-eL|8m7+En#Xwasa1Fd>oW90cyGFEWeGU$4DWNY*bD*L?pgcvoR_f6cE@&{2 zV6H?b+u8RU?c#^J?^ZHk3xA2qk5E9$zMLIiYFV~c=~%mwep~zrwbkk@had?jKN2cc zpOmEM3|+y#QcT8tu2`cF z-l-|7U!W=H-Gk??a*MHX*1k7RnAR=6Of=aN2=ZDKTa$U-^ZBa!Q=pGY2v7bS=a;;m zWQXwC*PvF1!dxo9-*x5L$dlFH^F;sval`yHDf5;6h8XSj+e$FPWd1ik`N=)k1}}HZ zL#xOzS!O1AP0lXC%qI(I5BnD>J8RQm%O$5on8ksjIi4_=1BHxo&O-*DqwDk*g6(5q z6h<1mwT_eyKh|S1#bk~7(y3rOAwLPQn#|AM5JI_FP#fUj(rIaFz}dNO3- zE|HyjAMrXv^|ut}(YkUOYt`YZN$<7y($-8925_yCJeUg1%SpOgI>u3^Kv)qQ&bXU3 zD11dDlM1F`72@fIB?E9^rabLU>S1CJ^w+{Mi^ps7T=2QF{ci`rB zj@O4*%wv5+bbsKIE+c{b%AuxhxZ=Ednwl%i0Hd9C7SD71(?32Sq#K>cBRVKeYYf`O zmZ4ee!rgt3jlxyUcDteEEF(MZky*{uJRZah6SzwUeQp)E2W=G&JZDEy6E0wDd-6+= z;{B9MkJMgLbvp!1sRZ-lWDek0i0##{rtv1Tz-`#+TYHFUUfj-h!7fe23RKck%5MMuFkR!=4Obja^ z(D?^1|Ib3xCTA6>#X!9QS8dT!hErbeqOo^X7_QqX1z@l{gu?5Yr5o$$rNdv@*elyc z>{C{LU|XKYyQjXIy)4j*0*?2}p-D7q<;umMopNG_xv z`+a14HAISr^dv#v=)bR=C@g zUJ#*0odzHdp7PUk*D*FA@&?}l0D8YnclQi@j2>zM_tP7JO2I4DgLKl68S#{=7SM7w z$$Qv@+|MhRFgR*L-3slfD$z-)HHG+aZRI9b%-4ZzGN*h+4zdMQA4#oQD4;k=?cBz( zCAAh=61c<5peGbwWf|=p>CwI_zC&$pQ5@aDvhx)h2UBSy2TPobe4o#sYeYW>S;$6K zKAx83aTw%8-(DJF5NImV_=lEzXy`ZAy2<>Bf{T)P6@dpfWr%#ccF^sZN8yY|pK%A} zG7sqEq<^WY&`dGsS_`WEld6oIeyew2U~@2dcq&~a{$6qGRS5x*K{1^yldzbPNAbWn z%MM-fLhR7~sl2LWz$wVh)xKw81}B|U*|vM)OySu)XBSV8hmPEF4;&1EW`&2VP}ed5 zM1PQa6!SzV^1mT>;G0@@-I+(xFd)|#lZBSo`%8Lea9=$~Up_3(0^X0Z!S8?aP(03} zh|!{a$yhJ6nUcWg;Y5r4WCfz#sRLU~Fyya3tE+hC?C|_c4N6^2UhsGcc*~ z2=gn#6mREURTIa;rOm7msHGR{@icQr za%jypCQ{%hCtqvgcH(fB$bw1wxI(cd$5AYCxH|${y!~ea6PBSq-kLOT5um&`(?yrL zo?$ojxylx&~@uT6ssb+-+N?|K#-QeBb(s0I2-@+4APxg+>va4uJuq_ ze_F7IlYw+_>CuWN%6S`$hOUL1uJ7n|W2q@5FaYdE>kbcL3S!Wr?kVFr2V;AmYCitO z?2RAn!sNgKVIda_LWJ**?m7<$P5~onHZY@VR%gFM?XD~2RyHOjRZrzxcG3@=Pz*0z zg3xvBX2@NAbvlSNy+X^nl=DvP$kDSCvpjJP?lHZgFrz&*r{UHgH7ji53V8hS-?iYJwJM;y98F_97`Wd&QMm}XOoomAD5*Dw^q1Bq{Rmsps0=7JP?W8mWAt)Cq0{XuVKWe!+66)|LjM4HdJu%(iQ z05UYBSDR5wBk!UCR)4O!@Ev1x z6hy8BxlCZh_CS>QHB^Qq@lYQ}hTT0RVjg~Lo7c~GcO)D#x=YeSyNZuMaBG_BE3&_} z_;4}kQ^tk}vA}vPA#tuK`yduxt+Go_Tg$Y7O(g4FoK33&lo%8KI&19yQ(5$lr#mYQ zXdM2rsG{!HjUN44aR>iuCAi6H@S6;A#gh-!qM!tOR6@k-sHbt!1gNCd{6ULT7kt_O z(d9}pk)54-Fm}DZ<2If|pOpX^p?>dZ4uBQq7VL6q(bq*dNxi6c-IY(>Ogby;#FkY0 zhjneaiT?;Ka-h@sn;zoFfOYQt`oScl(c;5 zBzglpTyz~bJ5;ab{IsqUI`J2`Qlie>%K51eNXuL2Dl>+g1?h-xmvpdyFOQ=@aXv5s zpk2Rs-uQ27unaRfUvZKLMYtQi&FKI*+GGltjjOenLAWMeuvUQPnv){}E#6EfgUy;J zpdDt6W*muTf*97v=@?zLE$>3|e@rMxn}X!wMlIdvfzAZ=8TL!f4^hjs2T!(D$52F4yuExU?1Dq0>6b=Z2(26 z3$;T$UP0|70WMkjMVTIN8vy;>ep0k}sw$8iWUtuZ$kioyDkloCH)bAK`uUgI-e<5< z(~f5X;`nv(rms5$H7a5n=&sTj9c}v{xfAkcGT&KIQJQyyYp#`@RhcBq4yq4Q7>tLQOb``7(}V*6UmrAQ%z8$2|o8SyvsOCZz>Eu}b8cC#Cvg4+w!#zPUqz zWNYVHQ*B7wij{;-UCdLxkONQ~s31D@e+{t7M2A<<$jH-A0a{bVvzkaDySKa28m1YY z_)f)oQ=>=Q`!w-|U{mJf?$$TJP8pk9z!V0&e zqoc?Y|5GBwbdlvLxYM?HP(VUsKNvkp@C6m`=&l9#B{IsgXYdp2Av?)Tkg)WMjeZ9_ zaNo}5$oq9C9jAMS=dxtO7c(?uY(x%kxSH;Vn*7emtdh&uDC<^oQsc9uii>XW5?p&^}bL6FXeEbn}D4K9&PA?P1 zsv}vN3M~=)qb-1=WnJPs3S7%}L>x4HPkZuwmB@<0N9d(`2TYj`CZ*W=gz$DPmM@$< z7@>H>2yyp>sxkxyA4|tlOC|}kaE&v6w(Ne-?@0~U7c*ARRS#ou`I*NrCiioQbr|Pz z&;p!hQQ-N!4w=KmlwtF9#LU8u#po=;>Lp>oDs|1=ntqZFIstbo2(SR^E1bREl$$fR z^o=u^Y3dB_!;1e5nEN@3Jj?Ld7?h)y@~0_B?jGcoz)5-x%SVWQ3r+ zjTxyMzdz-znpoclXIAVL!K7y0mf&fcJBAKYl=SYrmq2TCpALtwIVFn`*@cehlhh7+ zf!dBw>{aH1W0YucpZ}T}51vy%`)EKHERtAN^36Rp-Q5arLAo*`$YqM@HfgpyZkU=g zr&jP&c484iNvJx|Ah*S?7AGQF+kkLo6BglO?+&7e=rBWHP>%{F zF8Yd0=cZO{R2sF;cLPWb?e|WT^HNAw;Xs3l$sDThCGs@lH6s|43AQZNaCx;@G>STdRDOo?jcvt4--UM7-f* zwUjj%TgS(I`$ysiXfcH)_^P;fe?n3{3=Lo^>1oL*ferR0|6i65*+xuc4ekkUNowQS zBW(nBw@z--pZD*yYzP+fGS>7;YkDW&8w%)rfltOT6H|VKfSE|4~g^Lhk(v{@1OuL zI?+P9Cfp#x^IeKxfptRu2crfye*Us+Y{EL&fzE zDw>WahCgu*CMv*eL)Td7kh_3i;HFj62qsi%4>wj=e^?$RCKz@b`jAKxAUsifh9imu zo9=u+9qI9p$RFemyZt`NWe5oWq<7MoKhci7g3Li6Cm9^lG?}c2GDdfzT`> zVU15t8lHa7+8dSfbnAWc*vya_j8zv?p|L9mGf7GC+CvQ`guI7Hr3u2FC=d}B?N?m~ zd(5CAeuyoF?y1Myt(x7&YrDCzXR*Wp-1>BC!(I>`NP*kG6OyBo1l@>>vNZBb*W)KCsRQD?)Qf&gbPJ-bH9OW=@kq|a1PJ6 z`e+qT7uhuAI}i6O_D{_!Ybr+_my8-vvUt)Tg4W+J<(1i z!C{NZa!i%J{#0>+9yj*#DdpI>-Pfj0oY=$vIO~3;wy;zRgsRkSqM1@3q*EC7SQMA= zZrtbdy<)cMQ*4PK;fIe@ehfZ2(?o}fs~YNsH+x8_E3-0IT91D!0=kLT^H3)ot(3l% z;fbg_-&uZt=6HXqNcD4{nfMp~n1vi5PtC&egO(I~LMk=fH{nj*6dF9*lM2xMS&GPC z!fo9F*s)4>O5Dn$V^^pCbZ*8R>m#`EuJqu-a;8cWk}Y7Qb{QmPj1!ru*(WYkkpANm z)uID&&*Y8nFuR=@+wJ-6dt-(#kF`o)HxsYbm{g9d-V~V?zs~PPG;&2}{S&lQ5R#Mu z73+jQl&CQ`7XrrlzA}bKnIDRVAdybb`s_xCVb*=ZM?F^WlQ7I zb0*e8mf@M$8K+j80raKrtCcoR%lnX)p4?MALK0(Jd$6w4K|?EKZ7tBA&&b&^-_Up! z_IiK%iE{liQii^Y9YAXAo2#iLqb_<~#Xe%J?P5TgN~2BVWJ>|{AT37DubdMb<~bqe z6w_*5M7<dce)nnFWmD z{h6ut!5ovIF^tJ%4T< zI+V>bL+x+*@iU4~KrZs6kilVl6xxG$RKHexbk4D(@~PfJAiU~AVQ=@slD0wZV)0mW ztQlxFlMh8;uO$3(RzPK|Qui9lywOxE=A4$9v~uT2X7;(KOia&J>8fYp|5ecmc;yVB z$B+3{Zvx9^>1H+Db`4vT&uQH}l0#;pA1B$Ir0*gkGqx9_j-nfJ;3vf!ROX zwxAeeN-tZI_6)-|$u^UxE>2ux%)|6WyV&1sBM|C8srz-F6I&4Mmvk^Sy(OAdK8&0k z#gJ!lO2QtPNVwf2*a&Sih=t?nQUI=iWoPTuLyc6B0rHdBa&vANEy*2lwnDW_j)pYI z)lK;5m5gWrv@(I5eQ?@;&+wY&iZvl4-QN12LaWAcM{Hz(^)6}yX@Yq}kmrGaHv{bO z(c1H*=7TV35tW1^Ko@I3}t`I5*2!zT@f z%e8P5KuJ=?jKRn!hG#&3o@@Z$u<)f*fxpK)%D>xFSsCDtq;gva zi7h7L>3+m@vMyLtw}%Xo!_Ro>&pkXb$zc+;;itrLJqLu3PR(0L*Jz%(b<88~zrgO0 zi+)MY4BoX_ijXp)>8=5X`%?xD2-YuOSrU(<9K=KE@1P&ip3qB<4?e-@sX%u9?aF}6fq}V?0x%?N^sqJ8NUJtL$`eMR);Z;F?D?5TiXR%z z>Lk(6rnvDdWl^Voz+w##Z59BdYHHjCUd^g)x;6Sl?Is`Q!X5KwK~Hr zXO1S$fKqpew3FATPBHPkXuZu>$uzT-Oq>~Yh;NS$*TZc`N^t{wKm}m-*%5OEt5`)_?=U4&^*X+lw zvn17wD}C(mtfQQjh(l`?R|-sFn!AyA7ja>HmBXb-FkZ*9-JOT^mZbuRZRCu4d$^($ z%VgUUXB>oFlo5W2e$J%PK)75o4SIl#7x)9+^WFtml!;w%9h-CRy_marI4GB4QtKG4 zJu#m#FvDd51=+mI8_@a|pDf4zIx}`^A&4Zdy82p}_1{4ije}}86$LjWMV&@h6Uj~a z6iYIhXq)Z^hrLlxX^7$(Q_H~cg2nm0Mt{&}6RwWnc@t_~$V2_OK|Wbq{H2FgN9SMC zA0~zlU@|Kj+6{!E$RV*(za3&R0)0(3&HTD}K zuqU_Cbz79{9%1da-M`PAh8p8BQ_dNhW~v*aOiym24d{}PHnJfLm z7%=mUT(g&GSoke(Ft2ee_9C;8$wlX5__}$tg9K~=O9FaCp`UG=kONuz(l#X_IkmlHu|oYO*Kt_{AM06epGL6|tP7(XU&-Oh^Z_I4*q`=1-ZG$3p2rOFD6;!I zs|Z4TTiwn$mXJYG3fe*;!Aa#OHt z7dePM7uHK4O>y*j@>YJ@M#Xr=osSLc25t?FpE}W~{@=luP zxc0d`l!>(k!8#{aTn8KADuH6+O%n>>LJAKP?|%EU3+1=+l(dQPwr+N}L?A<&@?YWv zl-2zL(_{L3FU$Df-cLPlW7icwIl{s@^_h7l80*O6uU5QRQ!A=MWJ3Cz4tKQ=>C3EH z&+GnVdX*M3ww&LF@D#{-Pi(gTVK&`u3tFL8Ub15uZU|~}xgQlmb<-E;#xH8~c8srO zWRcsg#*=YUM{RqRAKVt@1#5u-o^@bn$*K;*pa-p}=ZCo(+d&*BCl)5Dk(!Btv^^0m z5}r>q>Tp~UCj|Bf&< zA5kd_f|^s}PIDQ%Bej-#%Xc<#8{rgASbKrD+whi+3==U$DuIV7i@*?T$#61)@J|D1 zpyeb6N@Hu|g^CL>7t*W-=wSvs(~Z9QPC_>u7NF}ey5R(~i!oIJh(P&@%3hxkpeNcM zHU^*-ESd3qw4$X~u*}NwENoR6g)!UTDRl=POCbT*`!P^A#2ZlOxg=~+tQ?DEZC2;G zYwpjcD0y=3uddbWwq_ZI3I(Qsvch$8K#|}`H0WU4>j))9$s1~^xS%a%wcFNOEZ(;t z=9J#{d|@H6C<5p#hvig{k?boq-I2kv@^FuwsYENRtIm#a1R=&S1E7bD9kc z*hzq{^*jJO1>>`&hS>jL!*x3PF;Vj0P7noz26OIBu}#NR8io@dAGC!h`l>7_wde@;PZ?|taLD7E?k9$~pY%&^zOS#%g`H0vd+aA4F2-$a|p$s|v9FbDuI+La7M z0eL@jVBBQNY`OjSYmYk7D3CXSa^z!`@xfdh4ox9G>l{7&FS(X`?)z=W3#@(eOh;xu z9RUNfK=QTv;PY&m?p6Kr%sJ=UHxWL;@+Dr@+ zmivkbcPZ!mZqx(=0RW2QW`=lG#)JnO$$}CbRsaSIdIoHPrRs>3|J+lYH}bLFoDW6{ z079CwT*KQSI?)l<#cd{kAh;GZTyU{y+9l{*x#`M^4Y)jjOz<2Vj>BxEGlI)U+QF-)@E>VpWNMBR5q}hR&zEq|Few8;XhvDxt1x23pgM~~esI8z+UT1nd zJ^xxv<-zbxZoe~NyAs+_sh@FBmrZGh%bZ8pUBe zVeBr&j;EVqLQybh*FCU1We=^F7dN!+emVi)F&XVVKcO%uS}nZ2IXym;G-2LD`|}L& zlLLKfxL0*Ae53h4C!fuxB^ra#{TwIvFiDb2`w$=;b(wb-<_wXaF2?g1euD(D>6>!# zpZ^TVutSfFM1_~aCjFa7?9!5C#0#8uVfKQj2o>%`kmfbMe+*c0islZReLra4sX0oo zdO;(O;xTlc*<`=XfqmMOecg7XxM!cnS3@jxnK5Wd!t+C%!nedz0RZv&8yG*~c6fpn zc^R($OQ>{8TZs(oEUbk6;g=*Uo34a%^d8T+S|j9<@5$x}X3m#h#UIxjE(=kZS z7wRw`9+t2Y4hN6X+RIy-KbV^EzGk22kPXQF>Cq(aF`N|6e|Pk1aleLHE)1(g)sTxz zjF_uplie&uCjR=kVw89l9~|Nb9?l|xPP4jOa#~-yUNeQ*7KW>x-jhIb{9irLKS1Zd z0SGl@i#`VXegD5>SQ`Hc^XLC_qNWJu6#pM7TQsBRwG%A3^lwmmx$(Zm_&^r^=8M?xT+Uuh1R&>m1k%EsHc0N@-W39=iAHV2= z!hZnhV8RprIoo1@vWfcUi)xViFNv4WTOstgp-Nt+gvg#uAJM%{-f&koC*j~B#nkJQ z$hM&6sB%dwe$SWjm4)2OiNQIN*M&reBk#?X())W>D1)yn-6}lT0wUIUW$AC zsZl&deV80_T>>SxS;-nuRtN~xNqZOS!v7*Wh%DO8N-{R3{4ZR5;g+p z6PkF@fN9-ZqUfH#eU4Hp0vHt|E@G8d^O8<{)ZwIMI5Xp9hcP1`c^dg{hb)+&<8^wK zKw7ijwmrPN*|gfHiI=o;v2|B_$goVTbIGLBtc`rROVd=cX+L+PME zEhYUmObjvG-{9w|@5%a_b%@uuA|mEQ;`yG;59_hYR&T?u$ZCfjQx7H|D`pKPNJ5vTf@nB6?$zP^T*GDR@6bLU;$4XOibx;w@x2ebQ zn#Zl7xE%3y8wG5E?kW_8*~-wutYP#Lf4tB0XjNTSr+5C1Ws}Em6GLIR4`wyk#cgZ` zQoyf*!Tr{$M4qR!GmSzstwhdcR*jqZPGU=!RD2n#=kAPH4nn9Vm<9C4c~Rm{&}n2y zjfQD;`l%>r@3gjTbJQ{8^dQXCU~`HHHV6 zGxYy)%MHA-xNPP;uJ@-2r~_cjAJCQ0vM}#v48H;Nvc&G_7h`SXoh9?UTIbs1W;>f0 zNf~HnBO~N)GAP{z-@qJDPk7VUp#CF~>~63y%R*L=()jEw=q(+SuyvE8qiQc_f3RfW z|BYiIbd$I{tc601u}oM;3g&54Ng-(Ex4Yj=v>}bQZn;NxqTPvO5V*I&O5*#q?u}x< zXiN8M&3N>X_@TfW>h$m8dls;3X49mKQ+4T`A0nJD76^uyp*=-R*&$Ji64nfLF%t@{ zA7i7$;1UmSNT~{QGsp%c?CJXg00W<}!XZct$+5m7iRLXzPU$lVgX~QwKvh9aUlHzn zK(X})-39;v2n5U-`LB%)7y!se?^uj6Vl_6`{r`Tk0CL)B*A=Gz#4ElO30LEOmuD0u zH1quU3Z5BmeSl3LTJ9_+=$*7zRMHzlq|h3CJ+Oj^F*~1faU2AUyOm1n$*ATMwO9*( z5ei%cjQnXLaU=-byNy|Yd*m80j&cf+s!$RD0NfS@GbUyKYeJJ34;bSB0u~a0ApMQ` zHq6iRy?TcG8y9ij|NlJGzY_)QlJfT}_H(g7p|G1vA#R{-&1RL;G75D8>_ zqi?a(0A~EJjRbC6XMZFAJAwX}sEaW*%*g|Qcee7RkhFXFPZ)^FfN|j~T8hpWjD0V> zUmYNAPLH>#LB9`3!z@t`&yisStBE#<8*DSZJ%q7|UI1q%3oHByD3u&a7oMY{B-Krstr9TOJ^>@c( zRT9a&p5_h z1eh69(~wQgWVle!O5A%5rh$>r7fY>O)T3JTyn(-{<_{m4Lq^g0VDZ=$t~$&p_#ym( zYd53sV6b38ah>^!M_*wk!R(~;e_zI${LZa@3(p1}Z1Z8@(CD-FBgfUS(MgQDf~ebb z@A+<`ZxciFd63#v^)ymR1uphm8U@C`{xieuJVo9}@`{U)y5p(dh-ufOTK)yMcQ@Js zgABjqpQ996N7wu6QSo8x!5SY+DwOIr?oTU#-0ApDSWQEbV-YnKdP}Lv$uaJYr_XAz zMx96ntvOWO9|F{axj4#&X6S5XrCwrT7zkvwaj%InC%UNZ60}K{6vqrPe{}(*q{;S^ zEniw|Op&c{LCGl9HE4d#mDH5dp`YeMT-9UnAL`}jie^)u;t^!=Fmg51za-ZwhtjF5 z47wCx9gFH;^7Gzv+S9aVc}ELv4yFx3!UcTfxKxLeW&L#!3GfEWYBdH1LMpf4ja}Ei zNy{@;h)hkbX$Y+JlwtiIbZf*cBKaexRWk1$ENu zow*&=qFwSveIBYF9BdA$<;zK0jbAy*Fm}pENkvr6G^_aXuNt_qpkwNscqvo5?46KOV%T0T;5tEI@q+$k zmFn3-dpt`iR{Gmm5W4k5pkjh0GAkP(%LvAVz>i7 z%a8=C9Z1(zaHx(tX7xtpO9VU6UUr)G3z)C-?ZFb*f2pnLnKQ4o?SVoXi2fteGa0R+ zvLCH0T?f72LZ%U8Rjg;9b5d_kn3Ft%@86@ooFL~ZDG3OetB+H+B`D^#gH|40t=eR; z*Ara-7Ti|DYj}?<_$UQIWO7~I;uLzFNn;RwlEz-ua|(|&#Pk3Q51Gr?tsS1yXkRdB zx<22|U>LC^O|gUSP?rZlL%{Wi^hE}G(o>>%?r%0W1#SbR9=`us6YPO|DlQ9jo?TMZ zQ67xGJRxuX(K>_L+hxTR#Q0Hh*9wZB+^V}g3IqF=*ycATo zN6;h45U$-jLHx^(rm#(ai!>IoLS!7sA4)xfT>n1j7u6&WS6~sTG{H+6I4Vkd0L*X8 z>Q#6Nwk(=k8)EF*Mo8nKFZ`JVV=~VD6OEL6R(^tNH7iv1Mg_6Nn?=FrPH% z1#V^_!^b%n&>*V~((?*r#pDrv_q&*yAF@zP{AtLX8fb{_@Ews^T|b~Ojv_y}nA!n{ZhRpuk1|B+6y`8S^0 zEF3751h0KS7V4w5o3*kDPMGg$osF0+6TezQ?UZVMrsc+Xk$h*_WC?|c#`kh$00093 z25IygMCq!(sv+eaMH{I3aSLXm_*n~P<~jTAqjCT`ei3Rtu&|)iy~LJSR^Ba7tTnIr zSmi@Y`oLi1;CAM-rdQ1JBre66kK?8vzMc*77gjHSYnwQTn?RNGGQPPQID5TAeElH5 z;jaF%rs)l#&%poS8sSs4r`4t9XK&%6!DBD;Vis>Ns39?Cpe9@O zYqfxk%%{JgGl7u_pb*FN8q0p`gEQG(Rzsy){Yy(7@c;dP7G-ouLoXqh;r<_;0fB>= z8RYOTs9?v2zhVe~Wl^v%YE-`-!BOz&XX2i76h|rd^v*LqKt4d* z`5UyE4c5kioi8Gyj5T3pVTw;}3Yro8VAs`(Zm00AV6$+IQ9km`CkQuPB)wv#%t`FQ4H;l3Db z!8)Ot%c-Lh^1^_J-jz)95 zjXa_f)F04qWtV1fDwY8l9WNQbyydfsCV7C^KQHy%Jw$9v8vz~yc5}6N%oFdZ*S?h^ zu5R!q4er4z&a0U(d_ul{i#OV#Smjj>@!Aq=HQGB|WDv5Ev&Atl zL*L4MPJ&p`GaM!okhhqfDHIeZ5=%d>UcG({tx`X$npG%SS?6Z8xZ9!>nlAHFLaZNP zu5xwYaC9tuz!b(qvuT%09Q@{MxT&DHb?lg84E7hf(YG*8RrMklD^=q+03%3KHGfeVuz4y@YB+JDh^2$TRp2(>bk|qaSAMwd_gR*`YroUjBPk8=5P-mifZl?u;WkI@?mj3vM#{JhCT2I6%GCF6G$M< zmIn+6!)e%?bM(#3T!QL z8d*UBXIuWly&j7~@q7U0XPw2mtCAQLC>MIta1#g;1Ilt4qGez7J-I_3>F{UJoBFF-rz>^r`S6}YMZ?>-l9Vu5;b>C zgmi(NmHx~d6P7=svfx+8^Z2ilA8PEX@3kD{FGtT){y~M=(9^ZIgN&8zs?T?I>-l}h zn~n_sxM9F}?fh4jVcH|T^kZDQo@#K@jPQ~4nt6Q{^n*A@H6_Y#7=X&@14{?yyLVth zer0;hJ$-1y1|{xpv@!AGTx~o`Zu%pmA^G46-Jcyd4T)337I#9g4D$>LeS54gV(G9T z=(4?AJ}uY#l!lGcVG#ih$=P}wpV0GA-2xmffYys-<<7X;9F4k)_v$8LcB($5P)@_T zbhs8Ptf!N#Tgn?cWM{XYp7UT<&mvRkKxR-nWTKyOY%lZADKu z&2RF`<@+1u4IQl0L}2^<5o_FNy^rdLcD;d~H&{A%bz3kT-2al&p@Fr9Q6w$yK4xJ&m6>ybaF^vArX>GWT>4 ztHkNzaZ@_~Md}NvR!o^-(lFSYwibjMG=B>Ht{9#bm0)V>gsqPvxF6&~?#qJYJ_$*r zx54<*DtUV`D3`Ni_uXlZk{Hr;z3gD|_6U{k_qX=M&f7Pbcf(~(6gKb;tPm+`z8n5~ z%S~0{T-ovQ)U|F zdgtR}Sv@BD&|TYTYpmWxEQ$idl79-~~M^#Q0yVzeA{reTQ&hBQbbj}rY? zp(xG7QzAjX05xSH@qbLwsu@Nuzg|Zr(KHVtHbGXabWleLDOTK5;HZAol+Bz^`H|?6 z*jtPuQ(-xQ^xiSyMn=^#!%ukvav%43IOIC>C)IR@fPfGQ_97$Bd(2%=U6&7`F zJT>u8@R-oFL(ru3^`qOOKpnJi`~JO$HO4G<{HR;hDvw|~L7{YRq6v#YxW+1vv^GB( zAk0io@96^Suwr>=n{9#(Nh_mf<*}lK`WZ}>hbe{c2o(wgXAG7{NOC$8vzeD0nUgv5 zMPGcqujYzN1z3xWt_bQ6=q?)v4FX7xcS7c{{nCoRjwkl8F@X z3^5-zuQOP<*gJOiM`Wv9x_`p};f@Q7bM%7yjE)-}8sNlZ2m4Z#dL4Rdy}R1o+0LlO zxuZiLL)jZ%7)DNwYcS&A*~($N|PtNtq*ZLOZ&5Zvp5Z4 zXh^qg*ITdhn8!6!azTutt$+zW2I`y{J?lfH5w%Xv2cq$Z*apfNQ@?B29Py1?zre*H z>#ZA%J(vdIeTkNf4(LHQiFW1E+Z_H|!}6puby}Fqe(Lg-bBoAY^wvH?xi?5hP_!pH zW1Pp$;UOt(S-hks)Uu6-J~=g;VZ@0X3N3_TUXrqsbWN{stXy}>7M0(MG!?N|uYnD` z?|M*vz*{vv&cV#L7PaS70(2wypK_0P;rkNLN`uUfyUOK3E>CBuCFj3)qjDxL36AJC zhk&)w$ziprGHkfdVqj6~%up&Th}HN_e9#|ANYH}OqS5V6FT{)GhfictH zs8!NIGVZdnzJYo1-r@7Ek^w}<{2lky=iqO^^ZWqGDD5a^XhMlpKp+Gq z@j<%0&+5+ROUn%5aa+Sr=R=;fq%xFX-`uj2ds_jp?#tHz020m<)kDLnK7){m6r9*P zBZYF{jZWx! znBp`Mk}J;id3sQic*R}1QWaOuQ2t^qGGO?C0)3f^uT=D8|HHM-Z5_F&!O>-aMf*L2 znq+mZ%2F$I8Xp)o3TD@dEZ4*zqsBj^|M$Plj{vG3>I}M)3n4MXzu?dS{{m#&YcUjv zaQM%_u8NJys*FBG>9@16VE3 z%$Q(AEqDGj6dWHk~?Y;gTROlNx8<_Xx}}kVdc){@2xbR#=N$tC%&5 zxnRdku8a60Ho%pDHF$Lou-_l_plYOaS3uxov13}gJ!26e5mie2N6vDAy@0IO2WpnG zg>)g$Sq6$Ldq44iEtZU^lqr6Ozw6`;;0Aa2!|+GrF~{;bNL~Fxd#kD)So&H8*_Y#l zJcoz(vFJTE>wsNeeM9|P7D}nT!1x)Y{QvLZPF(gjU?)#5;iqAl?}9h3h~&;v8ou

Y~^{kJm0eX?! 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 diff --git a/doc/backlog/R188-console-group0-authority.md b/doc/backlog/R188-console-group0-authority.md index 6b6650239..ee422080d 100644 --- a/doc/backlog/R188-console-group0-authority.md +++ b/doc/backlog/R188-console-group0-authority.md @@ -68,12 +68,10 @@ not block R187 completion. 7. Audit the S3 mini-cluster's local `console.toml` and restart path under the same authority boundary. Retain only launch inputs and bootstrap seeds locally after Group 0 cutover; do not replay a local topology copy. -8. Migrate the verified bare-metal deployment and operations material from the - old `doc/user-manual/user-guide.md` into - `doc/user-manual/bare-metal-user-guide.md`, organized by KV cluster, chunk - layer, and data access servers. State that bare-metal is not yet - production-ready. Remove the old combined guide only after its supported - material and links are migrated; Docker documentation remains independent. +8. Migrate the verified bare-metal deployment and operations material into + dedicated bare-metal deployment documentation, organized by KV cluster, + chunk layer, and data access servers. State that bare-metal is not yet + production-ready. Keep Docker deployment documentation independent. 9. Complete container crash diagnostics without changing host-wide collector policy. Respect file-based core patterns, Ubuntu Apport, systemd-coredump and Docker Desktop's Linux VM; document where dumps actually go or why collection diff --git a/doc/backlog/R189-access-iceberg-container-ecosystem.md b/doc/backlog/R189-access-iceberg-container-ecosystem.md index 2cb721da9..53e52530c 100644 --- a/doc/backlog/R189-access-iceberg-container-ecosystem.md +++ b/doc/backlog/R189-access-iceberg-container-ecosystem.md @@ -63,8 +63,8 @@ is a normal S3 object. separately; Kafka infrastructure is not required for the Python/SQL/engine acceptance above. Exercise a simple BI query through a tested SQL engine; do not claim direct BI-tool or catalog compatibility without its own fixture. -6. Add only passing, reproducible recipes to - `doc/user-manual/docker-single-node-user-guide.md`. Maintain a client +6. Add only passing, reproducible recipes to the container deployment + documentation. Maintain a client capability matrix with tested versions, read/write scope, known exclusions, and links to executable fixtures. Label the image and all examples as development/test, not production data storage or upgrade-stable service. diff --git a/doc/design/access-server/iceberge/design-crowdb-iceberg.md b/doc/design/access-server/iceberge/design-crowdb-iceberg.md index 6af69c9f8..425980573 100644 --- a/doc/design/access-server/iceberge/design-crowdb-iceberg.md +++ b/doc/design/access-server/iceberge/design-crowdb-iceberg.md @@ -239,7 +239,7 @@ namespace-write capability and no catalog management or clear privilege; reader, manager and clearer do not inherit namespace-write rights. All four can read the configuration endpoint and namespaces. Only writer may invoke namespace or table mutations. Management commands are separate from the Iceberg REST listener. Operational -configuration is in the [user guide](../../../user-manual/user-guide.md#9-iceberg-catalog-foundation). +configuration follows the selected deployment profile and its startup inputs. Iceberg FileIO uses reserved S3-shaped locations so existing Iceberg clients can address immutable metadata and data files. The shape is a compatibility diff --git a/doc/design/config/design-crowdb-config.md b/doc/design/config/design-crowdb-config.md index e3fc8145e..58d339210 100644 --- a/doc/design/config/design-crowdb-config.md +++ b/doc/design/config/design-crowdb-config.md @@ -152,9 +152,8 @@ not produce an empty or cached topology. Hardware/process mutation is disabled, while authenticated logical operations use the existing operation paths. Only the container profile overrides public listeners to Iceberg 80, S3 81 and -Web 8080. Bare-metal defaults remain independent. See the -[single-node container guide](../../user-manual/docker-single-node-user-guide.md) -for publication, volume and endpoint usage. +Web 8080. Bare-metal defaults remain independent. Container publication, +volume and endpoint usage are defined by the container deployment files. ## 7. Failure Handling diff --git a/doc/doc_index.md b/doc/doc_index.md index 1fb23e13c..8c54e702c 100644 --- a/doc/doc_index.md +++ b/doc/doc_index.md @@ -21,8 +21,6 @@ listed document or section needed by the task. | `doc/design/console/design-crowdb-console.md` | Console architecture and service lifecycle. | | `doc/design/config/design-crowdb-config.md` | Configuration ownership, precedence, validation, reload. | | `doc/design/access-server/design-crowdb-access-server.md` | S3, Iceberg, native Dataset access, and GPU delivery. | -| `doc/user-manual/user-guide.md` | Web UI, CLI, REST API, setup, operations, upgrade. | -| `doc/user-manual/docker-single-node-user-guide.md` | Single-node Docker preview, credentials, clients, recovery. | ## Backlog (`doc/backlog/`) diff --git a/doc/user-manual/build_html.py b/doc/user-manual/build_html.py deleted file mode 100644 index 8bc48d3d9..000000000 --- a/doc/user-manual/build_html.py +++ /dev/null @@ -1,585 +0,0 @@ -#!/usr/bin/env python3 -"""Convert user-guide.md to a standalone HTML page with tabbed code sections. - -Usage: - python3 doc/user-manual/build_html.py [source.md] - -Output: - doc/user-manual/user-guide.html - -The script is self-contained (no third-party dependencies). It handles the -markdown subset used in the user guide: headings, fenced code blocks, bold -text, bullet lists, tables, horizontal rules, and inline code. - -Sections labelled "**CLI:**" and "**curl:**" followed by a fenced code block -are automatically wrapped in a tabbed interface so the reader can switch -between CLI and curl examples with a single click. -""" - -from __future__ import annotations - -import html -import re -import sys -from pathlib import Path - -SCRIPT_DIR = Path(__file__).resolve().parent -SOURCE = SCRIPT_DIR / "user-guide.md" - -CSS = """ -:root { - /* ── Hugging Face inspired palette ──────────────────────────────── */ - --bg: #ffffff; - --bg-subtle: #fafafa; - --fg: #111827; - --fg-default: #374151; - --fg-muted: #6b7280; - --sidebar-bg: #fafafa; - --sidebar-border: #e5e7eb; - --code-bg: #f3f4f6; - --code-border: #e5e7eb; - --border: #e5e7eb; - --border-strong: #d1d5db; - --accent: #ffd21e; - --accent-hover: #f59e0b; - --accent-soft: #fff4cc; - --accent-on: #111827; - --link: #155dfc; - --link-hover: #0b4ed6; - --tab-active-bg: #ffffff; - --tab-inactive-bg: #f3f4f6; - --tab-active-fg: #111827; - --tab-inactive-fg: #6b7280; -} -@media (prefers-color-scheme: dark) { - :root { - --bg: #0d1117; - --bg-subtle: #161b22; - --fg: #e6edf3; - --fg-default: #c9d1d9; - --fg-muted: #8b949e; - --sidebar-bg: #161b22; - --sidebar-border: #30363d; - --code-bg: #161b22; - --code-border: #30363d; - --border: #30363d; - --border-strong: #484f58; - --accent: #ffd21e; - --accent-hover: #f59e0b; - --accent-soft: #2a2410; - --accent-on: #0d1117; - --link: #58a6ff; - --link-hover: #79b8ff; - --tab-active-bg: #21262d; - --tab-inactive-bg: #161b22; - --tab-active-fg: #e6edf3; - --tab-inactive-fg: #8b949e; - } -} -* { box-sizing: border-box; margin: 0; padding: 0; } -body { - font-family: "Source Sans Pro", -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Roboto, sans-serif; - background: var(--bg-subtle); - color: var(--fg-default); - line-height: 1.65; - font-size: 15px; -} - -/* ── Layout ─────────────────────────────────────────────────────── */ -.layout { - display: flex; - min-height: 100vh; -} -.sidebar { - width: 280px; - flex-shrink: 0; - background: var(--sidebar-bg); - border-right: 1px solid var(--sidebar-border); - position: fixed; - top: 0; - left: 0; - bottom: 0; - overflow-y: auto; - padding: 1.5rem 0; -} -.content { - margin-left: 280px; - flex: 1; - padding: 2rem 3rem; - max-width: calc(100vw - 280px); - background: var(--bg); -} - -/* ── Sidebar ────────────────────────────────────────────────────── */ -.sidebar-brand { - padding: 0 1.5rem 1rem; - font-size: 1.05rem; - font-weight: 700; - color: var(--fg); - border-bottom: 1px solid var(--sidebar-border); - margin-bottom: 0.75rem; - letter-spacing: -0.01em; -} -.sidebar-brand .brand-mark { - display: inline-block; - width: 20px; - height: 20px; - background: var(--accent); - border-radius: 4px; - vertical-align: middle; - margin-right: 0.5rem; -} -.sidebar nav { padding: 0 0.5rem; } -.sidebar nav a { - display: block; - padding: 0.35rem 1rem; - color: var(--fg-muted); - text-decoration: none; - font-size: 0.875rem; - border-radius: 6px; - transition: all 0.12s; - line-height: 1.4; -} -.sidebar nav a:hover { - color: var(--link); - background: var(--code-bg); -} -.sidebar nav a.active { - color: var(--link); - font-weight: 600; - background: var(--code-bg); -} -.sidebar nav a.sub { padding-left: 2rem; font-size: 0.82rem; } - -/* ── Content ────────────────────────────────────────────────────── */ -.content h1 { - font-size: 1.9rem; - font-weight: 700; - color: var(--fg); - margin-bottom: 0.5rem; - padding-bottom: 0.5rem; - border-bottom: 1px solid var(--border); - letter-spacing: -0.02em; -} -.content h2 { - font-size: 1.4rem; - font-weight: 600; - color: var(--fg); - margin-top: 2.5rem; - margin-bottom: 0.75rem; - padding-bottom: 0.3rem; - border-bottom: 1px solid var(--border); - scroll-margin-top: 1rem; - letter-spacing: -0.01em; -} -.content h3 { - font-size: 1.15rem; - font-weight: 600; - color: var(--fg); - margin-top: 1.75rem; - margin-bottom: 0.5rem; - scroll-margin-top: 1rem; -} -.content p { margin: 0.6rem 0; } -.content a { color: var(--link); text-decoration: none; } -.content a:hover { color: var(--link-hover); text-decoration: underline; } -.content ul { padding-left: 1.5rem; margin: 0.6rem 0; } -.content li { margin: 0.3rem 0; } -.content hr { border: none; border-top: 1px solid var(--border); margin: 2rem 0; } - -/* ── Inline code ────────────────────────────────────────────────── */ -code { - font-family: "IBM Plex Mono", "SF Mono", "Fira Code", "Consolas", monospace; - font-size: 0.85em; - background: var(--code-bg); - padding: 0.15em 0.4em; - border-radius: 4px; - border: 1px solid var(--code-border); -} - -/* ── Code blocks ────────────────────────────────────────────────── */ -pre { - background: var(--code-bg); - border: 1px solid var(--code-border); - border-radius: 6px; - padding: 0.85rem 1rem; - overflow-x: auto; - font-size: 0.84rem; - line-height: 1.5; -} -pre code { background: none; border: none; padding: 0; font-size: inherit; } - -/* ── Tables ─────────────────────────────────────────────────────── */ -table { - border-collapse: collapse; - width: 100%; - margin: 1rem 0; - font-size: 0.88rem; -} -th, td { - border: 1px solid var(--border); - padding: 0.5rem 0.75rem; - text-align: left; -} -th { - background: var(--code-bg); - font-weight: 600; - color: var(--fg); -} -tr:hover td { background: var(--code-bg); } - -/* ── Tabbed code ────────────────────────────────────────────────── */ -.tab-group { - margin: 1rem 0; - border-radius: 6px; - overflow: hidden; - border: 1px solid var(--border); -} -.tab-bar { - display: flex; - background: var(--tab-inactive-bg); - border-bottom: 1px solid var(--border); -} -.tab-btn { - padding: 0.45rem 1.1rem; - border: none; - background: transparent; - cursor: pointer; - font-size: 0.8rem; - font-weight: 500; - color: var(--tab-inactive-fg); - border-bottom: 2px solid transparent; - transition: all 0.12s; - font-family: inherit; -} -.tab-btn:hover { color: var(--fg); background: var(--tab-active-bg); } -.tab-btn.active { - color: var(--tab-active-fg); - background: var(--tab-active-bg); - border-bottom-color: var(--accent); - font-weight: 600; -} -.tab-panel { display: none; } -.tab-panel.active { display: block; } -.tab-panel-content { padding: 1rem; } -.tab-panel-content table { margin: 0.75rem 0; } -.tab-panel-content h4 { margin-top: 1.25rem; } -.tab-panel-content h4:first-child { margin-top: 0; } -.tab-panel pre { - border-radius: 0 0 6px 6px; - margin: 0; - border: none; - border-top: none; -} - -/* ── Mobile ─────────────────────────────────────────────────────── */ -@media (max-width: 768px) { - .sidebar { - position: static; - width: 100%; - border-right: none; - border-bottom: 1px solid var(--sidebar-border); - max-height: 300px; - } - .content { - margin-left: 0; - max-width: 100%; - padding: 1.5rem; - } - .layout { flex-direction: column; } -} -""" - -JS = """ -// Tab switching -document.querySelectorAll('.tab-group').forEach(function(group) { - var btns = group.querySelectorAll('.tab-btn'); - btns.forEach(function(btn) { - btn.addEventListener('click', function() { - var idx = btn.getAttribute('data-tab'); - btns.forEach(function(b) { b.classList.remove('active'); }); - group.querySelectorAll('.tab-panel').forEach(function(p) { - p.classList.toggle('active', p.getAttribute('data-panel') === idx); - }); - btn.classList.add('active'); - }); - }); -}); - -// Sidebar active section tracking -var navLinks = document.querySelectorAll('.sidebar nav a'); -var sections = []; -navLinks.forEach(function(link) { - var id = link.getAttribute('href'); - if (id && id.startsWith('#')) { - var el = document.getElementById(id.slice(1)); - if (el) sections.push({ id: id, el: el, link: link }); - } -}); - -function updateActive() { - var scrollY = window.scrollY + 100; - var current = sections[0]; - for (var i = 0; i < sections.length; i++) { - if (sections[i].el.offsetTop <= scrollY) current = sections[i]; - } - navLinks.forEach(function(l) { l.classList.remove('active'); }); - if (current) current.link.classList.add('active'); -} -window.addEventListener('scroll', updateActive, { passive: true }); -updateActive(); -""" - - -def escape(text: str) -> str: - return html.escape(text, quote=False) - - -def inline_format(text: str) -> str: - text = escape(text) - text = re.sub(r"`([^`]+)`", r"\1", text) - text = re.sub(r"\*\*([^*]+)\*\*", r"\1", text) - text = re.sub(r'\[([^\]]+)\]\((https?://[^\s)"<>]+)\)', r'\1', text) - return text - - -def convert_table(lines: list[str]) -> str: - rows = [l.strip().strip("|").split("|") for l in lines if l.strip().startswith("|")] - if len(rows) < 2: - return "".join(f"

{inline_format(l)}

" for l in lines) - header = [c.strip() for c in rows[0]] - body = [[c.strip() for c in r] for r in rows[2:]] if len(rows) > 2 else [] - out = "\n\n" - for h in header: - out += f"" - out += "\n\n\n" - for row in body: - out += "" - for cell in row: - out += f"" - out += "\n" - out += "\n
{inline_format(h)}
{inline_format(cell)}
\n" - return out - - -def slugify(text: str) -> str: - s = re.sub(r"[^\w\s-]", "", text.lower()) - return re.sub(r"[\s_-]+", "-", s).strip("-") - - -def convert_markdown(md: str) -> tuple[str, list[dict]]: - """Convert markdown to HTML body, return (html, toc_entries).""" - lines = md.split("\n") - i = 0 - out: list[str] = [] - toc: list[dict] = [] - in_list = False - - def close_list(): - nonlocal in_list - if in_list: - out.append("\n") - in_list = False - - while i < len(lines): - line = lines[i] - - # Skip HTML comments - if line.strip().startswith("" not in lines[i]: - i += 1 - i += 1 - continue - - # Fenced code block (standalone, not part of a tab group) - if line.strip().startswith("```"): - lang = line.strip().strip("`").strip() - code_lines: list[str] = [] - i += 1 - while i < len(lines) and not lines[i].strip().startswith("```"): - code_lines.append(lines[i]) - i += 1 - i += 1 - close_list() - code = escape("\n".join(code_lines)) - out.append(f'
{code}
\n') - continue - - # Tab pattern: **CLI:** / **curl:** / **Web UI:** followed by code block or other content - if re.match(r"^\*\*(CLI|curl|Web UI):\*\*\s*$", line.strip()): - tabs: list[tuple[str, str, bool]] = [] # (label, content, is_code) - while i < len(lines): - while i < len(lines) and lines[i].strip() == "": - i += 1 - if i >= len(lines): - break - m = re.match(r"^\*\*(CLI|curl|Web UI):\*\*\s*$", lines[i].strip()) - if not m: - break - label = m.group(1) - if label == "CLI": - label = "cli" - i += 1 - while i < len(lines) and lines[i].strip() == "": - i += 1 - if i < len(lines) and lines[i].strip().startswith("```"): - # Code-block tab (existing behaviour) - i += 1 - cl: list[str] = [] - while i < len(lines) and not lines[i].strip().startswith("```"): - cl.append(lines[i]) - i += 1 - i += 1 - tabs.append((label, escape("\n".join(cl)), True)) - else: - # Raw-markdown tab — capture lines until next tab marker, - # a horizontal rule, a new ## heading, or EOF. - raw_lines: list[str] = [] - while i < len(lines): - if re.match(r"^\*\*(CLI|curl|Web UI):\*\*\s*$", lines[i].strip()): - break - if lines[i].strip() == "---": - break - if re.match(r"^##\s+", lines[i]): - break - raw_lines.append(lines[i]) - i += 1 - tabs.append((label, "\n".join(raw_lines), False)) - - if tabs: - close_list() - out.append('
\n
\n') - for idx, (label, _, _) in enumerate(tabs): - active = " active" if idx == 0 else "" - out.append(f'\n') - out.append("
\n") - for idx, (_, content, is_code) in enumerate(tabs): - active = " active" if idx == 0 else "" - if is_code: - out.append(f'
{content}
\n') - else: - raw_html, _ = convert_markdown(content) - out.append(f'
{raw_html}
\n') - out.append("
\n") - continue - - # Headings - m = re.match(r"^(#{1,4})\s+(.*)", line) - if m: - close_list() - level = len(m.group(1)) - text_raw = m.group(2) - text = inline_format(text_raw) - slug = slugify(text_raw) - if level <= 2: - toc.append({"level": level, "text": text_raw, "slug": slug}) - out.append(f'{text}\n') - i += 1 - continue - - # Horizontal rule - if line.strip() == "---": - close_list() - out.append("
\n") - i += 1 - continue - - # Table - if line.strip().startswith("|"): - close_list() - table_lines: list[str] = [] - while i < len(lines) and lines[i].strip().startswith("|"): - table_lines.append(lines[i]) - i += 1 - out.append(convert_table(table_lines)) - continue - - # Bullet list - if re.match(r"^\s*[-*]\s+", line): - if not in_list: - out.append("
    \n") - in_list = True - text = inline_format(re.sub(r"^\s*[-*]\s+", "", line)) - out.append(f"
  • {text}
  • \n") - i += 1 - continue - - # Numbered list - if re.match(r"^\s*\d+\.\s+", line): - close_list() - text = inline_format(re.sub(r"^\s*\d+\.\s+", "", line)) - out.append(f"
  • {text}
  • \n") - i += 1 - continue - - # Blank line - if line.strip() == "": - close_list() - i += 1 - continue - - # Regular paragraph - close_list() - text = inline_format(line) - out.append(f"

    {text}

    \n") - i += 1 - - close_list() - return "".join(out), toc - - -def build_sidebar(toc: list[dict]) -> str: - lines = ['', "") - return "\n".join(lines) - - -def build(source: Path = SOURCE) -> None: - if not source.exists(): - print(f"error: {source} not found", file=sys.stderr) - sys.exit(1) - - md = source.read_text(encoding="utf-8") - output = source.with_suffix(".html") - title_match = re.search(r"^# (.+)$", md, re.MULTILINE) - title = escape(title_match.group(1) if title_match else "CROWDB User Guide") - body, toc = convert_markdown(md) - sidebar = build_sidebar(toc) - - doc = f""" - - - - -{title} - - - -
    - -
    -{body} -
    -
    - - - -""" - - output.write_text(doc, encoding="utf-8") - print(f"wrote {output} ({len(doc)} bytes)") - - -if __name__ == "__main__": - build(Path(sys.argv[1]) if len(sys.argv) > 1 else SOURCE) diff --git a/doc/user-manual/docker-single-node-user-guide.html b/doc/user-manual/docker-single-node-user-guide.html deleted file mode 100644 index 82888597d..000000000 --- a/doc/user-manual/docker-single-node-user-guide.html +++ /dev/null @@ -1,486 +0,0 @@ - - - - - -CROWDB Single-Node Container Guide - - - -
    - -
    -

    CROWDB Single-Node Container Guide

    -

    Run an Iceberg REST catalog and its storage on one Linux amd64 host.

    -

    For evaluation only; no production or upgrade guarantee.

    -

    Quick start

    -
    docker run -d --name crowdb-iceberg \
    -  -p 127.0.0.1:80:80 \
    -  crowdb/crowdb-iceberg:v0.1.0-dev
    -

    The image repository is crowdb/crowdb-iceberg; examples use the version tag

    -

    v0.1.0-dev. Docker pulls the image if it is not present locally. Docker creates

    -

    an anonymous volume for the data.

    -

    Check startup, then retrieve your client credentials:

    -
    docker inspect --format '{{.State.Health.Status}}' crowdb-iceberg
    -docker exec crowdb-iceberg crowdb-monitor credentials show --format env
    -

    Wait for healthy. Connect your Iceberg client to http://localhost using

    -

    ICEBERG_TOKEN from the credential output. Port 80 serves both the REST catalog

    -

    and Iceberg FileIO; no separate S3 port is needed for Iceberg.

    -

    Stop and start the same container without losing its data:

    -
    docker stop --time 120 crowdb-iceberg
    -docker start crowdb-iceberg
    -

    The quick start exposes only Iceberg to the host. The GUI is not ready for use

    -

    and is not published. For data you want to reuse after deleting and recreating

    -

    the container, use a named volume as shown below.

    -

    Common options

    -
      -
    • -d: run in the background.
    • -
    • --name crowdb-iceberg: give the container a convenient name for later commands.
    • -
    • -p 127.0.0.1:80:80: publish Iceberg on host loopback. Fixed host ports require
    • -
    -

    explicit mapping; the image cannot publish them automatically.

    -
      -
    • -v crowdb-data:/opt/crowdb/data: keep data in a named volume. Docker creates
    • -
    -

    it if needed; reuse it with the same image when recreating the container.

    -
      -
    • --restart unless-stopped: automatically restart after failure or Docker
    • -
    -

    daemon restart, unless you explicitly stopped the container.

    -
      -
    • --stop-timeout 120: allow 120 seconds for shutdown before Docker sends SIGKILL.
    • -
    • --log-driver json-file --log-opt max-size=30m --log-opt max-file=5:
    • -
    -

    rotate Docker's captured logs separately from CROWDB's internal logs.

    -

    Example: persistent data and automatic restart

    -

    Use this instead of the quick-start command:

    -
    docker run -d --name crowdb-iceberg \
    -  -p 127.0.0.1:80:80 \
    -  -v crowdb-data:/opt/crowdb/data \
    -  --restart unless-stopped --stop-timeout 120 \
    -  --log-driver json-file --log-opt max-size=30m --log-opt max-file=5 \
    -  crowdb/crowdb-iceberg:v0.1.0-dev
    -

    Do not attach the same data volume to two running containers. A bind mount can

    -

    replace the named volume, but its directory must be writable by UID/GID 10001.

    -

    Connect clients

    -

    The credential command prints ICEBERG_URI and ICEBERG_TOKEN, plus credentials

    -

    for the optional independent S3 service. Keep this output private. Credentials

    -

    remain the same when the data volume is reused.

    -

    Iceberg with PyIceberg

    -

    Set ICEBERG_URI and ICEBERG_TOKEN in your client environment using the values

    -

    from the credential command. With PyIceberg installed:

    -
    import os
    -from pyiceberg.catalog import load_catalog
    -
    -catalog = load_catalog(
    -    "crowdb",
    -    type="rest",
    -    uri=os.environ["ICEBERG_URI"],
    -    token=os.environ["ICEBERG_TOKEN"],
    -)
    -catalog.create_namespace_if_not_exists("demo")
    -print(catalog.list_namespaces())
    -

    Optional independent S3 access

    -

    Add -p 127.0.0.1:81:81 when creating the container if you also need the general

    -

    S3 API. Connect to http://localhost:81 with path-style addressing, region

    -

    us-east-1, and the printed AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY.

    -

    Uploading an S3 object does not register an Iceberg table.

    -

    The examples provide local HTTP access. Changing host ports or using a remote

    -

    client also requires reachable Iceberg FileIO URLs; changing only the client's

    -

    catalog URI is insufficient. The container's ports are independent of bare-metal

    -

    service defaults.

    -

    Troubleshooting

    -
    docker logs --tail 100 crowdb-iceberg
    -docker exec crowdb-iceberg crowdb-monitor liveness
    -docker exec crowdb-iceberg crowdb-monitor readiness
    -
      -
    • starting: initialization or recovery is still running; inspect the logs.
    • -
    • unhealthy: a required service is unavailable. Liveness checks the monitor;
    • -
    -

    readiness checks the complete deployment. Successful probes exit zero.

    -
      -
    • exited: inspect logs before restarting. Repeated child failures exhaust the
    • -
    -

    restart budget. Docker's restart policy handles container exits; an unhealthy

    -

    health check alone does not restart the container.

    -
      -
    • Port already allocated: another process uses host port 80. Free the port or
    • -
    -

    configure a different host mapping and reachable client/FileIO endpoints.

    -
      -
    • Existing volume rejected: use the original image and credentials. Do not edit
    • -
    -

    bootstrap manifests or secrets to bypass compatibility checks.

    -

    CROWDB logs are under /opt/crowdb/data/log. Monitor events are in

    -

    monitor/monitor.log; child logs have service-specific directories. Log rotation

    -

    uses a 30 MiB target and five retained files per channel across child restarts.

    -

    Docker's own log retention is configured separately, as in the extended example.

    -

    Data, backup and limitations

    -
      -
    • The data volume contains storage, metadata, credentials and logs. Anonymous
    • -
    -

    volumes are not automatically reused by a newly created container; record the

    -

    volume identity before deleting the original container.

    -
      -
    • Stop the container before copying the entire volume. Preserve ownership,
    • -
    -

    private permissions and sparse files. Restore with the exact image version;

    -

    copying only disk images is insufficient.

    -
      -
    • All services and four sparse 16 GiB disk images share one host. This provides
    • -
    -

    no host fault tolerance. Monitor actual filesystem space; configured disk

    -

    capacity is not a usable-capacity guarantee.

    -
      -
    • Deleted Iceberg content can continue occupying space. Physical reclamation
    • -
    -

    is disabled in this profile. Cross-version volume migration is not promised.

    -
      -
    • PyIceberg namespace/table operations and boto3 object operations have container
    • -
    -

    acceptance coverage. Spark, Flink, Trino, dataframe workflows and selected ORC

    -

    data are not certified by those checks.

    -

    Advanced crash diagnostics

    -

    Core dumps can contain credentials and user data. Linux host kernel.core_pattern

    -

    controls their destination; the image does not change it. A Docker core ulimit

    -

    alone does not guarantee a dump.

    -
      -
    • File patterns use the process's filesystem namespace; relative paths use its
    • -
    -

    working directory. The destination must be writable.

    -
      -
    • Ubuntu Apport can reject container crashes when its container forwarding
    • -
    -

    support is absent. This image does not include an Apport agent; do not assume

    -

    a report will appear under the host's /var/crash.

    -
      -
    • systemd-coredump uses the host journal and usually /var/lib/systemd/coredump;
    • -
    -

    inspect with coredumpctl on the host.

    -
      -
    • Docker Desktop uses its Linux VM's collector policy.
    • -
    -

    The image retains function symbols but currently has no DWARF source-line

    -

    information. Do not assume that the data volume contains usable core dumps.

    -

    See the Linux core manual

    -

    for host collection rules.

    - -
    -
    - - - diff --git a/doc/user-manual/docker-single-node-user-guide.md b/doc/user-manual/docker-single-node-user-guide.md deleted file mode 100644 index e49c09e7f..000000000 --- a/doc/user-manual/docker-single-node-user-guide.md +++ /dev/null @@ -1,169 +0,0 @@ - - - -# CROWDB Single-Node Container Guide - -Run an Iceberg REST catalog and its storage on one Linux amd64 host. -For evaluation only; no production or upgrade guarantee. - -## Quick start - -```sh -docker run -d --name crowdb-iceberg \ - -p 127.0.0.1:80:80 \ - crowdb/crowdb-iceberg:v0.1.0-dev -``` - -The image repository is `crowdb/crowdb-iceberg`; examples use the version tag -`v0.1.0-dev`. Docker pulls the image if it is not present locally. Docker creates -an anonymous volume for the data. - -Check startup, then retrieve your client credentials: - -```sh -docker inspect --format '{{.State.Health.Status}}' crowdb-iceberg -docker exec crowdb-iceberg crowdb-monitor credentials show --format env -``` - -Wait for `healthy`. Connect your Iceberg client to `http://localhost` using -`ICEBERG_TOKEN` from the credential output. Port 80 serves both the REST catalog -and Iceberg FileIO; no separate S3 port is needed for Iceberg. - -Stop and start the same container without losing its data: - -```sh -docker stop --time 120 crowdb-iceberg -docker start crowdb-iceberg -``` - -The quick start exposes only Iceberg to the host. The GUI is not ready for use -and is not published. For data you want to reuse after deleting and recreating -the container, use a named volume as shown below. - -## Common options - -- `-d`: run in the background. -- `--name crowdb-iceberg`: give the container a convenient name for later commands. -- `-p 127.0.0.1:80:80`: publish Iceberg on host loopback. Fixed host ports require - explicit mapping; the image cannot publish them automatically. -- `-v crowdb-data:/opt/crowdb/data`: keep data in a named volume. Docker creates - it if needed; reuse it with the same image when recreating the container. -- `--restart unless-stopped`: automatically restart after failure or Docker - daemon restart, unless you explicitly stopped the container. -- `--stop-timeout 120`: allow 120 seconds for shutdown before Docker sends SIGKILL. -- `--log-driver json-file --log-opt max-size=30m --log-opt max-file=5`: - rotate Docker's captured logs separately from CROWDB's internal logs. - -### Example: persistent data and automatic restart - -Use this instead of the quick-start command: - -```sh -docker run -d --name crowdb-iceberg \ - -p 127.0.0.1:80:80 \ - -v crowdb-data:/opt/crowdb/data \ - --restart unless-stopped --stop-timeout 120 \ - --log-driver json-file --log-opt max-size=30m --log-opt max-file=5 \ - crowdb/crowdb-iceberg:v0.1.0-dev -``` - -Do not attach the same data volume to two running containers. A bind mount can -replace the named volume, but its directory must be writable by UID/GID 10001. - -## Connect clients - -The credential command prints `ICEBERG_URI` and `ICEBERG_TOKEN`, plus credentials -for the optional independent S3 service. Keep this output private. Credentials -remain the same when the data volume is reused. - -### Iceberg with PyIceberg - -Set `ICEBERG_URI` and `ICEBERG_TOKEN` in your client environment using the values -from the credential command. With PyIceberg installed: - -```python -import os -from pyiceberg.catalog import load_catalog - -catalog = load_catalog( - "crowdb", - type="rest", - uri=os.environ["ICEBERG_URI"], - token=os.environ["ICEBERG_TOKEN"], -) -catalog.create_namespace_if_not_exists("demo") -print(catalog.list_namespaces()) -``` - -### Optional independent S3 access - -Add `-p 127.0.0.1:81:81` when creating the container if you also need the general -S3 API. Connect to `http://localhost:81` with path-style addressing, region -`us-east-1`, and the printed `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY`. -Uploading an S3 object does not register an Iceberg table. - -The examples provide local HTTP access. Changing host ports or using a remote -client also requires reachable Iceberg FileIO URLs; changing only the client's -catalog URI is insufficient. The container's ports are independent of bare-metal -service defaults. - -## Troubleshooting - -```sh -docker logs --tail 100 crowdb-iceberg -docker exec crowdb-iceberg crowdb-monitor liveness -docker exec crowdb-iceberg crowdb-monitor readiness -``` - -- `starting`: initialization or recovery is still running; inspect the logs. -- `unhealthy`: a required service is unavailable. Liveness checks the monitor; - readiness checks the complete deployment. Successful probes exit zero. -- `exited`: inspect logs before restarting. Repeated child failures exhaust the - restart budget. Docker's restart policy handles container exits; an unhealthy - health check alone does not restart the container. -- Port already allocated: another process uses host port 80. Free the port or - configure a different host mapping and reachable client/FileIO endpoints. -- Existing volume rejected: use the original image and credentials. Do not edit - bootstrap manifests or secrets to bypass compatibility checks. - -CROWDB logs are under `/opt/crowdb/data/log`. Monitor events are in -`monitor/monitor.log`; child logs have service-specific directories. Log rotation -uses a 30 MiB target and five retained files per channel across child restarts. -Docker's own log retention is configured separately, as in the extended example. - -## Data, backup and limitations - -- The data volume contains storage, metadata, credentials and logs. Anonymous - volumes are not automatically reused by a newly created container; record the - volume identity before deleting the original container. -- Stop the container before copying the entire volume. Preserve ownership, - private permissions and sparse files. Restore with the exact image version; - copying only disk images is insufficient. -- All services and four sparse 16 GiB disk images share one host. This provides - no host fault tolerance. Monitor actual filesystem space; configured disk - capacity is not a usable-capacity guarantee. -- Deleted Iceberg content can continue occupying space. Physical reclamation - is disabled in this profile. Cross-version volume migration is not promised. -- PyIceberg namespace/table operations and boto3 object operations have container - acceptance coverage. Spark, Flink, Trino, dataframe workflows and selected ORC - data are not certified by those checks. - -### Advanced crash diagnostics - -Core dumps can contain credentials and user data. Linux host `kernel.core_pattern` -controls their destination; the image does not change it. A Docker core ulimit -alone does not guarantee a dump. - -- File patterns use the process's filesystem namespace; relative paths use its - working directory. The destination must be writable. -- Ubuntu Apport can reject container crashes when its container forwarding - support is absent. This image does not include an Apport agent; do not assume - a report will appear under the host's `/var/crash`. -- systemd-coredump uses the host journal and usually `/var/lib/systemd/coredump`; - inspect with `coredumpctl` on the host. -- Docker Desktop uses its Linux VM's collector policy. - -The image retains function symbols but currently has no DWARF source-line -information. Do not assume that the data volume contains usable core dumps. -See the [Linux core manual](https://www.man7.org/linux/man-pages/man5/core.5.html) -for host collection rules. diff --git a/doc/user-manual/user-guide.html b/doc/user-manual/user-guide.html deleted file mode 100644 index 8e018cae5..000000000 --- a/doc/user-manual/user-guide.html +++ /dev/null @@ -1,1124 +0,0 @@ - - - - - -CrowdbKV User Guide - - - -
    - -
    -

    CROWDB User Guide

    -

    CROWDB is a distributed storage platform with an S3-compatible object data

    -

    plane and a multi-group Multi-Paxos key-value foundation. This guide starts

    -

    with the shortest usable path: create a persistent local S3 cluster and use

    -

    bucket and object operations. The later sections cover the underlying KV

    -

    cluster, physical topology, individual servers, upgrades, and recovery.

    -

    CROWDB provides three user-facing interfaces:

    -
      -
    • Web UI — the crowdb-web service provides a visual dashboard
    • -
    -

    with cluster topology, group health, a KV Operator panel (store/group

    -

    selector, paginated scan, inline CRUD, demo data injection), and

    -

    Swagger UI for browsing the OpenAPI spec of any registered

    -

    crowdb-kv-server instance.

    -
      -
    • CLI — crowdb-cli s3 owns the local S3 cluster lifecycle and sends
    • -
    -

    bucket/object requests directly to the cluster recorded by --root.

    -

    Lower-level management commands discover services through group 0 and call

    -

    them directly. Output is a human-readable console transcript.

    -
      -
    • HTTP APIs — the local access server exposes the S3 HTTP API. The
    • -
    -

    console service exposes the lower-level cluster management API documented

    -

    in §8.

    -

    S3 examples use the loopback access endpoint. The first local cluster normally

    -

    receives 127.0.0.1:16000; when that port is already assigned, use the

    -

    endpoint printed by s3 cluster start or `s3 cluster status.

    -
    S3_ENDPOINT=http://127.0.0.1:16000
    -

    Prerequisites

    -

    Before following the steps below:

    -
      -
    • Run pixi run build from the repository root.
    • -
    • Add target/release to PATH, or invoke the binaries by their full paths.
    • -
    • Choose a dedicated cluster directory. The directory is the persistent
    • -
    -

    cluster identity and contains configuration, logs, and data files.

    -
    -

    1. Quick Start: Persistent Local S3

    -

    1.1 Start or restart the cluster

    -

    Choose a directory and start the cluster:

    -
    S3_ROOT="$PWD/.crowdb-runtime/persistent/s3-local"
    -crowdb-cli s3 cluster start --root "$S3_ROOT"
    -

    An absent or empty directory creates a new file-backed cluster. A recognized

    -

    cluster directory restarts the same cluster with its existing data and port

    -

    assignments. A non-empty directory that is not a CROWDB cluster is rejected

    -

    without modification.

    -

    The command prints the S3 endpoint, Web management URL, running service count,

    -

    and cluster root. The default local endpoints are:

    -
    S3_ENDPOINT=http://127.0.0.1:16000
    -WEB_URL=http://127.0.0.1:14000
    -

    If the command prints different endpoints because a default port is already

    -

    assigned, use the printed values. Open WEB_URL for the topology, service, and

    -

    cluster management console.

    -

    Inspect the recorded processes without changing them:

    -
    crowdb-cli s3 cluster status --root "$S3_ROOT"
    -

    The CLI owns S3 mini-cluster lifecycle. The bundled Web service loads that

    -

    mini-cluster's console registry and presents its running services.

    -

    1.2 Bucket operations

    -

    Create a bucket:

    -
    crowdb-cli s3 bucket put --root "$S3_ROOT" photos
    -curl -X PUT "$S3_ENDPOINT/photos"
    -

    List buckets:

    -
    crowdb-cli s3 bucket list --root "$S3_ROOT"
    -curl "$S3_ENDPOINT/"
    -

    GET one bucket and show its XML result:

    -
    crowdb-cli s3 bucket get --root "$S3_ROOT" photos
    -curl "$S3_ENDPOINT/photos"
    -

    Remove an empty bucket:

    -
    crowdb-cli s3 bucket delete --root "$S3_ROOT" photos
    -curl -X DELETE "$S3_ENDPOINT/photos"
    -

    Removing a non-empty bucket returns the S3 error and leaves its objects intact.

    -

    1.3 Object CRUD

    -

    Create the bucket used by the following examples, then upload an object from a

    -

    file, literal text, generated random bytes, or standard input:

    -
    crowdb-cli s3 bucket put --root "$S3_ROOT" documents
    -
    -crowdb-cli s3 object put --root "$S3_ROOT" \
    -  documents reports/hello.txt --file ./hello.txt
    -
    -crowdb-cli s3 object put --root "$S3_ROOT" \
    -  documents reports/text.txt --text 'object content'
    -
    -crowdb-cli s3 object put --root "$S3_ROOT" \
    -  documents reports/random.bin --random-size 1048576
    -
    -printf 'hello from CROWDB\n' | crowdb-cli s3 object put \
    -  --root "$S3_ROOT" documents reports/stdin.txt
    -
    -curl -X PUT --data-binary @hello.txt \
    -  "$S3_ENDPOINT/documents/reports/hello.txt"
    -

    put creates a new object or replaces the bytes of an existing key.

    -

    Read an object to standard output or a file:

    -
    crowdb-cli s3 object get --root "$S3_ROOT" \
    -  documents reports/hello.txt
    -
    -crowdb-cli s3 object get --root "$S3_ROOT" \
    -  documents reports/hello.txt --output ./downloaded.txt
    -
    -curl "$S3_ENDPOINT/documents/reports/hello.txt" \
    -  --output ./downloaded-with-curl.txt
    -

    Check that an object exists:

    -
    crowdb-cli s3 object head --root "$S3_ROOT" \
    -  documents reports/hello.txt
    -
    -curl -I "$S3_ENDPOINT/documents/reports/hello.txt"
    -

    Delete an object:

    -
    crowdb-cli s3 object delete --root "$S3_ROOT" \
    -  documents reports/hello.txt
    -
    -curl -X DELETE "$S3_ENDPOINT/documents/reports/hello.txt"
    -

    1.4 List objects

    -

    List the first 100 keys below a prefix:

    -
    crowdb-cli s3 object list --root "$S3_ROOT" documents \
    -  --prefix reports/ --limit 100
    -
    -curl --get "$S3_ENDPOINT/documents" \
    -  --data-urlencode 'list-type=2' \
    -  --data-urlencode 'prefix=reports/' \
    -  --data-urlencode 'max-keys=100'
    -

    When a response is truncated, pass its opaque continuation token unchanged:

    -
    crowdb-cli s3 object list --root "$S3_ROOT" documents \
    -  --prefix reports/ --limit 100 --continuation "$TOKEN"
    -
    -curl --get "$S3_ENDPOINT/documents" \
    -  --data-urlencode 'list-type=2' \
    -  --data-urlencode 'prefix=reports/' \
    -  --data-urlencode 'max-keys=100' \
    -  --data-urlencode "continuation-token=$TOKEN"
    -

    1.5 Read a byte range

    -

    Ranges are inclusive. --range 3-9 returns seven bytes:

    -
    crowdb-cli s3 object get --root "$S3_ROOT" \
    -  documents reports/hello.txt --range 3-9
    -
    -curl -H 'Range: bytes=3-9' \
    -  "$S3_ENDPOINT/documents/reports/hello.txt"
    -

    1.6 Stop, restart, and delete

    -

    Stop every process while preserving the cluster directory and stored objects:

    -
    crowdb-cli s3 cluster stop --root "$S3_ROOT"
    -

    Restart from the same directory and read the same data:

    -
    crowdb-cli s3 cluster start --root "$S3_ROOT"
    -crowdb-cli s3 object get --root "$S3_ROOT" \
    -  documents reports/hello.txt
    -

    Permanently stop the cluster, release its port assignments, and remove its

    -

    directory:

    -
    crowdb-cli s3 cluster delete --root "$S3_ROOT"
    -

    delete is destructive. Use stop when the cluster must be started again.

    -

    Cluster lifecycle does not currently have an HTTP management endpoint.

    -
    -

    2. Advanced: Bootstrap a KV Cluster

    -

    The remaining sections describe lower-level cluster and server administration.

    -

    They are not required for the local S3 workflow above.

    -

    Management CLI commands omit --system-ip and --system-port for brevity.

    -

    They default to the system-group discovery endpoint 127.0.0.1:10000; either

    -

    flag may point to any system-group node because leader discovery is automatic.

    -

    The CROWDB_SYSTEM_IP/CROWDB_SYSTEM_PORT environment variables provide the

    -

    same overrides. The following console HTTP curl examples assume:

    -
    IP=127.0.0.1
    -PORT=14000
    -

    An S3 mini-cluster already starts crowdb-web with its persisted registry.

    -

    For a separately managed cluster, before using these commands:

    -
      -
    • Start crowdb-web with crowdb-web --port 14000. Add --test-mode for an
    • -
    -

    in-memory console configuration that is lost on restart.

    -
      -
    • Set CROWDB_KV_SERVER_BIN when crowdb-kv-server is not next to
    • -
    -

    crowdb-web and not available through PATH.

    -
      -
    • Use each machine's reachable hostname or IP instead of 127.0.0.1 for a
    • -
    -

    multi-machine deployment.

    -

    Server configuration files

    -

    KV server, diskdb, chunkdb, and diskio use TOML startup configuration. Valid

    -

    templates are shipped in each server's conf/ directory. Values resolve in

    -

    this order: compiled defaults, then file values, then CLI options that were

    -

    explicitly supplied. A CLI option you omit does not erase its file value.

    -

    KV server and diskio make --config optional; diskdb and chunkdb require a

    -

    config path. A malformed, unreadable, or invalid named file stops startup

    -

    instead of silently falling back to defaults. server.rpc_workers controls

    -

    the inbound RPC worker count (default 2 for the Rust servers and 4 for diskio),

    -

    must be positive, and takes effect only after restart. File watchers may report

    -

    static changes before restart, but the active listener does not change.

    -

    Console local deployment generates node-specific config files and reuses the

    -

    same paths when restarting services. Keep manually managed files with the

    -

    server's data and deployment records; group 0 currently stores topology, not

    -

    process configuration.

    -

    2.1 Register the physical topology

    -

    Create a rack, add nodes, and deploy a server on each node. The

    -

    deploy command starts crowdb-kv-server on the target node (via SSH

    -

    if ssh_user is set, or as a local subprocess otherwise). No manual

    -

    start needed.

    -
    -
    - - -
    -
    # Create a rack
    -crowdb-cli rack add --id r1 --name "rack-one"
    -
    -# Register each node (repeat for n2, n3)
    -crowdb-cli node add --id n1 --rack r1 --host 127.0.0.1
    -
    -# Deploy a crowdb-kv-server process on each node (repeat for n2, n3)
    -crowdb-cli server deploy --node n1 --rest-port 2001 --rpc-port 20001
    -
    # Create a rack
    -curl -X POST "http://$IP:$PORT/api/racks" -H 'Content-Type: application/json' \
    -  -d '{"id":"r1"}'
    -
    -# Register each node (repeat for n2, n3)
    -curl -X POST "http://$IP:$PORT/api/nodes" -H 'Content-Type: application/json' \
    -  -d '{"id":"n1","rack_id":"r1","host":"127.0.0.1","ssh_port":22,"ssh_user":""}'
    -
    -# Deploy a crowdb-kv-server process on each node (repeat for n2, n3)
    -curl -X POST "http://$IP:$PORT/api/nodes/n1/server/deploy" \
    -  -H 'Content-Type: application/json' \
    -  -d '{"rest_port":2001,"rpc_port":20001}'
    -
    -

    2.2 Initialize the cluster

    -

    Before creating data stores or groups, the cluster must be

    -

    initialized. This creates the system group (store 0, group 0) which

    -

    stores cluster topology metadata as KV entries, providing HA for

    -

    the topology itself.

    -
    -
    - - -
    -
    # Initialize with all deployed nodes
    -crowdb-cli cluster init --nodes n1,n2,n3
    -
    curl -X POST "http://$IP:$PORT/api/cluster/init" \
    -  -H 'Content-Type: application/json' \
    -  -d '{"nodes":["n1","n2","n3"]}'
    -
    -

    This creates store 0 and group 0 on each selected node, wires remotes

    -

    for multi-node, persists topology in console config, and writes

    -

    hardware hierarchy + KV-cluster topology into group 0 via

    -

    HardwareClient + KVClusterMetaClient (text-path keys, JSON

    -

    values). After initialization, data store/group creation is unblocked.

    -

    For a single-node dev cluster, pass one node:

    -
    crowdb-cli cluster init --nodes n1
    -

    2.3 Create a store and group

    -

    A store is the logical container that owns one or more groups.

    -
    -
    - - -
    -
    # Create a store on n1
    -crowdb-cli store add --store-id 3 --nodes n1
    -
    -# Create a group with an initial replica on n1
    -crowdb-cli paxos add \
    -  --store-id 3 --group-id 3 --replica-id 1 --nodes n1
    -
    curl -X POST "http://$IP:$PORT/api/stores" -H 'Content-Type: application/json' \
    -  -d '{"store_id":3,"nodes":["n1"]}'
    -
    -curl -X POST "http://$IP:$PORT/api/stores/3/groups" -H 'Content-Type: application/json' \
    -  -d '{"group_id":3,"replica_id":1,"nodes":["n1"]}'
    -
    -

    If the cluster has not been initialized, store/group creation returns

    -

    409 Conflict with a message directing you to run cluster init first.

    -

    2.4 Add the remaining replicas

    -
    -
    - - -
    -
    crowdb-cli replica add \
    -  --store-id 3 --group-id 3 --node n2 --replica-id 2
    -
    -crowdb-cli replica add \
    -  --store-id 3 --group-id 3 --node n3 --replica-id 3
    -
    curl -X POST "http://$IP:$PORT/api/stores/3/groups/3/replicas" \
    -  -H 'Content-Type: application/json' \
    -  -d '{"node_id":"n2","replica_id":2}'
    -
    -curl -X POST "http://$IP:$PORT/api/stores/3/groups/3/replicas" \
    -  -H 'Content-Type: application/json' \
    -  -d '{"node_id":"n3","replica_id":3}'
    -
    -

    The service orchestrates the full add-replica flow: creates the local

    -

    group on the target node, wires remotes bidirectionally, and the new

    -

    replica catches up via snapshot streaming before joining the voting set.

    -

    2.5 Verify and smoke test

    -
    -
    - - -
    -
    # Check group health
    -crowdb-cli paxos inspect --store-id 3 --group-id 3
    -# Look for "leader=" and replica states
    -
    -# Put / Get
    -crowdb-cli kv put --store-id 3 --group-id 3 \
    -  --key hello --value world
    -
    -crowdb-cli kv get --store-id 3 --group-id 3 --key hello
    -
    curl "http://$IP:$PORT/api/stores/3/groups/3"
    -
    -curl -X POST "http://$IP:$PORT/api/stores/3/groups/3/kv/put" \
    -  -H 'Content-Type: application/json' \
    -  -d '{"key":"hello","value":"world"}'
    -
    -curl "http://$IP:$PORT/api/stores/3/groups/3/kv/get?key=hello"
    -
    -
    -

    3. KV Operations

    -

    All KV operations target a specific (store_id, group_id).

    -
    -
    - - -
    -
    # Put
    -crowdb-cli kv put --store-id 3 --group-id 3 --key user:1 --value alice
    -
    -# Get
    -crowdb-cli kv get --store-id 3 --group-id 3 --key user:1
    -
    -# Delete
    -crowdb-cli kv delete --store-id 3 --group-id 3 --key user:1
    -
    -# Prefix scan (list mode — fast, latest values, S3-list semantics)
    -crowdb-cli kv scan --store-id 3 --group-id 3 --prefix user: --limit 100
    -
    curl -X POST "http://$IP:$PORT/api/stores/3/groups/3/kv/put" \
    -  -H 'Content-Type: application/json' \
    -  -d '{"key":"user:1","value":"alice"}'
    -
    -curl "http://$IP:$PORT/api/stores/3/groups/3/kv/get?key=user:1"
    -
    -curl -X POST "http://$IP:$PORT/api/stores/3/groups/3/kv/delete" \
    -  -H 'Content-Type: application/json' \
    -  -d '{"key":"user:1"}'
    -
    -curl "http://$IP:$PORT/api/stores/3/groups/3/kv/scan?prefix=user:&limit=100"
    -
    -

    The Web UI KV Operator panel provides the same operations with a

    -

    store/group selector, paginated scan, and inline editing.

    -

    3.1 Scan modes

    -

    CROWDB provides two range-read modes for different use cases:

    -
      -
    • List scan (kv scan) — the default scan. Fast, always returns the
    • -
    -

    latest value per key at each page's read point. S3-list semantics:

    -

    each page is independently consistent, but a key can vanish (deleted

    -

    between pages) or a value can drift (overwritten between pages) within

    -

    a single logical scan. No server-side state beyond the per-page read

    -

    barrier. Use for interactive listing, key discovery, and the KV

    -

    Operator UI.

    -
      -
    • Snapshot scan (snapshot create + snapshot scan) —
    • -
    -

    point-in-time-consistent. Pins a frozen view of the keyspace at a

    -

    specific slot; every page is served from the same frozen view. No key

    -

    vanishes, no value drifts, no phantom keys appear. Use for backup,

    -

    analytics, and any consumer that needs a consistent point-in-time

    -

    view. See §3.2 below.

    -

    3.2 Snapshot versioning

    -

    A snapshot scan pins a point-in-time view of the keyspace. Creating a

    -

    snapshot flushes the in-memory write buffer (L0) into the durable tree

    -

    (L1), then pins L1 at the current applied slot. The snapshot is a frozen,

    -

    immutable view. Iterating it is pure array traversal with no concurrency

    -

    concerns. Each snapshot has a server-side handle with a lease (default 5

    -

    minutes); the handle is reaped if the client disconnects, preventing

    -

    unbounded pin retention.

    -

    Create a snapshot:

    -
    crowdb-cli snapshot create --store-id 3 --group-id 3
    -# Returns: snapshot_handle=42, at_slot=12345
    -

    List active snapshots:

    -
    crowdb-cli snapshot list --store-id 3 --group-id 3
    -# Returns: handle, at_slot, lease_remaining for each active snapshot
    -

    Scan a snapshot (paginated, same prefix/start_after/limit as list scan):

    -
    # First page
    -crowdb-cli snapshot scan --store-id 3 --group-id 3 \
    -  --handle 42 --prefix user: --limit 100
    -
    -# Next page (start_after = last key from previous page)
    -crowdb-cli snapshot scan --store-id 3 --group-id 3 \
    -  --handle 42 --prefix user: --limit 100 \
    -  --start-after user:50
    -

    Release a snapshot (free the pinned pages):

    -
    crowdb-cli snapshot release --store-id 3 --group-id 3 --handle 42
    -
    -
    - -
    -
    # Create
    -curl -X POST "http://$IP:$PORT/api/stores/3/groups/3/snapshots"
    -
    -# List
    -curl "http://$IP:$PORT/api/stores/3/groups/3/snapshots"
    -
    -# Scan
    -curl "http://$IP:$PORT/api/stores/3/groups/3/snapshots/42/scan?prefix=user:&limit=100"
    -
    -# Release
    -curl -X DELETE "http://$IP:$PORT/api/stores/3/groups/3/snapshots/42"
    -
    -

    GC and snapshots: the engine's garbage collector reclaims tombstones

    -

    and stale versions with slot <= gc_watermark. Active snapshots protect

    -

    their pinned pages via refcount — GC never frees a page a live snapshot

    -

    still references. Once a snapshot is released (or its lease expires), the

    -

    next GC sweep can reclaim those pages. The GC watermark can be advanced

    -

    explicitly via the management API to control retention:

    -
    # Advance GC watermark (data with slot <= watermark becomes reclaimable)
    -curl -X POST "http://$IP:$PORT/api/stores/3/groups/3/gc-watermark" \
    -  -H 'Content-Type: application/json' \
    -  -d '{"slot":12000}'
    -
    -

    4. Cluster Management

    -

    4.1 Check cluster health

    -
    -
    - - -
    -
    # High-level summary (servers + store/group counts)
    -crowdb-cli cluster status
    -
    -# Full topology (logical stores/groups/replicas + physical nodes/servers)
    -crowdb-cli cluster topology
    -
    -# Inspect a specific store, group, or node
    -crowdb-cli cluster inspect s3          # store 3
    -crowdb-cli cluster inspect s3/g3       # group 3 in store 3
    -crowdb-cli cluster inspect n1          # node n1
    -
    # All nodes
    -curl "http://$IP:$PORT/api/nodes"
    -
    -# All deployed servers
    -curl "http://$IP:$PORT/api/servers"
    -
    -# A specific group
    -curl "http://$IP:$PORT/api/stores/3/groups/3"
    -# healthy: all replicas up, leader known
    -# degraded: some replicas down, quorum + leader available
    -# unavailable: quorum lost
    -
    -

    4.2 Add a read replica

    -
    -
    - - -
    -
    crowdb-cli replica add --store-id 3 --group-id 3 --node n4 --replica-id 4
    -
    curl -X POST "http://$IP:$PORT/api/stores/3/groups/3/replicas" \
    -  -H 'Content-Type: application/json' \
    -  -d '{"node_id":"n4","replica_id":4}'
    -
    -

    The new replica streams a snapshot from the leader, catches up, then

    -

    joins the voting set automatically.

    -

    4.3 Remove a replica

    -
    -
    - - -
    -
    crowdb-cli replica remove --store-id 3 --group-id 3 --replica-id 3
    -
    curl -X DELETE "http://$IP:$PORT/api/stores/3/groups/3/replicas/3"
    -
    -

    If the target is the leader, the service asks it to step down first,

    -

    waits for a new leader, then removes the replica.

    -

    4.4 Replace a failed node

    -
  • Provision the new machine with the same node ID, management port,
  • -

    and RPC port.

    -
  • Deploy the server via the service. The server auto-loads its
  • -

    store/group configuration from conf/node-config.json on startup.

    -

    No --stores/--groups CLI args needed for normal restart:

    -
    -
    - - -
    -
       crowdb-cli server deploy --node n1 --rest-port 2001 --rpc-port 20001
    -
       curl -X POST "http://$IP:$PORT/api/nodes/n1/server/deploy" \
    -     -H 'Content-Type: application/json' \
    -     -d '{"rest_port":2001,"rpc_port":20001}'
    -
    -

    If node-config.json is lost, fall back to explicit bootstrap args

    -

    by starting crowdb-kv-server manually with --stores/--groups/

    -

    --replica:

    -
       crowdb-kv-server \
    -     --management-addr 0.0.0.0 --management-port 2001 \
    -     --ports 20001 --election-profile default \
    -     --stores 3 --groups 3 --replica 1
    -
  • Verify group health.
  • -

    If the WAL and config directory were also lost, add the replacement as

    -

    a new replica with a new replica ID instead of reusing the old one.

    -
    -

    5. Rolling Upgrade

    -

    Upgrade one node at a time. Wait for each node to rejoin and catch up

    -

    before moving to the next.

    -

    For each node:

    -
  • Stop:
  • -
    -
    - - -
    -
       crowdb-cli server stop --node n1
    -
       curl -X POST "http://$IP:$PORT/api/nodes/n1/server/stop"
    -
    -
  • Install the new binary on the node.
  • -
  • Restart the server. The server auto-loads its store/group
  • -

    configuration from conf/node-config.json on startup:

    -
    -
    - - -
    -
       crowdb-cli server restart --node n1
    -
       curl -X POST "http://$IP:$PORT/api/nodes/n1/server/restart"
    -
    -

    If node-config.json is missing, start crowdb-kv-server manually

    -

    with explicit args:

    -
       crowdb-kv-server \
    -     --management-addr 0.0.0.0 --management-port 2001 \
    -     --ports 20001 --election-profile default \
    -     --stores 3 --groups 3 --replica 1
    -

    --stores/--groups tells the server to reopen the WAL and rejoin

    -

    as a full member. --replica must match the assigned replica ID.

    -
  • Wait for healthy:
  • -
       crowdb-cli cluster status
    -   crowdb-cli paxos inspect --store-id 3 --group-id 3
    -
  • Smoke test:
  • -
       crowdb-cli kv get --store-id 3 --group-id 3 --key hello
    -
  • Move to the next node.
  • -

    What to watch: after stopping a node, the remaining nodes elect a

    -

    new leader. Wait for the group view to show a leader before proceeding.

    -

    A brief latency spike during leader transition is normal.

    -
    -

    6. Emergency: Loss of Quorum

    -

    If two of three nodes fail, the remaining node cannot elect itself

    -

    leader. Writes and linearizable reads block.

    -
      -
    • Restore the failed nodes from backups and restart. The server
    • -
    -

    auto-loads from conf/node-config.json; if the config is lost, fall

    -

    back to --stores/--groups/--replica args. This is always the

    -

    safest path.

    -
      -
    • Recover with data loss (last resort): force the surviving node to
    • -
    -

    become leader by manually truncating the log. Only safe when the

    -

    other nodes are permanently lost.

    -

    Do not add a new node to a quorum-less group without first recovering

    -

    leadership.

    -
    -

    7. Backup

    -

    CROWDB durability comes from the per-store WAL (--wal-root), the

    -

    per-node config cache (--config-root), and the durable KV engine

    -

    (--data-root). For disaster recovery, back up:

    -
      -
    • {wal-root}/store{store_id}/ for each store
    • -
    • {config-root}/node-config.json — per-node store/group config cache
    • -
    • {data-root}/store{store_id}/group{group_id}/ if using crowdb-tree
    • -
    -

    durable KV engine

    -

    Restore by placing these on the replacement node and starting the

    -

    server. With node-config.json present, no --stores/--groups

    -

    bootstrap args are needed. If the config is lost, use explicit

    -

    --stores/--groups/--replica args to recover from WAL.

    -
    -

    8. API Reference

    -
    -
    - - -
    -

    Local S3 commands use --root to identify the cluster root and discover its access endpoint:

    -
      -
    • crowdb-cli s3 cluster start --root <path> — create or restart
    • -
    • crowdb-cli s3 cluster status --root <path> — inspect process liveness
    • -
    • crowdb-cli s3 cluster stop --root <path> — stop and preserve data
    • -
    • crowdb-cli s3 cluster delete --root <path> — stop and delete permanently
    • -
    • crowdb-cli s3 bucket put --root <path> <bucket>
    • -
    • crowdb-cli s3 bucket delete --root <path> <bucket>
    • -
    • crowdb-cli s3 bucket list --root <path>
    • -
    • crowdb-cli s3 bucket get --root <path> <bucket>
    • -
    • crowdb-cli s3 object put --root <path> <bucket> <key> [--file <path> | --text <content> | --random-size <bytes>]
    • -
    • crowdb-cli s3 object get --root <path> <bucket> <key> [--output <file>] [--range <start-end>]
    • -
    • crowdb-cli s3 object delete --root <path> <bucket> <key>
    • -
    • crowdb-cli s3 object head --root <path> <bucket> <key>
    • -
    • crowdb-cli s3 object list --root <path> <bucket> [--prefix <prefix>] [--limit <n>] [--continuation <token>]
    • -
    -

    Lower-level management commands accept --system-ip <addr> (default

    -

    127.0.0.1) and --system-port <port> (default 10000).

    -
      -
    • crowdb-cli cluster status — servers + store/group summary
    • -
    • crowdb-cli cluster topology — full logical + physical hierarchy
    • -
    • crowdb-cli cluster inspect <id> — s<sid>, s<sid>/g<gid>,
    • -
    -

    s<sid>/g<gid>/r<rid>, or <node-id>

    -
      -
    • crowdb-cli cluster init --nodes n1,n2,... — initialize cluster (system group)
    • -
    • crowdb-cli rack add --id <id> [--name <name>]
    • -
    • crowdb-cli rack remove --id <id>
    • -
    • crowdb-cli rack list
    • -
    • crowdb-cli node add --id <id> --rack <rack> [--host <host>] [--ssh-user <user>]
    • -
    • crowdb-cli node remove --id <id>
    • -
    • crowdb-cli node list
    • -
    • crowdb-cli node ping <node>
    • -
    • crowdb-cli server deploy --node <id> --rest-port <p> --rpc-port <p>
    • -
    • crowdb-cli server restart --node <id>
    • -
    • crowdb-cli server stop --node <id>
    • -
    • crowdb-cli server list
    • -
    • crowdb-cli store add --store-id <id> [--nodes n1,n2,...]
    • -
    • crowdb-cli store remove --store-id <id>
    • -
    • crowdb-cli store list
    • -
    • crowdb-cli store inspect --store-id <id>
    • -
    • crowdb-cli paxos add --store-id <s> --group-id <g> --replica-id <r> --nodes n1,n2,...
    • -
    • crowdb-cli paxos remove --store-id <s> --group-id <g>
    • -
    • crowdb-cli paxos list --store-id <s>
    • -
    • crowdb-cli paxos inspect --store-id <s> --group-id <g>
    • -
    • crowdb-cli replica add --store-id <s> --group-id <g> --node <n> [--replica-id <r>]
    • -
    • crowdb-cli replica remove --store-id <s> --group-id <g> --replica-id <r>
    • -
    • crowdb-cli kv put --store-id <s> --group-id <g> --key <k> --value <v>
    • -
    • crowdb-cli kv get --store-id <s> --group-id <g> --key <k>
    • -
    • crowdb-cli kv delete --store-id <s> --group-id <g> --key <k>
    • -
    • crowdb-cli kv scan --store-id <s> --group-id <g> --prefix <p> [--limit <n>] — list scan (fast, latest values, S3-list semantics)
    • -
    • crowdb-cli snapshot create --store-id <s> --group-id <g> — pin a point-in-time snapshot
    • -
    • crowdb-cli snapshot list --store-id <s> --group-id <g> — list active snapshots
    • -
    • crowdb-cli snapshot scan --store-id <s> --group-id <g> --handle <h> --prefix <p> [--limit <n>] [--start-after <k>] — scan a pinned snapshot
    • -
    • crowdb-cli snapshot release --store-id <s> --group-id <g> --handle <h> — release a snapshot
    • -
    -
    -

    S3 data plane

    -

    These endpoints use S3_ENDPOINT, whose local default is

    -

    http://127.0.0.1:16000. Cluster lifecycle and benchmark operations do not

    -

    currently have HTTP endpoints.

    - - - - - - - - - - - - - - - - - -
    OperationEndpoint
    List bucketsGET /
    Create bucketPUT /{bucket}
    Inspect bucketHEAD /{bucket}
    Delete empty bucketDELETE /{bucket}
    Put or replace objectPUT /{bucket}/{key}
    Get objectGET /{bucket}/{key}
    Get inclusive rangeGET /{bucket}/{key} with Range: bytes={start}-{end}
    Inspect objectHEAD /{bucket}/{key}
    Delete objectDELETE /{bucket}/{key}
    List objectsGET /{bucket}?list-type=2&prefix=...&max-keys=...
    Continue object listGET /{bucket}?list-type=2&continuation-token=...
    -

    Cluster lifecycle

    - - - - - - - -
    OperationEndpoint
    Initialize clusterPOST /api/cluster/init
    -

    Physical topology

    - - - - - - - - - - - - - - - - - - -
    OperationEndpoint
    List racksGET /api/racks
    Create rackPOST /api/racks
    Delete rackDELETE /api/racks/{rack_id}
    List nodesGET /api/nodes
    Add nodePOST /api/nodes
    Get nodeGET /api/nodes/{id}
    Remove nodeDELETE /api/nodes/{id}
    Ping nodePOST /api/nodes/{id}/ping
    Get server infoGET /api/nodes/{id}/server
    Deploy serverPOST /api/nodes/{id}/server/deploy
    Restart serverPOST /api/nodes/{id}/server/restart
    Stop serverPOST /api/nodes/{id}/server/stop
    -

    Logical topology (stores and groups)

    - - - - - - - - - - - - - - - - - - - -
    OperationEndpoint
    List storesGET /api/stores
    Create storePOST /api/stores
    Get storeGET /api/stores/{sid}
    Remove storeDELETE /api/stores/{sid}
    List groupsGET /api/stores/{sid}/groups
    Create groupPOST /api/stores/{sid}/groups
    Get group viewGET /api/stores/{sid}/groups/{gid}
    Remove groupDELETE /api/stores/{sid}/groups/{gid}
    List replicasGET /api/stores/{sid}/groups/{gid}/replicas
    Add replicaPOST /api/stores/{sid}/groups/{gid}/replicas
    Get replicaGET /api/stores/{sid}/groups/{gid}/replicas/{rid}
    Remove replicaDELETE /api/stores/{sid}/groups/{gid}/replicas/{rid}
    Resolve leader endpointGET /api/stores/{sid}/groups/{gid}/endpoint
    -

    KV data plane

    - - - - - - - - - - - - - - - -
    OperationEndpoint
    GetGET /api/stores/{sid}/groups/{gid}/kv/get?key=...
    PutPOST /api/stores/{sid}/groups/{gid}/kv/put
    DeletePOST /api/stores/{sid}/groups/{gid}/kv/delete
    Scan (list mode)GET /api/stores/{sid}/groups/{gid}/kv/scan?prefix=...&limit=N
    Create snapshotPOST /api/stores/{sid}/groups/{gid}/snapshots
    List snapshotsGET /api/stores/{sid}/groups/{gid}/snapshots
    Snapshot scanGET /api/stores/{sid}/groups/{gid}/snapshots/{handle}/scan?prefix=...&limit=N&start_after=...
    Release snapshotDELETE /api/stores/{sid}/groups/{gid}/snapshots/{handle}
    Set GC watermarkPOST /api/stores/{sid}/groups/{gid}/gc-watermark
    -

    Server management (per-node, internal)

    - - - - - - - - - - - - - - - - -
    OperationEndpoint
    System init (bootstrap group 0)POST /system/init
    Add storePOST /stores
    Remove storeDELETE /stores/{sid}
    Add groupPOST /stores/{sid}/groups
    Remove groupDELETE /stores/{sid}/groups/{gid}
    Add remote replicasPOST /stores/{sid}/groups/{gid}/remotes
    Step down leaderPOST /stores/{sid}/groups/{gid}/step-down
    Export topologyGET /topology
    Health checkGET /health
    MetricsGET /metrics
    -

    These endpoints are on the crowdb-kv-server management API (internal,

    -

    only called by crowdb-kv-client's KVClusterAdmin). The console's

    -

    POST /api/cluster/init orchestrates

    -

    /system/init across nodes and auto-finalizes.

    -
    -
    -

    9. Iceberg Catalog Foundation

    -

    The independent crowdb-iceberg binary exposes authenticated catalog configuration

    -

    only. Namespace, table and FileIO endpoints are not enabled. It uses an existing

    -

    healthy Group 0, Chunk-KV and chunk-storage deployment; S3 credentials and buckets

    -

    do not select or authorize an Iceberg catalog.

    -
    pixi run -- cargo build -p crowdb-access-server --bin crowdb-iceberg
    -export CROWDB_MANAGEMENT_SEEDS=127.0.0.1:10000
    -export CROWDB_ICEBERG_LISTEN=127.0.0.1:8181
    -

    Supply three distinct, randomly generated 32–256-character ASCII tokens through

    -

    your secret-management environment: CROWDB_ICEBERG_READ_TOKEN,

    -

    CROWDB_ICEBERG_MANAGE_TOKEN and CROWDB_ICEBERG_CLEAR_TOKEN. Configure every

    -

    instance consistently. Management credentials can rename/initialize; only the

    -

    clear credential can replace the catalog. All three can read configuration.

    -

    The listener is plain HTTP: keep it on a trusted loopback/private hop behind a

    -

    TLS-terminating proxy. Do not transmit bearer credentials over public plain HTTP.

    -

    Set CROWDB_ICEBERG_TOKEN to the appropriate management token for CLI commands.

    -

    Each mutation takes a fresh UUIDv7 request identity. Preserve both that identity

    -

    and the exact arguments when retrying an interrupted command.

    -
    export CROWDB_ICEBERG_TOKEN="$CROWDB_ICEBERG_MANAGE_TOKEN"
    -pixi run -- target/debug/crowdb-iceberg initialize "$INIT_UUIDV7" primary
    -pixi run -- target/debug/crowdb-iceberg status
    -pixi run -- target/debug/crowdb-iceberg rename "$RENAME_UUIDV7" renamed "$ACTIVE_EPOCH"
    -pixi run -- target/debug/crowdb-iceberg serve
    -

    Status reports CatalogId, activation epoch, name and phase. Rename preserves the

    -

    CatalogId. The server validates dependencies and reconciles the root before

    -

    opening its listener. Ctrl-C stops admission and drains accepted connections.

    -
    pixi run -- curl -H "Authorization: Bearer $CROWDB_ICEBERG_READ_TOKEN" \
    -  http://127.0.0.1:8181/v1/config
    -

    Absent or empty warehouse selects the active catalog. A nonempty warehouse

    -

    returns 404 NoSuchWarehouseException. Unsupported endpoints return 406; all

    -

    table-format capabilities are false and HTTP idempotency is not advertised.

    -

    Clear makes the old domain inaccessible and selects a new empty catalog. It is

    -

    not physical erasure. Obtain the exact epoch and CatalogId from status, then

    -

    explicitly confirm both:

    -
    export CROWDB_ICEBERG_TOKEN="$CROWDB_ICEBERG_CLEAR_TOKEN"
    -pixi run -- target/debug/crowdb-iceberg clear "$CLEAR_UUIDV7" empty \
    -  "$ACTIVE_EPOCH" "$ACTIVE_CATALOG_ID"
    -

    Admission returns 503 during maintenance. Default persisted limits require an

    -

    11-second grace after the durable fence is observed. Restart cannot shorten it.

    -

    Another healthy instance resumes interrupted operations. An uncertain command

    -

    must be retried with its original identity and input, not a newly generated key.

    -

    Requests have a 24-hour retry window; expired identities are rejected. Bounded

    -

    ledger-slot collisions can reject new operations without evicting live receipts.

    -

    Run backend restart, two-instance and official-client checks with

    -

    pixi run -e iceberg-e2e test-pyiceberg-e2e. This uses a separate disposable runtime

    -

    registry and leaves persistent local cluster reservations intact.

    - -
    -
    - - - diff --git a/doc/user-manual/user-guide.md b/doc/user-manual/user-guide.md deleted file mode 100644 index e5c8d01ed..000000000 --- a/doc/user-manual/user-guide.md +++ /dev/null @@ -1,1023 +0,0 @@ - - - -# CROWDB User Guide - -CROWDB is a distributed storage platform with an S3-compatible object data -plane and a multi-group Multi-Paxos key-value foundation. This guide starts -with the shortest usable path: create a persistent local S3 cluster and use -bucket and object operations. The later sections cover the underlying KV -cluster, physical topology, individual servers, upgrades, and recovery. - -CROWDB provides three user-facing interfaces: - -- **Web UI** — the `crowdb-web` service provides a visual dashboard - with cluster topology, group health, a KV Operator panel (store/group - selector, paginated scan, inline CRUD, demo data injection), and - Swagger UI for browsing the OpenAPI spec of any registered - `crowdb-kv-server` instance. -- **CLI** — `crowdb-cli s3` owns the local S3 cluster lifecycle and sends - bucket/object requests directly to the cluster recorded by `--root`. - Lower-level management commands discover services through group 0 and call - them directly. Output is a human-readable console transcript. -- **HTTP APIs** — the local access server exposes the S3 HTTP API. The - console service exposes the lower-level cluster management API documented - in §8. - -S3 examples use the loopback access endpoint. The first local cluster normally -receives `127.0.0.1:16000`; when that port is already assigned, use the -`endpoint` printed by `s3 cluster start` or `s3 cluster status. - -```bash -S3_ENDPOINT=http://127.0.0.1:16000 -``` - -### Prerequisites - -Before following the steps below: - -- Run `pixi run build` from the repository root. -- Add `target/release` to `PATH`, or invoke the binaries by their full paths. -- Choose a dedicated cluster directory. The directory is the persistent - cluster identity and contains configuration, logs, and data files. - ---- - -## 1. Quick Start: Persistent Local S3 - -### 1.1 Start or restart the cluster - -Choose a directory and start the cluster: - -```bash -S3_ROOT="$PWD/.crowdb-runtime/persistent/s3-local" -crowdb-cli s3 cluster start --root "$S3_ROOT" -``` - -An absent or empty directory creates a new file-backed cluster. A recognized -cluster directory restarts the same cluster with its existing data and port -assignments. A non-empty directory that is not a CROWDB cluster is rejected -without modification. - -The command prints the S3 endpoint, Web management URL, running service count, -and cluster root. The default local endpoints are: - -```bash -S3_ENDPOINT=http://127.0.0.1:16000 -WEB_URL=http://127.0.0.1:14000 -``` - -If the command prints different endpoints because a default port is already -assigned, use the printed values. Open `WEB_URL` for the topology, service, and -cluster management console. - -Inspect the recorded processes without changing them: - -```bash -crowdb-cli s3 cluster status --root "$S3_ROOT" -``` - -The CLI owns S3 mini-cluster lifecycle. The bundled Web service loads that -mini-cluster's console registry and presents its running services. - -### 1.2 Bucket operations - -Create a bucket: - -```bash -crowdb-cli s3 bucket put --root "$S3_ROOT" photos -curl -X PUT "$S3_ENDPOINT/photos" -``` - -List buckets: - -```bash -crowdb-cli s3 bucket list --root "$S3_ROOT" -curl "$S3_ENDPOINT/" -``` - -GET one bucket and show its XML result: - -```bash -crowdb-cli s3 bucket get --root "$S3_ROOT" photos -curl "$S3_ENDPOINT/photos" -``` - -Remove an empty bucket: - -```bash -crowdb-cli s3 bucket delete --root "$S3_ROOT" photos -curl -X DELETE "$S3_ENDPOINT/photos" -``` - -Removing a non-empty bucket returns the S3 error and leaves its objects intact. - -### 1.3 Object CRUD - -Create the bucket used by the following examples, then upload an object from a -file, literal text, generated random bytes, or standard input: - -```bash -crowdb-cli s3 bucket put --root "$S3_ROOT" documents - -crowdb-cli s3 object put --root "$S3_ROOT" \ - documents reports/hello.txt --file ./hello.txt - -crowdb-cli s3 object put --root "$S3_ROOT" \ - documents reports/text.txt --text 'object content' - -crowdb-cli s3 object put --root "$S3_ROOT" \ - documents reports/random.bin --random-size 1048576 - -printf 'hello from CROWDB\n' | crowdb-cli s3 object put \ - --root "$S3_ROOT" documents reports/stdin.txt - -curl -X PUT --data-binary @hello.txt \ - "$S3_ENDPOINT/documents/reports/hello.txt" -``` - -`put` creates a new object or replaces the bytes of an existing key. - -Read an object to standard output or a file: - -```bash -crowdb-cli s3 object get --root "$S3_ROOT" \ - documents reports/hello.txt - -crowdb-cli s3 object get --root "$S3_ROOT" \ - documents reports/hello.txt --output ./downloaded.txt - -curl "$S3_ENDPOINT/documents/reports/hello.txt" \ - --output ./downloaded-with-curl.txt -``` - -Check that an object exists: - -```bash -crowdb-cli s3 object head --root "$S3_ROOT" \ - documents reports/hello.txt - -curl -I "$S3_ENDPOINT/documents/reports/hello.txt" -``` - -Delete an object: - -```bash -crowdb-cli s3 object delete --root "$S3_ROOT" \ - documents reports/hello.txt - -curl -X DELETE "$S3_ENDPOINT/documents/reports/hello.txt" -``` - -### 1.4 List objects - -List the first 100 keys below a prefix: - -```bash -crowdb-cli s3 object list --root "$S3_ROOT" documents \ - --prefix reports/ --limit 100 - -curl --get "$S3_ENDPOINT/documents" \ - --data-urlencode 'list-type=2' \ - --data-urlencode 'prefix=reports/' \ - --data-urlencode 'max-keys=100' -``` - -When a response is truncated, pass its opaque continuation token unchanged: - -```bash -crowdb-cli s3 object list --root "$S3_ROOT" documents \ - --prefix reports/ --limit 100 --continuation "$TOKEN" - -curl --get "$S3_ENDPOINT/documents" \ - --data-urlencode 'list-type=2' \ - --data-urlencode 'prefix=reports/' \ - --data-urlencode 'max-keys=100' \ - --data-urlencode "continuation-token=$TOKEN" -``` - -### 1.5 Read a byte range - -Ranges are inclusive. `--range 3-9` returns seven bytes: - -```bash -crowdb-cli s3 object get --root "$S3_ROOT" \ - documents reports/hello.txt --range 3-9 - -curl -H 'Range: bytes=3-9' \ - "$S3_ENDPOINT/documents/reports/hello.txt" -``` - -### 1.6 Stop, restart, and delete - -Stop every process while preserving the cluster directory and stored objects: - -```bash -crowdb-cli s3 cluster stop --root "$S3_ROOT" -``` - -Restart from the same directory and read the same data: - -```bash -crowdb-cli s3 cluster start --root "$S3_ROOT" -crowdb-cli s3 object get --root "$S3_ROOT" \ - documents reports/hello.txt -``` - -Permanently stop the cluster, release its port assignments, and remove its -directory: - -```bash -crowdb-cli s3 cluster delete --root "$S3_ROOT" -``` - -`delete` is destructive. Use `stop` when the cluster must be started again. -Cluster lifecycle does not currently have an HTTP management endpoint. - ---- - -## 2. Advanced: Bootstrap a KV Cluster - -The remaining sections describe lower-level cluster and server administration. -They are not required for the local S3 workflow above. - -Management CLI commands omit `--system-ip` and `--system-port` for brevity. -They default to the system-group discovery endpoint `127.0.0.1:10000`; either -flag may point to any system-group node because leader discovery is automatic. -The `CROWDB_SYSTEM_IP`/`CROWDB_SYSTEM_PORT` environment variables provide the -same overrides. The following console HTTP `curl` examples assume: - -```bash -IP=127.0.0.1 -PORT=14000 -``` - -An S3 mini-cluster already starts `crowdb-web` with its persisted registry. -For a separately managed cluster, before using these commands: - -- Start `crowdb-web` with `crowdb-web --port 14000`. Add `--test-mode` for an - in-memory console configuration that is lost on restart. -- Set `CROWDB_KV_SERVER_BIN` when `crowdb-kv-server` is not next to - `crowdb-web` and not available through `PATH`. -- Use each machine's reachable hostname or IP instead of `127.0.0.1` for a - multi-machine deployment. - -### Server configuration files - -KV server, diskdb, chunkdb, and diskio use TOML startup configuration. Valid -templates are shipped in each server's `conf/` directory. Values resolve in -this order: compiled defaults, then file values, then CLI options that were -explicitly supplied. A CLI option you omit does not erase its file value. - -KV server and diskio make `--config` optional; diskdb and chunkdb require a -config path. A malformed, unreadable, or invalid named file stops startup -instead of silently falling back to defaults. `server.rpc_workers` controls -the inbound RPC worker count (default 2 for the Rust servers and 4 for diskio), -must be positive, and takes effect only after restart. File watchers may report -static changes before restart, but the active listener does not change. - -Console local deployment generates node-specific config files and reuses the -same paths when restarting services. Keep manually managed files with the -server's data and deployment records; group 0 currently stores topology, not -process configuration. - -### 2.1 Register the physical topology - -Create a rack, add nodes, and deploy a server on each node. The -`deploy` command starts `crowdb-kv-server` on the target node (via SSH -if `ssh_user` is set, or as a local subprocess otherwise). No manual -start needed. - -**CLI:** - -```bash -# Create a rack -crowdb-cli rack add --id r1 --name "rack-one" - -# Register each node (repeat for n2, n3) -crowdb-cli node add --id n1 --rack r1 --host 127.0.0.1 - -# Deploy a crowdb-kv-server process on each node (repeat for n2, n3) -crowdb-cli server deploy --node n1 --rest-port 2001 --rpc-port 20001 -``` - -**curl:** - -```bash -# Create a rack -curl -X POST "http://$IP:$PORT/api/racks" -H 'Content-Type: application/json' \ - -d '{"id":"r1"}' - -# Register each node (repeat for n2, n3) -curl -X POST "http://$IP:$PORT/api/nodes" -H 'Content-Type: application/json' \ - -d '{"id":"n1","rack_id":"r1","host":"127.0.0.1","ssh_port":22,"ssh_user":""}' - -# Deploy a crowdb-kv-server process on each node (repeat for n2, n3) -curl -X POST "http://$IP:$PORT/api/nodes/n1/server/deploy" \ - -H 'Content-Type: application/json' \ - -d '{"rest_port":2001,"rpc_port":20001}' -``` - -### 2.2 Initialize the cluster - -Before creating data stores or groups, the cluster must be -initialized. This creates the system group (store 0, group 0) which -stores cluster topology metadata as KV entries, providing HA for -the topology itself. - -**CLI:** - -```bash -# Initialize with all deployed nodes -crowdb-cli cluster init --nodes n1,n2,n3 -``` - -**curl:** - -```bash -curl -X POST "http://$IP:$PORT/api/cluster/init" \ - -H 'Content-Type: application/json' \ - -d '{"nodes":["n1","n2","n3"]}' -``` - -This creates store 0 and group 0 on each selected node, wires remotes -for multi-node, persists topology in console config, and writes -hardware hierarchy + KV-cluster topology into group 0 via -`HardwareClient` + `KVClusterMetaClient` (text-path keys, JSON -values). After initialization, data store/group creation is unblocked. - -For a single-node dev cluster, pass one node: - -```bash -crowdb-cli cluster init --nodes n1 -``` - -### 2.3 Create a store and group - -A store is the logical container that owns one or more groups. - -**CLI:** - -```bash -# Create a store on n1 -crowdb-cli store add --store-id 3 --nodes n1 - -# Create a group with an initial replica on n1 -crowdb-cli paxos add \ - --store-id 3 --group-id 3 --replica-id 1 --nodes n1 -``` - -**curl:** - -```bash -curl -X POST "http://$IP:$PORT/api/stores" -H 'Content-Type: application/json' \ - -d '{"store_id":3,"nodes":["n1"]}' - -curl -X POST "http://$IP:$PORT/api/stores/3/groups" -H 'Content-Type: application/json' \ - -d '{"group_id":3,"replica_id":1,"nodes":["n1"]}' -``` - -If the cluster has not been initialized, store/group creation returns -`409 Conflict` with a message directing you to run `cluster init` first. - -### 2.4 Add the remaining replicas - -**CLI:** - -```bash -crowdb-cli replica add \ - --store-id 3 --group-id 3 --node n2 --replica-id 2 - -crowdb-cli replica add \ - --store-id 3 --group-id 3 --node n3 --replica-id 3 -``` - -**curl:** - -```bash -curl -X POST "http://$IP:$PORT/api/stores/3/groups/3/replicas" \ - -H 'Content-Type: application/json' \ - -d '{"node_id":"n2","replica_id":2}' - -curl -X POST "http://$IP:$PORT/api/stores/3/groups/3/replicas" \ - -H 'Content-Type: application/json' \ - -d '{"node_id":"n3","replica_id":3}' -``` - -The service orchestrates the full add-replica flow: creates the local -group on the target node, wires remotes bidirectionally, and the new -replica catches up via snapshot streaming before joining the voting set. - -### 2.5 Verify and smoke test - -**CLI:** - -```bash -# Check group health -crowdb-cli paxos inspect --store-id 3 --group-id 3 -# Look for "leader=" and replica states - -# Put / Get -crowdb-cli kv put --store-id 3 --group-id 3 \ - --key hello --value world - -crowdb-cli kv get --store-id 3 --group-id 3 --key hello -``` - -**curl:** - -```bash -curl "http://$IP:$PORT/api/stores/3/groups/3" - -curl -X POST "http://$IP:$PORT/api/stores/3/groups/3/kv/put" \ - -H 'Content-Type: application/json' \ - -d '{"key":"hello","value":"world"}' - -curl "http://$IP:$PORT/api/stores/3/groups/3/kv/get?key=hello" -``` - ---- - -## 3. KV Operations - -All KV operations target a specific `(store_id, group_id)`. - -**CLI:** - -```bash -# Put -crowdb-cli kv put --store-id 3 --group-id 3 --key user:1 --value alice - -# Get -crowdb-cli kv get --store-id 3 --group-id 3 --key user:1 - -# Delete -crowdb-cli kv delete --store-id 3 --group-id 3 --key user:1 - -# Prefix scan (list mode — fast, latest values, S3-list semantics) -crowdb-cli kv scan --store-id 3 --group-id 3 --prefix user: --limit 100 -``` - -**curl:** - -```bash -curl -X POST "http://$IP:$PORT/api/stores/3/groups/3/kv/put" \ - -H 'Content-Type: application/json' \ - -d '{"key":"user:1","value":"alice"}' - -curl "http://$IP:$PORT/api/stores/3/groups/3/kv/get?key=user:1" - -curl -X POST "http://$IP:$PORT/api/stores/3/groups/3/kv/delete" \ - -H 'Content-Type: application/json' \ - -d '{"key":"user:1"}' - -curl "http://$IP:$PORT/api/stores/3/groups/3/kv/scan?prefix=user:&limit=100" -``` - -The Web UI KV Operator panel provides the same operations with a -store/group selector, paginated scan, and inline editing. - -### 3.1 Scan modes - -CROWDB provides two range-read modes for different use cases: - -- **List scan** (`kv scan`) — the default scan. Fast, always returns the - latest value per key at each page's read point. S3-list semantics: - each page is independently consistent, but a key can vanish (deleted - between pages) or a value can drift (overwritten between pages) within - a single logical scan. No server-side state beyond the per-page read - barrier. Use for interactive listing, key discovery, and the KV - Operator UI. -- **Snapshot scan** (`snapshot create` + `snapshot scan`) — - point-in-time-consistent. Pins a frozen view of the keyspace at a - specific slot; every page is served from the same frozen view. No key - vanishes, no value drifts, no phantom keys appear. Use for backup, - analytics, and any consumer that needs a consistent point-in-time - view. See §3.2 below. - -### 3.2 Snapshot versioning - -A snapshot scan pins a point-in-time view of the keyspace. Creating a -snapshot flushes the in-memory write buffer (L0) into the durable tree -(L1), then pins L1 at the current applied slot. The snapshot is a frozen, -immutable view. Iterating it is pure array traversal with no concurrency -concerns. Each snapshot has a server-side handle with a lease (default 5 -minutes); the handle is reaped if the client disconnects, preventing -unbounded pin retention. - -**Create a snapshot:** - -```bash -crowdb-cli snapshot create --store-id 3 --group-id 3 -# Returns: snapshot_handle=42, at_slot=12345 -``` - -**List active snapshots:** - -```bash -crowdb-cli snapshot list --store-id 3 --group-id 3 -# Returns: handle, at_slot, lease_remaining for each active snapshot -``` - -**Scan a snapshot (paginated, same prefix/start_after/limit as list scan):** - -```bash -# First page -crowdb-cli snapshot scan --store-id 3 --group-id 3 \ - --handle 42 --prefix user: --limit 100 - -# Next page (start_after = last key from previous page) -crowdb-cli snapshot scan --store-id 3 --group-id 3 \ - --handle 42 --prefix user: --limit 100 \ - --start-after user:50 -``` - -**Release a snapshot (free the pinned pages):** - -```bash -crowdb-cli snapshot release --store-id 3 --group-id 3 --handle 42 -``` - -**curl:** - -```bash -# Create -curl -X POST "http://$IP:$PORT/api/stores/3/groups/3/snapshots" - -# List -curl "http://$IP:$PORT/api/stores/3/groups/3/snapshots" - -# Scan -curl "http://$IP:$PORT/api/stores/3/groups/3/snapshots/42/scan?prefix=user:&limit=100" - -# Release -curl -X DELETE "http://$IP:$PORT/api/stores/3/groups/3/snapshots/42" -``` - -**GC and snapshots**: the engine's garbage collector reclaims tombstones -and stale versions with `slot <= gc_watermark`. Active snapshots protect -their pinned pages via refcount — GC never frees a page a live snapshot -still references. Once a snapshot is released (or its lease expires), the -next GC sweep can reclaim those pages. The GC watermark can be advanced -explicitly via the management API to control retention: - -```bash -# Advance GC watermark (data with slot <= watermark becomes reclaimable) -curl -X POST "http://$IP:$PORT/api/stores/3/groups/3/gc-watermark" \ - -H 'Content-Type: application/json' \ - -d '{"slot":12000}' -``` - ---- - -## 4. Cluster Management - -### 4.1 Check cluster health - -**CLI:** - -```bash -# High-level summary (servers + store/group counts) -crowdb-cli cluster status - -# Full topology (logical stores/groups/replicas + physical nodes/servers) -crowdb-cli cluster topology - -# Inspect a specific store, group, or node -crowdb-cli cluster inspect s3 # store 3 -crowdb-cli cluster inspect s3/g3 # group 3 in store 3 -crowdb-cli cluster inspect n1 # node n1 -``` - -**curl:** - -```bash -# All nodes -curl "http://$IP:$PORT/api/nodes" - -# All deployed servers -curl "http://$IP:$PORT/api/servers" - -# A specific group -curl "http://$IP:$PORT/api/stores/3/groups/3" -# healthy: all replicas up, leader known -# degraded: some replicas down, quorum + leader available -# unavailable: quorum lost -``` - -### 4.2 Add a read replica - -**CLI:** - -```bash -crowdb-cli replica add --store-id 3 --group-id 3 --node n4 --replica-id 4 -``` - -**curl:** - -```bash -curl -X POST "http://$IP:$PORT/api/stores/3/groups/3/replicas" \ - -H 'Content-Type: application/json' \ - -d '{"node_id":"n4","replica_id":4}' -``` - -The new replica streams a snapshot from the leader, catches up, then -joins the voting set automatically. - -### 4.3 Remove a replica - -**CLI:** - -```bash -crowdb-cli replica remove --store-id 3 --group-id 3 --replica-id 3 -``` - -**curl:** - -```bash -curl -X DELETE "http://$IP:$PORT/api/stores/3/groups/3/replicas/3" -``` - -If the target is the leader, the service asks it to step down first, -waits for a new leader, then removes the replica. - -### 4.4 Replace a failed node - -1. Provision the new machine with the same node ID, management port, - and RPC port. -2. Deploy the server via the service. The server auto-loads its - store/group configuration from `conf/node-config.json` on startup. - No `--stores`/`--groups` CLI args needed for normal restart: - - **CLI:** - - ```bash - crowdb-cli server deploy --node n1 --rest-port 2001 --rpc-port 20001 - ``` - - **curl:** - - ```bash - curl -X POST "http://$IP:$PORT/api/nodes/n1/server/deploy" \ - -H 'Content-Type: application/json' \ - -d '{"rest_port":2001,"rpc_port":20001}' - ``` - - If `node-config.json` is lost, fall back to explicit bootstrap args - by starting `crowdb-kv-server` manually with `--stores`/`--groups`/ - `--replica`: - - ```bash - crowdb-kv-server \ - --management-addr 0.0.0.0 --management-port 2001 \ - --ports 20001 --election-profile default \ - --stores 3 --groups 3 --replica 1 - ``` - -3. Verify group health. - -If the WAL and config directory were also lost, add the replacement as -a new replica with a new replica ID instead of reusing the old one. - ---- - -## 5. Rolling Upgrade - -Upgrade one node at a time. Wait for each node to rejoin and catch up -before moving to the next. - -For each node: - -1. **Stop:** - - **CLI:** - - ```bash - crowdb-cli server stop --node n1 - ``` - - **curl:** - - ```bash - curl -X POST "http://$IP:$PORT/api/nodes/n1/server/stop" - ``` - -2. **Install the new binary** on the node. - -3. **Restart the server.** The server auto-loads its store/group - configuration from `conf/node-config.json` on startup: - - **CLI:** - - ```bash - crowdb-cli server restart --node n1 - ``` - - **curl:** - - ```bash - curl -X POST "http://$IP:$PORT/api/nodes/n1/server/restart" - ``` - - If `node-config.json` is missing, start `crowdb-kv-server` manually - with explicit args: - - ```bash - crowdb-kv-server \ - --management-addr 0.0.0.0 --management-port 2001 \ - --ports 20001 --election-profile default \ - --stores 3 --groups 3 --replica 1 - ``` - - `--stores`/`--groups` tells the server to reopen the WAL and rejoin - as a full member. `--replica` must match the assigned replica ID. - -4. **Wait for healthy:** - - ```bash - crowdb-cli cluster status - crowdb-cli paxos inspect --store-id 3 --group-id 3 - ``` - -5. **Smoke test:** - - ```bash - crowdb-cli kv get --store-id 3 --group-id 3 --key hello - ``` - -6. Move to the next node. - -**What to watch:** after stopping a node, the remaining nodes elect a -new leader. Wait for the group view to show a leader before proceeding. -A brief latency spike during leader transition is normal. - ---- - -## 6. Emergency: Loss of Quorum - -If two of three nodes fail, the remaining node cannot elect itself -leader. Writes and linearizable reads block. - -- **Restore the failed nodes** from backups and restart. The server - auto-loads from `conf/node-config.json`; if the config is lost, fall - back to `--stores`/`--groups`/`--replica` args. This is always the - safest path. -- **Recover with data loss** (last resort): force the surviving node to - become leader by manually truncating the log. Only safe when the - other nodes are permanently lost. - -Do not add a new node to a quorum-less group without first recovering -leadership. - ---- - -## 7. Backup - -CROWDB durability comes from the per-store WAL (`--wal-root`), the -per-node config cache (`--config-root`), and the durable KV engine -(`--data-root`). For disaster recovery, back up: - -- `{wal-root}/store{store_id}/` for each store -- `{config-root}/node-config.json` — per-node store/group config cache -- `{data-root}/store{store_id}/group{group_id}/` if using crowdb-tree - durable KV engine - -Restore by placing these on the replacement node and starting the -server. With `node-config.json` present, no `--stores`/`--groups` -bootstrap args are needed. If the config is lost, use explicit -`--stores`/`--groups`/`--replica` args to recover from WAL. - ---- - -## 8. API Reference - -**CLI:** - -Local S3 commands use `--root` to identify the cluster root and discover its access endpoint: - -- **`crowdb-cli s3 cluster start --root `** — create or restart -- **`crowdb-cli s3 cluster status --root `** — inspect process liveness -- **`crowdb-cli s3 cluster stop --root `** — stop and preserve data -- **`crowdb-cli s3 cluster delete --root `** — stop and delete permanently -- **`crowdb-cli s3 bucket put --root `** -- **`crowdb-cli s3 bucket delete --root `** -- **`crowdb-cli s3 bucket list --root `** -- **`crowdb-cli s3 bucket get --root `** -- **`crowdb-cli s3 object put --root [--file | --text | --random-size ]`** -- **`crowdb-cli s3 object get --root [--output ] [--range ]`** -- **`crowdb-cli s3 object delete --root `** -- **`crowdb-cli s3 object head --root `** -- **`crowdb-cli s3 object list --root [--prefix ] [--limit ] [--continuation ]`** - -Lower-level management commands accept `--system-ip ` (default -`127.0.0.1`) and `--system-port ` (default `10000`). - -- **`crowdb-cli cluster status`** — servers + store/group summary -- **`crowdb-cli cluster topology`** — full logical + physical hierarchy -- **`crowdb-cli cluster inspect `** — `s`, `s/g`, - `s/g/r`, or `` -- **`crowdb-cli cluster init --nodes n1,n2,...`** — initialize cluster (system group) -- **`crowdb-cli rack add --id [--name ]`** -- **`crowdb-cli rack remove --id `** -- **`crowdb-cli rack list`** -- **`crowdb-cli node add --id --rack [--host ] [--ssh-user ]`** -- **`crowdb-cli node remove --id `** -- **`crowdb-cli node list`** -- **`crowdb-cli node ping `** -- **`crowdb-cli server deploy --node --rest-port

    --rpc-port

    `** -- **`crowdb-cli server restart --node `** -- **`crowdb-cli server stop --node `** -- **`crowdb-cli server list`** -- **`crowdb-cli store add --store-id [--nodes n1,n2,...]`** -- **`crowdb-cli store remove --store-id `** -- **`crowdb-cli store list`** -- **`crowdb-cli store inspect --store-id `** -- **`crowdb-cli paxos add --store-id --group-id --replica-id --nodes n1,n2,...`** -- **`crowdb-cli paxos remove --store-id --group-id `** -- **`crowdb-cli paxos list --store-id `** -- **`crowdb-cli paxos inspect --store-id --group-id `** -- **`crowdb-cli replica add --store-id --group-id --node [--replica-id ]`** -- **`crowdb-cli replica remove --store-id --group-id --replica-id `** -- **`crowdb-cli kv put --store-id --group-id --key --value `** -- **`crowdb-cli kv get --store-id --group-id --key `** -- **`crowdb-cli kv delete --store-id --group-id --key `** -- **`crowdb-cli kv scan --store-id --group-id --prefix

    [--limit ]`** — list scan (fast, latest values, S3-list semantics) -- **`crowdb-cli snapshot create --store-id --group-id `** — pin a point-in-time snapshot -- **`crowdb-cli snapshot list --store-id --group-id `** — list active snapshots -- **`crowdb-cli snapshot scan --store-id --group-id --handle --prefix

    [--limit ] [--start-after ]`** — scan a pinned snapshot -- **`crowdb-cli snapshot release --store-id --group-id --handle `** — release a snapshot - -**curl:** - -#### S3 data plane - -These endpoints use `S3_ENDPOINT`, whose local default is -`http://127.0.0.1:16000`. Cluster lifecycle and benchmark operations do not -currently have HTTP endpoints. - -| Operation | Endpoint | -| ---------------------- | ---------------------------------------------------------- | -| List buckets | `GET /` | -| Create bucket | `PUT /{bucket}` | -| Inspect bucket | `HEAD /{bucket}` | -| Delete empty bucket | `DELETE /{bucket}` | -| Put or replace object | `PUT /{bucket}/{key}` | -| Get object | `GET /{bucket}/{key}` | -| Get inclusive range | `GET /{bucket}/{key}` with `Range: bytes={start}-{end}` | -| Inspect object | `HEAD /{bucket}/{key}` | -| Delete object | `DELETE /{bucket}/{key}` | -| List objects | `GET /{bucket}?list-type=2&prefix=...&max-keys=...` | -| Continue object list | `GET /{bucket}?list-type=2&continuation-token=...` | - -#### Cluster lifecycle - -| Operation | Endpoint | -| --- | --- | -| Initialize cluster | `POST /api/cluster/init` | - -#### Physical topology - -| Operation | Endpoint | -| --- | --- | -| List racks | `GET /api/racks` | -| Create rack | `POST /api/racks` | -| Delete rack | `DELETE /api/racks/{rack_id}` | -| List nodes | `GET /api/nodes` | -| Add node | `POST /api/nodes` | -| Get node | `GET /api/nodes/{id}` | -| Remove node | `DELETE /api/nodes/{id}` | -| Ping node | `POST /api/nodes/{id}/ping` | -| Get server info | `GET /api/nodes/{id}/server` | -| Deploy server | `POST /api/nodes/{id}/server/deploy` | -| Restart server | `POST /api/nodes/{id}/server/restart` | -| Stop server | `POST /api/nodes/{id}/server/stop` | - -#### Logical topology (stores and groups) - -| Operation | Endpoint | -| --- | --- | -| List stores | `GET /api/stores` | -| Create store | `POST /api/stores` | -| Get store | `GET /api/stores/{sid}` | -| Remove store | `DELETE /api/stores/{sid}` | -| List groups | `GET /api/stores/{sid}/groups` | -| Create group | `POST /api/stores/{sid}/groups` | -| Get group view | `GET /api/stores/{sid}/groups/{gid}` | -| Remove group | `DELETE /api/stores/{sid}/groups/{gid}` | -| List replicas | `GET /api/stores/{sid}/groups/{gid}/replicas` | -| Add replica | `POST /api/stores/{sid}/groups/{gid}/replicas` | -| Get replica | `GET /api/stores/{sid}/groups/{gid}/replicas/{rid}` | -| Remove replica | `DELETE /api/stores/{sid}/groups/{gid}/replicas/{rid}` | -| Resolve leader endpoint | `GET /api/stores/{sid}/groups/{gid}/endpoint` | - -#### KV data plane - -| Operation | Endpoint | -| --- | --- | -| Get | `GET /api/stores/{sid}/groups/{gid}/kv/get?key=...` | -| Put | `POST /api/stores/{sid}/groups/{gid}/kv/put` | -| Delete | `POST /api/stores/{sid}/groups/{gid}/kv/delete` | -| Scan (list mode) | `GET /api/stores/{sid}/groups/{gid}/kv/scan?prefix=...&limit=N` | -| Create snapshot | `POST /api/stores/{sid}/groups/{gid}/snapshots` | -| List snapshots | `GET /api/stores/{sid}/groups/{gid}/snapshots` | -| Snapshot scan | `GET /api/stores/{sid}/groups/{gid}/snapshots/{handle}/scan?prefix=...&limit=N&start_after=...` | -| Release snapshot | `DELETE /api/stores/{sid}/groups/{gid}/snapshots/{handle}` | -| Set GC watermark | `POST /api/stores/{sid}/groups/{gid}/gc-watermark` | - -#### Server management (per-node, internal) - -| Operation | Endpoint | -| --- | --- | -| System init (bootstrap group 0) | `POST /system/init` | -| Add store | `POST /stores` | -| Remove store | `DELETE /stores/{sid}` | -| Add group | `POST /stores/{sid}/groups` | -| Remove group | `DELETE /stores/{sid}/groups/{gid}` | -| Add remote replicas | `POST /stores/{sid}/groups/{gid}/remotes` | -| Step down leader | `POST /stores/{sid}/groups/{gid}/step-down` | -| Export topology | `GET /topology` | -| Health check | `GET /health` | -| Metrics | `GET /metrics` | - -These endpoints are on the `crowdb-kv-server` management API (internal, -only called by `crowdb-kv-client`'s `KVClusterAdmin`). The console's -`POST /api/cluster/init` orchestrates -`/system/init` across nodes and auto-finalizes. - -## 9. Iceberg Catalog Foundation - -The independent `crowdb-iceberg` binary exposes authenticated catalog configuration -only. Namespace, table and FileIO endpoints are not enabled. It uses an existing -healthy Group 0, Chunk-KV and chunk-storage deployment; S3 credentials and buckets -do not select or authorize an Iceberg catalog. - -```bash -pixi run -- cargo build -p crowdb-access-server --bin crowdb-iceberg -export CROWDB_MANAGEMENT_SEEDS=127.0.0.1:10000 -export CROWDB_ICEBERG_LISTEN=127.0.0.1:8181 -``` - -Both `crowdb-iceberg` and `crowdb-access-server` accept `--config `. -The tracked Access TOML example is -`app/crowdb-access-server/conf/crowdb_access_server_config.toml`; it contains -shared DiskIO, read and small-write budgets, S3 resources, and Iceberg GC limits. The -processes read the same schema and use their respective sections. A provided -file takes precedence over legacy environment settings for fields it sets; -omitted fields retain their defaults or legacy environment values. Keep -credentials and bearer tokens in the environment. In the single-node -container, the rendered files are `/opt/crowdb/run/config/s3.toml` and -`/opt/crowdb/run/config/iceberg.toml`. - -Supply three distinct, randomly generated 32–256-character ASCII tokens through -your secret-management environment: `CROWDB_ICEBERG_READ_TOKEN`, -`CROWDB_ICEBERG_MANAGE_TOKEN` and `CROWDB_ICEBERG_CLEAR_TOKEN`. Configure every -instance consistently. Management credentials can rename/initialize; only the -clear credential can replace the catalog. All three can read configuration. -The listener is plain HTTP: keep it on a trusted loopback/private hop behind a -TLS-terminating proxy. Do not transmit bearer credentials over public plain HTTP. - -Set `CROWDB_ICEBERG_TOKEN` to the appropriate management token for CLI commands. -Each mutation takes a fresh UUIDv7 request identity. Preserve both that identity -and the exact arguments when retrying an interrupted command. - -```bash -export CROWDB_ICEBERG_TOKEN="$CROWDB_ICEBERG_MANAGE_TOKEN" -pixi run -- target/debug/crowdb-iceberg initialize "$INIT_UUIDV7" primary -pixi run -- target/debug/crowdb-iceberg status -pixi run -- target/debug/crowdb-iceberg rename "$RENAME_UUIDV7" renamed "$ACTIVE_EPOCH" -pixi run -- target/debug/crowdb-iceberg serve -``` - -Status reports CatalogId, activation epoch, name and phase. Rename preserves the -CatalogId. The server validates dependencies and reconciles the root before -opening its listener. Ctrl-C stops admission and drains accepted connections. - -```bash -pixi run -- curl -H "Authorization: Bearer $CROWDB_ICEBERG_READ_TOKEN" \ - http://127.0.0.1:8181/v1/config -``` - -Absent or empty `warehouse` selects the active catalog. A nonempty warehouse -returns 404 `NoSuchWarehouseException`. Unsupported endpoints return 406; all -table-format capabilities are false and HTTP idempotency is not advertised. - -Clear makes the old domain inaccessible and selects a new empty catalog. It is -not physical erasure. Obtain the exact epoch and CatalogId from `status`, then -explicitly confirm both: - -```bash -export CROWDB_ICEBERG_TOKEN="$CROWDB_ICEBERG_CLEAR_TOKEN" -pixi run -- target/debug/crowdb-iceberg clear "$CLEAR_UUIDV7" empty \ - "$ACTIVE_EPOCH" "$ACTIVE_CATALOG_ID" -``` - -Admission returns 503 during maintenance. Default persisted limits require an -11-second grace after the durable fence is observed. Restart cannot shorten it. -Another healthy instance resumes interrupted operations. An uncertain command -must be retried with its original identity and input, not a newly generated key. -Requests have a 24-hour retry window; expired identities are rejected. Bounded -ledger-slot collisions can reject new operations without evicting live receipts. - -Run backend restart, two-instance and official-client checks with -`pixi run -e iceberg-e2e test-pyiceberg-e2e`. This uses a separate disposable runtime -registry and leaves persistent local cluster reservations intact. diff --git a/doc/working/plan-console-authority.md b/doc/working/plan-console-authority.md index dd3c666c2..ac0987caa 100644 --- a/doc/working/plan-console-authority.md +++ b/doc/working/plan-console-authority.md @@ -145,12 +145,13 @@ is paused; it does not block the single-node image requirement. change the host-wide `core_pattern` from inside the container. Files: `container/single-node-container/{Dockerfile,entrypoint.sh,tests/**}`, `container/crowdb-monitor/src/**`, - `doc/user-manual/docker-single-node-user-guide.md`. + `container/single-node-container/README.md`. ## Documentation and completion -- [ ] **Bare-metal guide**: migrate verified KV, chunk and access setup from - the old combined guide, state the non-production boundary, then fix links and - remove the old guide. Keep the Docker end-user guide independent. +- [ ] **Bare-metal documentation**: migrate verified KV, chunk and access + setup into dedicated deployment documentation, state the non-production + boundary, then fix links and remove obsolete combined material. Keep Docker + deployment notes independent. - [ ] **Acceptance and cleanup**: run affected integration cases, full console and UI suites, Rust fmt and lint; update the relevant permanent architecture, then remove the requirement, backlog entry and this plan when complete. diff --git a/pixi.toml b/pixi.toml index 4eb1b0cb2..c6b6194d5 100644 --- a/pixi.toml +++ b/pixi.toml @@ -216,7 +216,7 @@ test-suite = { cmd = "bash tools/pixi-tasks/test-suite.sh", depends-on = ["build # ── Coverage & LOC ─────────────────────────────────────────────── coverage = { cmd = "cargo tarpaulin --workspace --out Html --output-dir target/coverage --exclude-files '*/tests/*'", depends-on = ["install-deps"] } -loc = "tokei . .github --exclude swagger-ui --exclude third-party --exclude 'lib/crowdb-tree/build-*' --exclude target --exclude dist --exclude node_modules --exclude 'doc/user-manual/*.html' --exclude 'doc/user-manual/*.py' --exclude package-lock.json" +loc = "tokei . .github --exclude swagger-ui --exclude third-party --exclude 'lib/crowdb-tree/build-*' --exclude target --exclude dist --exclude node_modules --exclude package-lock.json" # ── Clean ──────────────────────────────────────────────────────── # Removes build products and disposable namespaced runtime artifacts. Durable

KTK!)iGc#P?|RA(OR!_~m+m?M4AS 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 {LQ*1W$uW$@G86QTbJ3Udm2{F+ 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