Commit Graph
467 Commits
Author SHA1 Message Date
Ashish KolheandSubhradeep Chakraborty d06a468961 feat: Slack app Phase 2 — alerts in Slack: acknowledge, mute, Slack app destination and investigate (#15731)
Co-authored-by: Subhradeep Chakraborty <chakrabortysubhradeep556@gmail.com>
2026-10-29 03:55:08 +00:00
7308d3011b feat: public dashboards (serving v2, lazy materialization) (#14789)
Co-authored-by: Loakesh Indiran <rustyloaki@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Loakesh Indiran <66478092+Loaki07@users.noreply.github.com>
Co-authored-by: Hengfei Yang <hengfei.yang@gmail.com>
2026-10-27 07:51:38 +00:00
HuaijinandHengfei Yang 4d7e566c50 chore(deps): upgrade vortex to 0.90.0 (#15836)
Co-authored-by: Hengfei Yang <hengfei.yang@gmail.com>
2026-10-27 13:21:26 +08:00
Huaijin 613a71d627 fix(promql): read a label the stream lacks as "" in matchers (#15785) 2026-10-26 04:19:05 +00:00
Hengfei Yang 1a2d94adac chore: upgrade rust toolchain to nightly-2026-10-24 (#15782) 2026-10-26 01:23:11 +00:00
Ashish Kolhe 65d19cc701 fix(org): complete org deletion (#15394) 2026-10-24 11:24:07 +00:00
Ashish Kolheandgithub-actions[bot] ed1b8de97e feat: two-way Slack app (ask OpenObserve from Slack, instance workspace) (#15553)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-10-21 16:07:27 +00:00
Huaijin 1d39274686 fix(search): run each query under its own id when requests share a trace id (#15566) 2026-10-21 07:22:37 +00:00
Ashish Kolhe fd10278067 fix(auth): resolve the routed org in every auth layer and scope user removal, org listing and query management to it (#15444) 2026-10-19 07:14:52 +00:00
Ashish KolheandClaude 972890f3c1 fix(org-delete): org-exact meta-store prefixes, atomic settings update and atomic user delete (#15353)
Co-authored-by: Claude <noreply@anthropic.com>
2026-10-18 04:46:20 +00:00
Hengfei Yang 1731a15918 chore(deps): upgrade tantivy to 0.27.0 and drop the openobserve fork (#15491) 2026-10-17 15:18:25 +00:00
Huaijin 6664c7a910 chore: upgrade zip to 9.0.3 (#15455) 2026-10-16 11:17:41 +00:00
Huaijin cc9fc300b0 chore: remove dead Rust code in leaf crates and unused dependencies (#15337) 2026-10-16 06:29:26 +00:00
Hengfei Yang 15b5e2fd4c fix(tantivy): stop index builds from fsyncing their scratch files (#15420) 2026-10-15 14:32:16 +00:00
Huaijin 9e274395aa perf(search): patch sqlparser 0.62 to parse nested function calls in linear time (#15336) 2026-10-14 06:34:22 +00:00
Ashish Kolhe 6b521b678a fix: check stream read access when saving alerts, pipelines, reports and other background objects (#15213) 2026-10-09 13:41:12 +00:00
HuaijinandHengfei Yang 7ba60fd5e2 fix: replace promql-parser fork with upstream (#15187)
Co-authored-by: Hengfei Yang <hengfei.yang@gmail.com>
2026-10-08 16:31:19 +08:00
Prabhat Sharmaandgithub-actions[bot] 4020426135 feat(metrics): metrics v2 — alerts from charts, compare, forecast, formulas, Used in, builder round-trip, staleness markers and Prometheus 3.x functions (#15150)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-10-08 04:50:02 +00:00
Ashish Kolheandgithub-actions[bot] 46678ea8da fix: tighten input validation, auth and outbound request checks (#15054)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-10-08 00:54:05 +00:00
abc6c1c797 feat(synthetics): subtests — embed a browser test in another, and extract steps into one (#14645)
Co-authored-by: Loakesh Indiran <rustyloaki@gmail.com>
Co-authored-by: Loakesh Indiran <66478092+Loaki07@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-29 12:58:39 +00:00
IluckySi dbe3a94724 feat(ui): improve Profiles UI (#14895) 2026-09-28 11:14:35 +00:00
65e0e7edf6 feat(prompts): add managed prompt registry (#14818)
Co-authored-by: sai nikhil kethe <nikhil@openobserve.ai>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-28 10:18:16 +00:00
Hengfei Yang 104d3d22f4 perf(promql): read samples from MIDX v1 blocks (#14658) 2026-09-28 09:09:38 +00:00
Hengfei Yang 8538e9f787 refactor(metrics): locate MIDX regions from a tail trailer (#14860) 2026-09-24 11:45:00 +00:00
Ashish Kolhe 088aee43e3 fix: close sensitive-data, audit-trail and retention compliance gaps (#14833) 2026-09-24 10:50:04 +00:00
Loakesh Indiranandgithub-actions[bot] da518d9626 fix(alerts): guard destination deletes against every consumer, and show them in the UI (#14796)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-24 07:37:44 +00:00
Ashish Kolheandgithub-actions[bot] 0e79cbfc71 fix(anomaly): carry the level half-width into the fit and validate the anomaly config at its edges (#14770)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-24 07:47:46 +00:00
951ded3684 feat(iam): configurable password policy over api, enforced instance wide. (#14214)
Co-authored-by: omkark06 <omkar@zinclabs.io>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Bhargav <BJP232004@GMAIL.COM>
Co-authored-by: Shrinath Rao <shnath@openobserve.ai>
2026-09-23 16:06:37 +00:00
Hengfei YangandHuaijin 6785abe1f8 feat(metrics): generate sample blocks in MIDX during compaction (#14711)
Co-authored-by: Huaijin <haohuaijin@gmail.com>
2026-09-23 16:58:11 +08:00
Hengfei Yang 2285b7283d fix: return OTLP Status body for undecodable OTLP/HTTP protobuf requests (#14786) 2026-09-23 07:41:26 +00:00
Hengfei Yang 716c59ab47 fix(ingest): decode snappy request bodies on HTTP ingestion routes (#14784) 2026-09-23 05:30:30 +00:00
Hengfei Yang 1313a7a826 chore: move profiling deps behind openobserve-core/profiling (#14785) 2026-09-23 05:22:31 +00:00
IluckySiandHengfei Yang b3e86895f3 feat: ingest OpenObserve self CPU and memory profiles into _meta (#14701)
Co-authored-by: Hengfei Yang <hengfei.yang@gmail.com>
2026-09-23 03:06:27 +00:00
Hengfei Yang b14a223373 fix(deps): bump rustls to 0.23.45 (RUSTSEC-2026-0285) (#14728) 2026-09-22 05:25:14 +00:00
Hengfei Yang 9c33123325 fix(tracing): mark cross-process spans as client and server via tower middleware (#14709) 2026-09-22 00:45:51 +00:00
Hengfei Yang 6295dd1b46 fix: stop acknowledging failed OTLP gRPC exports as success (#14653) 2026-09-20 06:00:17 +00:00
Hengfei Yang ed15b8c038 perf(metrics): share the remote-write fast path with the JSON and OTLP endpoints (#14478) 2026-09-15 11:01:23 +00:00
Huaijin a7dd30a379 chore: upgrade DataFusion to 55.1.0 and Vortex (#14480) 2026-09-15 08:55:48 +00:00
Ashish Kolheandgithub-actions[bot] b02d048ed8 feat: authenticate Splunk HEC senders with an org ingestion token (#14455)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-15 05:40:52 +00:00
5379dbf644 fix: Derive MCP base URL scheme from the HTTP listener TLS setting (#14211)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Hengfei Yang <hengfei.yang@gmail.com>
2026-09-15 04:10:37 +00:00
Huaijin f10138fb29 perf(compactor): merge the open metrics-index hour's pending files as a closed hour would (#14253) 2026-09-08 15:34:52 +00:00
Hengfei Yang 2f53bf2f37 fix: keep trace columns in quick mode projections (#13376) (#14169) 2026-09-03 12:48:22 +00:00
Ashish Kolheandgithub-actions[bot] 07807995e4 feat: public status pages built on synthetics checks (#13974)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-31 11:45:51 +00:00
Huaijin 732f721d04 fix: vortex Utf8-typed string statistics broke Utf8View interval analysis (#14011) 2026-08-28 08:48:15 +00:00
b09b56e25a feat(llm): add Phase 3.0 experiment workflows (#13778)
## Summary

- allow dataset rows without an expected output across management APIs
and UI flows
- add immutable experiment preview, creation, listing, persistence,
RBAC, and super-cluster synchronization
- add Experiment as an evaluation target with slot hydration, scoring,
and result indexing
- harden idempotent creation, scorer-version pinning, score-index setup,
and upgrade migrations

## Tickets

- Related to
[openobserve/o2-enterprise#2398](https://github.com/openobserve/o2-enterprise/issues/2398)
- Related to
[openobserve/o2-enterprise#2399](https://github.com/openobserve/o2-enterprise/issues/2399)
- Related to
[openobserve/o2-enterprise#2400](https://github.com/openobserve/o2-enterprise/issues/2400)

## Paired Enterprise PR

-
[openobserve/o2-enterprise#2413](https://github.com/openobserve/o2-enterprise/pull/2413)

## Validation

- focused Rust and frontend tests for the affected dataset and
experiment seams
- experiment migration test passes
- upgraded local environment applies schema version 68 and returns 200
from GET /api/team3/experiments

---------

Co-authored-by: sai nikhil kethe <nikhil@openobserve.ai>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-26 05:47:37 +00:00
Ashish Kolhe b2639c4f9f feat(dbm): Database Monitoring (#13918)
## What this is

Database Monitoring: an aggregation and presentation layer over
telemetry OpenObserve already ingests. It reads three vantages — client
spans, server-vantage logs, and collector metrics — and turns them into
per-statement, per-instance and per-database views.

No new storage engine and no new ingest path. Statements are normalized
and rolled up into a stats stream, and the read API queries that stream
through the existing search path.

## Shape of the change

| | |
|---|---|
| Read endpoints | 17 |
| UI pages | 8 (plus a shell) |
| Commits | 141 |

**Backend** — `src/core/src/db_monitoring/` holds the non-HTTP half
(normalization, rollup, server-vantage parsing). The read API lives in
`src/api/management/src/request/db_monitoring/` as a handler / models /
service trio; the service layer is split one file per feature.

**Frontend** — `web/src/views/DatabaseMonitoring/`, with shared logic in
`web/src/composables/dbm/`.

## Feature availability

Databases, queries, query detail and history, endpoints, samples,
activity, insights, plans, server metrics, server queries, server
samples, instances, instance metrics, badges — is available in OSS.

## Reviewing this

It is a large diff, and most of it is additive: 327 files, and the two
largest non-test files are the shared service module and the deadlocks
service. Suggested order:

1. `src/api/management/src/request/db_monitoring/handler.rs` — the 17
entry points and their gating
2. `src/api/management/src/request/db_monitoring/service/common.rs` —
auth, SQL building, the shared read path
3. `src/core/src/db_monitoring/rollup.rs` — how the stats stream is
produced
4. `web/src/views/DatabaseMonitoring/` — the pages

Time semantics are the subtlest part. Rollup rows are stamped at the
window **end**, so reads are exclusive at both edges; `stats_read_range`
carries the reasoning and a test pins it.

## Known follow-ups

- **The server-vantage stream named as `_o2_dbm_server`.** 

## Testing

- Rust: unit tests across the DBM core and read service, including
SQL-building, auth-gating and normalizer cases
- Web: unit tests across the DBM pages and composables
- Playwright: DBM suite covering the pages and cross-page navigation

Verified before opening: OSS and enterprise builds both compile
warning-free, `cargo fmt` clean, `vue-tsc` clean, and the full web unit
suite passes (44,704 tests).
2026-08-24 11:08:00 +00:00
Huaijin 37e5f55684 feat(metrics): .midx series index for TSID-major files and PromQL pruning through it (#13899)
The query half of the TSID-major layout: #13882 wrote `tsid-major-v3-*`
files ordered by `(__hash__, _timestamp)`; this PR adds the `.midx`
series index next to them and makes PromQL prune through it. Supersedes
the remaining scope of #13839.

## Write side

- Every `tsid-major-v3-*` file written by the hour-end merge gets a
`.midx` series index (Arrow IPC + zstd), stored like the Tantivy index
under a derived root distinct from the data tree:
`files/{org}/metrics/{stream}/{date}/{hour}/tsid-major-v3-{id}.parquet`
→ `files/{org}/midx/{stream}/{date}/{hour}/tsid-major-v3-{id}.midx`
(Tantivy: `files/{org}/index/{stream}_{type}/…/{id}.ttv`): **one row per
contiguous TSID run** — its `row_count: u32` plus the label columns of
the run's first row (source types preserved). Runs tile the data file,
so each run's starting row is the prefix sum of the preceding counts and
is not stored; `__hash__` is excluded (matchers never reference it). The
whole index is one ZSTD-compressed IPC batch. Runs crossing an input
RecordBatch boundary appear as two adjacent entries; query-time range
coalescing makes that representation equivalent without buffering
samples. File and row-group boundaries stay independent of TSID
boundaries.
- `TsidSeriesIndexWriter` lives inside
`search/datafusion/merge/tsid_major.rs` and collects run entries
alongside the Parquet writer (one buffered row per run), encoding and
compressing once at finish. `MergedFile` carries `series_index:
Option<Vec<u8>>`; the compactor uploads it to the `midx/` tree
(compliance mode included).
- `.midx` is intentionally **not** tracked in `file_list`; its path is
derived from the data file's key
(`MetricsFileLayout::series_index_path`) and it is deleted together with
the parent in the delayed-deletion pass (`deleted.rs`); like the
`index/` tree, the `midx/` day directories are covered by retention's
local-disk deletion and the gc tool.

## Query side (PromQL)

- `promql_service::…::tsid_series_index::search` runs where Tantivy used
to, before the metrics table is registered: for each TSID-major file it
loads the `.midx` (concurrent, `2×target_partitions ≤ 64`), projects the
row-range columns plus the matched labels **by name against each series
index's own schema** (label sets / column orders differ between files),
builds the matcher filter **per series index** with
`physical_expr::create_physical_expr` — the exact same predicates as
`apply_matchers` (`matcher_predicates`) — evaluates it over the run
rows, coalesces adjacent/overlapping ranges and attaches them as
`FileSelection::RowRanges` (converted into a Parquet access plan by the
generic helper from #13861, no per-row bitmap).
- **Applied per file**: files of any other layout in the same query
(still-open hours, legacy data, late arrivals) are left in place and
scanned in full. A matched label missing from a series index
over-selects that file; a query whose matchers cannot be evaluated at
all selects the whole file; any series index failure logs and falls back
to a full scan. The final PromQL matchers are always applied, so results
are exact in every case.
- Evaluated selections are cached (LRU, 256 MiB) keyed by account +
series index path + the full matcher set.
- The Tantivy path in the PromQL storage scan is removed
(`convert_matchers_to_index_condition`, index-field plumbing,
`is_add_filter_back` bookkeeping). SQL search is untouched.

## Benchmark (from the #13839 POC, same layout + series index)

478,144,896 rows, 7 CPUs, one-hour PromQL range, step 15s, result cache
off, release + mimalloc:

| Layout | Filter steady median | No-filter steady median | Data files |
Total storage |
|---|---:|---:|---:|---:|
| main, 2 GiB | 695.709 ms | 4.638 s | 88 | 4,925.81 MiB |
| TSID-major + `.midx` | 269.496 ms | 2.793 s | 80 | 1,294.61 MiB |

## Validation

- `cargo fmt --check`; `cargo check` / `clippy` on config, search,
compaction, promql-service, jobs, search_service (`--tests`) clean
- `config tsid_layout` 3 passed (incl. `series_index_path`); `search
tsid_major mode:: merge::` 10 passed (rotation test now also asserts the
series index, exact-run and coalescing series index tests); `compaction
merge deleted` 33 passed; `promql-service` 20 passed + 1 pre-existing
wall-clock flake (`cache::test_promql_cache_set_and_get`, fails
intermittently on main too)
- End-to-end on the #13839 branch (same code paths): hour-end merge
writes N `tsid-major-v3-*` + `.midx`; PromQL takes the series index path
(`selected 39 ranges across 4/4 files`), counts/sums match the synthetic
data, cold-cache identical after restart; a mixed hour (3 major + 1 late
`tsid-sorted`) reports `1 files without a series index left to
tantivy/full scan` with unchanged results; delayed deletion removes
`.parquet` **and** `.midx`
2026-08-21 10:05:46 +00:00
Huaijin 08d31371cc refactor(metrics): prepare indexed metrics groundwork (#13907)
## Summary

- rename the existing TSID-major config, modules, types, merge modes,
and persisted file prefixes to indexed-metrics terminology
- add the promql-service dependency declarations needed by the follow-up
metrics-index work
- carry the exact Cargo.lock content from #13899, including h2 0.4.16
and the promql-service dependency entries

## Scope

This is no-new-feature groundwork split from #13899. It retains the
existing metrics layout mechanics under the new names and does not add
.midx sidecar writing, index lookup/query pruning, or index lifecycle
handling.

The old environment variable and file prefixes are intentionally not
kept as compatibility aliases because this experimental path has not
been used in a deployment.

## Validation

- cargo fmt --all -- --check
- cargo metadata --locked --no-deps --format-version 1
- cargo check -p promql-service
- cargo test -p config --lib -- layout (5 passed)
- cargo test -p search --lib -- datafusion::merge (8 passed)
- cargo test -p compaction --lib -- merge (32 passed)
- cargo check -p openobserve-jobs
2026-08-21 04:58:50 +00:00
Huaijin 789eee463f feat(metrics): TSID-major Parquet layout behind ZO_METRICS_TSID_MAJOR_ENABLED (#13882)
The storage-layout half of #13839, without the `.sidx` series index and
its PromQL query path (those stay in #13839, which will rebase on this).

## What

Behind `ZO_METRICS_TSID_MAJOR_ENABLED` (default off), Parquet metrics
files of a stream whose `__hash__` column is `UInt64` are written
ordered by `(__hash__, _timestamp)` so every series is contiguous:

- **`config::meta::promql::tsid_layout`** —
`metrics_tsid_major_enabled(stream_type)` (flag + configured Parquet),
`metrics_tsid_major_stream(stream_type, schema)` (also `__hash__:
UInt64`; JSON-ingested metrics store `__hash__` as a string and keep the
classic layout), and `MetricsFileLayout { Legacy, HashSorted, TsidMajor
}` encoded in the file-name prefix (`tsid-sorted-*`, `tsid-major-v3-*`)
so readers and later merges know the physical order without opening the
file.
- **`MergeMode::HashSorted`** (open hour): the ingester movers and
incremental compactor merges write one hash-ordered file named
`tsid-sorted-*`. In pack mode the single-segment upload-as-is fast path
is skipped for such streams (segments are in arrival order).
- **`MergeMode::TsidMajor`** (closed hour): `merges_whole_batch()` —
every file of the hour, regardless of size — into size-split
`tsid-major-v3-*` files written at RecordBatch boundaries using
`ZO_COMPACT_MAX_FILE_SIZE` as logical original size
(`search/datafusion/merge/tsid_major.rs`). `MergeMode::for_compactor /
for_ingester` take the schema and pick the mode via
`metrics_tsid_major_stream`; downsampling keeps precedence. The
compactor skips a closed hour that is already all `tsid-major-v3-*`.
- **`MergedFile.layout`** decides the output file name
(`layout.file_name` / `mark_file_key`) in the compactor and the ingester
movers.
- **Merging pre-sorted inputs**: `MergeMode::input_sort_order(&files)`
declares `FileSortOrder::HashTimestampAsc` when every input is
hash-ordered (`tsid-sorted-*` / `tsid-major-v3-*`). Overlapping hash
ranges make DataFusion drop the order for its size-based groups; the
listing adapter regroups one file per partition (single-file groups are
trivially ordered) and keeps the declared `output_ordering`, so
`EnforceSorting` turns the `SortExec` into a `SortPreservingMergeExec`
over N pre-sorted streams (memory N × batch instead of the whole hour).
A batch that mixes layouts declares no order and falls back to the full
sort.

Two file states only: `tsid-sorted-*` (ordered, not finalized) and
`tsid-major-v3-*` (finalized). Nothing changes for streams without the
flag, non-Parquet metrics, or a non-UInt64 `__hash__`.

## Known limitation

The SQL query path still derives `sorted_by_time` from the SQL shape
only, so with the flag on a `SELECT … FROM <metrics> ORDER BY _timestamp
DESC LIMIT n` may assume a timestamp order that hash-sorted files do not
have. PromQL is unaffected (no ordering declared). To be addressed with
the query-side work.

## Validation

- `cargo fmt --check`; `cargo check` / `cargo clippy` on config, search,
compaction, jobs, promql-service, search_service (`--tests`) clean
- `cargo test -p config --lib -- tsid_layout` 2 passed; `cargo test -p
search --lib -- merge:: mode:: tsid_major listing_adapter` 9 passed
(mode selection, per-layout input order, hash `ORDER BY`, size-split
rotation, SPM plan shape); `cargo test -p compaction --lib -- merge` 31
passed
- End-to-end on #13839 (debug build, local disk, OTLP gauge 300 series):
ingester uploads `tsid-sorted-*` verified `(__hash__, _timestamp)`
ordered; hour-end `merge [tsid_major] input sort order:
hash_timestamp_asc` → plan `SortPreservingMergeExec` ← `DataSourceExec{N
single-file groups}` (no `SortExec`) → `tsid-major-v3-*`; incremental
`merge [hash_sorted]` 2 → 1 `tsid-sorted-*`; JSON-ingested stream (Utf8
`__hash__`) keeps the classic layout with no compactor errors.

## TODO (follow-up, not in this PR)

**Sort TSID-major streams by `(__hash__, _timestamp)` already at WAL
dump time.** `Partition::persist` full-sorts every dumped chunk anyway
(today by `_timestamp DESC` in `merge_record_batches`); switching the
sort key for TSID-major streams is near-free at dump time and removes
the biggest ingester-side cost of this layout — the full `SortExec` over
the merge input:

1. **persist**: lexsort the chunk by `(__hash__ ASC, _timestamp ASC)`
(same gate as the writers, `metrics_tsid_major_stream`) and stamp a
`sort_order: hash_timestamp_asc` key-value into the Parquet footer. A
file-name marker is not an option for WAL files —
`parse_time_range_from_filename` splits on `.` and a prefix would break
WAL time pruning.
2. **ingester mover**: it already reads every input's footer for the
schema; declare `HashTimestampAsc` on the table only when *all* inputs
carry the footer marker (else `FileSortOrder::None`, full sort — handles
mixed files during rolling upgrades / flag flips, same role
`MetricsFileLayout::of` plays for the compactor). The merge then becomes
a `SortPreservingMergeExec` over pre-sorted files instead of a full
sort.
3. **pack, single segment**: the as-is fast path (skipped for TSID-major
streams in this PR because segments were time-sorted) can come back —
the segment is already hash-sorted, upload it unchanged and mark it
`tsid-sorted-*`.
4. **pack, multiple segments**: one `MemTable` partition per segment
with a declared sort order, so the in-memory merge is also an SPM
instead of a full sort.

Query-side is already safe for this change: metrics SQL never declares
`sorted_by_time` and PromQL declares no order, so hash-sorted WAL files
cannot mislead a reader. The cost is slightly coarser time clustering
inside WAL files, negligible at WAL file sizes. Steps 1–2 carry most of
the win; 3–4 are incremental.
2026-08-20 09:20:00 +00:00
Huaijin 4e805038fe refactor: merge result / access-plan / deletion helpers groundwork for TSID-major metrics (#13861)
Behavior-neutral refactors split out of #13839 so that PR only carries
the TSID-major feature. Nothing here changes what is written, read or
deleted; every commit is independently reviewable.

## Commits

- `cba75d244b` **promql**: `apply_matchers` split into
`matcher_predicates(schema, matchers) -> Vec<Expr>` + `apply_matchers`
(unchanged behavior; lets other inputs evaluate the same matcher
semantics).
- `b5ff6fed21` **parquet**: `new_parquet_writer` / `append_metadata`
generic over `W: AsyncFileWriter` instead of `&mut Vec<u8>`; existing
callers unchanged.
- `b07d84216a` **merge**: `MergeParquetResult::{Single, Multiple}` →
`MergeParquetResult { files: Vec<MergedFile { buf, meta }>, file_format
}` + `into_single()`. The compactor's two copies of the upload / index /
push sequence become one loop (`compressed_size` is set from the buffer
for every file, as the `Multiple` arm already did); downsampling maps
its `(bufs, metas)` into it; ingester movers use `into_single()` instead
of panicking/erroring on `Multiple`.
- `014b6c5721` **compactor**: `deleted.rs` `derived_files()` /
`delete_from_storage()` replace three copies of collect / `storage::del`
/ ignore-not-found (data, `.ttv`, flattened).
- `7163786362` **search**: `generate_parquet_access_plan_from_ranges`
extracted from the bitmap path (`set_slices` → ranges); an out-of-range,
empty or unsorted range is rejected with `assert!` (`56de386522`; a
producer that violates the sorted/non-overlapping contract is a bug, not
something to degrade around) instead of the previous `unreachable!()` on
the bitmap path. Adds `FileSelection::RowRanges(Arc<Vec<Range<usize>>>)`
as the matching selection kind (no producer yet; Parquet only, Vortex
full scan like `RowGroups`).
- `754014ccf3` **test**: `ListingTableAdapter` plan-shape test for
`FileSortOrder::HashTimestampAsc` (from #13853): three overlapping
hash-sorted files → `SortPreservingMergeExec` over three single-file
groups, no `SortExec`, globally ordered rows. `tempfile` as `search`
dev-dependency.

## Validation

- `cargo fmt --check`
- `cargo clippy -p config -p promql -p search -p compaction -p
openobserve-jobs -p search_service -p promql-service --tests` clean
- `cargo test -p promql --lib -- utils` 10, `-p search --lib -- merge::
helpers listing_adapter` 14, `-p compaction --lib -- merge deleted` 31,
`-p config --lib -- test_file_key` 4 passed
- `merge/downsampling.rs` is enterprise-only and was updated by hand;
needs the enterprise CI compile.
2026-08-18 10:54:23 +00:00