Repository navigation
Conversation
…Parser Switch OpenMetricsParser internal label buffers to byte-offset LabelSpans and expose OmScratch and raw slice accessors matching PromParser so Layer 1 ArrowDecoder can reuse parser scratch buffers across OpenMetrics scrapes without per-sample allocations. Signed-off-by: Bartek Plotka <bwplotka@gmail.com>
…nd exemplars Signed-off-by: Bartek Plotka <bwplotka@gmail.com>
Group classic histogram (_bucket, _sum, _count, _created) and summary (quantile, _sum, _count, _created) series into HistogramDataPoints and SummaryDataPoints batches using flat reusable scratch buffers on ArrowDecoder. Add OpenMetrics 1.0 dispatch via OpenMetricsParser, # UNIT metadata, info and stateset projection, exemplars (NumberDpExemplars, HistogramDpExemplars, and their attribute tables with 16-byte trace_id and 8-byte span_id decoding), and configurable translation policies. Signed-off-by: Bartek Plotka <bwplotka@gmail.com>
…families Signed-off-by: Bartek Plotka <bwplotka@gmail.com>
…ries Validate duplicate and reserved labels on dropped series via validate_empty_label instead of extract_series_labels_into_temp so dropped family label keys and values are not interned into key_interner or val_interner. Signed-off-by: Bartek Plotka <bwplotka@gmail.com>
Set current_family.has_samples in append_counter_created_series so late # HELP or # TYPE lines after _created are rejected, and defer post-sample chunk flushing on Prometheus text counters until the next entry is inspected so trailing _created lines stay attached to their counter row. Signed-off-by: Bartek Plotka <bwplotka@gmail.com>
Check OpenMetrics start_timestamp once per PendingDpGroup in ArrowDecoder, cache negative start_timestamp lookups in OpenMetricsParser, and stop start_timestamp lookahead as soon as the series key changes. Signed-off-by: Bartek Plotka <bwplotka@gmail.com>
Require summary quantile bounds to be finite and histogram bucket bounds to be finite or +Inf before pushing into pending_points. Signed-off-by: Bartek Plotka <bwplotka@gmail.com>
Build MetricAttrs after data-point and exemplar attribute tables so InfoPolicy::MetricAttrs does not intern prometheus.type or type names into DpAttrs dictionaries, and reuse scratch_u16 and temp_attrs instead of allocating temporary vectors per batch. Signed-off-by: Bartek Plotka <bwplotka@gmail.com>
85ee938 to
b387609
Compare
…plar nulls Add OTAP round-trip and dhat bounded-memory coverage for OpenMetrics gaugehistograms (_bucket, _gsum, _gcount, _created), Prometheus text _created lines across counters, histograms, and summaries, unsorted le and quantile lines, non-finite bound rejection, nullable histogram sum, mixed exemplar span_id/trace_id nullability, uppercase hex IDs, attribute-free exemplars, and InfoPolicy::MetricAttrs. Signed-off-by: Bartek Plotka <bwplotka@gmail.com>
b387609 to
863187d
Compare
…modules Split src/arrow.rs (4,244 -> 1,273 lines) into focused submodules: - src/arrow/options.rs: DecodeOptions, translation policy enums, and DecodeError - src/arrow/columns.rs: zero-allocation column/table builders (MetricTable, NumberDpTable, SummaryDpTable, HistogramDpTable, ExemplarTable, AttrTable, AttrEncoding, ScratchBuffers) - src/arrow/family.rs: CurrentFamily, SeriesRole, PendingDpGroup, PendingExemplar, and FamilyAssembly - src/arrow/tests.rs: unit tests Signed-off-by: Bartek Plotka <bwplotka@gmail.com>
863187d to
65e0227
Compare
| break None; | ||
| } | ||
| let Some(peeked_family) = peek.metric_name().strip_suffix(CREATED_SUFFIX) else { | ||
| if !peek |
There was a problem hiding this comment.
issue: OM _created lookahead stops earlier than Go
The new break None at omparse.rs:576 ends the lookahead at the first non-_created line of another series, while Go keeps scanning (https://github.com/prometheus/prometheus/blob/v0.315.1/model/textparse/openmetricsparse.go#L350-L353), which means for foo_total{a="1"} 1, foo_total{a="2"} 2, foo_created{a="1"} 100 Go gives a="1" a start time of 100000 and Rust gives none. Before 3027b54 Rust matched Go here.
|
|
||
| // Attach `_created` in Prometheus text to the preceding counter data point of the active | ||
| // family when its base labels match. | ||
| if let Some(batch_id) = self.current_family.batch_id |
There was a problem hiding this comment.
issue: Text _created lines that don't follow their counter are dropped
arrow.rs:743 attaches a text _created line only when its labels match the data point right before it and otherwise returns Ok without emitting anything, so with foo_total{a="1"}, foo_total{a="2"}, foo_created{a="1"} 100 the a="1" sample is lost with no error, while Go's text parser keeps it as its own series.
| if CHUNKED | ||
| && self.current_family.batch_id.is_none() | ||
| && self.um_metric_types.len() >= MAX_METRICS_PER_BATCH | ||
| && self.assembly.is_empty() |
There was a problem hiding this comment.
issue: decode_stream ignores max_data_points_per_batch inside a histogram
arrow.rs:499 starts a new chunk only when self.assembly.is_empty(), and the assembly flushes only when the family changes, so one histogram family with 100 series and with_max_data_points_per_batch(10) comes out as a single chunk of 100 data points, which breaks the bound the decode_stream doc at arrow.rs:274 promises.
| } | ||
|
|
||
| #[inline] | ||
| fn collect_sorted_group_points(&mut self, group_idx: u32) { |
There was a problem hiding this comment.
issue: Histogram decode time grows with the square of the series count
flush() calls collect_sorted_group_points (family.rs:411) once per group and each call scans every point in the family, so decode time grows 4x each time the series count doubles, and 16k series with 6 buckets take 476ms in release against 10ms for the same number of gauge lines.
| let parent_id = self | ||
| .current_family | ||
| .emit_into_batch(&mut self.metrics, opts)?; | ||
| for &(k, v) in &self.temp_attrs { |
There was a problem hiding this comment.
issue: InfoPolicy::MetricAttrs merges every info series into one metric
arrow.rs:681 appends the labels of each info sample to the same metric row, so target_info{env="a"} and target_info{env="b"} give parent 0 two env keys in MetricAttrs, which OTLP does not allow (https://opentelemetry.io/docs/specs/otel/common/#attribute-collections), should each series become its own metric row, or should this policy reject families with more than one series?
| if !value.is_finite() { | ||
| return Ok(()); | ||
| } | ||
| let created_ms = (value * 1000.0) as i64; |
There was a problem hiding this comment.
issue: A huge _created value fails the whole scrape
arrow.rs:738 casts value * 1000.0 to i64, which saturates to i64::MAX for foo_created 1e300, and ms_to_ns then returns TimestampOverflow, so decode fails and every other metric in the scrape is lost, while Go's StartTimestamp has no error path for this value (https://github.com/prometheus/prometheus/blob/v0.315.1/model/textparse/openmetricsparse.go#L366). The same cast is at arrow.rs:820 and omparse.rs:587.
|
|
||
| /// A metric family uses a `# TYPE` not yet supported by `ArrowDecoder`. | ||
| #[error("unsupported metric type {metric_type:?} for metric {metric:?}")] | ||
| UnsupportedMetricType { |
There was a problem hiding this comment.
nitpick: UnsupportedMetricType is never returned now
options.rs:195 keeps the public variant and its "not yet supported" doc, but nothing in src builds it now that histograms and summaries are accepted, so callers keep match arms for an error that can't happen.
Signed-off-by: Bartek Plotka <bwplotka@gmail.com>
Prometheus text exposition format (0.0.4) has no _created suffix semantics; only OpenMetrics 1.0 attaches _created series via OpenMetricsParser::start_timestamp. Remove _created classification from CurrentFamily::classify_series so Prometheus text *_created series are emitted as regular metrics rather than being attached only when adjacent or dropped when non-adjacent. Signed-off-by: Bartek Plotka <bwplotka@gmail.com>
In decode_stream, count staged groups in FamilyAssembly toward the active batch's data-point budget. When a new series in an active histogram or summary family does not match any already-staged group once the batch has reached max_data_points_per_batch, flush the pending groups and emit the batch before starting the next group. Signed-off-by: Bartek Plotka <bwplotka@gmail.com>
Index PendingDpGroup entries in FamilyAssembly with an open-addressed hash table over their interned attribute slices and chain bucket/quantile points and exemplars onto per-group singly linked lists. This keeps interleaved group lookup O(1) and family flushing O(P + E) instead of O(G * (P + E)). Signed-off-by: Bartek Plotka <bwplotka@gmail.com>
Remove InfoPolicy, StatesetPolicy, and the metadata-only Empty metric type projection (InfoPolicy::MetricAttrs) so the Arrow decoder only emits standard OpenTelemetry metric types. Signed-off-by: Bartek Plotka <bwplotka@gmail.com>
In OpenMetricsParser::start_timestamp, verify that peek.value * 1000.0 lies within i64::MIN..i64::MAX before casting to i64 so values such as 1e300 return None instead of saturating to i64::MAX. Also use checked_mul(1_000_000) when converting _created millisecond timestamps to nanoseconds in ArrowDecoder so out-of-range _created values omit start_time_unix_nano rather than failing the scrape. Signed-off-by: Bartek Plotka <bwplotka@gmail.com>
All Prometheus and OpenMetrics 1.0 metric types are now handled by ArrowDecoder, so DecodeError::UnsupportedMetricType is unreachable. Signed-off-by: Bartek Plotka <bwplotka@gmail.com>
|
All addressed. One notable change is that I am killing |
Fixes #8
Implements Phase 2 Layer 1 metric family assembly in
ArrowDecoderso full Prometheus text and OpenMetrics 1.0 scrapes (includingavalanchetargets) decode directly into OTAP Arrow record batches with zero steady-state heap allocations after warmup.Commits
parse: expose reusable scratch and raw slice accessors on OpenMetricsParser- AddsOpenMetricsParser::with_scratch,take_scratch, raw metadata/label/exemplar slice accessors, andformat_openmetrics_float_intosoArrowDecodercan reuse buffers across OpenMetrics 1.0 scrapes without per-line allocations.arrow: add OTAP schemas and record slots for histograms, summaries, and exemplars- ExtendsMetricPayloadTypeandOtapMetricRecordsto all 12 metric payload tables (UnivariateMetrics,NumberDataPoints,SummaryDataPoints,HistogramDataPoints,MetricAttrs,NumberDpAttrs,SummaryDpAttrs,HistogramDpAttrs,NumberDpExemplars,HistogramDpExemplars,NumberDpExemplarAttrs,HistogramDpExemplarAttrs) and adds cached schemas for summaries, histograms, and exemplars.arrow: assemble histograms, summaries, exemplars, and OpenMetrics types- Groups_bucket,quantile,_sum,_count, and_createdlines intoHistogramDataPointsandSummaryDataPoints, dispatchesFormatType::OpenMetricsthroughOpenMetricsParser, supports# UNIT,info,stateset,_created/start_time_unix_nano, exemplars (trace_id/span_id), and translation policies (CounterSuffixPolicy,UnitSuffixPolicy,InfoPolicy,StatesetPolicy,UntypedPolicy), verified againstotap_to_otlpanddhatintests/otap-compat.