diff --git a/.gitignore b/.gitignore index ad67955..1e5c1e5 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ target # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ +/otel-arrow/ diff --git a/README.md b/README.md index b66512d..95fa5d3 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# ASAPCollector — OTAP Dataflow processor +# ASAPCollector — OTAP Dataflow processor -This repository is a **standalone, buildable snapshot** of ASAP's -[OTAP Dataflow](https://github.com/open-telemetry/otel-arrow/tree/main/rust/otap-dataflow) -processor implementation. +This repository is a **standalone, buildable** implementation of +ASAP's [OTAP Dataflow](https://github.com/open-telemetry/otel-arrow/tree/main/rust/otap-dataflow) +processor. It ports ASAP's edge **precompute + sketching** runtime (windowed DDSketch / KLL / HLL / CountSketch / CountMin-Sketch aggregation) onto @@ -11,86 +11,115 @@ OTAP Dataflow's Arrow-native streaming model as a single unified ## Layout -| Path | What it is | Builds standalone? | -|---|---|---| -| [`asap-precompute-rs/`](./asap-precompute-rs) | The Rust crate: host-neutral precompute runtime **plus** the OTAP integration layer under the `otap` feature (`src/otap/`). | **Yes** — `cargo build --features otap` | -| [`otap-patch/`](./otap-patch) | The overlay applied onto the upstream OTAP Dataflow workspace at build time: the actual `local::Processor` node + `linkme` registration. | No — depends on the OTAP workspace crates (`otap-df-engine`, `otap-df-otap`, …); included here as reference for how the crate plugs into OTAP. | - -## The OTAP processor, in two layers +One crate: [`asap-precompute-rs/`](./asap-precompute-rs). Three feature +levels, each `cargo build`-able on its own with no manual staging or +overlay step: -**Layer A — the processor node** (`otap-patch/all/mod.rs`) -The piece OTAP's runtime actually sees: - -- `AsapSketchesProcessor` implements +| Feature | What it adds | Extra dependency | +|---|---|---| +| *(default, no features)* | Host-neutral precompute runtime — the five sketch types, windowing, overflow policy. No Arrow, no OTAP, no Tokio. | none | +| `otap` | The Arrow codec (`encode_batch`/`decode_batch`, `OtapMetricRecords`), the legacy `SeriesDictionary`/`otap::wire` transport, and `AsapSketchesPlugin`'s Tokio-task lifecycle. | `arrow-*`, `tokio` | +| `otap-engine` | The real OTAP node: `AsapSketchesProcessor` (a genuine `local::Processor`, `linkme`-registered) and its direct `SketchEnvelope <-> OtapPdata` binding (`otap::codec`). | the real `otel-arrow-dfe-*` crates, via a plain git dependency pinned to a specific `open-telemetry/otel-arrow` commit — see `Cargo.toml` | + +`otap-engine`'s git dependency is what used to require staging this +crate's files into a separately-checked-out copy of the OTAP Dataflow +workspace (the old `otap-patch/` overlay). That's gone: Cargo resolves +the named `otel-arrow-dfe-*` packages directly from a clone of that +repo and unifies dependency versions across the whole build graph the +same way it already does for the `asap_sketchlib` git dependency — +`cargo build --features otap-engine` just works. + +## The real OTAP node (`otap::processor` / `otap::codec`) + +- [`processor.rs`](./asap-precompute-rs/src/otap/processor.rs) — + `AsapSketchesProcessor` implements `otel_arrow_dfe_engine::local::processor::Processor` — an `async fn process(msg: Message, effect_handler)` handling `Message::PData` and `NodeControlMsg::{TimerTick, Config, Shutdown, …}`. - (Crate prefix `otel-arrow-dfe-*`, not the older `otap-df-*` — upstream - renamed these very recently, otel-arrow issue #1848.) -- Registered via `#[distributed_slice(OTAP_PROCESSOR_FACTORIES)]` under - the URN `urn:asap:processor:asap_sketches`, with a `ProcessorFactory` - (`create` + `validate_config` + `WiringContract::UNRESTRICTED`). -- `AsapSketchesUserConfig` is the TOML/serde shape validated at - `--validate-and-exit` time (see - [`otap-patch/plugins/asap_sketches/sample.toml`](./otap-patch/plugins/asap_sketches/sample.toml)). - -> Note: the `OtapPdata ↔ OtapMetricRecords` binding is now implemented -> (`otap-patch/all/otap_bridge.rs`) — real OTLP metrics in, -> sketch/estimate output back out as real OTLP metrics, for the -> **producer role** (`AsapSketchesPlugin::start_from_envelopes`'s -> **receiver role** — ingesting another `asap_sketches` node's -> `SketchStreamBatch` output — isn't covered; that format doesn't fit -> OTAP's metrics shape). The adapter drives a bare `Precompute` -> instance directly rather than `AsapSketchesPlugin`'s own Tokio-task -> lifecycle, whose emit shape (`SketchStreamBatch`, PR #5/#6's -> dictionary economics) diverged from what this binding needs. -> **Unverified**: `otap-patch/` has no standalone build in this repo -> (see Layer A/B split below), so this binding has been checked -> against upstream source reads, not a compiler — see -> `otap_bridge.rs`'s module doc for exactly what's confirmed. - -**Layer B — the runtime lifecycle + Arrow codec** (`asap-precompute-rs/src/otap/`) + Registered via `#[distributed_slice(OTAP_PROCESSOR_FACTORIES)]` under + the URN `urn:asap:processor:asap_sketches`. `AsapSketchesUserConfig` + is the TOML/serde config shape validated at `--validate-and-exit` + time — see [`plugins/asap_sketches/sample.toml`](./asap-precompute-rs/plugins/asap_sketches/sample.toml). +- [`codec.rs`](./asap-precompute-rs/src/otap/codec.rs) — the direct + `SketchEnvelope <-> OtapPdata` binding: implements OTAP's own + `MetricsView` trait family straight over `&[SketchEnvelope]` + (encode) and decodes a real `OtapPdata` straight into + `Vec` (decode). No intermediate `RecordBatch` or + `OtapMetricRecords` two-batch hop — those exist as a general, + real-OTAP-free carrier format for other potential Strategy-B + adapters (Telegraf, Vector), but this repo only ever ships to OTAP, + so the OTAP-facing path skips straight to the real type. + +**There is exactly one transport: the pipeline.** `AsapSketchesProcessor` +sends only via `effect_handler.send_message_with_source_node` — an +earlier revision also carried a direct-TCP "wire lane" as a second +transport; that's been removed. It turned out to be solving a problem +OTAP's real Arrow encoding already solves: the custom wire lane +existed to give sketch traffic dictionary/schema-reuse economics (the +legacy `SeriesDictionary` SCHEMA/DICTIONARY/RECORD tiering), but +`codec.rs`'s real `OtapPdata` gets that for free — OTAP's own Arrow +encoder dictionary-encodes the metric name and every string-valued +attribute key/value by construction. Confirmed against the real +workspace and guarded by a permanent regression test +(`codec.rs`'s `real_otap_encoding_dictionary_encodes_metric_name_and_string_attributes`); +see `processor.rs`'s module doc, "There is exactly one transport: the +pipeline", for the full rationale and the real schema this produces. + +**Build/lint/test-verified**, by this repo's own `cargo build +--features otap-engine` / `cargo test --features otap-engine` / +`cargo clippy --features otap-engine --all-targets -D warnings` / +`cargo fmt --check`, against `open-telemetry/otel-arrow` commit +`3e85c346` (2026-08-24, pinned in `Cargo.toml`). + +## The rest of the runtime lifecycle + Arrow codec | File | Responsibility | |---|---| -| [`lifecycle.rs`](./asap-precompute-rs/src/otap/lifecycle.rs) | `AsapSketchesPlugin` — Tokio runtime with three tasks: **input** (`Stream` → `flatten` → `decode_batch` → `observe`), **flush ticker** (modeled on `NodeControlMsg::Wakeup`; `tick` → `encode_batch` → `lift` → emit), **control-channel** poll (`update_config`), plus a graceful drain on shutdown. | +| [`lifecycle.rs`](./asap-precompute-rs/src/otap/lifecycle.rs) | `AsapSketchesPlugin` — Tokio runtime with three tasks: **input** (`Stream` → `flatten` → `decode_batch` → `observe`), **flush ticker** (modeled on `NodeControlMsg::Wakeup`; `tick` → `encode_batch` → `lift` → emit), **control-channel** poll (`update_config`), plus a graceful drain on shutdown. Also how `otap::processor` constructs its bare `Precompute`. | | [`config.rs`](./asap-precompute-rs/src/otap/config.rs) | `PluginConfig` + `resolve()` — the 5-sketch `sketch_type` dispatch table (factory + observer + `SketchType`). | -| [`decode.rs`](./asap-precompute-rs/src/otap/decode.rs) / [`encode.rs`](./asap-precompute-rs/src/otap/encode.rs) | Arrow codec: `RecordBatch ↔ Vec` / `[SketchEnvelope]`, keyed on well-known columns + Strategy-B `_asap_*` carrier keys ([`schema.rs`](./asap-precompute-rs/src/otap/schema.rs)). | -| [`records.rs`](./asap-precompute-rs/src/otap/records.rs) | `OtapMetricRecords` + `flatten`/`lift` — projects OTAP's sibling-batch family (metrics + per-row attribute child batch joined by `parent_id`) ↔ flat `RecordBatch`, including the attribute-lift that keeps emitted batches passing OTAP's strict schema validator. | +| [`decode.rs`](./asap-precompute-rs/src/otap/decode.rs) / [`encode.rs`](./asap-precompute-rs/src/otap/encode.rs) | Arrow codec: `RecordBatch ↔ Vec` / `[SketchEnvelope]`, keyed on well-known columns + Strategy-B `_asap_*` carrier keys ([`schema.rs`](./asap-precompute-rs/src/otap/schema.rs)). Backs the legacy `dictionary`/`wire` transport below, not `otap::codec`. | +| [`records.rs`](./asap-precompute-rs/src/otap/records.rs) | `OtapMetricRecords` + `flatten`/`lift` — projects an `OtapArrowRecords`-shaped sibling-batch family (metrics + per-row attribute child batch joined by `parent_id`) ↔ flat `RecordBatch`. Real-OTAP-free; same scope note as above. | +| [`dictionary.rs`](./asap-precompute-rs/src/otap/dictionary.rs) / [`wire.rs`](./asap-precompute-rs/src/otap/wire.rs) | `SeriesDictionary`/`SketchStreamBatch` — the SCHEMA/DICTIONARY/RECORD wire economics, and its Arrow-IPC/TCP transport. Tested, but not part of the `otap-engine` path — kept for the standalone `sketch_producer_node`/`sketch_receiver_node` example binaries. | -Both layers sit on the host-neutral runtime in the same crate -(`precompute`, `window`, `snapshot_cache`, `sketches/*`) — ASAP's -host-neutral edge precompute runtime. +All of this sits on the host-neutral runtime in the same crate +(`precompute`, `window`, `snapshot_cache`, `sketches/*`). ## Build & test ```sh cd asap-precompute-rs -cargo build --features otap -cargo test --features otap # 156 tests: runtime + OTAP codec + lifecycle -cargo clippy --all-targets --features otap -- -D warnings +cargo build --features otap-engine +cargo test --features otap-engine # runtime + codec + lifecycle + real pipeline tests +cargo clippy --all-targets --features otap-engine -- -D warnings cargo fmt --check ``` The default (no-feature) build excludes Arrow/Tokio and compiles the -row-oriented runtime only; the `otap` feature turns on the OTAP codec + -plugin lifecycle. +row-oriented runtime only; `otap` turns on the Arrow codec + legacy +transport + plugin lifecycle; `otap-engine` additionally turns on the +real OTAP node (and pulls in the real OTAP Dataflow crates — a heavier, +slower build: datafusion, tonic, and the rest of that dependency tree). ## Dependency note [`asap_sketchlib`](https://github.com/ProjectASAP/asap_sketchlib) is a -public dependency. +public dependency. `otap-engine` additionally depends on +`open-telemetry/otel-arrow`'s `otel-arrow-dfe-*` crates, git-pinned to +a specific commit in `Cargo.toml`. ## Status Phases **B** (Arrow codec) and **C** (full 5-sketch plugin lifecycle) -are complete and tested here. Phase **D** (the `linkme` registration + -OTAP submodule build wiring, plus the `OtapPdata` binding) is present -as the `otap-patch/` overlay — the producer-role binding is now -implemented (`otap_bridge.rs`) but **unverified** (no standalone build -of `otap-patch/` in this repo — see the Layer A note above). The -receiver-role `OtapPdata` binding and cross-host byte-parity -(Phase **E**) are the remaining work. +are complete and tested. Phase **D** (the `linkme` registration + the +real `OtapPdata` binding) is complete and build/lint/test-verified by +this repo's own build. `tests/otap_pipeline_e2e.rs` parses a genuine +OTAP pipeline YAML, builds it through `OTAP_PIPELINE_FACTORY`, runs the +resulting `RuntimePipeline`, injects a real `OtapPdata`, and verifies +the sketch emitted through the configured exporter. The receiver role — ingesting +another `asap_sketches` node's *legacy* `SketchStreamBatch` output — +isn't addressed by this adapter; that format has its own standalone +example binaries. Cross-host byte-parity (Phase **E**) is the +remaining work. --- diff --git a/asap-precompute-rs/Cargo.lock b/asap-precompute-rs/Cargo.lock index e6e40ef..2bf2236 100644 --- a/asap-precompute-rs/Cargo.lock +++ b/asap-precompute-rs/Cargo.lock @@ -2,6 +2,32 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "addr2line" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.17", + "once_cell", + "version_check", +] + [[package]] name = "ahash" version = "0.8.12" @@ -16,6 +42,21 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "aho-corasick" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -31,19 +72,70 @@ version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" +[[package]] +name = "arc-swap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c049c0be4daef0b145cb3555416b3b8ef5b7888a38aea1a3a155801fe7b0810b" +dependencies = [ + "rustversion", +] + +[[package]] +name = "arrayvec" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" + +[[package]] +name = "arrow" +version = "58.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cfdd0833e32a9874d2b55089333ad310c0be208aafa277385ce2461dec90be3" +dependencies = [ + "arrow-arith", + "arrow-array", + "arrow-buffer", + "arrow-cast", + "arrow-csv", + "arrow-data", + "arrow-ipc", + "arrow-json", + "arrow-ord", + "arrow-row", + "arrow-schema", + "arrow-select", + "arrow-string", +] + +[[package]] +name = "arrow-arith" +version = "58.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a41203398f0eaa6f7ec8e62c0da742a21abf282c148fc157f6c35c90e29981a" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "chrono", + "num-traits", +] + [[package]] name = "arrow-array" version = "58.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae33dad492b7df00a217563a7b0ef2874df68a0deea1b1a3acf628152f7f7a69" dependencies = [ - "ahash", + "ahash 0.8.12", "arrow-buffer", "arrow-data", "arrow-schema", "chrono", - "half", - "hashbrown", + "chrono-tz", + "half 2.7.1", + "hashbrown 0.17.1", "num-complex", "num-integer", "num-traits", @@ -56,11 +148,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9552f96391c005e6ab449fa941420935e7e062489b12b8b1b08879b2163f5b5" dependencies = [ "bytes", - "half", + "half 2.7.1", "num-bigint", "num-traits", ] +[[package]] +name = "arrow-cast" +version = "58.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8a327c9649f30d8406995f27642b68df354713cca3baaaf100f076f18d5f34" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-ord", + "arrow-schema", + "arrow-select", + "atoi", + "base64 0.22.1", + "chrono", + "comfy-table", + "half 2.7.1", + "lexical-core", + "num-traits", + "ryu", +] + +[[package]] +name = "arrow-csv" +version = "58.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af0dd6d90d1955e9f9a014c1e563ee8aeffc21909085d25623e1da44d96eca26" +dependencies = [ + "arrow-array", + "arrow-cast", + "arrow-schema", + "chrono", + "csv", + "csv-core", + "regex", +] + [[package]] name = "arrow-data" version = "58.4.0" @@ -69,7 +198,7 @@ checksum = "2b24852db04738907e06c04ea61e42fe7fda962a34513022dc0d0e754fb7976b" dependencies = [ "arrow-buffer", "arrow-schema", - "half", + "half 2.7.1", "num-integer", "num-traits", ] @@ -86,6 +215,59 @@ dependencies = [ "arrow-schema", "arrow-select", "flatbuffers", + "lz4_flex", + "zstd", +] + +[[package]] +name = "arrow-json" +version = "58.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a96aed3931c076adee39ec2a40d8219fc7f09e79bcdaca1df16272993e1e14" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-cast", + "arrow-ord", + "arrow-schema", + "arrow-select", + "chrono", + "half 2.7.1", + "indexmap", + "itoa", + "lexical-core", + "memchr", + "num-traits", + "ryu", + "serde_core", + "serde_json", + "simdutf8", +] + +[[package]] +name = "arrow-ord" +version = "58.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63a083ec750f5c043f02946b4baf05fcdbb55f4560a3277055caca5cc99f3eb0" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "arrow-select", +] + +[[package]] +name = "arrow-row" +version = "58.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514ba0ef0d4c5896202dae736251ce415abb43a950bed570fb7981b8716c0e4c" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "half 2.7.1", ] [[package]] @@ -93,6 +275,10 @@ name = "arrow-schema" version = "58.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21ca356ad6425cecb6eb7b28e4f659f1ee7880fbb1a16127de7dd62901efee9e" +dependencies = [ + "serde_core", + "serde_json", +] [[package]] name = "arrow-select" @@ -100,12 +286,29 @@ version = "58.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c58da39eb3d8350ad4a549e5c2bc49284dac554016c69829310350f1731b0aad" dependencies = [ - "ahash", + "ahash 0.8.12", + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "num-traits", +] + +[[package]] +name = "arrow-string" +version = "58.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6789b388467525e3271326b6b4915666ecfdf5142aef09779445c954b67543c" +dependencies = [ "arrow-array", "arrow-buffer", "arrow-data", "arrow-schema", + "arrow-select", + "memchr", "num-traits", + "regex", + "regex-syntax", ] [[package]] @@ -116,12 +319,22 @@ dependencies = [ "arrow-ipc", "arrow-schema", "asap_sketchlib", + "async-trait", "futures", + "humantime-serde", + "linkme", + "otel-arrow-dfe-config", + "otel-arrow-dfe-engine", + "otel-arrow-dfe-otap", + "otel-arrow-dfe-pdata", + "otel-arrow-dfe-pdata-views", + "otel-arrow-dfe-state", + "otel-arrow-dfe-telemetry", "proptest", - "prost", + "prost 0.13.5", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-stream", ] @@ -132,8 +345,8 @@ version = "0.2.2" source = "git+https://github.com/ProjectASAP/asap_sketchlib?rev=010457de46ba9ec5574a53476bbe8b4bdb02b965#010457de46ba9ec5574a53476bbe8b4bdb02b965" dependencies = [ "bytes", - "prost", - "rand", + "prost 0.13.5", + "rand 0.9.4", "rmp-serde", "serde", "serde-big-array", @@ -144,793 +357,3846 @@ dependencies = [ ] [[package]] -name = "autocfg" -version = "1.5.1" +name = "async-channel" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] [[package]] -name = "bit-set" -version = "0.8.0" +name = "async-compression" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +checksum = "3976abdc8fe7d1133d43d304afd42abdf5bc3e1319d263d223bde07b5efc4be8" dependencies = [ - "bit-vec", + "compression-codecs", + "compression-core", + "pin-project-lite", + "tokio", ] [[package]] -name = "bit-vec" -version = "0.8.0" +name = "async-trait" +version = "0.1.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" +checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.4", +] [[package]] -name = "bitflags" -version = "2.13.0" +name = "atoi" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] [[package]] -name = "bumpalo" -version = "3.20.3" +name = "atomic-waker" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] -name = "bytes" -version = "1.12.0" +name = "autocfg" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] -name = "cc" -version = "1.2.66" +name = "axum" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d6cac793997bd970000024b2934968efe83b382de4fdcf4fcb46b6ee4ad996" +checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" dependencies = [ - "find-msvc-tools", - "shlex", + "axum-core", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "sync_wrapper", + "tower", + "tower-layer", + "tower-service", ] [[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "chrono" -version = "0.4.45" +name = "axum-core" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" dependencies = [ - "iana-time-zone", - "num-traits", - "windows-link", + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", ] [[package]] -name = "const-random" -version = "0.1.18" +name = "backtrace" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" dependencies = [ - "const-random-macro", + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-link", ] [[package]] -name = "const-random-macro" -version = "0.1.16" +name = "backtrace-ext" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +checksum = "537beee3be4a18fb023b570f80e3ae28003db9167a751266b259926e25539d50" dependencies = [ - "getrandom 0.2.17", - "once_cell", - "tiny-keccak", + "backtrace", ] [[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "crunchy" -version = "0.2.4" +name = "base64" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] -name = "either" -version = "1.16.0" +name = "base64" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" [[package]] -name = "errno" -version = "0.3.14" +name = "bit-set" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" dependencies = [ - "libc", - "windows-sys", + "bit-vec", ] [[package]] -name = "fastrand" -version = "2.4.1" +name = "bit-vec" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] -name = "find-msvc-tools" -version = "0.1.9" +name = "bitflags" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" [[package]] -name = "flatbuffers" -version = "25.12.19" +name = "bitvec" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35f6839d7b3b98adde531effaf34f0c2badc6f4735d26fe74709d8e513a96ef3" +checksum = "ddcec3d12c579d40898fe0a9a358a803c23e9c52ca3c425707f81c9436211837" dependencies = [ - "bitflags", - "rustc_version", + "funty", + "radium", + "tap", + "wyz", ] [[package]] -name = "fnv" -version = "1.0.7" +name = "block-buffer" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" +dependencies = [ + "hybrid-array", +] [[package]] -name = "futures" -version = "0.3.32" +name = "borsh" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +checksum = "a88b7ea17d208c4193f2c1e6de3c35fe71f98c96982d5ced308bdcc749ff6e1f" dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", + "borsh-derive", + "bytes", + "cfg_aliases", ] [[package]] -name = "futures-channel" -version = "0.3.32" +name = "borsh-derive" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +checksum = "d8f347189c62a579b8cd5f80714efa178f52e461dc2e6d701d264f5ff22e566c" dependencies = [ - "futures-core", - "futures-sink", + "once_cell", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.118", ] [[package]] -name = "futures-core" -version = "0.3.32" +name = "bumpalo" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] -name = "futures-executor" -version = "0.3.32" +name = "byte-unit" +version = "5.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +checksum = "4a813de7f2bbedb7dce265b64f1cf5908ebe4d56281ece8d847e98113788b9b0" dependencies = [ - "futures-core", - "futures-task", - "futures-util", + "rust_decimal", + "schemars", + "serde", + "utf8-width", ] [[package]] -name = "futures-io" -version = "0.3.32" +name = "bytecheck" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] [[package]] -name = "futures-macro" -version = "0.3.32" +name = "bytecheck_derive" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] -name = "futures-sink" -version = "0.3.32" +name = "bytemuck" +version = "1.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" +checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" [[package]] -name = "futures-task" -version = "0.3.32" +name = "byteorder" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] -name = "futures-util" -version = "0.3.32" +name = "bytes" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "slab", -] +checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" [[package]] -name = "getrandom" -version = "0.2.17" +name = "cc" +version = "1.2.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +checksum = "f5d6cac793997bd970000024b2934968efe83b382de4fdcf4fcb46b6ee4ad996" dependencies = [ - "cfg-if", + "find-msvc-tools", + "jobserver", "libc", - "wasi", + "shlex", ] [[package]] -name = "getrandom" -version = "0.3.4" +name = "cfg-if" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "libc", - "r-efi 5.3.0", - "wasip2", -] +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] -name = "getrandom" -version = "0.4.3" +name = "cfg_aliases" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" -dependencies = [ - "cfg-if", - "libc", - "r-efi 6.0.0", -] +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" [[package]] -name = "half" -version = "2.7.1" +name = "chacha20" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" dependencies = [ "cfg-if", - "crunchy", - "num-traits", - "zerocopy", + "cpufeatures", + "rand_core 0.10.1", ] [[package]] -name = "hashbrown" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" - -[[package]] -name = "iana-time-zone" -version = "0.1.65" +name = "chrono" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", + "iana-time-zone", "js-sys", - "log", + "num-traits", + "serde", "wasm-bindgen", - "windows-core", + "windows-link", ] [[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" +name = "chrono-tz" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +checksum = "a6139a8597ed92cf816dfb33f5dd6cf0bb93a6adc938f11039f371bc5bcd26c3" dependencies = [ - "cc", + "chrono", + "phf", ] [[package]] -name = "itertools" -version = "0.14.0" +name = "ciborium" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" dependencies = [ - "either", + "ciborium-io", + "ciborium-ll", + "serde", ] [[package]] -name = "itoa" -version = "1.0.18" +name = "ciborium-io" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" [[package]] -name = "js-sys" -version = "0.3.103" +name = "ciborium-ll" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" dependencies = [ - "cfg-if", - "futures-util", - "wasm-bindgen", + "ciborium-io", + "half 2.7.1", ] [[package]] -name = "libc" -version = "0.2.186" +name = "combine" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] [[package]] -name = "libm" -version = "0.2.16" +name = "comfy-table" +version = "7.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" +checksum = "958c5d6ecf1f214b4c2bbbbf6ab9523a864bd136dcf71a7e8904799acfe1ad47" +dependencies = [ + "unicode-segmentation", + "unicode-width 0.2.2", +] [[package]] -name = "linux-raw-sys" -version = "0.12.1" +name = "compression-codecs" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" +checksum = "ce2548391e9c1929c21bf6aa2680af86fe4c1b33e6cea9ac1cfeec0bd11218cf" +dependencies = [ + "compression-core", + "flate2", + "memchr", + "zstd", + "zstd-safe", +] [[package]] -name = "lock_api" -version = "0.4.14" +name = "compression-core" +version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] +checksum = "cc14f565cf027a105f7a44ccf9e5b424348421a1d8952a8fc9d499d313107789" [[package]] -name = "log" -version = "0.4.33" +name = "concurrent-queue" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] [[package]] -name = "memchr" -version = "2.8.2" +name = "const-oid" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" [[package]] -name = "mio" -version = "1.2.1" +name = "const-random" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" dependencies = [ - "libc", - "wasi", - "windows-sys", + "const-random-macro", ] [[package]] -name = "num-bigint" -version = "0.4.8" +name = "const-random-macro" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" dependencies = [ - "num-integer", - "num-traits", + "getrandom 0.2.17", + "once_cell", + "tiny-keccak", ] [[package]] -name = "num-complex" -version = "0.4.6" +name = "core-foundation" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ - "num-traits", + "core-foundation-sys", + "libc", ] [[package]] -name = "num-integer" -version = "0.1.46" +name = "core-foundation" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" dependencies = [ - "num-traits", + "core-foundation-sys", + "libc", ] [[package]] -name = "num-traits" -version = "0.2.19" +name = "core-foundation-sys" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", - "libm", -] +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] -name = "once_cell" -version = "1.21.4" +name = "cpu-time" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" +checksum = "e9e393a7668fe1fad3075085b86c781883000b4ede868f43627b34a87c8b7ded" +dependencies = [ + "libc", + "winapi", +] [[package]] -name = "parking_lot" -version = "0.12.5" +name = "cpufeatures" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" dependencies = [ - "lock_api", - "parking_lot_core", + "libc", ] [[package]] -name = "parking_lot_core" -version = "0.9.12" +name = "crc32fast" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +checksum = "8498c871161e1742aaa9d52551b2d6ebdd4c3d45a3be423e3728f33b955be550" dependencies = [ "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-link", ] [[package]] -name = "pin-project-lite" -version = "0.2.17" +name = "crossbeam-utils" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" [[package]] -name = "ppv-lite86" -version = "0.2.21" +name = "crunchy" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] -name = "proc-macro2" -version = "1.0.106" +name = "crypto-common" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" dependencies = [ - "unicode-ident", + "hybrid-array", ] [[package]] -name = "proptest" -version = "1.11.0" +name = "csv" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" +checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" dependencies = [ - "bit-set", - "bit-vec", - "bitflags", - "num-traits", - "rand", - "rand_chacha", - "rand_xorshift", - "regex-syntax", - "rusty-fork", - "tempfile", - "unarray", + "csv-core", + "itoa", + "ryu", + "serde_core", ] [[package]] -name = "prost" -version = "0.13.5" +name = "csv-core" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" dependencies = [ - "bytes", - "prost-derive", + "memchr", ] [[package]] -name = "prost-derive" -version = "0.13.5" +name = "dashmap" +version = "6.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +checksum = "e6361d5c062261c78a176addb82d4c821ae42bed6089de0e12603cd25de2059c" dependencies = [ - "anyhow", - "itertools", - "proc-macro2", - "quote", - "syn", + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", ] [[package]] -name = "quick-error" -version = "1.2.3" +name = "data-encoding" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" [[package]] -name = "quote" -version = "1.0.46" +name = "datafusion" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +checksum = "93db0e623840612f7f2cd757f7e8a8922064192363732c88692e0870016e141b" dependencies = [ - "proc-macro2", + "arrow", + "arrow-schema", + "async-trait", + "bytes", + "chrono", + "datafusion-catalog", + "datafusion-catalog-listing", + "datafusion-common", + "datafusion-common-runtime", + "datafusion-datasource", + "datafusion-datasource-arrow", + "datafusion-datasource-csv", + "datafusion-datasource-json", + "datafusion-execution", + "datafusion-expr", + "datafusion-expr-common", + "datafusion-functions", + "datafusion-functions-aggregate", + "datafusion-functions-table", + "datafusion-functions-window", + "datafusion-optimizer", + "datafusion-physical-expr", + "datafusion-physical-expr-adapter", + "datafusion-physical-expr-common", + "datafusion-physical-optimizer", + "datafusion-physical-plan", + "datafusion-session", + "futures", + "itertools 0.14.0", + "log", + "object_store", + "parking_lot", + "rand 0.9.4", + "regex", + "tempfile", + "tokio", + "url", + "uuid", ] [[package]] -name = "r-efi" -version = "5.3.0" +name = "datafusion-catalog" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +checksum = "37cefde60b26a7f4ff61e9d2ff2833322f91df2b568d7238afe67bde5bdffb66" +dependencies = [ + "arrow", + "async-trait", + "dashmap", + "datafusion-common", + "datafusion-common-runtime", + "datafusion-datasource", + "datafusion-execution", + "datafusion-expr", + "datafusion-physical-expr", + "datafusion-physical-plan", + "datafusion-session", + "futures", + "itertools 0.14.0", + "log", + "object_store", + "parking_lot", + "tokio", +] [[package]] -name = "r-efi" -version = "6.0.0" +name = "datafusion-catalog-listing" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +checksum = "17e112307715d6a7a331111a4c2330ff54bc237183511c319e3708a4cff431fb" +dependencies = [ + "arrow", + "async-trait", + "datafusion-catalog", + "datafusion-common", + "datafusion-datasource", + "datafusion-execution", + "datafusion-expr", + "datafusion-physical-expr", + "datafusion-physical-expr-adapter", + "datafusion-physical-expr-common", + "datafusion-physical-plan", + "futures", + "itertools 0.14.0", + "log", + "object_store", +] [[package]] -name = "rand" -version = "0.9.4" +name = "datafusion-common" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +checksum = "d72a11ca44a95e1081870d3abb80c717496e8a7acb467a1d3e932bb636af5cc2" +dependencies = [ + "ahash 0.8.12", + "arrow", + "arrow-ipc", + "chrono", + "half 2.7.1", + "hashbrown 0.16.1", + "indexmap", + "itertools 0.14.0", + "libc", + "log", + "object_store", + "paste", + "tokio", + "web-time", +] + +[[package]] +name = "datafusion-common-runtime" +version = "53.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89f4afaed29670ec4fd6053643adc749fe3f4bc9d1ce1b8c5679b22c67d12def" dependencies = [ - "rand_chacha", - "rand_core", + "futures", + "log", + "tokio", ] [[package]] -name = "rand_chacha" -version = "0.9.0" +name = "datafusion-datasource" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +checksum = "e9fb386e1691355355a96419978a0022b7947b44d4a24a6ea99f00b6b485cbb6" dependencies = [ - "ppv-lite86", - "rand_core", + "arrow", + "async-trait", + "bytes", + "chrono", + "datafusion-common", + "datafusion-common-runtime", + "datafusion-execution", + "datafusion-expr", + "datafusion-physical-expr", + "datafusion-physical-expr-adapter", + "datafusion-physical-expr-common", + "datafusion-physical-plan", + "datafusion-session", + "futures", + "glob", + "itertools 0.14.0", + "log", + "object_store", + "rand 0.9.4", + "tokio", + "url", ] [[package]] -name = "rand_core" -version = "0.9.5" +name = "datafusion-datasource-arrow" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +checksum = "ffa6c52cfed0734c5f93754d1c0175f558175248bf686c944fb05c373e5fc096" dependencies = [ - "getrandom 0.3.4", + "arrow", + "arrow-ipc", + "async-trait", + "bytes", + "datafusion-common", + "datafusion-common-runtime", + "datafusion-datasource", + "datafusion-execution", + "datafusion-expr", + "datafusion-physical-expr-common", + "datafusion-physical-plan", + "datafusion-session", + "futures", + "itertools 0.14.0", + "object_store", + "tokio", ] [[package]] -name = "rand_xorshift" -version = "0.4.0" +name = "datafusion-datasource-csv" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +checksum = "503f29e0582c1fc189578d665ff57d9300da1f80c282777d7eb67bb79fb8cdca" dependencies = [ - "rand_core", + "arrow", + "async-trait", + "bytes", + "datafusion-common", + "datafusion-common-runtime", + "datafusion-datasource", + "datafusion-execution", + "datafusion-expr", + "datafusion-physical-expr-common", + "datafusion-physical-plan", + "datafusion-session", + "futures", + "object_store", + "regex", + "tokio", ] [[package]] -name = "redox_syscall" -version = "0.5.18" +name = "datafusion-datasource-json" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +checksum = "e33804749abc8d0c8cb7473228483cb8070e524c6f6086ee1b85a64debe2b3d2" dependencies = [ - "bitflags", + "arrow", + "async-trait", + "bytes", + "datafusion-common", + "datafusion-common-runtime", + "datafusion-datasource", + "datafusion-execution", + "datafusion-expr", + "datafusion-physical-expr-common", + "datafusion-physical-plan", + "datafusion-session", + "futures", + "object_store", + "serde_json", + "tokio", + "tokio-stream", ] [[package]] -name = "regex-syntax" -version = "0.8.11" +name = "datafusion-doc" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" +checksum = "8de6ac0df1662b9148ad3c987978b32cbec7c772f199b1d53520c8fa764a87ee" [[package]] -name = "rmp" -version = "0.8.15" +name = "datafusion-execution" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c" +checksum = "c03c7fbdaefcca4ef6ffe425a5fc2325763bfb426599bb0bf4536466efabe709" dependencies = [ - "num-traits", + "arrow", + "arrow-buffer", + "async-trait", + "chrono", + "dashmap", + "datafusion-common", + "datafusion-expr", + "datafusion-physical-expr-common", + "futures", + "log", + "object_store", + "parking_lot", + "rand 0.9.4", + "tempfile", + "url", ] [[package]] -name = "rmp-serde" -version = "1.3.1" +name = "datafusion-expr" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155" +checksum = "574b9b6977fedbd2a611cbff12e5caf90f31640ad9dc5870f152836d94bad0dd" dependencies = [ - "rmp", - "serde", + "arrow", + "async-trait", + "chrono", + "datafusion-common", + "datafusion-doc", + "datafusion-expr-common", + "datafusion-functions-aggregate-common", + "datafusion-functions-window-common", + "datafusion-physical-expr-common", + "indexmap", + "itertools 0.14.0", + "paste", + "serde_json", ] [[package]] -name = "rustc_version" -version = "0.4.1" +name = "datafusion-expr-common" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +checksum = "7d7c3adf3db8bf61e92eb90cb659c8e8b734593a8f7c8e12a843c7ddba24b87e" dependencies = [ - "semver", + "arrow", + "datafusion-common", + "indexmap", + "itertools 0.14.0", + "paste", ] [[package]] -name = "rustix" -version = "1.1.4" +name = "datafusion-functions" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +checksum = "f28aa4e10384e782774b10e72aca4d93ef7b31aa653095d9d4536b0a3dbc51b6" dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys", + "arrow", + "arrow-buffer", + "base64 0.22.1", + "chrono", + "chrono-tz", + "datafusion-common", + "datafusion-doc", + "datafusion-execution", + "datafusion-expr", + "datafusion-expr-common", + "datafusion-macros", + "hex", + "itertools 0.14.0", + "log", + "memchr", + "num-traits", + "rand 0.9.4", + "regex", + "unicode-segmentation", + "uuid", ] [[package]] -name = "rustversion" -version = "1.0.22" +name = "datafusion-functions-aggregate" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "00aa6217e56098ba84e0a338176fe52f0a84cca398021512c6c8c5eff806d0ad" +dependencies = [ + "ahash 0.8.12", + "arrow", + "datafusion-common", + "datafusion-doc", + "datafusion-execution", + "datafusion-expr", + "datafusion-functions-aggregate-common", + "datafusion-macros", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "half 2.7.1", + "log", + "num-traits", + "paste", +] [[package]] -name = "rusty-fork" -version = "0.3.1" +name = "datafusion-functions-aggregate-common" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" +checksum = "b511250349407db7c43832ab2de63f5557b19a20dfd236b39ca2c04468b50d47" dependencies = [ - "fnv", - "quick-error", - "tempfile", - "wait-timeout", + "ahash 0.8.12", + "arrow", + "datafusion-common", + "datafusion-expr-common", + "datafusion-physical-expr-common", ] [[package]] -name = "scopeguard" -version = "1.2.0" +name = "datafusion-functions-table" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +checksum = "72b40d3f5bbb3905f9ccb1ce9485a9595c77b69758a7c24d3ba79e334ff51e7e" +dependencies = [ + "arrow", + "async-trait", + "datafusion-catalog", + "datafusion-common", + "datafusion-expr", + "datafusion-physical-plan", + "parking_lot", + "paste", +] [[package]] -name = "semver" -version = "1.0.28" +name = "datafusion-functions-window" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +checksum = "d4e88ec9d57c9b685d02f58bfee7be62d72610430ddcedb82a08e5d9925dbfb6" +dependencies = [ + "arrow", + "datafusion-common", + "datafusion-doc", + "datafusion-expr", + "datafusion-functions-window-common", + "datafusion-macros", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "log", + "paste", +] [[package]] -name = "serde" -version = "1.0.228" +name = "datafusion-functions-window-common" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "8307bb93519b1a91913723a1130cfafeee3f72200d870d88e91a6fc5470ede5c" dependencies = [ - "serde_core", - "serde_derive", + "datafusion-common", + "datafusion-physical-expr-common", ] [[package]] -name = "serde-big-array" -version = "0.5.1" +name = "datafusion-macros" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11fc7cc2c76d73e0f27ee52abbd64eec84d46f370c88371120433196934e4b7f" +checksum = "2e367e6a71051d0ebdd29b2f85d12059b38b1d1f172c6906e80016da662226bd" dependencies = [ - "serde", + "datafusion-doc", + "quote", + "syn 2.0.118", ] [[package]] -name = "serde_bytes" -version = "0.11.19" +name = "datafusion-optimizer" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" +checksum = "e929015451a67f77d9d8b727b2bf3a40c4445fdef6cdc53281d7d97c76888ace" dependencies = [ - "serde", - "serde_core", + "arrow", + "chrono", + "datafusion-common", + "datafusion-expr", + "datafusion-expr-common", + "datafusion-physical-expr", + "indexmap", + "itertools 0.14.0", + "log", + "regex", + "regex-syntax", ] [[package]] -name = "serde_core" -version = "1.0.228" +name = "datafusion-physical-expr" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "4b1e68aba7a4b350401cfdf25a3d6f989ad898a7410164afe9ca52080244cb59" dependencies = [ - "serde_derive", + "ahash 0.8.12", + "arrow", + "datafusion-common", + "datafusion-expr", + "datafusion-expr-common", + "datafusion-functions-aggregate-common", + "datafusion-physical-expr-common", + "half 2.7.1", + "hashbrown 0.16.1", + "indexmap", + "itertools 0.14.0", + "parking_lot", + "paste", + "petgraph", + "tokio", ] [[package]] -name = "serde_derive" -version = "1.0.228" +name = "datafusion-physical-expr-adapter" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "ea22315f33cf2e0adc104e8ec42e285f6ed93998d565c65e82fec6a9ee9f9db4" dependencies = [ - "proc-macro2", - "quote", - "syn", + "arrow", + "datafusion-common", + "datafusion-expr", + "datafusion-functions", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "itertools 0.14.0", ] [[package]] -name = "serde_json" -version = "1.0.150" +name = "datafusion-physical-expr-common" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +checksum = "b04b45ea8ad3ac2d78f2ea2a76053e06591c9629c7a603eda16c10649ecf4362" dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", + "ahash 0.8.12", + "arrow", + "chrono", + "datafusion-common", + "datafusion-expr-common", + "hashbrown 0.16.1", + "indexmap", + "itertools 0.14.0", + "parking_lot", ] [[package]] -name = "shlex" -version = "2.0.1" +name = "datafusion-physical-optimizer" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" +checksum = "7cb13397809a425918f608dfe8653f332015a3e330004ab191b4404187238b95" +dependencies = [ + "arrow", + "datafusion-common", + "datafusion-execution", + "datafusion-expr", + "datafusion-expr-common", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "datafusion-physical-plan", + "datafusion-pruning", + "itertools 0.14.0", +] [[package]] -name = "signal-hook-registry" -version = "1.4.8" +name = "datafusion-physical-plan" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +checksum = "5edc023675791af9d5fb4cc4c24abf5f7bd3bd4dcf9e5bd90ea1eff6976dcc79" dependencies = [ - "errno", - "libc", + "ahash 0.8.12", + "arrow", + "arrow-ord", + "arrow-schema", + "async-trait", + "datafusion-common", + "datafusion-common-runtime", + "datafusion-execution", + "datafusion-expr", + "datafusion-functions", + "datafusion-functions-aggregate-common", + "datafusion-functions-window-common", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "futures", + "half 2.7.1", + "hashbrown 0.16.1", + "indexmap", + "itertools 0.14.0", + "log", + "num-traits", + "parking_lot", + "pin-project-lite", + "tokio", ] [[package]] -name = "slab" -version = "0.4.12" +name = "datafusion-pruning" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" +checksum = "ac8c76860e355616555081cab5968cec1af7a80701ff374510860bcd567e365a" +dependencies = [ + "arrow", + "datafusion-common", + "datafusion-datasource", + "datafusion-expr-common", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "datafusion-physical-plan", + "itertools 0.14.0", + "log", +] [[package]] -name = "smallvec" -version = "1.15.2" +name = "datafusion-session" +version = "53.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" +checksum = "5412111aa48e2424ba926112e192f7a6b7e4ccb450145d25ce5ede9f19dc491e" +dependencies = [ + "async-trait", + "datafusion-common", + "datafusion-execution", + "datafusion-expr", + "datafusion-physical-plan", + "parking_lot", +] [[package]] -name = "socket2" -version = "0.6.4" +name = "diff" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" dependencies = [ - "libc", - "windows-sys", + "block-buffer", + "const-oid", + "crypto-common", ] [[package]] -name = "syn" -version = "2.0.118" +name = "displaydoc" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" dependencies = [ "proc-macro2", "quote", - "unicode-ident", + "syn 3.0.4", ] [[package]] -name = "tempfile" -version = "3.27.0" +name = "dyn-clone" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" -dependencies = [ - "fastrand", - "getrandom 0.4.3", - "once_cell", - "rustix", - "windows-sys", +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "event-listener" +version = "5.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a23add41df1562121a9393cb065eab5146a1242410f23a644851e90cfd669d2" +dependencies = [ + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "flatbuffers" +version = "25.12.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35f6839d7b3b98adde531effaf34f0c2badc6f4735d26fe74709d8e513a96ef3" +dependencies = [ + "bitflags", + "rustc_version", +] + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "flume" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e139bc46ca777eb5efaf62df0ab8cc5fd400866427e56c68b22e414e53bd3be" +dependencies = [ + "futures-core", + "futures-sink", + "spin", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "rand_core 0.10.1", +] + +[[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" + +[[package]] +name = "glob" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" + +[[package]] +name = "h2" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef8e5e5a340588f4452631496976cf8636d4a7ecf600239fdc27615d2530bc16" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "half" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "num-traits", + "zerocopy", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "http" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humantime" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15cdd26707701c53297e2fa6afb323d55fbc1d0810c3aec078ae3ef0424c3c15" + +[[package]] +name = "humantime-serde" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" +dependencies = [ + "humantime", + "serde", +] + +[[package]] +name = "hybrid-array" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707114b52a152fa7bdb290cd7cd5912d9467273b6d74e21b8d81aca1f8533f6b" +dependencies = [ + "typenum", +] + +[[package]] +name = "hyper" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d22053281f852e11534f5198498373cbb59295120a20771d90f7ed1897490a72" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-timeout" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +dependencies = [ + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "system-configuration", + "tokio", + "tower-service", + "tracing", + "windows-registry", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" + +[[package]] +name = "icu_properties" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" + +[[package]] +name = "icu_provider" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", +] + +[[package]] +name = "inotify" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cc00ea907cab49550b7da656f80ebb97be1b997d931fbcd28d39734e17ce592" +dependencies = [ + "bitflags", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c033f80b2c113cdf91ab7a33faa9cbc014726dcad99880c8609af2a370edf37d" +dependencies = [ + "libc", +] + +[[package]] +name = "ipnet" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a756c3fac73139e83f14c2d742155dd2b78d3ee56597b419a0579b7bdd6dd78" + +[[package]] +name = "is_ci" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b4baf93f58d4425749ca49a51c50ebab072c5df6994d08fed93541c331481dc" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys", + "log", + "simd_cesu8", + "thiserror 2.0.20", + "walkdir", + "windows-link", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn 2.0.118", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.118", +] + +[[package]] +name = "jobserver" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" +dependencies = [ + "getrandom 0.4.3", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "kqueue" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d763e5b24120b4ddf50de6c92308156765aabfbbccebf401da7cff2d70a41ea" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07293a4e297ac234359b510362495713f75ea345d5307140414f20c69ffeb087" +dependencies = [ + "bitflags", + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lexical-core" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d8d125a277f807e55a77304455eb7b1cb52f2b18c143b60e766c120bd64a594" +dependencies = [ + "lexical-parse-float", + "lexical-parse-integer", + "lexical-util", + "lexical-write-float", + "lexical-write-integer", +] + +[[package]] +name = "lexical-parse-float" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52a9f232fbd6f550bc0137dcb5f99ab674071ac2d690ac69704593cb4abbea56" +dependencies = [ + "lexical-parse-integer", + "lexical-util", +] + +[[package]] +name = "lexical-parse-integer" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a7a039f8fb9c19c996cd7b2fcce303c1b2874fe1aca544edc85c4a5f8489b34" +dependencies = [ + "lexical-util", +] + +[[package]] +name = "lexical-util" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2604dd126bb14f13fb5d1bd6a66155079cb9fa655b37f875b3a742c705dbed17" + +[[package]] +name = "lexical-write-float" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c438c87c013188d415fbabbb1dceb44249ab81664efbd31b14ae55dabb6361" +dependencies = [ + "lexical-util", + "lexical-write-integer", +] + +[[package]] +name = "lexical-write-integer" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "409851a618475d2d5796377cad353802345cba92c867d9fbcde9cf4eac4e14df" +dependencies = [ + "lexical-util", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "linkme" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3045e122bd98aef8ec3ad58ce84f0791f64e70163d1a02710af4aa11a4d54cc5" +dependencies = [ + "linkme-impl", +] + +[[package]] +name = "linkme-impl" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77060ebe535362c3da75682cd17b0431017b6e7c5661e714fc69a7ad017d1301" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.4", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" + +[[package]] +name = "lz4_flex" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef0d4ed8669f8f8826eb00dc878084aa8f253506c4fd5e8f58f5bce72ddb97e" +dependencies = [ + "twox-hash", +] + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + +[[package]] +name = "memchr" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" + +[[package]] +name = "memmap2" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0" +dependencies = [ + "libc", +] + +[[package]] +name = "memory-stats" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c73f5c649995a115e1a0220b35e4df0a1294500477f97a91d0660fb5abeb574a" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "miette" +version = "7.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7" +dependencies = [ + "backtrace", + "backtrace-ext", + "cfg-if", + "miette-derive", + "owo-colors", + "supports-color", + "supports-hyperlinks", + "supports-unicode", + "terminal_size", + "textwrap", + "unicode-width 0.1.14", +] + +[[package]] +name = "miette-derive" +version = "7.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "multimap" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" + +[[package]] +name = "nix" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "notify" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" +dependencies = [ + "bitflags", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "log", + "mio", + "notify-types", + "walkdir", + "windows-sys 0.60.2", +] + +[[package]] +name = "notify-types" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "num-bigint" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_enum" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + +[[package]] +name = "object_store" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622acbc9100d3c10e2ee15804b0caa40e55c933d5aa53814cd520805b7958a49" +dependencies = [ + "async-trait", + "bytes", + "chrono", + "futures-channel", + "futures-core", + "futures-util", + "http", + "humantime", + "itertools 0.14.0", + "parking_lot", + "percent-encoding", + "thiserror 2.0.20", + "tokio", + "tracing", + "url", + "walkdir", + "wasm-bindgen-futures", + "web-time", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "opentelemetry" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0142c63252a9e054e68a4c61a5778f7b14f576274d593f8ce883d191a099682" +dependencies = [ + "futures-core", + "futures-sink", + "js-sys", + "pin-project-lite", + "thiserror 2.0.20", + "tracing", +] + +[[package]] +name = "opentelemetry-resource-detectors" +version = "0.11.0" +source = "git+https://github.com/open-telemetry/opentelemetry-rust-contrib.git?rev=7819998d4bd015b7ae7576b1e26aaa56314f03e6#7819998d4bd015b7ae7576b1e26aaa56314f03e6" +dependencies = [ + "opentelemetry", + "opentelemetry-semantic-conventions", + "opentelemetry_sdk", +] + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c913ac17a6c451661ee255f4625d143e51647ae78ebd969b75e41c4442f4fe47" + +[[package]] +name = "opentelemetry_sdk" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b59f80e1ac4d5ff7a2db8fb6c80badb7f0f3f858211fba08dd9aaec750894f9" +dependencies = [ + "futures-channel", + "futures-executor", + "futures-util", + "opentelemetry", + "portable-atomic", + "thiserror 2.0.20", +] + +[[package]] +name = "otel-arrow-dfe-channel" +version = "0.51.0" +source = "git+https://github.com/open-telemetry/otel-arrow?rev=3e85c3460361446ebfce99e9f35fffd2dd5ab740#3e85c3460361446ebfce99e9f35fffd2dd5ab740" +dependencies = [ + "thiserror 2.0.20", + "tokio", +] + +[[package]] +name = "otel-arrow-dfe-component-inventory-syntax" +version = "0.51.0" +source = "git+https://github.com/open-telemetry/otel-arrow?rev=3e85c3460361446ebfce99e9f35fffd2dd5ab740#3e85c3460361446ebfce99e9f35fffd2dd5ab740" +dependencies = [ + "proc-macro2", + "syn 2.0.118", +] + +[[package]] +name = "otel-arrow-dfe-config" +version = "0.51.0" +source = "git+https://github.com/open-telemetry/otel-arrow?rev=3e85c3460361446ebfce99e9f35fffd2dd5ab740#3e85c3460361446ebfce99e9f35fffd2dd5ab740" +dependencies = [ + "byte-unit", + "humantime-serde", + "miette", + "reqwest", + "schemars", + "serde", + "serde_json", + "serde_yaml", + "thiserror 2.0.20", + "tracing-subscriber", +] + +[[package]] +name = "otel-arrow-dfe-engine" +version = "0.51.0" +source = "git+https://github.com/open-telemetry/otel-arrow?rev=3e85c3460361446ebfce99e9f35fffd2dd5ab740#3e85c3460361446ebfce99e9f35fffd2dd5ab740" +dependencies = [ + "async-channel", + "async-trait", + "bitflags", + "bytemuck", + "cpu-time", + "data-encoding", + "flume", + "futures", + "futures-core", + "linkme", + "memory-stats", + "nix", + "otel-arrow-dfe-channel", + "otel-arrow-dfe-component-inventory-syntax", + "otel-arrow-dfe-config", + "otel-arrow-dfe-engine-macros", + "otel-arrow-dfe-pdata", + "otel-arrow-dfe-state", + "otel-arrow-dfe-telemetry", + "otel-arrow-dfe-telemetry-macros", + "parking_lot", + "secrecy", + "serde_json", + "smallvec", + "socket2", + "thiserror 2.0.20", + "tokio", + "uuid", +] + +[[package]] +name = "otel-arrow-dfe-engine-macros" +version = "0.51.0" +source = "git+https://github.com/open-telemetry/otel-arrow?rev=3e85c3460361446ebfce99e9f35fffd2dd5ab740#3e85c3460361446ebfce99e9f35fffd2dd5ab740" +dependencies = [ + "otel-arrow-dfe-component-inventory-syntax", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "otel-arrow-dfe-expohisto" +version = "0.51.0" +source = "git+https://github.com/open-telemetry/otel-arrow?rev=3e85c3460361446ebfce99e9f35fffd2dd5ab740#3e85c3460361446ebfce99e9f35fffd2dd5ab740" + +[[package]] +name = "otel-arrow-dfe-otap" +version = "0.51.0" +source = "git+https://github.com/open-telemetry/otel-arrow?rev=3e85c3460361446ebfce99e9f35fffd2dd5ab740#3e85c3460361446ebfce99e9f35fffd2dd5ab740" +dependencies = [ + "arc-swap", + "arrow", + "async-trait", + "base64 0.23.1", + "bytes", + "cfg-if", + "flate2", + "futures", + "http", + "http-body-util", + "humantime", + "humantime-serde", + "hyper", + "hyper-util", + "ipnet", + "linkme", + "notify", + "object_store", + "otel-arrow-dfe-channel", + "otel-arrow-dfe-config", + "otel-arrow-dfe-engine", + "otel-arrow-dfe-engine-macros", + "otel-arrow-dfe-pdata", + "otel-arrow-dfe-quiver", + "otel-arrow-dfe-telemetry", + "otel-arrow-dfe-telemetry-macros", + "parking_lot", + "prost 0.14.4", + "prost-types", + "reqwest", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "secrecy", + "serde", + "serde_json", + "slotmap", + "smallvec", + "socket2", + "thiserror 2.0.20", + "tokio", + "tokio-rustls", + "tokio-stream", + "tokio-util", + "tonic", + "tonic-middleware", + "tower", + "tower-service", + "url", + "zstd", +] + +[[package]] +name = "otel-arrow-dfe-pdata" +version = "0.51.0" +source = "git+https://github.com/open-telemetry/otel-arrow?rev=3e85c3460361446ebfce99e9f35fffd2dd5ab740#3e85c3460361446ebfce99e9f35fffd2dd5ab740" +dependencies = [ + "ahash 0.8.12", + "arrow", + "arrow-ipc", + "arrow-schema", + "base64 0.23.1", + "bytes", + "ciborium", + "datafusion", + "hex", + "itertools 0.15.0", + "nix", + "num_enum", + "otel-arrow-dfe-config", + "otel-arrow-dfe-pdata-otlp-macros", + "otel-arrow-dfe-pdata-otlp-model", + "otel-arrow-dfe-pdata-views", + "paste", + "pretty_assertions", + "prost 0.14.4", + "rand 0.10.2", + "replace_with", + "roaring", + "serde", + "serde_cbor", + "serde_json", + "sha2", + "simdutf8", + "smallvec", + "thiserror 2.0.20", + "tokio", + "tonic", + "tonic-prost", +] + +[[package]] +name = "otel-arrow-dfe-pdata-otlp-macros" +version = "0.51.0" +source = "git+https://github.com/open-telemetry/otel-arrow?rev=3e85c3460361446ebfce99e9f35fffd2dd5ab740#3e85c3460361446ebfce99e9f35fffd2dd5ab740" +dependencies = [ + "otel-arrow-dfe-pdata-otlp-model", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "otel-arrow-dfe-pdata-otlp-model" +version = "0.51.0" +source = "git+https://github.com/open-telemetry/otel-arrow?rev=3e85c3460361446ebfce99e9f35fffd2dd5ab740#3e85c3460361446ebfce99e9f35fffd2dd5ab740" +dependencies = [ + "tonic-prost-build", +] + +[[package]] +name = "otel-arrow-dfe-pdata-views" +version = "0.51.0" +source = "git+https://github.com/open-telemetry/otel-arrow?rev=3e85c3460361446ebfce99e9f35fffd2dd5ab740#3e85c3460361446ebfce99e9f35fffd2dd5ab740" + +[[package]] +name = "otel-arrow-dfe-quiver" +version = "0.51.0" +source = "git+https://github.com/open-telemetry/otel-arrow?rev=3e85c3460361446ebfce99e9f35fffd2dd5ab740#3e85c3460361446ebfce99e9f35fffd2dd5ab740" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-cast", + "arrow-ipc", + "arrow-schema", + "arrow-select", + "bytes", + "crc32fast", + "memmap2", + "nix", + "parking_lot", + "thiserror 2.0.20", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "otel-arrow-dfe-state" +version = "0.51.0" +source = "git+https://github.com/open-telemetry/otel-arrow?rev=3e85c3460361446ebfce99e9f35fffd2dd5ab740#3e85c3460361446ebfce99e9f35fffd2dd5ab740" +dependencies = [ + "chrono", + "flume", + "otel-arrow-dfe-config", + "otel-arrow-dfe-telemetry", + "serde", + "serde_json", + "thiserror 2.0.20", + "tokio", + "tokio-util", +] + +[[package]] +name = "otel-arrow-dfe-telemetry" +version = "0.51.0" +source = "git+https://github.com/open-telemetry/otel-arrow?rev=3e85c3460361446ebfce99e9f35fffd2dd5ab740#3e85c3460361446ebfce99e9f35fffd2dd5ab740" +dependencies = [ + "arc-swap", + "bytes", + "chrono", + "flume", + "opentelemetry", + "opentelemetry-resource-detectors", + "opentelemetry_sdk", + "otel-arrow-dfe-config", + "otel-arrow-dfe-expohisto", + "otel-arrow-dfe-pdata", + "otel-arrow-dfe-pdata-views", + "otel-arrow-dfe-telemetry-macros", + "parking_lot", + "serde", + "serde_yaml", + "slotmap", + "smallvec", + "thiserror 2.0.20", + "tokio", + "tokio-util", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "otel-arrow-dfe-telemetry-macros" +version = "0.51.0" +source = "git+https://github.com/open-telemetry/otel-arrow?rev=3e85c3460361446ebfce99e9f35fffd2dd5ab740#3e85c3460361446ebfce99e9f35fffd2dd5ab740" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "owo-colors" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "petgraph" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" +dependencies = [ + "fixedbitset", + "hashbrown 0.15.5", + "indexmap", + "serde", +] + +[[package]] +name = "phf" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_shared" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06005508882fb681fd97892ecff4b7fd0fee13ef1aa569f8695dae7ab9099981" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkg-config" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" + +[[package]] +name = "portable-atomic" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" + +[[package]] +name = "potential_utf" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" +dependencies = [ + "zerovec", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "pretty_assertions" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" +dependencies = [ + "diff", + "yansi", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.118", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags", + "num-traits", + "rand 0.9.4", + "rand_chacha 0.9.0", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "prost" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +dependencies = [ + "bytes", + "prost-derive 0.13.5", +] + +[[package]] +name = "prost" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528ac67416ff8646872a3c02cad9cc4ee5dc9f9540c9b10771855c95cb2e5ae1" +dependencies = [ + "bytes", + "prost-derive 0.14.4", +] + +[[package]] +name = "prost-build" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03da047801ff44bb6a4d407d4860c05fd70bb81714e6b2f3812603d5b145b042" +dependencies = [ + "heck", + "itertools 0.14.0", + "log", + "multimap", + "petgraph", + "prettyplease", + "prost 0.14.4", + "prost-types", + "pulldown-cmark", + "pulldown-cmark-to-cmark", + "regex", + "syn 2.0.118", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +dependencies = [ + "anyhow", + "itertools 0.14.0", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "prost-derive" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" +dependencies = [ + "anyhow", + "itertools 0.14.0", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "prost-types" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f94967dc7688f3054c7fac87473ffae4cc4c3904800e2d9f5b857246d8963b0a" +dependencies = [ + "prost 0.14.4", +] + +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "pulldown-cmark" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9f068eba8e7071c5f9511831b44f32c740d5adf574e990f946ddb53db2f314e" +dependencies = [ + "bitflags", + "memchr", + "unicase", +] + +[[package]] +name = "pulldown-cmark-to-cmark" +version = "22.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab1ad36992cead65f02aa399a373a42730922f1525d988172634fdefdecb8a60" +dependencies = [ + "pulldown-cmark", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" +dependencies = [ + "chacha20", + "getrandom 0.4.3", + "rand_core 0.10.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + +[[package]] +name = "rand_xorshift" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +dependencies = [ + "rand_core 0.9.5", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "ref-cast" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e440fb4e4b4147295338efb76001ab9e4efc0e5839df2c47fc5ac2381d365c3" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecd8964f8453721699a1ed72037b0db49ce2f5a5138486ee89bed6f67cdf3a" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.4", +] + +[[package]] +name = "regex" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "replace_with" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51743d3e274e2b18df81c4dc6caf8a5b8e15dbe799e0dca05c7617380094e884" + +[[package]] +name = "reqwest" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "mime", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rkyv" +version = "0.7.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2297bf9c81a3f0dc96bc9521370b88f054168c29826a75e89c55ff196e7ed6a1" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d7b42d4b8d06048d3ac8db0eb31bcb942cbeb709f0b5f2b2ebde398d3038f5" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rmp" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "rmp-serde" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155" +dependencies = [ + "rmp", + "serde", +] + +[[package]] +name = "roaring" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18bd8a37d17a58532776dcdf6041ce64929adca78e8489d5cacbafe99229d3e1" +dependencies = [ + "bytemuck", + "byteorder", +] + +[[package]] +name = "rust_decimal" +version = "1.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be2a24f50780bc85f09cc6ac299bdf1424302742d77221106859c9d8b102126a" +dependencies = [ + "arrayvec", + "borsh", + "bytes", + "num-traits", + "rand 0.8.7", + "rkyv", + "serde", + "serde_json", + "wasm-bindgen", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b74b56ffa8bb2830709a538c2cbcae9aa062db0d2a42563bfb09bdaae44020eb" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06" +dependencies = [ + "log", + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pki-types" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-platform-verifier" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + +[[package]] +name = "rustls-webpki" +version = "0.103.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "rusty-fork" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "schemars" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "687274d293b6cdc6e73e0fee520bf2049650090d7164f87672d212a3c530cf4a" +dependencies = [ + "dyn-clone", + "ref-cast", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98c67716b46af2f0b8cf752abc930f6f9aecfbf671ecfb531db8a31dbe4e2ba" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 3.0.4", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "secrecy" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a" +dependencies = [ + "serde", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde-big-array" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11fc7cc2c76d73e0f27ee52abbd64eec84d46f370c88371120433196934e4b7f" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_bytes" +version = "0.11.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" +dependencies = [ + "half 1.8.3", + "serde", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "serde_derive_internals" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f852137cce035d6a4df67ccce505ff6b3e9fd3a10e3e52b24dc71e650bb1a9bd" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.4", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" + +[[package]] +name = "simd_cesu8" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520" +dependencies = [ + "rustc_version", + "simdutf8", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "siphasher" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "slotmap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038" +dependencies = [ + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" + +[[package]] +name = "socket2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "spin" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3763264f6b73151db08c50ff20d7d8a0b8796e021cdea7ceedad07b80155fa0e" +dependencies = [ + "lock_api", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "supports-color" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fc7232dd8d2e4ac5ce4ef302b1d81e0b80d055b9d77c7c4f51f6aa4c867d6" +dependencies = [ + "is_ci", +] + +[[package]] +name = "supports-hyperlinks" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e396b6523b11ccb83120b115a0b7366de372751aa6edf19844dfb13a6af97e91" + +[[package]] +name = "supports-unicode" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "system-configuration" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +dependencies = [ + "bitflags", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.3", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "terminal_size" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" +dependencies = [ + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "textwrap" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" +dependencies = [ + "unicode-linebreak", + "unicode-width 0.2.2", ] [[package]] @@ -939,7 +4205,16 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" +dependencies = [ + "thiserror-impl 2.0.20", ] [[package]] @@ -950,7 +4225,27 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.4", +] + +[[package]] +name = "thread_local" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070" +dependencies = [ + "cfg-if", ] [[package]] @@ -962,6 +4257,31 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tinystr" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.52.3" @@ -976,51 +4296,380 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d06f0b082ba57c26b79407372e57cf2a1e28124f78e9479fe80322cf53420b" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-util" +version = "0.7.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "futures-util", + "libc", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.25.13+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b" +dependencies = [ + "indexmap", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.1.3+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" +dependencies = [ + "winnow", +] + +[[package]] +name = "tonic" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac2a5518c70fa84342385732db33fb3f44bc4cc748936eb5833d2df34d6445ef" +dependencies = [ + "async-trait", + "axum", + "base64 0.22.1", + "bytes", + "flate2", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project", + "rustls-native-certs", + "socket2", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", + "zstd", +] + +[[package]] +name = "tonic-build" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f61875ac5293cf72e6c8cf0158086428c82c37229e98c840878f1706b0322" +dependencies = [ + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "tonic-middleware" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28272673571986af2ff4337088c2b7314dc20f45e81c4fe1e75cd78feb619002" +dependencies = [ + "async-trait", + "futures-util", + "tonic", + "tower", +] + +[[package]] +name = "tonic-prost" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50849f68853be452acf590cde0b146665b8d507b3b8af17261df47e02c209ea0" +dependencies = [ + "bytes", + "prost 0.14.4", + "tonic", +] + +[[package]] +name = "tonic-prost-build" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "654e5643eff75d7f8c99197ce1440ed19a3474eada74c12bbac488b2cafdae27" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build", + "prost-types", + "quote", + "syn 2.0.118", + "tempfile", + "tonic-build", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "indexmap", + "pin-project-lite", + "slab", + "sync_wrapper", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "async-compression", + "bitflags", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "pin-project-lite", + "tokio", + "tokio-util", + "tower", + "tower-layer", + "tower-service", + "url", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "matchers", + "once_cell", + "regex-automata", + "sharded-slab", + "thread_local", + "tracing", + "tracing-core", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "twox-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c" +dependencies = [ + "rand 0.9.4", +] + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] -name = "tokio-stream" -version = "0.1.19" +name = "url" +version = "2.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3d06f0b082ba57c26b79407372e57cf2a1e28124f78e9479fe80322cf53420b" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", + "form_urlencoded", + "idna", + "percent-encoding", + "serde", ] [[package]] -name = "twox-hash" -version = "2.1.2" +name = "utf8-width" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c" -dependencies = [ - "rand", -] +checksum = "159a7cadce548703edd50d24069bc294c5415ecab0a480e0cd1ca06d112dc94a" [[package]] -name = "unarray" -version = "0.1.4" +name = "utf8_iter" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] -name = "unicode-ident" -version = "1.0.24" +name = "uuid" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +checksum = "f053576934f05a761a402421fbbe3d425d9366f75f978806a037b3ca481abecc" +dependencies = [ + "getrandom 0.4.3", + "js-sys", + "wasm-bindgen", +] [[package]] name = "version_check" @@ -1037,6 +4686,25 @@ dependencies = [ "libc", ] +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -1065,6 +4733,16 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.126" @@ -1084,7 +4762,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.118", "wasm-bindgen-shared", ] @@ -1097,6 +4775,79 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-streams" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-core" version = "0.62.2" @@ -1118,7 +4869,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -1129,7 +4880,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -1138,6 +4889,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link", + "windows-result", + "windows-strings", +] + [[package]] name = "windows-result" version = "0.4.1" @@ -1156,6 +4918,24 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + [[package]] name = "windows-sys" version = "0.61.2" @@ -1165,18 +4945,200 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winnow" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" +dependencies = [ + "memchr", +] + [[package]] name = "wit-bindgen" version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" +[[package]] +name = "writeable" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "xxhash-rust" version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d93c89cdc2d3a63c3ec48ffe926931bdc069eafa8e4402fe6d8f790c9d1e576" +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + +[[package]] +name = "yoke" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.8.53" @@ -1194,7 +5156,67 @@ checksum = "4714fd92cf900833d49538023a9b3915155210801d1c1169eba513b2addefd71" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" + +[[package]] +name = "zerotrie" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.4", ] [[package]] @@ -1202,3 +5224,31 @@ name = "zmij" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.16+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/asap-precompute-rs/Cargo.toml b/asap-precompute-rs/Cargo.toml index 5695684..0c53ac2 100644 --- a/asap-precompute-rs/Cargo.toml +++ b/asap-precompute-rs/Cargo.toml @@ -19,7 +19,7 @@ thiserror = "1" # Pinned to 58.3 to match the OTAP Dataflow workspace's own pin # (rust/otap-dataflow/Cargo.toml as of otel-arrow@3e85c34, 2026-08-24) # rather than an independently-chosen version: `RecordBatch` built here -# must be the literal same type `otap-patch/`'s upstream-facing binding +# must be the literal same type `otap::codec`'s upstream-facing binding # code hands to OTAP's engine, which only holds if both sides resolve # to one canonical `arrow-array` version. An older/newer pin here would # make that binding a type mismatch even though both are "Arrow". @@ -38,6 +38,29 @@ arrow-ipc = { version = "58.3", optional = true } tokio = { version = "1", features = ["rt", "rt-multi-thread", "sync", "time", "macros", "net", "io-util"], optional = true } futures = { version = "0.3", optional = true } +# Real OTAP Dataflow engine + pdata crates, for `otap::processor` / +# `otap::codec` (the `AsapSketchesProcessor` node + its direct +# `SketchEnvelope <-> OtapPdata` binding). Plain git dependencies on +# named packages inside `open-telemetry/otel-arrow`'s own Cargo +# workspace — Cargo clones the whole repo and resolves each named +# package's own `workspace = true` deps against *that* clone's +# workspace root, so this crate does NOT need to be staged as a member +# of that workspace itself (unlike the old `otap-patch/` overlay this +# replaced, which did). Pinned to the same rev as every other real-OTAP +# reference in this crate (otel-arrow issue #1848's very recent +# `otap_df_*` -> `otel_arrow_dfe_*` rename is already reflected here). +otel-arrow-dfe-engine = { git = "https://github.com/open-telemetry/otel-arrow", rev = "3e85c3460361446ebfce99e9f35fffd2dd5ab740", package = "otel-arrow-dfe-engine", optional = true } +otel-arrow-dfe-otap = { git = "https://github.com/open-telemetry/otel-arrow", rev = "3e85c3460361446ebfce99e9f35fffd2dd5ab740", package = "otel-arrow-dfe-otap", optional = true } +otel-arrow-dfe-config = { git = "https://github.com/open-telemetry/otel-arrow", rev = "3e85c3460361446ebfce99e9f35fffd2dd5ab740", package = "otel-arrow-dfe-config", optional = true } +otel-arrow-dfe-pdata = { git = "https://github.com/open-telemetry/otel-arrow", rev = "3e85c3460361446ebfce99e9f35fffd2dd5ab740", package = "otel-arrow-dfe-pdata", optional = true } +otel-arrow-dfe-pdata-views = { git = "https://github.com/open-telemetry/otel-arrow", rev = "3e85c3460361446ebfce99e9f35fffd2dd5ab740", package = "otel-arrow-dfe-pdata-views", optional = true } +otel-arrow-dfe-state = { git = "https://github.com/open-telemetry/otel-arrow", rev = "3e85c3460361446ebfce99e9f35fffd2dd5ab740", package = "otel-arrow-dfe-state", optional = true } +otel-arrow-dfe-telemetry = { git = "https://github.com/open-telemetry/otel-arrow", rev = "3e85c3460361446ebfce99e9f35fffd2dd5ab740", package = "otel-arrow-dfe-telemetry", optional = true } +linkme = { version = "0.3", optional = true } +async-trait = { version = "0.1", optional = true } +humantime-serde = { version = "1", optional = true } +serde_json = { version = "1", optional = true } + [dev-dependencies] proptest = "1" serde_json = "1" @@ -54,8 +77,32 @@ tokio-stream = "0.1" default = [] # Enables the OTAP codec + plugin lifecycle at `crate::otap` # (decode_batch / encode_batch over `arrow::RecordBatch`, plus the -# `AsapSketchesPlugin` Tokio runtime). +# `AsapSketchesPlugin` Tokio runtime, plus the legacy +# `SeriesDictionary` / `otap::wire` transport). No dependency on real +# OTAP Dataflow crates — this is the standalone-buildable half. otap = ["dep:arrow-array", "dep:arrow-schema", "dep:arrow-ipc", "dep:tokio", "dep:futures"] +# Enables `otap::processor`/`otap::codec` — the real +# `local::Processor` node (`AsapSketchesProcessor`) and its +# direct `SketchEnvelope <-> OtapPdata` binding, registered via +# `linkme` under `urn:asap:processor:asap_sketches`. Pulls in the real +# OTAP Dataflow engine/pdata crates (via git, see the dependency +# comments above) — heavier and slower to build than plain `otap` +# (datafusion, tonic, the works), so it's its own feature rather than +# folded into `otap`. +otap-engine = [ + "otap", + "dep:otel-arrow-dfe-engine", + "dep:otel-arrow-dfe-otap", + "dep:otel-arrow-dfe-config", + "dep:otel-arrow-dfe-pdata", + "dep:otel-arrow-dfe-pdata-views", + "dep:otel-arrow-dfe-state", + "dep:otel-arrow-dfe-telemetry", + "dep:linkme", + "dep:async-trait", + "dep:humantime-serde", + "dep:serde_json", +] [[example]] name = "sketch_pipeline_demo" diff --git a/asap-precompute-rs/plugins/asap_sketches/README.md b/asap-precompute-rs/plugins/asap_sketches/README.md new file mode 100644 index 0000000..a988632 --- /dev/null +++ b/asap-precompute-rs/plugins/asap_sketches/README.md @@ -0,0 +1,72 @@ +# `asap_sketches` — OTAP-Rust unified ASAP precompute plugin + +One plugin parameterized by `sketch_type` that adapts the +host-neutral ASAP precompute runtime to OTAP Dataflow's Arrow-native +streaming model. + +This directory holds the user-facing configuration reference: sample +TOML and this README. The actual code — the `local::Processor` +adapter, the direct `SketchEnvelope <-> OtapPdata` codec, and the +`linkme` registration — lives in +[`asap-precompute-rs/src/otap/{processor,codec}.rs`](../../src/otap/), +behind the `otap-engine` Cargo feature. There is no separate overlay +crate or build-time staging step: `asap-precompute-rs` depends +directly on the real OTAP Dataflow crates (via git, pinned to a +specific `open-telemetry/otel-arrow` commit — see `Cargo.toml`) when +that feature is enabled, and `cargo build --features otap-engine` +builds the whole thing. + +Design notes: + +- This is a single unified plugin rather than five per-sketch + plugins. +- Registered under the URN `urn:asap:processor:asap_sketches` + (`processor.rs`'s `ASAP_SKETCHES_PROCESSOR_FACTORY`). +- The configuration shape is documented by + [`sample.toml`](./sample.toml), and validated by + `AsapSketchesUserConfig` (`processor.rs`). +- There is exactly one transport: whatever OTAP pipeline this node is + wired into (`effect_handler.send_message_with_source_node`). No + direct-TCP alternative — see `processor.rs`'s module doc, "There is + exactly one transport: the pipeline", for why. + +## Configuration + +See [`sample.toml`](./sample.toml) for the canonical TOML block. +Field reference: + +| Field | Required | Description | +|---|---|---| +| `sketch_type` | yes | One of `ddsketch` / `kll` / `hll` / `countsketch` / `countminsketch`. Case-insensitive. | +| `window_size` | yes | Duration string (e.g. `"10s"`). Tumbling window rotation period. | +| `output_metric_name` | yes | Metric name stamped onto every emitted envelope. | +| `agg_id` | optional | Controller-plan join key. Defaults to `0`. | +| `sketch_params.relative_accuracy` | ddsketch | DDSketch alpha (`0 < α < 1`). Default `0.01`. | +| `sketch_params.k` | kll | KLL buffer size. Default `200`. | +| `sketch_params.seed` | kll | Optional deterministic compaction seed. Default unseeded. | +| `sketch_params.precision` | hll | HLL register-count exponent (4-18). Default `12`. | +| `sketch_params.width` | countsketch, cms | Sketch column count. Default `2048`. | +| `sketch_params.depth` | countsketch, cms | Sketch row count. Default `4`. | +| `controller_url` | optional | Legacy bootstrap field, predates the engine's own OpAMP controller extension (which now owns `instance_uid`/`endpoint`/`agent_description` centrally) — likely a deprecation candidate rather than something to keep pushing new values into. | +| `agent_id` | optional | Same caveat as `controller_url`. | + +## Lifecycle + +`AsapSketchesProcessor` (`processor.rs`) drives a bare `Precompute` +instance directly from OTAP's own per-message/per-timer `process()` +calls — no separate Tokio-task lifecycle needed: + +1. **`Message::PData`** — `codec::decode_pdata_to_observations()` + turns the real `OtapPdata` directly into `Vec` + (content-routed: a data point carrying `_asap_envelope` merges as a + pre-aggregated sketch via `Precompute::observe_envelope`; everything + else is a plain scalar sample). +2. **`NodeControlMsg::TimerTick`** (regular flush) / **`Shutdown`** + (final drain) — `Precompute::tick`/`drain` produce + `SketchEnvelope`s, and `codec::encode_envelopes_to_pdata()` builds + the real `OtapPdata` directly from them (no intermediate + `RecordBatch` or two-batch family) before sending via + `effect_handler.send_message_with_source_node`. +3. **`NodeControlMsg::Config`** — parses the same + `AsapSketchesUserConfig` JSON shape used at startup and applies it + via `Precompute::update_config`. diff --git a/otap-patch/plugins/asap_sketches/sample.toml b/asap-precompute-rs/plugins/asap_sketches/sample.toml similarity index 60% rename from otap-patch/plugins/asap_sketches/sample.toml rename to asap-precompute-rs/plugins/asap_sketches/sample.toml index 724fd68..673a06f 100644 --- a/otap-patch/plugins/asap_sketches/sample.toml +++ b/asap-precompute-rs/plugins/asap_sketches/sample.toml @@ -4,9 +4,8 @@ # plugin is parameterized by `sketch_type`; multi-sketch deployments # declare multiple blocks with different `id` + `sketch_type` values. # -# Phase C (asap-precompute-rs/src/otap/) implements the lifecycle -# around this config block; Phase D will wire the TOML loader once the -# OTAP submodule binding lands. +# Registered under urn:asap:processor:asap_sketches +# (asap-precompute-rs/src/otap/processor.rs, `otap-engine` feature). [[pipelines.metrics.processors]] id = "asap_ddsketch" @@ -21,13 +20,11 @@ plugin = "asap_sketches" ## "countminsketch" (frequency, biased upward) sketch_type = "ddsketch" - ## Window size. Rust duration string parsed via OTAP's TOML - ## convention; passed verbatim into `PrecomputeConfig::window`. + ## Window size. Rust duration string, parsed via humantime; + ## passed verbatim into `PrecomputeConfig::window`. window_size = "10s" - ## Output metric name stamped onto every emitted envelope. The - ## codec uses this as the stable `metric` column of every - ## emitted RecordBatch. + ## Output metric name stamped onto every emitted envelope. output_metric_name = "http_request_duration_ms" ## Sketch-specific tuning knobs. Only the keys relevant to @@ -39,10 +36,12 @@ plugin = "asap_sketches" # width = 2048 # countsketch / countminsketch — width # depth = 4 # countsketch / countminsketch — depth - ## Bootstrap — controller for plan delivery via the - ## `ControlChannel` impl wired in Phase D. - ## Sketch-runtime params (sketch_type, window, sketch_params) above - ## are also push-overridable from the controller plan; the values - ## here are bootstrap defaults used until the first plan arrives. - controller_url = "http://controller:8080" - agent_id = "otap-host-01" + ## Legacy bootstrap fields — predate the engine's own OpAMP + ## controller extension, which now owns instance identification and + ## remote-config delivery centrally. Sketch-runtime params + ## (sketch_type, window, sketch_params) above are push-overridable + ## via NodeControlMsg::Config from that path; these two fields are + ## likely deprecation candidates rather than something to configure + ## going forward. + # controller_url = "http://controller:8080" + # agent_id = "otap-host-01" diff --git a/asap-precompute-rs/src/otap/codec.rs b/asap-precompute-rs/src/otap/codec.rs new file mode 100644 index 0000000..88b3525 --- /dev/null +++ b/asap-precompute-rs/src/otap/codec.rs @@ -0,0 +1,1366 @@ +// Copyright The ASAP Authors +// SPDX-License-Identifier: MIT + +//! Direct `SketchEnvelope <-> OtapPdata` binding — the real `OtapPdata` +//! is built straight from a `&[SketchEnvelope]` slice, and a real +//! `OtapPdata` is decoded straight into `Vec`. No +//! intermediate `RecordBatch` or `OtapMetricRecords` two-batch family +//! in between. +//! +//! # Why direct, not via `encode_batch`/`decode_batch`/`records::{flatten,lift}` +//! +//! Those three exist as a *general*, real-OTAP-free carrier format — +//! useful for other Strategy-B adapters (Telegraf, Vector) that might +//! one day consume the same flat shape, and for testing this codec +//! without any OTAP dependency at all. But this repo only ever ships +//! to OTAP, and no second adapter has actually shown up. Going through +//! them here would mean: `SketchEnvelope` -> flat `RecordBatch` +//! (`encode_batch`) -> two-batch `OtapMetricRecords` (`lift`) -> real +//! `OtapPdata` (this module) — three representations for what's really +//! one job. This module skips straight to the last step, implementing +//! OTAP's own `MetricsView` trait directly over `&[SketchEnvelope]`. +//! +//! `encode_batch`/`decode_batch`/`records::{flatten,lift}` still exist +//! and are still tested — they back the legacy `SeriesDictionary` / +//! `otap::wire` transport (`dictionary.rs`, standalone example +//! binaries), which predates this module and isn't part of this path. +//! +//! # A real bug this rewrite fixes +//! +//! `encode_batch` unconditionally sets `_asap_envelope` for every row, +//! including estimate-mode envelopes (`payload` empty, the gauge value +//! rides in `value` instead). `arrow_array::BinaryArray` treats +//! `Some(&[])` as a *present, empty* value, not null — confirmed +//! empirically (`BinaryArray::from_opt_vec(vec![Some(&[])]).is_null(0) +//! == false`) — so `decode_batch`'s `if env_arr.is_null(row)` check +//! never trips for an estimate-mode row, and it gets misrouted through +//! the envelope path with an empty payload instead of the scalar path. +//! This module's direct encoder only attaches `_asap_envelope` (and +//! its sibling `_asap_*` attributes) when `!envelope.payload.is_empty()`, +//! and its decoder additionally guards with `.filter(|b| !b.is_empty())` +//! defensively on the way in. The legacy `encode_batch`/`decode_batch` +//! pair still has the original behavior — untouched here since fixing +//! it isn't in scope for this rewrite, but worth knowing about if that +//! path is ever exercised with estimate-mode envelopes. +//! +//! # Provenance / verification status +//! +//! Written against, and build/lint/test-verified against, upstream +//! `open-telemetry/otel-arrow` commit +//! `3e85c3460361446ebfce99e9f35fffd2dd5ab740` (2026-08-24) via a plain +//! git dependency (see `Cargo.toml`'s `otap-engine` feature) — no +//! manual staging into a local checkout required, unlike the +//! `otap-patch/` overlay this module replaced. +//! +//! # Scope +//! +//! What "handling a metric" means on decode splits into two cases, +//! decided by content, not by which OTLP metric type carried it: +//! +//! - **A data point carrying `_asap_envelope`** (this module's own +//! encode output, or any other `asap_sketches` node's) routes +//! through `ObservationValueKind::Envelope`; `Precompute::observe` +//! already dispatches those to `observe_envelope` (merge as a +//! pre-aggregated sketch). +//! - **A genuine (non-envelope) OTLP metric** — Gauge/Sum data points +//! become scalar `Observation`s. Histogram/ExponentialHistogram/ +//! Summary data points are skipped (counted, not silently dropped — +//! see [`DecodeOutcome::skipped_non_scalar`]). +//! +//! On encode, every row is assumed to share one metric name — an +//! `AsapSketchesProcessor` instance has exactly one +//! `PluginConfig::output_metric_name`, so this holds by construction +//! for anything this crate's own `Precompute` produces; +//! [`encode_envelopes_to_pdata`] still checks it explicitly and errors +//! loudly on a real mismatch rather than silently dropping rows. +//! +//! Resource and scope are not modelled — every emitted metric attaches +//! to an empty `Resource` / unnamed `Scope`, same as the codec this +//! replaced. + +use thiserror::Error; + +use otel_arrow_dfe_otap::pdata::OtapPdata; +use otel_arrow_dfe_pdata::encode::encode_metrics_otap_batch; +use otel_arrow_dfe_pdata::otap::OtapArrowRecords; +use otel_arrow_dfe_pdata::views::otap::OtapMetricsView; +use otel_arrow_dfe_pdata::{OtapPayload, TryIntoWithOptions}; +use otel_arrow_dfe_pdata_views::views::common::{ + AnyValueView, AttributeView, InstrumentationScopeView, Str, ValueType, +}; +use otel_arrow_dfe_pdata_views::views::metrics::{ + AggregationTemporality, BucketsView, DataPointFlags, DataType as MetricKind, DataView, + ExemplarView, ExponentialHistogramDataPointView, ExponentialHistogramView, GaugeView, + HistogramDataPointView, HistogramView, MetricView, MetricsView, NumberDataPointView, + ResourceMetricsView, ScopeMetricsView, SumView, SummaryDataPointView, SummaryView, + Value as DpValue, ValueAtQuantileView, +}; +use otel_arrow_dfe_pdata_views::views::resource::ResourceView; + +use crate::envelope::SketchEnvelope; +use crate::observation::{KeyValue, Observation, ObservationValue}; + +use super::decode::{parse_encoding, parse_sketch_type}; +use super::schema::{ + ATTR_AGG_ID, ATTR_ENCODING, ATTR_ENVELOPE, ATTR_SCHEMA_VERSION, ATTR_SKETCH_TYPE, + ATTR_WINDOW_END_MS, ATTR_WINDOW_START_MS, +}; + +/// Failure modes for [`encode_envelopes_to_pdata`] / [`decode_pdata_to_observations`]. +#[derive(Debug, Error)] +pub enum CodecError { + /// More than one distinct `metric_name` appeared across the + /// envelopes passed to one [`encode_envelopes_to_pdata`] call — an + /// `AsapSketchesProcessor` instance has exactly one + /// `output_metric_name`, so this indicates envelopes from more + /// than one processor instance were mixed into a single call. + #[error( + "otap codec: one encode call carries more than one metric name ({first:?} and {second:?} seen)" + )] + MixedMetricNames { + /// First metric name seen. + first: String, + /// A second, different metric name seen in the same call. + second: String, + }, + /// Building the real `OtapArrowRecords::Metrics` batch failed. + #[error("otap codec: encoding real OTAP metrics batch failed: {0}")] + Encode(String), + /// Reading the real `OtapArrowRecords::Metrics` batch failed. + #[error("otap codec: reading real OTAP metrics batch failed: {0}")] + Decode(String), +} + +// ============================================================================ +// Encode direction: &[SketchEnvelope] -> OtapPdata +// ============================================================================ + +/// Encodes a slice of [`SketchEnvelope`]s directly into a real +/// `OtapPdata` carrying an `OtapArrowRecords::Metrics` payload — the +/// actual "put the sketch envelope bytes onto an OTAP metric" step. +/// +/// Builds a fresh, contextless `OtapPdata` (`OtapPdata::new_todo_context`) +/// rather than propagating any single input message's context: the +/// processor's flush ticker emits one window's worth of envelopes on +/// its own wall-clock schedule, decoupled from any specific triggering +/// input message, so there is no single Ack/Nack chain to attach the +/// output to. +/// +/// Returns `Ok` of an `OtapPdata` with zero data points for an empty +/// `envelopes` slice (matches `encode_metrics_otap_batch`'s own +/// "empty in, empty batch out" contract). +pub fn encode_envelopes_to_pdata(envelopes: &[SketchEnvelope]) -> Result { + let view = AsapMetricsView::try_from_envelopes(envelopes)?; + let arrow_records = + encode_metrics_otap_batch(&view).map_err(|e| CodecError::Encode(e.to_string()))?; + let payload: OtapPayload = arrow_records.into(); + Ok(OtapPdata::new_todo_context(payload)) +} + +/// Zero-copy adapter presenting a `&[SketchEnvelope]` as a +/// `MetricsView` directly — one Resource (empty), one Scope (unnamed), +/// one Metric (the envelopes' shared `metric_name`), one Gauge, one +/// `NumberDataPoint` per envelope. +struct AsapMetricsView<'a> { + metric_name: String, + envelopes: &'a [SketchEnvelope], +} + +impl<'a> AsapMetricsView<'a> { + fn try_from_envelopes(envelopes: &'a [SketchEnvelope]) -> Result { + let mut metric_name: Option<&str> = None; + for env in envelopes { + match metric_name { + None => metric_name = Some(&env.metric_name), + Some(seen) if seen == env.metric_name => {} + Some(seen) => { + return Err(CodecError::MixedMetricNames { + first: seen.to_string(), + second: env.metric_name.clone(), + }); + } + } + } + Ok(Self { + metric_name: metric_name.unwrap_or_default().to_string(), + envelopes, + }) + } + + /// One row's worth of attributes, built directly from the + /// envelope's own fields — no intermediate Arrow columns. The + /// `_asap_*` Strategy-B carriers only appear when this row is + /// actually an envelope-payload row (see this module's doc, "A + /// real bug this rewrite fixes"). + fn attributes_for_row(&self, row: usize) -> Vec> { + let env = &self.envelopes[row]; + let mut attrs = Vec::with_capacity(env.labels.len() + 7); + if !env.payload.is_empty() { + attrs.push(AsapAttribute { + key: ATTR_ENVELOPE, + value: AsapAnyValue::Bytes(&env.payload), + }); + attrs.push(AsapAttribute { + key: ATTR_SKETCH_TYPE, + value: AsapAnyValue::Str(env.sketch_type.name()), + }); + attrs.push(AsapAttribute { + key: ATTR_AGG_ID, + value: AsapAnyValue::Int(env.agg_id), + }); + attrs.push(AsapAttribute { + key: ATTR_SCHEMA_VERSION, + value: AsapAnyValue::Int(u64::from(env.schema_version)), + }); + attrs.push(AsapAttribute { + key: ATTR_WINDOW_START_MS, + value: AsapAnyValue::Int(env.window_start_ms), + }); + attrs.push(AsapAttribute { + key: ATTR_WINDOW_END_MS, + value: AsapAnyValue::Int(env.window_end_ms), + }); + attrs.push(AsapAttribute { + key: ATTR_ENCODING, + value: AsapAnyValue::Str(env.encoding.name()), + }); + } + for kv in &env.labels { + attrs.push(AsapAttribute { + key: &kv.key, + value: AsapAnyValue::Str(&kv.value), + }); + } + attrs + } +} + +impl<'a> MetricsView for AsapMetricsView<'a> { + type ResourceMetrics<'res> + = AsapResourceMetricsView<'res, 'a> + where + Self: 'res; + type ResourceMetricsIter<'res> + = std::vec::IntoIter> + where + Self: 'res; + + fn resources(&self) -> Self::ResourceMetricsIter<'_> { + if self.envelopes.is_empty() { + Vec::new().into_iter() + } else { + vec![AsapResourceMetricsView { view: self }].into_iter() + } + } +} + +struct AsapResourceMetricsView<'v, 'a> { + view: &'v AsapMetricsView<'a>, +} + +impl<'v, 'a> ResourceMetricsView for AsapResourceMetricsView<'v, 'a> { + type Resource<'res> + = AsapNoResource + where + Self: 'res; + type ScopeMetrics<'scp> + = AsapScopeMetricsView<'scp, 'a> + where + Self: 'scp; + type ScopesIter<'scp> + = std::vec::IntoIter> + where + Self: 'scp; + + fn resource(&self) -> Option> { + None + } + + fn scopes(&self) -> Self::ScopesIter<'_> { + vec![AsapScopeMetricsView { view: self.view }].into_iter() + } + + fn schema_url(&self) -> Option> { + None + } +} + +struct AsapScopeMetricsView<'v, 'a> { + view: &'v AsapMetricsView<'a>, +} + +impl<'v, 'a> ScopeMetricsView for AsapScopeMetricsView<'v, 'a> { + type Scope<'scp> + = AsapNoScope + where + Self: 'scp; + type Metric<'met> + = AsapMetricView<'met, 'a> + where + Self: 'met; + type MetricIter<'met> + = std::vec::IntoIter> + where + Self: 'met; + + fn scope(&self) -> Option> { + None + } + + fn metrics(&self) -> Self::MetricIter<'_> { + vec![AsapMetricView { view: self.view }].into_iter() + } + + fn schema_url(&self) -> Str<'_> { + b"" + } +} + +struct AsapMetricView<'v, 'a> { + view: &'v AsapMetricsView<'a>, +} + +impl<'v, 'a> MetricView for AsapMetricView<'v, 'a> { + type Data<'dat> + = AsapDataView<'dat, 'a> + where + Self: 'dat; + type Attribute<'att> + = AsapAttribute<'att> + where + Self: 'att; + type AttributeIter<'att> + = std::vec::IntoIter> + where + Self: 'att; + + fn name(&self) -> Str<'_> { + self.view.metric_name.as_bytes() + } + + fn description(&self) -> Str<'_> { + b"" + } + + fn unit(&self) -> Str<'_> { + b"" + } + + fn data(&self) -> Option> { + Some(AsapDataView { view: self.view }) + } + + fn metadata(&self) -> Self::AttributeIter<'_> { + Vec::new().into_iter() + } +} + +struct AsapDataView<'v, 'a> { + view: &'v AsapMetricsView<'a>, +} + +impl<'v, 'a> DataView<'v> for AsapDataView<'v, 'a> { + type Gauge<'gauge> + = AsapGaugeView<'gauge, 'a> + where + Self: 'gauge; + type Sum<'sum> + = AsapNoSum + where + Self: 'sum; + type Histogram<'histogram> + = AsapNoHistogram + where + Self: 'histogram; + type ExponentialHistogram<'exp> + = AsapNoExpHistogram + where + Self: 'exp; + type Summary<'summary> + = AsapNoSummary + where + Self: 'summary; + + fn value_type(&self) -> MetricKind { + MetricKind::Gauge + } + + fn as_gauge(&self) -> Option> { + Some(AsapGaugeView { view: self.view }) + } + + fn as_sum(&self) -> Option> { + None + } + + fn as_histogram(&self) -> Option> { + None + } + + fn as_exponential_histogram(&self) -> Option> { + None + } + + fn as_summary(&self) -> Option> { + None + } +} + +struct AsapGaugeView<'v, 'a> { + view: &'v AsapMetricsView<'a>, +} + +impl<'v, 'a> GaugeView for AsapGaugeView<'v, 'a> { + type NumberDataPoint<'dp> + = AsapNumberDataPointView<'dp, 'a> + where + Self: 'dp; + type NumberDataPointIter<'dp> + = std::vec::IntoIter> + where + Self: 'dp; + + fn data_points(&self) -> Self::NumberDataPointIter<'_> { + (0..self.view.envelopes.len()) + .map(|row| AsapNumberDataPointView { + view: self.view, + row, + }) + .collect::>() + .into_iter() + } +} + +struct AsapNumberDataPointView<'v, 'a> { + view: &'v AsapMetricsView<'a>, + row: usize, +} + +impl<'v, 'a> NumberDataPointView for AsapNumberDataPointView<'v, 'a> { + type Attribute<'att> + = AsapAttribute<'att> + where + Self: 'att; + type AttributeIter<'att> + = std::vec::IntoIter> + where + Self: 'att; + type Exemplar<'ex> + = AsapNoExemplar + where + Self: 'ex; + type ExemplarIter<'ex> + = std::vec::IntoIter + where + Self: 'ex; + + fn start_time_unix_nano(&self) -> u64 { + 0 + } + + fn time_unix_nano(&self) -> u64 { + self.view.envelopes[self.row] + .window_end_ms + .saturating_mul(1_000_000) + } + + fn value(&self) -> Option { + Some(DpValue::Double(self.view.envelopes[self.row].value)) + } + + fn attributes(&self) -> Self::AttributeIter<'_> { + self.view.attributes_for_row(self.row).into_iter() + } + + fn exemplars(&self) -> Self::ExemplarIter<'_> { + Vec::new().into_iter() + } + + fn flags(&self) -> DataPointFlags { + DataPointFlags::new(0) + } +} + +/// One typed attribute value ASAP's envelopes can carry — mirrors the +/// three-way union OTAP's own attribute value model supports for +/// what this codec actually produces (bytes / str / int). +enum AsapAnyValue<'a> { + Str(&'a str), + Int(u64), + Bytes(&'a [u8]), +} + +struct AsapAttribute<'a> { + key: &'a str, + value: AsapAnyValue<'a>, +} + +impl<'a> AttributeView for AsapAttribute<'a> { + type Val<'val> + = AsapAnyValueView<'val> + where + Self: 'val; + + fn key(&self) -> Str<'_> { + self.key.as_bytes() + } + + fn value(&self) -> Option> { + Some(AsapAnyValueView(&self.value)) + } +} + +struct AsapAnyValueView<'a>(&'a AsapAnyValue<'a>); + +impl<'a> AnyValueView<'a> for AsapAnyValueView<'a> { + type KeyValue = AsapAttribute<'a>; + type ArrayIter<'arr> + = std::iter::Empty + where + Self: 'arr; + type KeyValueIter<'kv> + = std::iter::Empty + where + Self: 'kv; + + fn value_type(&self) -> ValueType { + match self.0 { + AsapAnyValue::Str(_) => ValueType::String, + AsapAnyValue::Int(_) => ValueType::Int64, + AsapAnyValue::Bytes(_) => ValueType::Bytes, + } + } + + fn as_string(&self) -> Option> { + match self.0 { + AsapAnyValue::Str(s) => Some(s.as_bytes()), + _ => None, + } + } + + fn as_bool(&self) -> Option { + None + } + + fn as_int64(&self) -> Option { + match self.0 { + // Lossy above i64::MAX, which ASAP never actually + // produces here (the only `_asap_*` int-typed attributes + // are small counters/timestamps well under that bound). + AsapAnyValue::Int(v) => Some(*v as i64), + _ => None, + } + } + + fn as_double(&self) -> Option { + None + } + + fn as_bytes(&self) -> Option<&[u8]> { + match self.0 { + AsapAnyValue::Bytes(b) => Some(b), + _ => None, + } + } + + fn as_array(&self) -> Option> { + None + } + + fn as_kvlist(&self) -> Option> { + None + } +} + +// -- Uninhabited placeholder types ------------------------------------------- +// +// `resource()`/`scope()` always return `None`, and only Gauge is ever +// produced (`as_sum`/`as_histogram`/`as_exponential_histogram`/ +// `as_summary` always return `None`, and there are no exemplars) — but +// the view traits still require *some* concrete, well-formed type for +// each associated type regardless of whether an instance is ever +// constructed. An uninhabited enum (`enum X {}`) lets every trait +// method be `match *self {}` — valid because no value of an +// uninhabited type can ever exist to call it on, so the body coerces +// to any return type without ever actually running. + +enum AsapNoResource {} +impl ResourceView for AsapNoResource { + type Attribute<'att> + = AsapAttribute<'att> + where + Self: 'att; + type AttributesIter<'att> + = std::vec::IntoIter> + where + Self: 'att; + + fn attributes(&self) -> Self::AttributesIter<'_> { + match *self {} + } + + fn dropped_attributes_count(&self) -> u32 { + match *self {} + } +} + +enum AsapNoScope {} +impl InstrumentationScopeView for AsapNoScope { + type Attribute<'att> + = AsapAttribute<'att> + where + Self: 'att; + type AttributeIter<'att> + = std::vec::IntoIter> + where + Self: 'att; + + fn name(&self) -> Option> { + match *self {} + } + + fn version(&self) -> Option> { + match *self {} + } + + fn attributes(&self) -> Self::AttributeIter<'_> { + match *self {} + } + + fn dropped_attributes_count(&self) -> u32 { + match *self {} + } +} + +enum AsapNoExemplar {} +impl ExemplarView for AsapNoExemplar { + type Attribute<'att> + = AsapAttribute<'att> + where + Self: 'att; + type AttributeIter<'att> + = std::vec::IntoIter> + where + Self: 'att; + + fn filtered_attributes(&self) -> Self::AttributeIter<'_> { + match *self {} + } + + fn time_unix_nano(&self) -> u64 { + match *self {} + } + + fn value(&self) -> Option { + match *self {} + } + + fn span_id(&self) -> Option<&[u8; 8]> { + match *self {} + } + + fn trace_id(&self) -> Option<&[u8; 16]> { + match *self {} + } +} + +enum AsapNoSum {} +impl SumView for AsapNoSum { + type NumberDataPoint<'dp> + = AsapNumberDataPointView<'dp, 'static> + where + Self: 'dp; + type NumberDataPointIter<'dp> + = std::vec::IntoIter> + where + Self: 'dp; + + fn data_points(&self) -> Self::NumberDataPointIter<'_> { + match *self {} + } + + fn aggregation_temporality(&self) -> AggregationTemporality { + match *self {} + } + + fn is_monotonic(&self) -> bool { + match *self {} + } +} + +enum AsapNoHistogram {} +impl HistogramView for AsapNoHistogram { + type HistogramDataPoint<'dp> + = AsapNoHistogramDataPoint + where + Self: 'dp; + type HistogramDataPointIter<'dp> + = std::vec::IntoIter + where + Self: 'dp; + + fn data_points(&self) -> Self::HistogramDataPointIter<'_> { + match *self {} + } + + fn aggregation_temporality(&self) -> AggregationTemporality { + match *self {} + } +} + +enum AsapNoHistogramDataPoint {} +impl HistogramDataPointView for AsapNoHistogramDataPoint { + type Attribute<'att> + = AsapAttribute<'att> + where + Self: 'att; + type AttributeIter<'att> + = std::vec::IntoIter> + where + Self: 'att; + type BucketCountIter<'bc> + = std::iter::Empty + where + Self: 'bc; + type ExplicitBoundsIter<'eb> + = std::iter::Empty + where + Self: 'eb; + type Exemplar<'ex> + = AsapNoExemplar + where + Self: 'ex; + type ExemplarIter<'ex> + = std::vec::IntoIter + where + Self: 'ex; + + fn attributes(&self) -> Self::AttributeIter<'_> { + match *self {} + } + + fn start_time_unix_nano(&self) -> u64 { + match *self {} + } + + fn time_unix_nano(&self) -> u64 { + match *self {} + } + + fn count(&self) -> u64 { + match *self {} + } + + fn sum(&self) -> Option { + match *self {} + } + + fn bucket_counts(&self) -> Self::BucketCountIter<'_> { + match *self {} + } + + fn explicit_bounds(&self) -> Self::ExplicitBoundsIter<'_> { + match *self {} + } + + fn exemplars(&self) -> Self::ExemplarIter<'_> { + match *self {} + } + + fn flags(&self) -> DataPointFlags { + match *self {} + } + + fn min(&self) -> Option { + match *self {} + } + + fn max(&self) -> Option { + match *self {} + } +} + +enum AsapNoExpHistogram {} +impl ExponentialHistogramView for AsapNoExpHistogram { + type ExponentialHistogramDataPoint<'dp> + = AsapNoExpHistogramDataPoint + where + Self: 'dp; + type ExponentialHistogramDataPointIter<'dp> + = std::vec::IntoIter + where + Self: 'dp; + + fn data_points(&self) -> Self::ExponentialHistogramDataPointIter<'_> { + match *self {} + } + + fn aggregation_temporality(&self) -> AggregationTemporality { + match *self {} + } +} + +enum AsapNoExpHistogramDataPoint {} +impl ExponentialHistogramDataPointView for AsapNoExpHistogramDataPoint { + type Attribute<'att> + = AsapAttribute<'att> + where + Self: 'att; + type AttributeIter<'att> + = std::vec::IntoIter> + where + Self: 'att; + type Exemplar<'ex> + = AsapNoExemplar + where + Self: 'ex; + type ExemplarIter<'ex> + = std::vec::IntoIter + where + Self: 'ex; + type Buckets<'b> + = AsapNoBuckets + where + Self: 'b; + + fn start_time_unix_nano(&self) -> u64 { + match *self {} + } + + fn time_unix_nano(&self) -> u64 { + match *self {} + } + + fn count(&self) -> u64 { + match *self {} + } + + fn sum(&self) -> Option { + match *self {} + } + + fn min(&self) -> Option { + match *self {} + } + + fn max(&self) -> Option { + match *self {} + } + + fn scale(&self) -> i32 { + match *self {} + } + + fn zero_count(&self) -> u64 { + match *self {} + } + + fn positive(&self) -> Option> { + match *self {} + } + + fn negative(&self) -> Option> { + match *self {} + } + + fn attributes(&self) -> Self::AttributeIter<'_> { + match *self {} + } + + fn exemplars(&self) -> Self::ExemplarIter<'_> { + match *self {} + } + + fn flags(&self) -> DataPointFlags { + match *self {} + } + + fn zero_threshold(&self) -> f64 { + match *self {} + } +} + +enum AsapNoBuckets {} +impl BucketsView for AsapNoBuckets { + type BucketCountIter<'bc> + = std::iter::Empty + where + Self: 'bc; + + fn offset(&self) -> i32 { + match *self {} + } + + fn bucket_counts(&self) -> Self::BucketCountIter<'_> { + match *self {} + } +} + +enum AsapNoSummary {} +impl SummaryView for AsapNoSummary { + type SummaryDataPoint<'dp> + = AsapNoSummaryDataPoint + where + Self: 'dp; + type SummaryDataPointIter<'dp> + = std::vec::IntoIter + where + Self: 'dp; + + fn data_points(&self) -> Self::SummaryDataPointIter<'_> { + match *self {} + } +} + +enum AsapNoSummaryDataPoint {} +impl SummaryDataPointView for AsapNoSummaryDataPoint { + type Attribute<'att> + = AsapAttribute<'att> + where + Self: 'att; + type AttributeIter<'att> + = std::vec::IntoIter> + where + Self: 'att; + type ValueAtQuantile<'q> + = AsapNoValueAtQuantile + where + Self: 'q; + type ValueAtQuantileIter<'q> + = std::vec::IntoIter + where + Self: 'q; + + fn start_time_unix_nano(&self) -> u64 { + match *self {} + } + + fn time_unix_nano(&self) -> u64 { + match *self {} + } + + fn count(&self) -> u64 { + match *self {} + } + + fn sum(&self) -> f64 { + match *self {} + } + + fn quantile_values(&self) -> Self::ValueAtQuantileIter<'_> { + match *self {} + } + + fn attributes(&self) -> Self::AttributeIter<'_> { + match *self {} + } + + fn flags(&self) -> DataPointFlags { + match *self {} + } +} + +enum AsapNoValueAtQuantile {} +impl ValueAtQuantileView for AsapNoValueAtQuantile { + fn quantile(&self) -> f64 { + match *self {} + } + + fn value(&self) -> f64 { + match *self {} + } +} + +// ============================================================================ +// Decode direction: OtapPdata -> Vec +// ============================================================================ + +/// Outcome of [`decode_pdata_to_observations`]. +#[derive(Debug, Default)] +pub struct DecodeOutcome { + /// One `Observation` per scalar (Gauge/Sum) data point seen — + /// empty when `pdata` wasn't Metrics, decoded to zero data points, + /// or every data point was non-scalar. + pub observations: Vec, + /// Histogram / ExponentialHistogram / Summary data points seen and + /// skipped — not silently dropped, see this module's doc "Scope" + /// section for why they aren't expanded. + pub skipped_non_scalar: usize, +} + +/// Converts a real `OtapPdata` directly into `Vec` — +/// feeds `Precompute::observe` on the producer role's ingest path. +/// +/// Accepts `pdata` by value (consumed) — the caller's `Message::PData` +/// match arm already owns it and has no further use for the original +/// context once this conversion runs. +pub fn decode_pdata_to_observations(pdata: OtapPdata) -> Result { + let (_context, payload) = pdata.into_parts(); + let arrow_records: OtapArrowRecords = payload + .try_into_with_default() + .map_err(|e| CodecError::Decode(format!("{e}")))?; + + let OtapArrowRecords::Metrics(_) = &arrow_records else { + return Ok(DecodeOutcome::default()); + }; + + let view = OtapMetricsView::try_from(&arrow_records) + .map_err(|e| CodecError::Decode(format!("{e}")))?; + + let mut acc = DecodeAccumulator::default(); + let mut skipped_non_scalar = 0usize; + + for resource in view.resources() { + for scope in resource.scopes() { + for metric in scope.metrics() { + let name = String::from_utf8_lossy(metric.name()).into_owned(); + let Some(data) = metric.data() else { + continue; + }; + // Gauge/Sum each expose their own concrete + // `NumberDataPointView` implementation (different + // types, both borrowing from `gauge`/`sum`) — walked + // inline per branch via a generic helper rather than + // collected into one `Vec<_>` first, since collecting + // would need `gauge`/`sum` to outlive the branch that + // produced them. + if let Some(gauge) = data.as_gauge() { + for dp in gauge.data_points() { + acc.push_data_point(dp, &name)?; + } + } else if let Some(sum) = data.as_sum() { + for dp in sum.data_points() { + acc.push_data_point(dp, &name)?; + } + } else { + // Histogram / ExponentialHistogram / Summary: no + // single well-defined scalar — count, don't expand. + skipped_non_scalar += match data.value_type() { + MetricKind::Histogram => data + .as_histogram() + .map(|h| h.data_points().count()) + .unwrap_or(0), + MetricKind::ExponentialHistogram => data + .as_exponential_histogram() + .map(|h| h.data_points().count()) + .unwrap_or(0), + MetricKind::Summary => data + .as_summary() + .map(|s| s.data_points().count()) + .unwrap_or(0), + _ => 0, + }; + } + } + } + } + + Ok(DecodeOutcome { + observations: acc.observations, + skipped_non_scalar, + }) +} + +/// Accumulates one `Observation` per data point while walking the real +/// `OtapMetricsView` — factored out of [`decode_pdata_to_observations`] +/// so the Gauge and Sum branches (whose `NumberDataPointView` +/// implementations are different concrete types, both borrowing from +/// the `gauge`/`sum` view that produced them) can share one push +/// method via a generic function instead of duplicating the per-data- +/// point body twice, or trying to unify their different concrete +/// iterator types into one `Vec` first (which doesn't compile — +/// `gauge`/`sum` would need to outlive the branch that produced them). +#[derive(Default)] +struct DecodeAccumulator { + observations: Vec, +} + +impl DecodeAccumulator { + /// Builds and appends one `Observation` from a data point — a + /// no-op if the data point carries no value. + /// + /// Mirrors `decode.rs`'s `decode_batch` row-decode logic exactly + /// (same `_asap_*` key recognition, same envelope-vs-scalar + /// routing, same hard error on an unrecognized + /// `_asap_sketch_type`/`_asap_encoding`), just reading attributes + /// directly off the real `NumberDataPointView` instead of off + /// Arrow columns. + fn push_data_point( + &mut self, + dp: D, + metric_name: &str, + ) -> Result<(), CodecError> { + let Some(value) = dp.value() else { + return Ok(()); + }; + let value = match value { + DpValue::Double(v) => v, + DpValue::Integer(v) => v as f64, + }; + let timestamp_ms = dp.time_unix_nano() / 1_000_000; + + let mut envelope_bytes: Option> = None; + let mut sketch_type_raw: Option = None; + let mut agg_id: Option = None; + let mut schema_version: Option = None; + let mut window_start_ms: Option = None; + let mut window_end_ms: Option = None; + let mut encoding_raw: Option = None; + let mut labels: Vec = Vec::new(); + + for attr in dp.attributes() { + let Some(val) = attr.value() else { continue }; + let key = String::from_utf8_lossy(attr.key()).into_owned(); + match key.as_str() { + ATTR_ENVELOPE => envelope_bytes = val.as_bytes().map(<[u8]>::to_vec), + ATTR_SKETCH_TYPE => { + sketch_type_raw = val + .as_string() + .map(|s| String::from_utf8_lossy(s).into_owned()); + } + ATTR_AGG_ID => agg_id = val.as_int64().filter(|v| *v >= 0).map(|v| v as u64), + ATTR_SCHEMA_VERSION => { + schema_version = val.as_int64().filter(|v| *v >= 0).map(|v| v as u32); + } + ATTR_WINDOW_START_MS => { + window_start_ms = val.as_int64().filter(|v| *v >= 0).map(|v| v as u64); + } + ATTR_WINDOW_END_MS => { + window_end_ms = val.as_int64().filter(|v| *v >= 0).map(|v| v as u64); + } + ATTR_ENCODING => { + encoding_raw = val + .as_string() + .map(|s| String::from_utf8_lossy(s).into_owned()); + } + _ => { + // Non-reserved: treated as a per-row label. Only + // string-typed values are representable as a + // label (matches decode.rs's Utf8-only label + // column behavior) — a non-string, non-reserved + // attribute is dropped, same as the legacy path. + if let Some(s) = val.as_string() { + labels.push(KeyValue::new(key, String::from_utf8_lossy(s).into_owned())); + } + } + } + } + + // `.filter(|b| !b.is_empty())`: defensive guard against the + // same empty-but-present-Binary-cell class of bug this + // module's encoder fixes on the way out (see module doc, "A + // real bug this rewrite fixes") — an empty `_asap_envelope` + // from some other producer still routes to the scalar path + // here rather than an empty-payload envelope. + let observation_value = match envelope_bytes.filter(|b| !b.is_empty()) { + Some(payload) => { + let sketch_type = match sketch_type_raw { + Some(s) => { + parse_sketch_type(0, &s).map_err(|e| CodecError::Decode(e.to_string()))? + } + None => crate::envelope::SketchType::Unspecified, + }; + let encoding = match encoding_raw { + Some(s) => { + parse_encoding(0, &s).map_err(|e| CodecError::Decode(e.to_string()))? + } + None => crate::envelope::Encoding::Unspecified, + }; + ObservationValue::envelope(SketchEnvelope { + schema_version: schema_version.unwrap_or(0), + sketch_type, + agg_id: agg_id.unwrap_or(0), + resource_labels: Vec::new(), + labels: labels.clone(), + window_start_ms: window_start_ms.unwrap_or(0), + window_end_ms: window_end_ms.unwrap_or(0), + encoding, + payload, + hash_spec: None, + metric_name: metric_name.to_string(), + count: 0, + aggregation_temporality: 0, + value, + }) + } + None => ObservationValue::float(value), + }; + + self.observations.push(Observation::new( + timestamp_ms, + metric_name.to_string(), + Vec::new(), // resource_labels — no resource child batch modelled, matches decode.rs. + labels, + observation_value, + )); + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::envelope::{Encoding, SketchType}; + use crate::observation::{KeyValue as Kv, ObservationValueKind}; + + fn one_envelope( + metric_name: &str, + value: f64, + attr_key: &str, + attr_value: &str, + ) -> SketchEnvelope { + SketchEnvelope { + schema_version: 1, + sketch_type: SketchType::Unspecified, + agg_id: 0, + resource_labels: vec![], + labels: vec![Kv::new(attr_key, attr_value)], + window_start_ms: 1_000, + window_end_ms: 2_000, + encoding: Encoding::Unspecified, + payload: Vec::new(), + hash_spec: None, + metric_name: metric_name.to_string(), + count: 0, + aggregation_temporality: 0, + value, + } + } + + #[test] + fn encode_then_decode_round_trips_a_scalar_metric() { + let env = one_envelope("http_request_duration_ms", 42.5, "path", "/api"); + let pdata = encode_envelopes_to_pdata(std::slice::from_ref(&env)).expect("encode"); + assert_eq!( + pdata.signal_type(), + otel_arrow_dfe_config::SignalType::Metrics + ); + + let outcome = decode_pdata_to_observations(pdata).expect("decode"); + assert_eq!(outcome.skipped_non_scalar, 0); + assert_eq!(outcome.observations.len(), 1); + let obs = &outcome.observations[0]; + assert_eq!(obs.metric, "http_request_duration_ms"); + assert_eq!(obs.value.kind, ObservationValueKind::Float); + assert_eq!(obs.value.float, 42.5); + assert_eq!(obs.labels.len(), 1); + assert_eq!(obs.labels[0].key, "path"); + assert_eq!(obs.labels[0].value, "/api"); + } + + #[test] + fn encode_then_decode_round_trips_a_sketch_envelope_carried_as_a_metric_attribute() { + let mut env = one_envelope("sketch_stream", 0.0, "region", "us-east"); + env.sketch_type = SketchType::DDSketch; + env.encoding = Encoding::ProtoFull; + env.agg_id = 7; + env.payload = vec![0xde, 0xad, 0xbe, 0xef]; + + let pdata = encode_envelopes_to_pdata(std::slice::from_ref(&env)).expect("encode"); + let outcome = decode_pdata_to_observations(pdata).expect("decode"); + assert_eq!(outcome.observations.len(), 1); + let obs = &outcome.observations[0]; + assert_eq!(obs.value.kind, ObservationValueKind::Envelope); + let decoded_env = obs.value.envelope.as_ref().expect("envelope"); + assert_eq!(decoded_env.payload, vec![0xde, 0xad, 0xbe, 0xef]); + assert_eq!(decoded_env.sketch_type, SketchType::DDSketch); + assert_eq!(decoded_env.encoding, Encoding::ProtoFull); + assert_eq!(decoded_env.agg_id, 7); + assert_eq!(decoded_env.window_start_ms, 1_000); + assert_eq!(decoded_env.window_end_ms, 2_000); + } + + /// The bug this rewrite fixes: an estimate-mode envelope (empty + /// `payload`, the estimate rides in `value`) must round-trip as a + /// scalar `Float` observation, not get misrouted through the + /// envelope path with an empty payload. + #[test] + fn estimate_mode_envelope_round_trips_as_a_scalar_not_an_empty_envelope() { + let env = one_envelope("p99_latency_ms", 123.4, "route", "/checkout"); + assert!( + env.payload.is_empty(), + "estimate-mode envelope: no sketch payload" + ); + + let pdata = encode_envelopes_to_pdata(std::slice::from_ref(&env)).expect("encode"); + let outcome = decode_pdata_to_observations(pdata).expect("decode"); + assert_eq!(outcome.observations.len(), 1); + let obs = &outcome.observations[0]; + assert_eq!( + obs.value.kind, + ObservationValueKind::Float, + "estimate-mode envelope must decode as Float, not Envelope" + ); + assert_eq!(obs.value.float, 123.4); + } + + #[test] + fn decode_returns_empty_outcome_for_zero_data_points() { + let pdata = encode_envelopes_to_pdata(&[]).expect("encode empty"); + let outcome = decode_pdata_to_observations(pdata).expect("decode empty"); + assert!(outcome.observations.is_empty()); + assert_eq!(outcome.skipped_non_scalar, 0); + } + + #[test] + fn mixed_metric_names_are_rejected() { + let env_a = one_envelope("a", 1.0, "k", "v"); + let env_b = one_envelope("b", 2.0, "k", "v"); + let err = encode_envelopes_to_pdata(&[env_a, env_b]).expect_err("mixed names rejected"); + assert!(matches!(err, CodecError::MixedMetricNames { .. })); + } + + /// This is the fact `processor.rs`'s "There is exactly one + /// transport: the pipeline" doc rests on: OTAP's real Arrow + /// encoder (`encode_metrics_otap_batch`, which + /// [`encode_envelopes_to_pdata`] calls) dictionary-encodes the + /// metric name and every string-valued attribute key/value on its + /// own, by construction — this adapter doesn't have to ask for + /// it. If this ever regresses upstream (a schema change stops + /// using `DataType::Dictionary` for these columns), the rationale + /// for not reinventing `SeriesDictionary`'s SCHEMA/DICTIONARY/ + /// RECORD tiering on this path goes with it — this test exists so + /// that regression is loud, not discovered by someone re-deriving + /// it from scratch. + #[test] + fn real_otap_encoding_dictionary_encodes_metric_name_and_string_attributes() { + use arrow_schema::DataType; + use otel_arrow_dfe_pdata::proto::opentelemetry::arrow::v1::ArrowPayloadType; + + let env = one_envelope("http_request_duration_ms", 42.5, "path", "/api"); + let pdata = encode_envelopes_to_pdata(std::slice::from_ref(&env)).expect("encode"); + + let (_context, payload) = pdata.into_parts(); + let arrow_records: OtapArrowRecords = payload + .try_into_with_default() + .expect("payload converts to real OtapArrowRecords"); + + let metrics_batch = arrow_records + .get(ArrowPayloadType::UnivariateMetrics) + .expect("a UnivariateMetrics batch was populated"); + let metrics_schema = metrics_batch.schema(); + let name_type = metrics_schema + .field_with_name("name") + .expect("metrics batch has a name column") + .data_type(); + assert!( + matches!(name_type, DataType::Dictionary(_, _)), + "expected the metric name column to be dictionary-encoded, got {name_type:?}" + ); + + let attrs_batch = arrow_records + .get(ArrowPayloadType::NumberDpAttrs) + .expect("a NumberDpAttrs batch was populated"); + let attrs_schema = attrs_batch.schema(); + for column in ["key", "str"] { + let field_type = attrs_schema + .field_with_name(column) + .unwrap_or_else(|_| panic!("attrs batch has a {column} column")) + .data_type(); + assert!( + matches!(field_type, DataType::Dictionary(_, _)), + "expected attrs column {column:?} to be dictionary-encoded, got {field_type:?}" + ); + } + } +} diff --git a/asap-precompute-rs/src/otap/mod.rs b/asap-precompute-rs/src/otap/mod.rs index 31e7f88..94d69e6 100644 --- a/asap-precompute-rs/src/otap/mod.rs +++ b/asap-precompute-rs/src/otap/mod.rs @@ -9,11 +9,9 @@ //! per-row Strategy-B field. //! //! The codec **owns no Tokio tasks, no timers, no control-channel -//! inbox**. The full plugin lifecycle (`Wakeup`-driven flush ticker, -//! `NodeControlMsg` handling, control-channel poll task, graceful -//! drain) is **Phase C** and lives in `otap-patch/plugins/asap_sketches/` -//! once that arrives — kept deliberately separate from the codec to -//! match the two-layer split. +//! inbox**. The full `Wakeup`-driven flush ticker / control-channel +//! poll / graceful-drain lifecycle lives in [`lifecycle::AsapSketchesPlugin`] +//! — kept deliberately separate from the codec. //! //! # Schema (v1) //! @@ -80,47 +78,42 @@ //! (non-envelope) observation ingestion and for whatever still needs //! OTAP-Metrics-payload compatibility. //! -//! # Phase C — full plugin lifecycle -//! -//! Phase B shipped the stateless codec (`decode_batch` / -//! `encode_batch`) plus a [`StubPlugin`] anchor. Phase C layers the -//! Tokio-driven plugin lifecycle on top: +//! # Plugin lifecycle //! //! - [`config::PluginConfig`] + [`config::resolve`] — high-level //! plugin configuration with 5-sketch `sketch_type` dispatch //! (DDSketch / KLL / HLL / CountSketch / CountMinSketch). //! - [`records::OtapMetricRecords`] + [`records::flatten`] / -//! [`records::lift`] — local model of the upstream OTAP -//! `OtapArrowRecords` family with the bidirectional -//! sibling-batch ↔ flat-batch projection that Phase B deferred. +//! [`records::lift`] — local, real-OTAP-free model of an +//! `OtapArrowRecords`-shaped sibling-batch family, with the +//! bidirectional sibling-batch ↔ flat-batch projection. Backs the +//! legacy `dictionary`/`wire` transport (below), not `codec`/ +//! `processor`. //! - [`lifecycle::AsapSketchesPlugin`] — Tokio runtime: input task //! consumes the host-supplied stream, `Wakeup`-driven flush //! ticker emits batches, control-channel poll task picks up //! plan changes, graceful drain on shutdown. //! -//! The OTAP submodule wiring (linkme distributed-slice registration, -//! `build_asap_otap.sh`, `otap-patch/all/mod.rs` patch) is **Phase D** -//! and is deliberately not touched here. The -//! Phase C plugin lifecycle is exercised end-to-end via the -//! `tests/otap_lifecycle.rs` harness. -//! -//! # Out of scope (Phase D / E, deliberately deferred) +//! # The real OTAP node (`codec` / `processor`, `otap-engine` feature) //! -//! - `linkme` distributed-slice plugin registration in -//! `otap-patch/all/mod.rs`. -//! - `build_asap_otap.sh`. -//! - Cross-host envelope parity (Phase E). -//! - `OtapArrowRecords` binding to the upstream Rust type — Phase D -//! wires [`records::OtapMetricRecords`] to the upstream -//! `OtapPdata` shape. +//! [`processor::AsapSketchesProcessor`] is the real +//! `local::Processor` node OTAP's engine actually runs, +//! registered under `urn:asap:processor:asap_sketches` via `linkme`. +//! [`codec`] is its direct `SketchEnvelope <-> OtapPdata` binding — no +//! intermediate `RecordBatch` or [`records::OtapMetricRecords`] hop; +//! see `codec.rs`'s module doc for why. Gated behind the `otap-engine` +//! Cargo feature, which pulls in the real OTAP Dataflow engine/pdata +//! crates via a plain git dependency (see `Cargo.toml`) — no manual +//! staging into a local checkout required, `cargo build --features +//! otap-engine` just works. //! -//! # Stub plugin shell (kept for back-compat with Phase B tests) +//! # Stub plugin shell (kept for back-compat with early tests) //! //! [`StubPlugin`] is a no-op lifecycle wrapper that threads //! `decode_batch` / `encode_batch` against any -//! [`crate::precompute::Precompute`]. Phase C's full plugin lives in -//! [`lifecycle::AsapSketchesPlugin`]; the stub is retained to keep -//! Phase B's tests passing as a regression backstop. +//! [`crate::precompute::Precompute`]. [`lifecycle::AsapSketchesPlugin`] +//! is the full plugin; the stub is retained to keep early tests +//! passing as a regression backstop. mod decode; mod dictionary; @@ -133,6 +126,11 @@ pub mod lifecycle; pub mod records; pub mod wire; +#[cfg(feature = "otap-engine")] +pub mod codec; +#[cfg(feature = "otap-engine")] +pub mod processor; + pub use decode::{decode_batch, OtapDecodeError}; pub use dictionary::{ SchemaSnapshot, SeriesDictionary, SeriesDictionaryDecoder, SketchStreamBatch, diff --git a/asap-precompute-rs/src/otap/plugin.rs b/asap-precompute-rs/src/otap/plugin.rs index 24556da..411fe6e 100644 --- a/asap-precompute-rs/src/otap/plugin.rs +++ b/asap-precompute-rs/src/otap/plugin.rs @@ -14,9 +14,9 @@ //! control-channel inbox, and no `linkme` distributed-slice //! registration**. The full plugin (Tokio interval-driven flush //! ticker, `NodeControlMsg::Wakeup` handling, control-channel poll -//! task, graceful drain) is **Phase C** in -//! `otap-patch/plugins/asap_sketches/`. Comments below mark the seams -//! Phase C will fill in. +//! task, graceful drain) is [`super::lifecycle::AsapSketchesPlugin`]; +//! the real OTAP node (`linkme` registration included) is +//! [`super::processor::AsapSketchesProcessor`], `otap-engine` feature. use std::sync::Mutex; diff --git a/otap-patch/all/mod.rs b/asap-precompute-rs/src/otap/processor.rs similarity index 60% rename from otap-patch/all/mod.rs rename to asap-precompute-rs/src/otap/processor.rs index 797901a..ad729e7 100644 --- a/otap-patch/all/mod.rs +++ b/asap-precompute-rs/src/otap/processor.rs @@ -1,17 +1,18 @@ // Copyright The ASAP Authors // SPDX-License-Identifier: MIT -//! `asap_sketches_registry` — OTAP-Rust `linkme` distributed-slice -//! registration for the unified ASAP `asap_sketches` processor. +//! `linkme` distributed-slice registration for the unified ASAP +//! `asap_sketches` processor, and the real +//! `local::Processor` adapter itself. //! //! ## What this file does //! //! **(1)** Declares an [`OTAP_PROCESSOR_FACTORIES`] entry for //! `urn:asap:processor:asap_sketches`. This is the //! `#[distributed_slice]` static that puts the plugin into the -//! binary's link scope at compile time — the OTAP runtime -//! discovers it via `system_info()` at startup (the function that -//! produces the binary's "Available Component URNs:" banner). +//! binary's link scope at compile time — the OTAP runtime discovers +//! it via `system_info()` at startup (the function that produces the +//! binary's "Available Component URNs:" banner). //! //! **(2)** Implements a real [`local::Processor`] adapter — //! [`AsapSketchesProcessor`] — that ingests real OTAP metric traffic, @@ -22,15 +23,12 @@ //! [`create_asap_sketches_processor`]'s doc for why (that lifecycle's //! current emit shape, `SketchStreamBatch`, diverged from what this //! adapter needs after PR #5/#6's dictionary-economics work). The -//! actual `OtapPdata` <-> `OtapMetricRecords` conversion lives in -//! `otap_bridge` — **that module has not been build-verified against -//! a real OTAP Dataflow workspace**; see its own module doc for -//! exactly what's confirmed vs. assumed. +//! actual `OtapPdata <-> Vec` conversion lives in +//! [`super::codec`]. //! //! **(3)** Validates the user-facing TOML config against -//! [`asap_precompute_rs::otap::PluginConfig`]'s shape — the -//! `validate_config` hook lets `df_engine --validate-and-exit` -//! catch typos before runtime. +//! [`crate::otap::PluginConfig`]'s shape — the `validate_config` hook +//! lets `df_engine --validate-and-exit` catch typos before runtime. //! //! ## URN //! @@ -46,20 +44,51 @@ //! identically for the others — no per-platform translation in the //! controller. //! +//! ## There is exactly one transport: the pipeline +//! +//! [`AsapSketchesProcessor`] only ever sends via +//! `effect_handler.send_message_with_source_node` — whatever this +//! node's own pipeline wiring connects it to. An earlier revision also +//! carried a direct-TCP "wire lane" (a hand-rolled +//! `OtapWireWriter`/`OtapWireReader` pair, plus a standalone +//! `AsapSketchesReceiver` node) as an alternative transport; that's +//! gone. +//! +//! It's gone because it turned out to be solving a problem OTAP's real +//! Arrow-native metrics protocol already solves. The custom wire lane +//! existed to give sketch traffic real dictionary/schema-reuse +//! economics — the SCHEMA-once-per-`agg_id`, DICTIONARY-once-per-series +//! design in [`super::dictionary`] (`SeriesDictionary` / +//! `SketchStreamBatch`). But [`super::codec`]'s real `OtapPdata` +//! already gets that for free, without any hand-rolled scheme: OTAP's +//! Arrow encoder (`encode_metrics_otap_batch`) dictionary-encodes the +//! metric name and every string-valued attribute key/value by +//! construction — see `codec.rs`'s +//! `real_otap_encoding_dictionary_encodes_metric_name_and_string_attributes` +//! test. `SeriesDictionary` / `SketchStreamBatch` stay in the tree — +//! tested, and still the transport for the legacy +//! `crate::otap::wire` example binaries — but they aren't part of this +//! adapter's path. +//! //! ## Scope not covered here //! -//! This adapter only handles the **producer role** — real OTLP -//! metrics in, sketch envelopes (or, in `transmit_sketch = false` -//! estimate mode, quantile/cardinality gauges) out, both as ordinary -//! OTAP metric traffic. The **receiver role** — ingesting another -//! `asap_sketches` node's `SketchStreamBatch` output -//! (`AsapSketchesPlugin::start_from_envelopes`) as `OtapPdata` — needs -//! a different binding (that format doesn't fit OTAP's metrics shape -//! at all) and isn't addressed by this file. - -#![deny(unsafe_op_in_unsafe_fn)] - -mod otap_bridge; +//! Ingesting another `asap_sketches` node's *legacy* `SketchStreamBatch` +//! wire format (`AsapSketchesPlugin::start_from_envelopes`, +//! `crate::otap::wire`) isn't addressed here — that's a different, +//! ASAP-native hop with its own standalone example binaries +//! (`examples/sketch_producer_node.rs` / `sketch_receiver_node.rs`) as +//! its home, not something this OTAP-facing adapter needs to speak. +//! +//! ## Provenance / verification status +//! +//! Build/lint/test-verified against upstream `open-telemetry/otel-arrow` +//! commit `3e85c3460361446ebfce99e9f35fffd2dd5ab740` (2026-08-24) via +//! the plain git dependency this module's crate carries under the +//! `otap-engine` feature (see `Cargo.toml`) — `cargo build --features +//! otap-engine` / `cargo clippy --features otap-engine --all-targets +//! -D warnings` / `cargo fmt --check` / `cargo test --features +//! otap-engine` all pass without any manual staging into a local +//! checkout of that repo. use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::Arc; @@ -69,15 +98,12 @@ use async_trait::async_trait; use linkme::distributed_slice; use serde::{Deserialize, Serialize}; -// NOTE: `otel_arrow_dfe_*` is the current (2026-08-24) upstream naming -// — see otap_bridge.rs's module doc for the very recent `otap_df_*` -// rename this assumes. Revert to `otap_df_*` throughout this file (and -// otap_bridge.rs) if the actual pinned commit predates it. use otel_arrow_dfe_config::error::Error as OtapConfigError; use otel_arrow_dfe_config::node::NodeUserConfig; use otel_arrow_dfe_engine::config::ProcessorConfig; use otel_arrow_dfe_engine::context::PipelineContext; use otel_arrow_dfe_engine::control::NodeControlMsg; +use otel_arrow_dfe_engine::effect_handler::TimerCancelHandle; use otel_arrow_dfe_engine::error::Error; use otel_arrow_dfe_engine::local::processor as local; use otel_arrow_dfe_engine::message::Message; @@ -87,12 +113,13 @@ use otel_arrow_dfe_engine::ProcessorFactory; use otel_arrow_dfe_otap::pdata::OtapPdata; use otel_arrow_dfe_otap::OTAP_PROCESSOR_FACTORIES; -use asap_precompute_rs::config::PrecomputeConfigSet; -use asap_precompute_rs::otap::config::resolve as resolve_plugin_config; -use asap_precompute_rs::otap::{decode_batch, encode_batch, flatten, lift, AsapSketchesPlugin, PluginConfig}; -use asap_precompute_rs::precompute::Precompute; +use crate::config::PrecomputeConfigSet; +use crate::envelope::SketchEnvelope; +use crate::otap::config::resolve as resolve_plugin_config; +use crate::otap::{AsapSketchesPlugin, PluginConfig}; +use crate::precompute::{Precompute, PrecomputeError}; -use otap_bridge::{otap_metric_records_to_pdata, pdata_to_otap_metric_records}; +use super::codec::{decode_pdata_to_observations, encode_envelopes_to_pdata}; /// Public URN for the unified ASAP `asap_sketches` processor. Survives /// across hosts unchanged so a controller plan addressed at this URN @@ -100,7 +127,7 @@ use otap_bridge::{otap_metric_records_to_pdata, pdata_to_otap_metric_records}; pub const ASAP_SKETCHES_PROCESSOR_URN: &str = "urn:asap:processor:asap_sketches"; /// User-facing TOML / YAML config for the OTAP `asap_sketches` plugin. -/// Mirrors `otap-patch/plugins/asap_sketches/sample.toml` 1:1. +/// Mirrors `plugins/asap_sketches/sample.toml` 1:1. /// /// Field-by-field documentation lives in the plugin's `README.md`; /// this struct is the deserialization shape OTAP's loader hands the @@ -132,11 +159,11 @@ pub struct AsapSketchesUserConfig { } impl AsapSketchesUserConfig { - /// Translate the user-facing config into Phase C's runtime config. - /// `sketch_params` deserializes lazily — the runtime walks the JSON - /// map per sketch type. + /// Translate the user-facing config into the runtime config. + /// `sketch_params` deserializes lazily — the runtime walks the + /// JSON map per sketch type. fn into_plugin_config(self) -> Result { - use asap_precompute_rs::config::SketchParams; + use crate::config::SketchParams; let mut params = SketchParams::new(); for (k, v) in self.sketch_params { @@ -156,18 +183,21 @@ impl AsapSketchesUserConfig { sketch_params: params, ..PluginConfig::default() }; - // Bounce the config through the Phase C resolver so config - // mistakes (unsupported sketch_type, zero window) surface here - // rather than at first observe(). - let _ = asap_precompute_rs::otap::config::resolve(&cfg).map_err(|e| { - OtapConfigError::InvalidUserConfig { - error: format!("asap_sketches: {e}"), - } + // Bounce the config through the resolver so config mistakes + // (unsupported sketch_type, zero window) surface here rather + // than at first observe(). + let _ = resolve_plugin_config(&cfg).map_err(|e| OtapConfigError::InvalidUserConfig { + error: format!("asap_sketches: {e}"), })?; Ok(cfg) } } +fn requires_precompute_rebuild(current: &PluginConfig, next: &PluginConfig) -> bool { + !current.sketch_type.eq_ignore_ascii_case(&next.sketch_type) + || current.sketch_params != next.sketch_params +} + /// `linkme` registration entry — the static the OTAP runtime walks at /// startup to populate `OTAP_PIPELINE_FACTORY.processor_factory_map`. /// @@ -188,18 +218,17 @@ pub static ASAP_SKETCHES_PROCESSOR_FACTORY: ProcessorFactory = Proces /// Static config validation hook. Surfaces `--validate-and-exit` /// errors for typos in the user's TOML before any runtime allocation. fn validate_asap_sketches_config(config: &serde_json::Value) -> Result<(), OtapConfigError> { - let user: AsapSketchesUserConfig = serde_json::from_value(config.clone()).map_err(|e| { - OtapConfigError::InvalidUserConfig { + let user: AsapSketchesUserConfig = + serde_json::from_value(config.clone()).map_err(|e| OtapConfigError::InvalidUserConfig { error: format!("asap_sketches: {e}"), - } - })?; + })?; let _ = user.into_plugin_config()?; Ok(()) } /// Factory function — invoked once per pipeline instance at startup. -/// Translates the user-supplied TOML into Phase C's [`PluginConfig`], -/// resolves it to a `Precompute` instance via +/// Translates the user-supplied TOML into [`PluginConfig`], resolves +/// it to a `Precompute` instance via /// [`AsapSketchesPlugin::from_plugin_config`] (reusing its validated /// construction path), and wraps the bare `Precompute` in OTAP's /// `local::Processor` adapter — **not** the plugin's own Tokio-task @@ -209,6 +238,7 @@ pub fn create_asap_sketches_processor( node: NodeId, node_config: Arc, processor_config: &ProcessorConfig, + _capabilities: &otel_arrow_dfe_engine::capability::registry::Capabilities, ) -> Result, OtapConfigError> { let user: AsapSketchesUserConfig = serde_json::from_value(node_config.config.clone()).map_err(|e| { @@ -217,7 +247,6 @@ pub fn create_asap_sketches_processor( } })?; let plugin_config = user.into_plugin_config()?; - let window_size = plugin_config.window_size; // `from_plugin_config` is pure (no Tokio) — it just validates and // resolves. Grab the `Arc` it constructs and @@ -225,11 +254,11 @@ pub fn create_asap_sketches_processor( // spawns Tokio tasks around a `Stream` // and currently emits `SketchStreamBatch` (the asap_sketches -> // asap_sketches wire-transport shape from PR #6, dictionary - // economics) — not the OTAP-Metrics-shaped `OtapMetricRecords` - // this adapter needs for `effect_handler.send_message`. OTAP's - // own per-message `process()` + `effect_handler.start_periodic_timer` - // is a better-fitting host for a callback-driven `Precompute` - // than bridging that Stream-based lifecycle would be. + // economics) — not what this adapter needs for + // `effect_handler.send_message`. OTAP's own per-message + // `process()` + `effect_handler.start_periodic_timer` is a + // better-fitting host for a callback-driven `Precompute` than + // bridging that Stream-based lifecycle would be. let plugin = AsapSketchesPlugin::from_plugin_config(&plugin_config).map_err(|e| { OtapConfigError::InvalidUserConfig { error: format!("asap_sketches: plugin construction: {e}"), @@ -239,37 +268,35 @@ pub fn create_asap_sketches_processor( drop(plugin); Ok(ProcessorWrapper::local( - AsapSketchesProcessor::new(precompute, window_size), + AsapSketchesProcessor::new(precompute, plugin_config), node, node_config, processor_config, )) } -/// OTAP `local::Processor` adapter — the real -/// `OtapPdata` <-> `OtapMetricRecords` binding (`otap_bridge`) driving -/// a bare [`Precompute`] instance directly, rather than through -/// [`AsapSketchesPlugin`]'s own Tokio-task lifecycle (see -/// [`create_asap_sketches_processor`]'s doc for why: that lifecycle's -/// emit shape and this adapter's needed shape have diverged since -/// PR #5/#6). `Precompute::observe`/`tick`/`drain` are themselves -/// callback-style, not stream-based, so driving them directly from -/// OTAP's own per-message/per-timer `process()` calls needs no -/// bridging machinery at all. +/// OTAP `local::Processor` adapter — the real `OtapPdata` +/// binding ([`super::codec`]) driving a bare [`Precompute`] instance +/// directly, rather than through [`AsapSketchesPlugin`]'s own +/// Tokio-task lifecycle (see [`create_asap_sketches_processor`]'s doc +/// for why: that lifecycle's emit shape and this adapter's needed +/// shape have diverged since PR #5/#6). `Precompute::observe`/`tick`/ +/// `drain` are themselves callback-style, not stream-based, so driving +/// them directly from OTAP's own per-message/per-timer `process()` +/// calls needs no bridging machinery at all. /// -/// # Verification status -/// -/// See `otap_bridge.rs`'s module doc — the `OtapPdata` conversions -/// this adapter calls have not been build-verified against a real -/// OTAP Dataflow workspace. +/// Sends exclusively via `effect_handler.send_message_with_source_node` +/// — see this module's doc, "There is exactly one transport: the +/// pipeline", for why there's no second, direct-TCP transport here. pub struct AsapSketchesProcessor { precompute: Arc, + plugin_config: PluginConfig, window_size: Duration, /// `effect_handler.start_periodic_timer` is `async` and OTAP has /// no dedicated "processor started" hook — armed on the first /// `process()` call instead (`Message::PData` or any control /// message) rather than at construction time. - timer_started: bool, + timer: Option>, /// `PrecomputeConfigSet::version` for the next `NodeControlMsg::Config` /// this processor applies — monotonically increasing, independent /// of any external controller's own versioning since this @@ -278,11 +305,13 @@ pub struct AsapSketchesProcessor { } impl AsapSketchesProcessor { - fn new(precompute: Arc, window_size: Duration) -> Self { + fn new(precompute: Arc, plugin_config: PluginConfig) -> Self { + let window_size = plugin_config.window_size; Self { precompute, + plugin_config, window_size, - timer_started: false, + timer: None, next_config_version: Arc::new(AtomicU64::new(1)), } } @@ -291,9 +320,17 @@ impl AsapSketchesProcessor { /// shared by the `TimerTick` (regular flush) and `Shutdown` /// (final drain) paths. `envs` empty is a no-op, matching /// `Precompute::tick`/`drain`'s own "nothing to flush" contract. + /// + /// [`encode_envelopes_to_pdata`] builds the real `OtapPdata` + /// directly from `envs` — no flat-`RecordBatch`/`OtapMetricRecords` + /// hop, and deliberately not `SeriesDictionary::encode` (the + /// SCHEMA/DICTIONARY/RECORD wire economics from PR #5/#6, the + /// older `crate::otap::wire` format) — see this module's doc, + /// "There is exactly one transport: the pipeline", for why that + /// scheme is redundant on this path. async fn emit_envelopes( &self, - envs: &[asap_precompute_rs::envelope::SketchEnvelope], + envs: &[SketchEnvelope], effect_handler: &mut local::EffectHandler, ) -> Result<(), Error> { use otel_arrow_dfe_engine::MessageSourceLocalEffectHandlerExtension as _; @@ -301,25 +338,9 @@ impl AsapSketchesProcessor { if envs.is_empty() { return Ok(()); } - // The documented Phase-B path: SketchEnvelopes -> flat - // RecordBatch (encode_batch) -> OTAP-validator-safe two-batch - // family (lift) -> real OtapPdata (otap_bridge). Deliberately - // NOT `SeriesDictionary::encode` (the SCHEMA/DICTIONARY/RECORD - // wire economics from PR #5/#6) — that format is for the - // asap_sketches -> asap_sketches transport hop - // (`otap::wire`), not for riding inside an arbitrary OTAP - // pipeline as a generic metric. - let flat = match encode_batch(envs) { - Ok(flat) => flat, - Err(_e) => return Ok(()), // drop the bad window, keep the processor alive - }; - let records = match lift(&flat) { - Ok(records) => records, - Err(_e) => return Ok(()), - }; - let pdata = match otap_metric_records_to_pdata(&records) { + let pdata = match encode_envelopes_to_pdata(envs) { Ok(pdata) => pdata, - Err(_e) => return Ok(()), + Err(_e) => return Ok(()), // drop the bad window, keep the processor alive }; effect_handler.send_message_with_source_node(pdata).await?; Ok(()) @@ -333,23 +354,20 @@ impl local::Processor for AsapSketchesProcessor { msg: Message, effect_handler: &mut local::EffectHandler, ) -> Result<(), Error> { - if !self.timer_started { - self.timer_started = true; + if self.timer.is_none() { // Best-effort: if this fails, the processor still ingests // and observes correctly, it just never flushes a window - // on its own — degraded, not broken. `TimerCancelHandle` - // is dropped immediately: the timer keeps firing for this - // processor's lifetime rather than being cancellable - // (there's currently no shutdown-adjacent place to hold - // the handle across `process()` calls without adding - // another `Option<...>` field for a cancellation this - // adapter never actually exercises). - let _ = effect_handler.start_periodic_timer(self.window_size).await; + // on its own. Retaining the handle lets a later config push + // cancel and recreate the timer when window_size changes. + self.timer = effect_handler + .start_periodic_timer(self.window_size) + .await + .ok(); } match msg { Message::PData(pdata) => { - let outcome = match pdata_to_otap_metric_records(pdata) { + let outcome = match decode_pdata_to_observations(pdata) { Ok(outcome) => outcome, Err(_e) => return Ok(()), // drop the bad batch, keep the processor alive }; @@ -361,29 +379,19 @@ impl local::Processor for AsapSketchesProcessor { )) .await; } - let Some(records) = outcome.records else { - return Ok(()); - }; - let flat = match flatten(&records) { - Ok(flat) => flat, - Err(_e) => return Ok(()), - }; - let observations = match decode_batch(&flat) { - Ok(observations) => observations, - Err(_e) => return Ok(()), - }; - for obs in &observations { + for obs in &outcome.observations { // Note: `observe` here handles both "genuine OTLP // metric" and "sketch shipped as binary inside an // OTAP metric" (an upstream asap_sketches node's // `_asap_envelope`-tagged output) — no branching - // needed at this call site. `decode_batch` already - // tags the latter as `ObservationValueKind::Envelope`, - // and `Precompute::observe` already routes those + // needed at this call site. `decode_pdata_to_observations` + // already tags the latter as + // `ObservationValueKind::Envelope`, and + // `Precompute::observe` already routes those // internally to `observe_envelope` (merge as a // pre-aggregated sketch) instead of expanding them - // to scalar samples. See otap_bridge.rs's module - // doc ("Scope") for the full picture. + // to scalar samples. See `codec.rs`'s module doc + // ("Scope") for the full picture. // // LateData / SeriesCapExceeded are expected, // already-tallied-in-stats outcomes (mirrors @@ -394,7 +402,6 @@ impl local::Processor for AsapSketchesProcessor { // and is at least surfaced via `effect_handler.info` // — a real error channel is follow-up work, but // this keeps it from being completely invisible. - use asap_precompute_rs::precompute::PrecomputeError; match self.precompute.observe(obs) { Ok(()) | Err(PrecomputeError::LateData) @@ -428,11 +435,44 @@ impl local::Processor for AsapSketchesProcessor { let Ok((pcfg, _dispatch)) = resolve_plugin_config(&plugin_config) else { return Ok(()); }; - let version = self.next_config_version.fetch_add(1, Ordering::Relaxed); - self.precompute.update_config(&PrecomputeConfigSet { - version, - configs: vec![pcfg], - }); + + // The sketch factory and observer are fixed when a + // PrecomputeImpl is constructed. If a push changes the + // algorithm or its construction parameters, updating only + // PrecomputeConfig would stamp the new type onto sketches + // still produced by the old implementation. Flush the old + // instance and replace it with a consistently constructed + // one instead. + let factory_changed = + requires_precompute_rebuild(&self.plugin_config, &plugin_config); + if factory_changed { + let replacement = match AsapSketchesPlugin::from_plugin_config(&plugin_config) { + Ok(plugin) => plugin.precompute().clone(), + Err(_) => return Ok(()), + }; + let pending = self.precompute.drain(); + self.emit_envelopes(&pending, effect_handler).await?; + self.precompute = replacement; + } else { + let version = self.next_config_version.fetch_add(1, Ordering::Relaxed); + self.precompute.update_config(&PrecomputeConfigSet { + version, + configs: vec![pcfg], + }); + } + + if plugin_config.window_size != self.window_size { + if let Some(timer) = self.timer.take() { + let _ = timer.cancel().await; + } + self.timer = Some( + effect_handler + .start_periodic_timer(plugin_config.window_size) + .await?, + ); + self.window_size = plugin_config.window_size; + } + self.plugin_config = plugin_config; Ok(()) } Message::Control(NodeControlMsg::CollectTelemetry { .. }) => Ok(()), @@ -442,8 +482,8 @@ impl local::Processor for AsapSketchesProcessor { } /// Wall-clock millisecond timestamp — mirrors -/// `asap_precompute_rs::otap::lifecycle`'s private `wall_clock_ms` -/// (not exported for reuse across the crate boundary). +/// `crate::otap::lifecycle`'s private `wall_clock_ms` (not exported +/// for reuse across the module boundary). fn asap_wall_clock_ms() -> u64 { use std::time::{SystemTime, UNIX_EPOCH}; SystemTime::now() @@ -454,9 +494,8 @@ fn asap_wall_clock_ms() -> u64 { #[cfg(test)] mod tests { - //! Unit tests for the registration crate. Confined to config-shape - //! validation — full lifecycle tests live in - //! `asap-precompute-rs/tests/otap_lifecycle.rs` (Phase C). + //! Unit tests confined to config-shape validation — full lifecycle + //! tests live in `tests/otap_lifecycle.rs`. use super::*; use serde_json::json; @@ -512,7 +551,10 @@ mod tests { }); let err = validate_asap_sketches_config(&cfg).expect_err("zero window rejected"); let msg = format!("{err}"); - assert!(msg.contains("window_size"), "error should mention window: {msg}"); + assert!( + msg.contains("window_size"), + "error should mention window: {msg}" + ); } #[test] @@ -545,7 +587,33 @@ mod tests { fn registered_factory_carries_expected_urn() { // Sanity check: the registry static the binary discovers via // linkme exposes the canonical URN. - assert_eq!(ASAP_SKETCHES_PROCESSOR_FACTORY.name, ASAP_SKETCHES_PROCESSOR_URN); - assert_eq!(ASAP_SKETCHES_PROCESSOR_URN, "urn:asap:processor:asap_sketches"); + assert_eq!( + ASAP_SKETCHES_PROCESSOR_FACTORY.name, + ASAP_SKETCHES_PROCESSOR_URN + ); + assert_eq!( + ASAP_SKETCHES_PROCESSOR_URN, + "urn:asap:processor:asap_sketches" + ); + } + + #[test] + fn algorithm_and_factory_parameter_changes_require_rebuild() { + let current = PluginConfig::default(); + + let mut algorithm_change = current.clone(); + algorithm_change.sketch_type = "hll".to_string(); + assert!(requires_precompute_rebuild(¤t, &algorithm_change)); + + let mut parameter_change = current.clone(); + parameter_change + .sketch_params + .insert("relative_accuracy".to_string(), 0.005); + assert!(requires_precompute_rebuild(¤t, ¶meter_change)); + + let mut runtime_only_change = current.clone(); + runtime_only_change.output_metric_name = "renamed".to_string(); + runtime_only_change.window_size = Duration::from_secs(60); + assert!(!requires_precompute_rebuild(¤t, &runtime_only_change)); } } diff --git a/asap-precompute-rs/tests/otap_pipeline_e2e.rs b/asap-precompute-rs/tests/otap_pipeline_e2e.rs new file mode 100644 index 0000000..c13cbd7 --- /dev/null +++ b/asap-precompute-rs/tests/otap_pipeline_e2e.rs @@ -0,0 +1,268 @@ +#![cfg(feature = "otap-engine")] + +//! Runs `asap_sketches` inside a real OTAP `RuntimePipeline` built from YAML. + +use std::sync::{Arc, Mutex, OnceLock}; +use std::time::{Duration, Instant}; + +use asap_precompute_rs::envelope::{Encoding, SketchEnvelope, SketchType}; +use asap_precompute_rs::observation::KeyValue; +use asap_precompute_rs::otap::codec::{decode_pdata_to_observations, encode_envelopes_to_pdata}; +use async_trait::async_trait; +use linkme::distributed_slice; +use otel_arrow_dfe_config::node::NodeUserConfig; +use otel_arrow_dfe_config::observed_state::{ObservedStateSettings, SendPolicy}; +use otel_arrow_dfe_config::pipeline::PipelineConfig; +use otel_arrow_dfe_config::policy::{ChannelCapacityPolicy, TelemetryPolicy}; +use otel_arrow_dfe_config::{DeployedPipelineKey, PipelineGroupId, PipelineId}; +use otel_arrow_dfe_engine::capability::registry::Capabilities; +use otel_arrow_dfe_engine::config::{ExporterConfig, ReceiverConfig}; +use otel_arrow_dfe_engine::context::{ControllerContext, PipelineContext}; +use otel_arrow_dfe_engine::control::{ + pipeline_completion_msg_channel, runtime_ctrl_msg_channel, NodeControlMsg, RuntimeControlMsg, +}; +use otel_arrow_dfe_engine::exporter::ExporterWrapper; +use otel_arrow_dfe_engine::local::{exporter as local_exporter, receiver as local_receiver}; +use otel_arrow_dfe_engine::message::{ExporterInbox, Message}; +use otel_arrow_dfe_engine::node::NodeId; +use otel_arrow_dfe_engine::receiver::ReceiverWrapper; +use otel_arrow_dfe_engine::terminal_state::TerminalState; +use otel_arrow_dfe_engine::{ + ExporterFactory, MessageSourceLocalEffectHandlerExtension, ReceiverFactory, +}; +use otel_arrow_dfe_otap::pdata::OtapPdata; +use otel_arrow_dfe_otap::{ + OTAP_EXPORTER_FACTORIES, OTAP_PIPELINE_FACTORY, OTAP_RECEIVER_FACTORIES, +}; +use otel_arrow_dfe_state::store::ObservedStateStore; +use otel_arrow_dfe_telemetry::InternalTelemetrySystem; + +const SOURCE_URN: &str = "urn:asap:receiver:one_metric"; +const SINK_URN: &str = "urn:asap:exporter:capture"; + +static CAPTURED: OnceLock>> = OnceLock::new(); + +fn captured() -> &'static Mutex> { + CAPTURED.get_or_init(|| Mutex::new(Vec::new())) +} + +fn scalar_input() -> OtapPdata { + let envelope = SketchEnvelope { + schema_version: 1, + sketch_type: SketchType::Unspecified, + agg_id: 0, + resource_labels: vec![], + labels: vec![KeyValue::new("route", "/checkout")], + window_start_ms: 1_000, + window_end_ms: 2_000, + encoding: Encoding::Unspecified, + payload: vec![], + hash_spec: None, + metric_name: "request.duration".to_owned(), + count: 0, + aggregation_temporality: 0, + value: 42.5, + }; + encode_envelopes_to_pdata(&[envelope]).expect("encode scalar input") +} + +struct OneMetricReceiver; + +#[async_trait(?Send)] +impl local_receiver::Receiver for OneMetricReceiver { + async fn start( + self: Box, + mut ctrl: local_receiver::ControlChannel, + effect_handler: local_receiver::EffectHandler, + ) -> Result { + effect_handler + .send_message_with_source_node(scalar_input()) + .await?; + loop { + match ctrl.recv().await { + Ok(NodeControlMsg::Shutdown { .. }) | Err(_) => break, + Ok(_) => {} + } + } + Ok(TerminalState::default()) + } +} + +fn create_source( + _ctx: PipelineContext, + node: NodeId, + config: Arc, + runtime: &ReceiverConfig, + _capabilities: &Capabilities, +) -> Result, otel_arrow_dfe_config::error::Error> { + Ok(ReceiverWrapper::local( + OneMetricReceiver, + node, + config, + runtime, + )) +} + +#[distributed_slice(OTAP_RECEIVER_FACTORIES)] +static SOURCE_FACTORY: ReceiverFactory = ReceiverFactory { + name: SOURCE_URN, + create: create_source, + wiring_contract: otel_arrow_dfe_engine::wiring_contract::WiringContract::UNRESTRICTED, + validate_config: otel_arrow_dfe_config::validation::no_config, +}; + +struct CaptureExporter; + +#[async_trait(?Send)] +impl local_exporter::Exporter for CaptureExporter { + async fn start( + self: Box, + mut inbox: ExporterInbox, + _effect_handler: local_exporter::EffectHandler, + ) -> Result { + loop { + match inbox.recv().await? { + Message::PData(pdata) => captured().lock().expect("capture mutex").push(pdata), + Message::Control(NodeControlMsg::Shutdown { .. }) => break, + Message::Control(_) => {} + } + } + Ok(TerminalState::default()) + } +} + +fn create_sink( + _ctx: PipelineContext, + node: NodeId, + config: Arc, + runtime: &ExporterConfig, + _capabilities: &Capabilities, +) -> Result, otel_arrow_dfe_config::error::Error> { + Ok(ExporterWrapper::local( + CaptureExporter, + node, + config, + runtime, + )) +} + +#[distributed_slice(OTAP_EXPORTER_FACTORIES)] +static SINK_FACTORY: ExporterFactory = ExporterFactory { + name: SINK_URN, + create: create_sink, + wiring_contract: otel_arrow_dfe_engine::wiring_contract::WiringContract::UNRESTRICTED, + validate_config: otel_arrow_dfe_config::validation::no_config, +}; + +#[test] +fn yaml_pipeline_ingests_flushes_and_exports_a_sketch() { + captured().lock().expect("capture mutex").clear(); + let yaml = format!( + r#" +nodes: + source: + type: "{SOURCE_URN}" + sketches: + type: "urn:asap:processor:asap_sketches" + config: + sketch_type: "ddsketch" + window_size: "20ms" + output_metric_name: "request.duration.p99" + agg_id: 0 + sink: + type: "{SINK_URN}" +connections: + - from: source + to: sketches + - from: sketches + to: sink +"# + ); + let config = PipelineConfig::from_yaml("asap-e2e".into(), "sketches".into(), &yaml) + .expect("pipeline YAML parses and validates"); + + let telemetry = InternalTelemetrySystem::default(); + let pipeline_ctx = ControllerContext::new(telemetry.registry()).pipeline_context_with( + PipelineGroupId::from("asap-e2e"), + PipelineId::from("sketches"), + 0, + 1, + 0, + ); + let entity_key = pipeline_ctx.register_pipeline_entity(); + let runtime = OTAP_PIPELINE_FACTORY + .build( + pipeline_ctx.clone(), + config, + ChannelCapacityPolicy::default(), + TelemetryPolicy::default(), + None, + Default::default(), + None, + None, + ) + .expect("pipeline builds from registered factories"); + + let channel_policy = ChannelCapacityPolicy::default(); + let (runtime_tx, runtime_rx) = runtime_ctrl_msg_channel(channel_policy.control.pipeline); + let (completion_tx, completion_rx) = + pipeline_completion_msg_channel(channel_policy.control.completion); + let observed = ObservedStateStore::new(&ObservedStateSettings::default(), telemetry.registry()); + let shutdown_tx = runtime_tx.clone(); + let shutdown = std::thread::spawn(move || { + std::thread::sleep(Duration::from_millis(100)); + shutdown_tx + .try_send(RuntimeControlMsg::Shutdown { + deadline: Instant::now() + Duration::from_secs(1), + reason: "e2e complete".to_owned(), + }) + .expect("request pipeline shutdown"); + }); + let pipeline_key = DeployedPipelineKey { + pipeline_group_id: pipeline_ctx.pipeline_group_id(), + pipeline_id: pipeline_ctx.pipeline_id(), + core_id: 0, + deployment_generation: 0, + }; + let (_pressure_tx, pressure_rx) = tokio::sync::watch::channel( + otel_arrow_dfe_engine::memory_limiter::MemoryPressureChanged::initial(), + ); + let _entity_guard = otel_arrow_dfe_engine::entity_context::set_pipeline_entity_key( + pipeline_ctx.metrics_registry(), + entity_key, + ); + runtime + .run_forever( + pipeline_key, + pipeline_ctx, + observed.reporter(SendPolicy::default()), + telemetry.reporter(), + Duration::from_secs(1), + pressure_rx, + runtime_tx, + runtime_rx, + completion_tx, + completion_rx, + ) + .expect("running pipeline shuts down cleanly"); + shutdown.join().expect("shutdown thread"); + + let outputs = captured().lock().expect("capture mutex"); + assert!(!outputs.is_empty(), "pipeline exported no sketch window"); + let mut decoded = Vec::new(); + for pdata in outputs.iter().cloned() { + decoded.extend( + decode_pdata_to_observations(pdata) + .expect("decode pipeline output") + .observations, + ); + } + let envelope = decoded + .iter() + .find_map(|observation| observation.value.envelope.as_ref()) + .expect("export contains a serialized sketch envelope"); + assert_eq!(envelope.metric_name, "request.duration.p99"); + assert_eq!(envelope.sketch_type, SketchType::DDSketch); + assert_eq!(envelope.labels, vec![KeyValue::new("route", "/checkout")]); + assert!(!envelope.payload.is_empty()); +} diff --git a/otap-patch/all/Cargo.toml b/otap-patch/all/Cargo.toml deleted file mode 100644 index 57578d2..0000000 --- a/otap-patch/all/Cargo.toml +++ /dev/null @@ -1,85 +0,0 @@ -# `asap_sketches_registry` — OTAP-side `linkme` distributed-slice -# registration crate for the unified `asap_sketches` processor plugin. -# -# A small overlay applied onto the OTAP Dataflow workspace at build -# time that brings the `OTAP_PROCESSOR_FACTORIES` registration for the -# ASAP precompute plugin into the binary's link scope. -# -# The Cargo manifest is intentionally minimal. Heavy lifting (the -# Tokio lifecycle, codec, 5-sketch dispatch) lives in -# `asap-precompute-rs`; this crate is the linkme registration entry + -# the thin OTAP `local::Processor` adapter that bridges -# OTAP's `OtapPdata` shape onto `AsapSketchesPlugin`. -# -# Dependency notes: -# -# - `asap-precompute-rs` is path-deped from the staged location. -# `build_asap_otap.sh` copies this manifest to -# `/otel-arrow/rust/otap-dataflow/crates/asap-sketches-registry/Cargo.toml` -# at build time — five levels deep from the repo root, so the -# relative path back to `asap-precompute-rs/` is five `../` hops. -# - `linkme` / `otel-arrow-dfe-engine` / `otel-arrow-dfe-otap` / -# `otel-arrow-dfe-config` / `otel-arrow-dfe-pdata` / -# `otel-arrow-dfe-pdata-views` / `arrow-*` are all taken from the OTAP -# workspace via `workspace = true` so the versions track the upstream -# submodule's pin (avoids the "two distinct versions of linkme [or -# arrow-array] in the binary" failure mode — see otap_bridge.rs's -# module doc for why the arrow-array version specifically has to be -# the literal same one `asap-precompute-rs` resolves to). -# -# Package names here use the `otel-arrow-dfe-*` prefix (not the older -# `otap-df-*` one asap-precompute-rs's own docs sometimes reference) — -# upstream renamed these very recently (otel-arrow issue #1848, -# `.chloggen/otel-arrow-dfe-crate-prefix.yaml`, unreleased as of -# otel-arrow@3e85c34). If the actual pinned commit `build_asap_otap.sh` -# checks out predates that rename, these names (and `otap_bridge.rs`'s -# `otel_arrow_dfe_*` imports) need reverting to `otap-df-*` / -# `otap_df_*`. - -[package] -name = "asap_sketches_registry" -version = "0.1.0" -edition = "2024" -description = "OTAP-Rust linkme distributed-slice registration for the ASAP unified `asap_sketches` processor" -license = "MIT" -publish = false - -# Single library file; named `mod.rs` rather than `lib.rs` to match the -# `otap-patch/all/mod.rs` path the overlay layout calls out. -[lib] -name = "asap_sketches_registry" -path = "mod.rs" - -[dependencies] -# Path-dep injected at staging time by `build_asap_otap.sh`. The -# literal path here resolves once the crate is copied into -# `/crates/asap-sketches-registry/` — five levels deep from -# the repo root (`/otel-arrow/rust/otap-dataflow/crates/asap-sketches-registry`), -# so five `../` hops land back at the repo root. -asap-precompute-rs = { path = "../../../../../asap-precompute-rs", features = ["otap"] } - -# Pulled from the OTAP workspace so the linkme version matches the -# binary's. Mismatched linkme versions cause silent registration -# misses. -linkme = { workspace = true } -async-trait = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } -humantime-serde = { workspace = true } - -otel-arrow-dfe-engine = { workspace = true } -otel-arrow-dfe-otap = { workspace = true } -otel-arrow-dfe-config = { workspace = true } - -# For otap_bridge.rs's OtapMetricRecords <-> real OtapArrowRecords -# conversion: reading real metric batches (otel-arrow-dfe-pdata-views' -# MetricsView trait family) and building them -# (otel-arrow-dfe-pdata::encode::encode_metrics_otap_batch). -otel-arrow-dfe-pdata = { workspace = true } -otel-arrow-dfe-pdata-views = { workspace = true } -thiserror = { workspace = true } - -# Must resolve to the same version asap-precompute-rs's own `arrow-*` -# deps do (both pinned "58.3") — see otap_bridge.rs's module doc. -arrow-array = { workspace = true } -arrow-schema = { workspace = true } diff --git a/otap-patch/all/otap_bridge.rs b/otap-patch/all/otap_bridge.rs deleted file mode 100644 index 1c0b2c6..0000000 --- a/otap-patch/all/otap_bridge.rs +++ /dev/null @@ -1,1280 +0,0 @@ -// Copyright The ASAP Authors -// SPDX-License-Identifier: MIT - -//! Bridge between ASAP's flat, host-neutral [`OtapMetricRecords`] (the -//! shape `asap_precompute_rs::otap::records::flatten`/`decode_batch` -//! already know how to walk) and OTAP's real `OtapPdata` / -//! `OtapArrowRecords::Metrics` shape — the "one seam left" the crate -//! root README calls out. -//! -//! This is the piece that actually puts a self-describing sketch's -//! serialized bytes onto the wire *as an OTAP metric*: `encode_batch` -//! (already implemented, unchanged by this file) turns a -//! `SketchEnvelope` into an `OtapMetricRecords` whose per-row -//! attribute batch carries the envelope bytes as a `_asap_envelope` -//! `Bytes`-typed attribute (Strategy B, `otap/schema.rs`); this module -//! is the layer above that turns *that* into something OTAP's real -//! engine can carry as `Message::PData(OtapPdata)`. -//! -//! # Provenance / verification status -//! -//! Written against upstream `open-telemetry/otel-arrow` commit -//! `3e85c3460361446ebfce99e9f35fffd2dd5ab740` (2026-08-24), reading -//! `rust/otap-dataflow/crates/pdata/src/{payload.rs,otap.rs,lib.rs, -//! encode/mod.rs}` and `rust/otap-dataflow/crates/pdata-views/src/ -//! views/{metrics.rs,common.rs,resource.rs}` directly for every type -//! and function signature referenced below. The OTAP Dataflow crates -//! were renamed `otap_df_*` -> `otel_arrow_dfe_*` very recently and -//! unreleased as of that commit (`.chloggen/otel-arrow-dfe-crate- -//! prefix.yaml`, issue #1848); this file targets the new names. -//! -//! **This file has not been build-verified against a real OTAP -//! Dataflow workspace checkout** — `otap-patch/` isn't standalone- -//! buildable in the environment this was written in (see the repo -//! README's "No — depends on the OTAP workspace crates" note on this -//! directory). Treat every signature here as "read from source, not -//! compiled" and expect a build pass to shake out mismatches against -//! whatever commit this repo's own build script actually pins. -//! -//! # Scope -//! -//! What "handling a metric" means on decode splits into two cases, -//! decided by content, not by which OTLP metric type carried it: -//! -//! - **A data point carrying `_asap_envelope`** (this module's own -//! encode output, or any other `asap_sketches` node's) — this -//! module doesn't special-case it at all. It round-trips into -//! `OtapMetricRecords` like any other attribute, and -//! `decode_batch` (unchanged, downstream of this module) already -//! recognizes `_asap_envelope` and produces an -//! `ObservationValueKind::Envelope`-kind `Observation`; -//! `Precompute::observe` already dispatches those internally to -//! `observe_envelope` (merge as a pre-aggregated sketch), never -//! expanding them to scalar samples. So "sketch as a binary inside -//! an OTAP metric" already gets sketch-side handling for free, by -//! construction, with no branching needed here. -//! - **A genuine (non-envelope) OTLP metric** — real telemetry. -//! Gauge/Sum (`NumberDataPoints`) are handled normally: each data -//! point becomes a scalar `Observation`, the shape both -//! `Observation` and `OtapMetricRecords` (well-known -//! `time_unix_nano`/`metric`/`value` columns) already assume. -//! Histogram / ExponentialHistogram / Summary data points are -//! skipped (counted, not silently dropped — see -//! [`DecodeOutcome::skipped_non_scalar`]) rather than expanded, -//! since there's no single well-defined scalar to extract from a -//! bucket/quantile set without picking a lossy expansion strategy -//! this module doesn't want to own. -//! -//! On encode, every row is assumed to share one metric name — an -//! `AsapSketchesProcessor` instance has exactly one -//! `PluginConfig::output_metric_name`, so this holds by construction -//! for anything `encode_batch` itself produces; [`otap_metric_records_to_pdata`] -//! still checks it explicitly and errors loudly on a real mismatch -//! rather than silently dropping rows for the "wrong" metric. -//! -//! Resource and scope are not modelled — [`OtapMetricRecords`] itself -//! deliberately omits them (see that type's own doc); every emitted -//! metric attaches to an empty `Resource` / unnamed `Scope`. - -use std::collections::BTreeMap; -use std::sync::Arc; - -use arrow_array::{Array, BinaryArray, Float64Array, RecordBatch, StringArray, UInt32Array, UInt64Array}; -use arrow_schema::{DataType, Field, Schema}; -use thiserror::Error; - -use otel_arrow_dfe_otap::pdata::OtapPdata; -use otel_arrow_dfe_pdata::encode::encode_metrics_otap_batch; -use otel_arrow_dfe_pdata::otap::OtapArrowRecords; -use otel_arrow_dfe_pdata::views::otap::OtapMetricsView; -use otel_arrow_dfe_pdata::{OtapPayload, TryFromWithOptions, TryIntoWithOptions}; -use otel_arrow_dfe_pdata_views::views::common::{ - AnyValueView, AttributeView, InstrumentationScopeView, Str, ValueType, -}; -use otel_arrow_dfe_pdata_views::views::metrics::{ - AggregationTemporality, BucketsView, DataPointFlags, DataType as MetricKind, DataView, - ExemplarView, ExponentialHistogramDataPointView, ExponentialHistogramView, GaugeView, - HistogramDataPointView, HistogramView, MetricView, MetricsView, NumberDataPointView, - ResourceMetricsView, ScopeMetricsView, SumView, SummaryDataPointView, SummaryView, - Value as DpValue, ValueAtQuantileView, -}; -use otel_arrow_dfe_pdata_views::views::resource::ResourceView; - -use asap_precompute_rs::otap::records::{ - ATTR_BATCH_BYTES, ATTR_BATCH_INT, ATTR_BATCH_KEY, ATTR_BATCH_PARENT_ID, ATTR_BATCH_STR, -}; -use asap_precompute_rs::otap::{ - OtapMetricRecords, COLUMN_METRIC, COLUMN_TIME_UNIX_NANO, COLUMN_VALUE, -}; - -/// Failure modes for [`otap_metric_records_to_pdata`] / [`pdata_to_otap_metric_records`]. -#[derive(Debug, Error)] -pub enum BridgeError { - /// A required column was missing or had the wrong Arrow type. - #[error("otap bridge: column {column:?} on batch {batch:?} missing or wrong type")] - BadColumn { - /// Which sibling batch. - batch: &'static str, - /// Column name. - column: &'static str, - }, - /// More than one distinct `metric` name appeared in one - /// `OtapMetricRecords.metrics` batch — an - /// `AsapSketchesProcessor` instance has exactly one - /// `output_metric_name`, so this indicates the caller fed rows - /// from more than one processor instance into a single call. - #[error( - "otap bridge: one OtapMetricRecords batch carries more than one metric name ({first:?} and {second:?} seen)" - )] - MixedMetricNames { - /// First metric name seen. - first: String, - /// A second, different metric name seen in the same batch. - second: String, - }, - /// Building the real `OtapArrowRecords::Metrics` batch failed. - #[error("otap bridge: encoding real OTAP metrics batch failed: {0}")] - Encode(String), - /// Reading the real `OtapArrowRecords::Metrics` batch failed. - #[error("otap bridge: reading real OTAP metrics batch failed: {0}")] - Decode(String), - /// Constructing the flat output `RecordBatch` failed. - #[error("otap bridge: arrow record-batch construction failed: {0}")] - Arrow(#[from] arrow_schema::ArrowError), -} - -// ============================================================================ -// Encode direction: OtapMetricRecords -> OtapPdata -// ============================================================================ - -/// Converts an [`OtapMetricRecords`] (as `encode_batch` produces it) -/// into a real `OtapPdata` carrying an `OtapArrowRecords::Metrics` -/// payload — the actual "put the sketch envelope bytes onto an OTAP -/// metric" step. -/// -/// Builds a fresh, contextless `OtapPdata` (`OtapPdata::new_todo_context`) -/// rather than propagating any single input message's context: the -/// plugin's flush ticker emits one window's worth of envelopes on its -/// own wall-clock schedule, decoupled from any specific triggering -/// input message, so there is no single Ack/Nack chain to attach the -/// output to (the same reasoning any periodic/windowed aggregator's -/// output would follow). -pub fn otap_metric_records_to_pdata(records: &OtapMetricRecords) -> Result { - let view = AsapMetricsView::try_new(records)?; - let arrow_records = - encode_metrics_otap_batch(&view).map_err(|e| BridgeError::Encode(e.to_string()))?; - let payload: OtapPayload = arrow_records.into(); - Ok(OtapPdata::new_todo_context(payload)) -} - -/// Zero-copy-ish adapter presenting an [`OtapMetricRecords`]'s two -/// flat batches as a `MetricsView` — one Resource (empty), one Scope -/// (unnamed), one Metric (`records.metrics`'s single distinct `metric` -/// value), one Gauge, N `NumberDataPoint`s (one per row). -struct AsapMetricsView { - metric_name: String, - n_rows: usize, - time_col: UInt64Array, - value_col: Float64Array, - /// `attr_index[parent_id]` = row indices into `records.attributes` - /// carrying that parent's attributes. Precomputed once so per-data-point - /// `attributes()` calls don't rescan the whole attribute batch. - attr_index: BTreeMap>, - attr_key_col: StringArray, - attr_bytes_col: Option, - attr_str_col: Option, - attr_int_col: Option, -} - -impl<'a> AsapMetricsView { - fn try_new(records: &'a OtapMetricRecords) -> Result { - let n_rows = records.metrics.num_rows(); - - let metric_col = require_string(&records.metrics, "metrics", COLUMN_METRIC)?; - let mut metric_name: Option = None; - for row in 0..n_rows { - let name = metric_col.value(row); - match &metric_name { - None => metric_name = Some(name.to_string()), - Some(seen) if seen == name => {} - Some(seen) => { - return Err(BridgeError::MixedMetricNames { - first: seen.clone(), - second: name.to_string(), - }); - } - } - } - - let time_col = require_uint64(&records.metrics, "metrics", COLUMN_TIME_UNIX_NANO)?.clone(); - let value_col = require_float64(&records.metrics, "metrics", COLUMN_VALUE)?.clone(); - let parent_col = require_uint32(&records.metrics, "metrics", ATTR_BATCH_PARENT_ID)?; - - let attr_parent_col = require_uint32(&records.attributes, "attributes", ATTR_BATCH_PARENT_ID)?; - let attr_key_col = require_string(&records.attributes, "attributes", ATTR_BATCH_KEY)?.clone(); - let attr_bytes_col = optional_binary(&records.attributes, ATTR_BATCH_BYTES)?; - let attr_str_col = optional_string(&records.attributes, ATTR_BATCH_STR)?; - let attr_int_col = optional_uint64(&records.attributes, ATTR_BATCH_INT)?; - - let mut attr_index: BTreeMap> = BTreeMap::new(); - for row in 0..records.attributes.num_rows() { - if attr_parent_col.is_null(row) { - continue; - } - attr_index - .entry(attr_parent_col.value(row)) - .or_default() - .push(row); - } - // Not currently used beyond validating the column exists — - // parent_id on `metrics` isn't needed for row->attribute - // linking here (each metrics row's own index doubles as its - // parent_id by `encode_batch`'s own convention), but keep the - // column checked so a future divergence surfaces as a - // decode-time BadColumn error rather than silent misjoin. - let _ = parent_col; - - Ok(Self { - metric_name: metric_name.unwrap_or_default(), - n_rows, - time_col, - value_col, - attr_index, - attr_key_col, - attr_bytes_col, - attr_str_col, - attr_int_col, - }) - } - - fn attributes_for_row(&self, parent_id: u32) -> Vec> { - let Some(rows) = self.attr_index.get(&parent_id) else { - return Vec::new(); - }; - rows.iter() - .filter_map(|&row| { - let key = self.attr_key_col.value(row); - let value = attr_row_value( - self.attr_bytes_col.as_ref(), - self.attr_str_col.as_ref(), - self.attr_int_col.as_ref(), - row, - )?; - Some(AsapAttribute { key, value }) - }) - .collect() - } -} - -impl MetricsView for AsapMetricsView { - type ResourceMetrics<'res> - = AsapResourceMetricsView<'res> - where - Self: 'res; - type ResourceMetricsIter<'res> - = std::vec::IntoIter> - where - Self: 'res; - - fn resources(&self) -> Self::ResourceMetricsIter<'_> { - if self.n_rows == 0 { - Vec::new().into_iter() - } else { - vec![AsapResourceMetricsView { view: self }].into_iter() - } - } -} - -struct AsapResourceMetricsView<'a> { - view: &'a AsapMetricsView, -} - -impl<'a> ResourceMetricsView for AsapResourceMetricsView<'a> { - type Resource<'res> - = AsapNoResource - where - Self: 'res; - type ScopeMetrics<'scp> - = AsapScopeMetricsView<'scp> - where - Self: 'scp; - type ScopesIter<'scp> - = std::vec::IntoIter> - where - Self: 'scp; - - fn resource(&self) -> Option> { - None - } - - fn scopes(&self) -> Self::ScopesIter<'_> { - vec![AsapScopeMetricsView { view: self.view }].into_iter() - } - - fn schema_url(&self) -> Option> { - None - } -} - -struct AsapScopeMetricsView<'a> { - view: &'a AsapMetricsView, -} - -impl<'a> ScopeMetricsView for AsapScopeMetricsView<'a> { - type Scope<'scp> - = AsapNoScope - where - Self: 'scp; - type Metric<'met> - = AsapMetricView<'met> - where - Self: 'met; - type MetricIter<'met> - = std::vec::IntoIter> - where - Self: 'met; - - fn scope(&self) -> Option> { - None - } - - fn metrics(&self) -> Self::MetricIter<'_> { - vec![AsapMetricView { view: self.view }].into_iter() - } - - fn schema_url(&self) -> Str<'_> { - b"" - } -} - -struct AsapMetricView<'a> { - view: &'a AsapMetricsView, -} - -impl<'a> MetricView for AsapMetricView<'a> { - type Data<'dat> - = AsapDataView<'dat> - where - Self: 'dat; - type Attribute<'att> - = AsapAttribute<'att> - where - Self: 'att; - type AttributeIter<'att> - = std::vec::IntoIter> - where - Self: 'att; - - fn name(&self) -> Str<'_> { - self.view.metric_name.as_bytes() - } - - fn description(&self) -> Str<'_> { - b"" - } - - fn unit(&self) -> Str<'_> { - b"" - } - - fn data(&self) -> Option> { - Some(AsapDataView { view: self.view }) - } - - fn metadata(&self) -> Self::AttributeIter<'_> { - Vec::new().into_iter() - } -} - -struct AsapDataView<'a> { - view: &'a AsapMetricsView, -} - -impl<'a> DataView<'a> for AsapDataView<'a> { - type Gauge<'gauge> - = AsapGaugeView<'gauge> - where - Self: 'gauge; - type Sum<'sum> - = AsapNoSum - where - Self: 'sum; - type Histogram<'histogram> - = AsapNoHistogram - where - Self: 'histogram; - type ExponentialHistogram<'exp> - = AsapNoExpHistogram - where - Self: 'exp; - type Summary<'summary> - = AsapNoSummary - where - Self: 'summary; - - fn value_type(&self) -> MetricKind { - MetricKind::Gauge - } - - fn as_gauge(&self) -> Option> { - Some(AsapGaugeView { view: self.view }) - } - - fn as_sum(&self) -> Option> { - None - } - - fn as_histogram(&self) -> Option> { - None - } - - fn as_exponential_histogram(&self) -> Option> { - None - } - - fn as_summary(&self) -> Option> { - None - } -} - -struct AsapGaugeView<'a> { - view: &'a AsapMetricsView, -} - -impl<'a> GaugeView for AsapGaugeView<'a> { - type NumberDataPoint<'dp> - = AsapNumberDataPointView<'dp> - where - Self: 'dp; - type NumberDataPointIter<'dp> - = std::vec::IntoIter> - where - Self: 'dp; - - fn data_points(&self) -> Self::NumberDataPointIter<'_> { - (0..self.view.n_rows) - .map(|row| AsapNumberDataPointView { - view: self.view, - row, - }) - .collect::>() - .into_iter() - } -} - -struct AsapNumberDataPointView<'a> { - view: &'a AsapMetricsView, - row: usize, -} - -impl<'a> NumberDataPointView for AsapNumberDataPointView<'a> { - type Attribute<'att> - = AsapAttribute<'att> - where - Self: 'att; - type AttributeIter<'att> - = std::vec::IntoIter> - where - Self: 'att; - type Exemplar<'ex> - = AsapNoExemplar - where - Self: 'ex; - type ExemplarIter<'ex> - = std::vec::IntoIter - where - Self: 'ex; - - fn start_time_unix_nano(&self) -> u64 { - 0 - } - - fn time_unix_nano(&self) -> u64 { - self.view.time_col.value(self.row) - } - - fn value(&self) -> Option { - if self.view.value_col.is_null(self.row) { - None - } else { - Some(DpValue::Double(self.view.value_col.value(self.row))) - } - } - - fn attributes(&self) -> Self::AttributeIter<'_> { - // `encode_batch`'s own convention: the metrics row's ordinal - // index doubles as the parent_id its attribute rows join - // against (see records.rs's `ATTR_BATCH_PARENT_ID` doc). - self.view - .attributes_for_row(self.row as u32) - .into_iter() - } - - fn exemplars(&self) -> Self::ExemplarIter<'_> { - Vec::new().into_iter() - } - - fn flags(&self) -> DataPointFlags { - DataPointFlags::new(0) - } -} - -/// One typed attribute value ASAP's flat attribute batch can carry — -/// mirrors `records.rs`'s internal `AttrValue` three-way union -/// (`bytes` / `str` / `int` columns). -enum AsapAnyValue<'a> { - Str(&'a str), - Int(u64), - Bytes(&'a [u8]), -} - -struct AsapAttribute<'a> { - key: &'a str, - value: AsapAnyValue<'a>, -} - -impl<'a> AttributeView for AsapAttribute<'a> { - type Val<'val> - = AsapAnyValueView<'val> - where - Self: 'val; - - fn key(&self) -> Str<'_> { - self.key.as_bytes() - } - - fn value(&self) -> Option> { - Some(AsapAnyValueView(&self.value)) - } -} - -struct AsapAnyValueView<'a>(&'a AsapAnyValue<'a>); - -impl<'a> AnyValueView<'a> for AsapAnyValueView<'a> { - type KeyValue = AsapAttribute<'a>; - type ArrayIter<'arr> - = std::iter::Empty - where - Self: 'arr; - type KeyValueIter<'kv> - = std::iter::Empty - where - Self: 'kv; - - fn value_type(&self) -> ValueType { - match self.0 { - AsapAnyValue::Str(_) => ValueType::String, - AsapAnyValue::Int(_) => ValueType::Int64, - AsapAnyValue::Bytes(_) => ValueType::Bytes, - } - } - - fn as_string(&self) -> Option> { - match self.0 { - AsapAnyValue::Str(s) => Some(s.as_bytes()), - _ => None, - } - } - - fn as_bool(&self) -> Option { - None - } - - fn as_int64(&self) -> Option { - match self.0 { - // Lossy above i64::MAX, which ASAP never actually - // produces here (the only `_asap_*` int-typed attributes - // are small counters/timestamps well under that bound). - AsapAnyValue::Int(v) => Some(*v as i64), - _ => None, - } - } - - fn as_double(&self) -> Option { - None - } - - fn as_bytes(&self) -> Option<&[u8]> { - match self.0 { - AsapAnyValue::Bytes(b) => Some(b), - _ => None, - } - } - - fn as_array(&self) -> Option> { - None - } - - fn as_kvlist(&self) -> Option> { - None - } -} - -/// Reads the value at `row` from whichever of the three typed-value -/// columns actually has a non-null entry there — mirrors -/// `records.rs`'s private `build_attr_index` join logic (bytes, then -/// str, then int; exactly one is non-null per row by construction). -fn attr_row_value<'a>( - bytes: Option<&'a BinaryArray>, - strs: Option<&'a StringArray>, - ints: Option<&'a UInt64Array>, - row: usize, -) -> Option> { - if let Some(arr) = bytes { - if !arr.is_null(row) { - return Some(AsapAnyValue::Bytes(arr.value(row))); - } - } - if let Some(arr) = strs { - if !arr.is_null(row) { - return Some(AsapAnyValue::Str(arr.value(row))); - } - } - if let Some(arr) = ints { - if !arr.is_null(row) { - return Some(AsapAnyValue::Int(arr.value(row))); - } - } - None -} - -// -- Uninhabited placeholder types ------------------------------------------- -// -// `resource()`/`scope()` always return `None`, and only Gauge is ever -// produced (`as_sum`/`as_histogram`/`as_exponential_histogram`/ -// `as_summary` always return `None`, and there are no exemplars) — but -// the view traits still require *some* concrete, well-formed type for -// each associated type regardless of whether an instance is ever -// constructed. An uninhabited enum (`enum X {}`) lets every trait -// method be `match *self {}` — valid because no value of an -// uninhabited type can ever exist to call it on, so the body coerces -// to any return type without ever actually running. - -enum AsapNoResource {} -impl ResourceView for AsapNoResource { - type Attribute<'att> - = AsapAttribute<'att> - where - Self: 'att; - type AttributesIter<'att> - = std::vec::IntoIter> - where - Self: 'att; - fn attributes(&self) -> Self::AttributesIter<'_> { - match *self {} - } - fn dropped_attributes_count(&self) -> u32 { - match *self {} - } -} - -enum AsapNoScope {} -impl InstrumentationScopeView for AsapNoScope { - type Attribute<'att> - = AsapAttribute<'att> - where - Self: 'att; - type AttributeIter<'att> - = std::vec::IntoIter> - where - Self: 'att; - fn name(&self) -> Option> { - match *self {} - } - fn version(&self) -> Option> { - match *self {} - } - fn attributes(&self) -> Self::AttributeIter<'_> { - match *self {} - } - fn dropped_attributes_count(&self) -> u32 { - match *self {} - } -} - -enum AsapNoExemplar {} -impl ExemplarView for AsapNoExemplar { - type Attribute<'att> - = AsapAttribute<'att> - where - Self: 'att; - type AttributeIter<'att> - = std::vec::IntoIter> - where - Self: 'att; - fn filtered_attributes(&self) -> Self::AttributeIter<'_> { - match *self {} - } - fn time_unix_nano(&self) -> u64 { - match *self {} - } - fn value(&self) -> Option { - match *self {} - } - fn span_id(&self) -> Option<&otel_arrow_dfe_pdata_views::SpanId> { - match *self {} - } - fn trace_id(&self) -> Option<&otel_arrow_dfe_pdata_views::TraceId> { - match *self {} - } -} - -enum AsapNoSum {} -impl SumView for AsapNoSum { - type NumberDataPoint<'dp> - = AsapNumberDataPointView<'dp> - where - Self: 'dp; - type NumberDataPointIter<'dp> - = std::vec::IntoIter> - where - Self: 'dp; - fn data_points(&self) -> Self::NumberDataPointIter<'_> { - match *self {} - } - fn aggregation_temporality(&self) -> AggregationTemporality { - match *self {} - } - fn is_monotonic(&self) -> bool { - match *self {} - } -} - -enum AsapNoHistogram {} -impl HistogramView for AsapNoHistogram { - type HistogramDataPoint<'dp> - = AsapNoHistogramDataPoint - where - Self: 'dp; - type HistogramDataPointIter<'dp> - = std::vec::IntoIter - where - Self: 'dp; - fn data_points(&self) -> Self::HistogramDataPointIter<'_> { - match *self {} - } - fn aggregation_temporality(&self) -> AggregationTemporality { - match *self {} - } -} - -enum AsapNoHistogramDataPoint {} -impl HistogramDataPointView for AsapNoHistogramDataPoint { - type Attribute<'att> - = AsapAttribute<'att> - where - Self: 'att; - type AttributeIter<'att> - = std::vec::IntoIter> - where - Self: 'att; - type BucketCountIter<'bc> - = std::iter::Empty - where - Self: 'bc; - type ExplicitBoundsIter<'eb> - = std::iter::Empty - where - Self: 'eb; - type Exemplar<'ex> - = AsapNoExemplar - where - Self: 'ex; - type ExemplarIter<'ex> - = std::vec::IntoIter - where - Self: 'ex; - fn attributes(&self) -> Self::AttributeIter<'_> { - match *self {} - } - fn start_time_unix_nano(&self) -> u64 { - match *self {} - } - fn time_unix_nano(&self) -> u64 { - match *self {} - } - fn count(&self) -> u64 { - match *self {} - } - fn sum(&self) -> Option { - match *self {} - } - fn bucket_counts(&self) -> Self::BucketCountIter<'_> { - match *self {} - } - fn explicit_bounds(&self) -> Self::ExplicitBoundsIter<'_> { - match *self {} - } - fn exemplars(&self) -> Self::ExemplarIter<'_> { - match *self {} - } - fn flags(&self) -> DataPointFlags { - match *self {} - } - fn min(&self) -> Option { - match *self {} - } - fn max(&self) -> Option { - match *self {} - } -} - -enum AsapNoExpHistogram {} -impl ExponentialHistogramView for AsapNoExpHistogram { - type ExponentialHistogramDataPoint<'edp> - = AsapNoExpHistogramDataPoint - where - Self: 'edp; - type ExponentialHistogramDataPointIter<'edp> - = std::vec::IntoIter - where - Self: 'edp; - fn data_points(&self) -> Self::ExponentialHistogramDataPointIter<'_> { - match *self {} - } - fn aggregation_temporality(&self) -> AggregationTemporality { - match *self {} - } -} - -enum AsapNoExpHistogramDataPoint {} -impl ExponentialHistogramDataPointView for AsapNoExpHistogramDataPoint { - type Attribute<'att> - = AsapAttribute<'att> - where - Self: 'att; - type AttributeIter<'att> - = std::vec::IntoIter> - where - Self: 'att; - type Buckets<'b> - = AsapNoBuckets - where - Self: 'b; - type Exemplar<'ex> - = AsapNoExemplar - where - Self: 'ex; - type ExemplarIter<'ex> - = std::vec::IntoIter - where - Self: 'ex; - fn attributes(&self) -> Self::AttributeIter<'_> { - match *self {} - } - fn start_time_unix_nano(&self) -> u64 { - match *self {} - } - fn time_unix_nano(&self) -> u64 { - match *self {} - } - fn count(&self) -> u64 { - match *self {} - } - fn sum(&self) -> Option { - match *self {} - } - fn scale(&self) -> i32 { - match *self {} - } - fn zero_count(&self) -> u64 { - match *self {} - } - fn positive(&self) -> Option> { - match *self {} - } - fn negative(&self) -> Option> { - match *self {} - } - fn flags(&self) -> DataPointFlags { - match *self {} - } - fn exemplars(&self) -> Self::ExemplarIter<'_> { - match *self {} - } - fn min(&self) -> Option { - match *self {} - } - fn max(&self) -> Option { - match *self {} - } - fn zero_threshold(&self) -> f64 { - match *self {} - } -} - -enum AsapNoBuckets {} -impl BucketsView for AsapNoBuckets { - type BucketCountIter<'bc> - = std::iter::Empty - where - Self: 'bc; - fn offset(&self) -> i32 { - match *self {} - } - fn bucket_counts(&self) -> Self::BucketCountIter<'_> { - match *self {} - } -} - -enum AsapNoSummary {} -impl SummaryView for AsapNoSummary { - type SummaryDataPoint<'dp> - = AsapNoSummaryDataPoint - where - Self: 'dp; - type SummaryDataPointIter<'dp> - = std::vec::IntoIter - where - Self: 'dp; - fn data_points(&self) -> Self::SummaryDataPointIter<'_> { - match *self {} - } -} - -enum AsapNoSummaryDataPoint {} -impl SummaryDataPointView for AsapNoSummaryDataPoint { - type Attribute<'att> - = AsapAttribute<'att> - where - Self: 'att; - type AttributeIter<'att> - = std::vec::IntoIter> - where - Self: 'att; - type ValueAtQuantile<'vaq> - = AsapNoValueAtQuantile - where - Self: 'vaq; - type ValueAtQuantileIter<'vaq> - = std::vec::IntoIter - where - Self: 'vaq; - fn attributes(&self) -> Self::AttributeIter<'_> { - match *self {} - } - fn start_time_unix_nano(&self) -> u64 { - match *self {} - } - fn time_unix_nano(&self) -> u64 { - match *self {} - } - fn count(&self) -> u64 { - match *self {} - } - fn sum(&self) -> f64 { - match *self {} - } - fn quantile_values(&self) -> Self::ValueAtQuantileIter<'_> { - match *self {} - } - fn flags(&self) -> DataPointFlags { - match *self {} - } -} - -enum AsapNoValueAtQuantile {} -impl ValueAtQuantileView for AsapNoValueAtQuantile { - fn quantile(&self) -> f64 { - match *self {} - } - fn value(&self) -> f64 { - match *self {} - } -} - -// ============================================================================ -// Decode direction: OtapPdata -> OtapMetricRecords -// ============================================================================ - -/// Outcome of [`pdata_to_otap_metric_records`]: the reconstructed flat -/// batch pair (`None` if `pdata` carried a non-Metrics signal or truly -/// zero metric rows), plus a count of rows this call had to skip. -pub struct DecodeOutcome { - /// `None` when `pdata` wasn't Metrics, or decoded to zero rows. - pub records: Option, - /// Histogram / ExponentialHistogram / Summary data points seen and - /// skipped — not silently dropped, see this module's doc "Scope" - /// section for why they aren't expanded. - pub skipped_non_scalar: usize, -} - -/// Converts a real `OtapPdata` into ASAP's flat [`OtapMetricRecords`] -/// shape (feeding `decode_batch` on the producer role's ingest path). -/// -/// Accepts `pdata` by value (consumed) — the caller's `Message::PData` -/// match arm already owns it and has no further use for the original -/// context once this conversion runs. -pub fn pdata_to_otap_metric_records(pdata: OtapPdata) -> Result { - let (_context, payload) = pdata.into_parts(); - let arrow_records: OtapArrowRecords = payload - .try_into_with_default() - .map_err(|e| BridgeError::Decode(format!("{e}")))?; - - let OtapArrowRecords::Metrics(_) = &arrow_records else { - return Ok(DecodeOutcome { - records: None, - skipped_non_scalar: 0, - }); - }; - - let view = - OtapMetricsView::try_from(&arrow_records).map_err(|e| BridgeError::Decode(format!("{e}")))?; - - let mut time_unix_nano: Vec = Vec::new(); - let mut metric_names: Vec = Vec::new(); - let mut values: Vec = Vec::new(); - let mut attr_parent_ids: Vec = Vec::new(); - let mut attr_keys: Vec = Vec::new(); - let mut attr_strs: Vec> = Vec::new(); - let mut attr_ints: Vec> = Vec::new(); - let mut attr_bytes: Vec>> = Vec::new(); - let mut skipped_non_scalar = 0usize; - let mut next_parent_id: u32 = 0; - - for resource in view.resources() { - for scope in resource.scopes() { - for metric in scope.metrics() { - let name = String::from_utf8_lossy(metric.name()).into_owned(); - let Some(data) = metric.data() else { - continue; - }; - let number_dps: Vec<_> = if let Some(gauge) = data.as_gauge() { - gauge.data_points().collect() - } else if let Some(sum) = data.as_sum() { - sum.data_points().collect() - } else { - // Histogram / ExponentialHistogram / Summary: no - // single well-defined scalar — count, don't expand. - skipped_non_scalar += match data.value_type() { - MetricKind::Histogram => data - .as_histogram() - .map(|h| h.data_points().count()) - .unwrap_or(0), - MetricKind::ExponentialHistogram => data - .as_exponential_histogram() - .map(|h| h.data_points().count()) - .unwrap_or(0), - MetricKind::Summary => { - data.as_summary().map(|s| s.data_points().count()).unwrap_or(0) - } - _ => 0, - }; - continue; - }; - - for dp in number_dps { - let Some(value) = dp.value() else { continue }; - let value = match value { - DpValue::Double(v) => v, - DpValue::Integer(v) => v as f64, - }; - - let parent_id = next_parent_id; - next_parent_id += 1; - - time_unix_nano.push(dp.time_unix_nano()); - metric_names.push(name.clone()); - values.push(value); - - for attr in dp.attributes() { - let Some(val) = attr.value() else { continue }; - attr_parent_ids.push(parent_id); - attr_keys.push(String::from_utf8_lossy(attr.key()).into_owned()); - let (mut s, mut i, mut b) = (None, None, None); - match val.value_type() { - ValueType::String => { - s = val.as_string().map(|v| String::from_utf8_lossy(v).into_owned()); - } - ValueType::Bytes => { - b = val.as_bytes().map(|v| v.to_vec()); - } - ValueType::Int64 => { - // ASAP's int attribute column is - // unsigned; a genuinely negative int - // attribute (rare for label-shaped - // data) isn't representable, so it's - // stringified instead of silently - // reinterpreted as a huge positive - // value. - match val.as_int64() { - Some(v) if v >= 0 => i = Some(v as u64), - Some(v) => s = Some(v.to_string()), - None => {} - } - } - ValueType::Double => { - s = val.as_double().map(|v| v.to_string()); - } - ValueType::Bool => { - s = val.as_bool().map(|v| v.to_string()); - } - ValueType::Empty | ValueType::Array | ValueType::KeyValueList => { - // Not representable in ASAP's flat - // label model; drop this one - // attribute (the data point itself is - // still kept). - attr_parent_ids.pop(); - attr_keys.pop(); - continue; - } - } - attr_strs.push(s); - attr_ints.push(i); - attr_bytes.push(b); - } - } - } - } - } - - if time_unix_nano.is_empty() { - return Ok(DecodeOutcome { - records: None, - skipped_non_scalar, - }); - } - - let parent_ids: Vec = (0..time_unix_nano.len() as u32).collect(); - let metrics = RecordBatch::try_new( - Arc::new(Schema::new(vec![ - Field::new(COLUMN_TIME_UNIX_NANO, DataType::UInt64, false), - Field::new(COLUMN_METRIC, DataType::Utf8, false), - Field::new(COLUMN_VALUE, DataType::Float64, false), - Field::new(ATTR_BATCH_PARENT_ID, DataType::UInt32, false), - ])), - vec![ - Arc::new(UInt64Array::from(time_unix_nano)), - Arc::new(StringArray::from(metric_names)), - Arc::new(Float64Array::from(values)), - Arc::new(UInt32Array::from(parent_ids)), - ], - )?; - - let attributes = RecordBatch::try_new( - Arc::new(Schema::new(vec![ - Field::new(ATTR_BATCH_PARENT_ID, DataType::UInt32, false), - Field::new(ATTR_BATCH_KEY, DataType::Utf8, false), - Field::new(ATTR_BATCH_STR, DataType::Utf8, true), - Field::new(ATTR_BATCH_INT, DataType::UInt64, true), - Field::new(ATTR_BATCH_BYTES, DataType::Binary, true), - ])), - vec![ - Arc::new(UInt32Array::from(attr_parent_ids)), - Arc::new(StringArray::from(attr_keys)), - Arc::new(StringArray::from(attr_strs)), - Arc::new(UInt64Array::from(attr_ints)), - Arc::new(BinaryArray::from_opt_vec( - attr_bytes.iter().map(|b| b.as_deref()).collect(), - )), - ], - )?; - - Ok(DecodeOutcome { - records: Some(OtapMetricRecords { metrics, attributes }), - skipped_non_scalar, - }) -} - -// -- Small typed-column helpers (mirrors records.rs's own private ones) ----- - -fn require_string<'a>( - batch: &'a RecordBatch, - which: &'static str, - column: &'static str, -) -> Result<&'a StringArray, BridgeError> { - batch - .column_by_name(column) - .and_then(|c| c.as_any().downcast_ref::()) - .ok_or(BridgeError::BadColumn { - batch: which, - column, - }) -} - -fn require_uint64<'a>( - batch: &'a RecordBatch, - which: &'static str, - column: &'static str, -) -> Result<&'a UInt64Array, BridgeError> { - batch - .column_by_name(column) - .and_then(|c| c.as_any().downcast_ref::()) - .ok_or(BridgeError::BadColumn { - batch: which, - column, - }) -} - -fn require_uint32<'a>( - batch: &'a RecordBatch, - which: &'static str, - column: &'static str, -) -> Result<&'a UInt32Array, BridgeError> { - batch - .column_by_name(column) - .and_then(|c| c.as_any().downcast_ref::()) - .ok_or(BridgeError::BadColumn { - batch: which, - column, - }) -} - -fn require_float64<'a>( - batch: &'a RecordBatch, - which: &'static str, - column: &'static str, -) -> Result<&'a Float64Array, BridgeError> { - batch - .column_by_name(column) - .and_then(|c| c.as_any().downcast_ref::()) - .ok_or(BridgeError::BadColumn { - batch: which, - column, - }) -} - -fn optional_string(batch: &RecordBatch, column: &'static str) -> Result, BridgeError> { - match batch.column_by_name(column) { - None => Ok(None), - Some(c) => c - .as_any() - .downcast_ref::() - .cloned() - .map(Some) - .ok_or(BridgeError::BadColumn { - batch: "attributes", - column, - }), - } -} - -fn optional_uint64(batch: &RecordBatch, column: &'static str) -> Result, BridgeError> { - match batch.column_by_name(column) { - None => Ok(None), - Some(c) => c - .as_any() - .downcast_ref::() - .cloned() - .map(Some) - .ok_or(BridgeError::BadColumn { - batch: "attributes", - column, - }), - } -} - -fn optional_binary(batch: &RecordBatch, column: &'static str) -> Result, BridgeError> { - match batch.column_by_name(column) { - None => Ok(None), - Some(c) => c - .as_any() - .downcast_ref::() - .cloned() - .map(Some) - .ok_or(BridgeError::BadColumn { - batch: "attributes", - column, - }), - } -} diff --git a/otap-patch/plugins/asap_sketches/README.md b/otap-patch/plugins/asap_sketches/README.md deleted file mode 100644 index 8d0630e..0000000 --- a/otap-patch/plugins/asap_sketches/README.md +++ /dev/null @@ -1,82 +0,0 @@ -# `asap_sketches` — OTAP-Rust unified ASAP precompute plugin - -One plugin parameterized by `sketch_type` that adapts the -host-neutral `asap-precompute-rs` runtime to OTAP Dataflow's -Arrow-native streaming model. - -This directory holds the **plugin shell** scaffolding: sample TOML, -this README, and a future `Cargo.toml` / `src/lib.rs` once Phase D -wires the OTAP submodule's plugin registry. The actual lifecycle — -input stream consumption, `Wakeup`-driven flush, control-channel -poll, graceful drain — lives upstream in -[`asap-precompute-rs/src/otap/`](../../../asap-precompute-rs/src/otap/), -gated by the `otap` feature. - -Design notes: - -- This is a single unified plugin rather than five per-sketch - plugins. -- The lifecycle contract is: `linkme` slice registration, async - `Stream` consumption, `NodeControlMsg::Wakeup` - scheduled flush, graceful-shutdown drain. -- This directory layout is the target shape. -- The configuration shape is documented by - [`sample.toml`](./sample.toml). - -## Phase status - -- **Phase B** — codec (`decode_batch` / `encode_batch`) shipped. -- **Phase C** — full plugin lifecycle + 5-sketch dispatch live in - [`asap-precompute-rs/src/otap/{config,lifecycle,records}.rs`](../../../asap-precompute-rs/src/otap/). - The plugin shell here documents the user-facing configuration; - the binary-level `linkme` registration is **deliberately - deferred** to Phase D. -- **Phase D** (next) — the overlay build script and - `otap-patch/all/mod.rs` `linkme` distributed-slice registration. - Phase D adds `Cargo.toml` + `src/lib.rs` here once the OTAP - submodule binding is wired in. -- **Phase E** (optional) — cross-host envelope parity, gated on - cross-language byte-parity. - -## Configuration - -See [`sample.toml`](./sample.toml) for the canonical TOML block. -Field reference: - -| Field | Required | Description | -|---|---|---| -| `sketch_type` | yes | One of `ddsketch` / `kll` / `hll` / `countsketch` / `countminsketch`. Case-insensitive. | -| `window_size` | yes | Duration string (e.g. `"10s"`). Tumbling window rotation period. | -| `output_metric_name` | yes | Metric name stamped onto every emitted envelope. | -| `agg_id` | optional | Controller-plan join key. Defaults to `0`. | -| `sketch_params.relative_accuracy` | ddsketch | DDSketch alpha (`0 < α < 1`). Default `0.01`. | -| `sketch_params.k` | kll | KLL buffer size. Default `200`. | -| `sketch_params.seed` | kll | Optional deterministic compaction seed. Default unseeded. | -| `sketch_params.precision` | hll | HLL register-count exponent (4-18). Default `12`. | -| `sketch_params.width` | countsketch, cms | Sketch column count. Default `2048`. | -| `sketch_params.depth` | countsketch, cms | Sketch row count. Default `4`. | -| `controller_url` | optional | Bootstrap controller address for the `HttpPollChannel`. | -| `agent_id` | optional | Agent identifier reported to the controller. | - -## Lifecycle - -The plugin owns three async tasks: - -1. **Input task** — consumes the host-supplied - `Stream`. For each batch: - `flatten()` → `decode_batch()` → `Precompute::observe()`. -2. **Flush ticker** — Tokio `interval(window_size)` modeled on - `NodeControlMsg::Wakeup`. On each tick: `Precompute::tick()` → - `encode_batch()` → `lift()` → emit a synthesized - `OtapMetricRecords` family. `lift()` is the Strategy-B - attribute-lift step that places `_asap_*` keys onto the per-row - attribute child batch (so the resulting batch passes OTAP's - strict schema validator at - `crates/pdata/src/schema/payloads.rs::check_match`). -3. **Control-channel task** — polls the `ControlChannel` impl - (`HttpPollChannel` in production) and applies plan changes via - `Precompute::update_config`. - -Graceful shutdown drains the active window once before exit so -shutting down before the natural window boundary doesn't drop -in-flight observations. diff --git a/otap-patch/plugins/asap_sketches/src/mod.rs b/otap-patch/plugins/asap_sketches/src/mod.rs deleted file mode 100644 index 29011d2..0000000 --- a/otap-patch/plugins/asap_sketches/src/mod.rs +++ /dev/null @@ -1,27 +0,0 @@ -//! `asap_sketches` — OTAP-Rust plugin shell entry point. -//! -//! This module is the binary-level binding between the OTAP -//! submodule's `linkme` distributed-slice registry and the -//! `asap-precompute-rs` lifecycle code. **Phase C does not populate -//! the registry call** — that work is Phase D. -//! -//! Phase C scope: -//! - Plugin shell directory layout established (this file + -//! `sample.toml` + `README.md` siblings). -//! - User-facing configuration documented. -//! -//! Phase D scope (NOT done here): -//! - `Cargo.toml` for this crate (path-dep on `asap-precompute-rs`). -//! - `pub static`-shaped `linkme::distributed_slice` entry that -//! registers the plugin with the OTAP runtime. -//! - Patches to `otap-patch/all/mod.rs` to bring the slice into -//! the host binary's link scope. -//! - Build script for the overlay. -//! -//! See [`asap-precompute-rs/src/otap/lifecycle.rs`](../../../asap-precompute-rs/src/otap/lifecycle.rs) -//! for the actual plugin runtime; the Phase D code here is just an -//! adapter from OTAP's `local::Processor` trait surface -//! onto `AsapSketchesPlugin::start()`. - -// Phase C placeholder — body populated in Phase D once the OTAP -// submodule is wired in.