## Problem
Fixes#13946. The sum of histogram bucket counts exceeded `count(*)`
over the same time range (e.g. 2744 vs 2704).
#12313 made the leader snap the follower scan `start_time` **down** to
the histogram bucket boundary in `RemoteScanNodes::get_remote_node()`,
to keep the first chart bucket fully populated. But that widens the
actual scan window: records in `[bucket_start, requested_start)` — data
the user explicitly excluded — get counted into the first bucket, while
`count(*)` (a separate query without `histogram_interval`) scans the
exact requested range. The reporter confirmed the diagnosis: aligning
`count(*)`'s start to the bucket grid makes the two numbers match.
#13896 was the same root cause surfacing on sub-10s ranges (worked
around in #13916 by dropping to 1s buckets there, without touching the
snap).
## Fix
Revert the snap: the scan window sent to followers now equals the
requested range exactly.
- `distributed_plan/node.rs`: `start_time` passes through unchanged; a
comment pins the invariant and a regression test
(`test_get_remote_node_keeps_raw_start_time_for_histogram`) asserts the
window is not widened when `histogram_interval > 0`.
- `sql/histogram.rs`: delete the now-unused `histogram_bucket_start()`
(zero remaining references, including in the enterprise repo).
- `sql/visitor/histogram_interval.rs`: reword two comments that were
written in terms of the snap. The 1s interval floor for sub-10s ranges
from #13916 is kept — a bucket wider than the whole range is bad
charting regardless of the snap.
Histogram semantics are now the same as Elasticsearch/Kibana
`date_histogram`: buckets only count in-range records, so `sum(buckets)
== count(*)` always holds, and the first bucket may be partial.
Safety notes:
- The tantivy `SimpleHistogram` fast path is unaffected: it aligns
bucket *labels* itself (`start_time - start_time % rounding_by`) and
only counts docs matched by the query's own time filter, consistent with
the `date_bin` path.
- The results cache is unaffected: `write_results` already aligns the
cache-accepted window *inward* and drops edge buckets, so partial (and
previously over-counted) first buckets never entered the cache.
## Trade-off / follow-up for the UI
The left-edge display artifact that motivated #12313 (first bar looks
lower because its bucket is cut by the range start) returns by design.
Suggested frontend handling, either:
- align the picker's `start_time` down to the returned
`histogram_interval` and issue **all** page queries (histogram + logs +
count) over that aligned range — Grafana's approach; totals stay
consistent and no partial bucket exists, or
- detect `buckets[0].key < start_time` and dim/annotate the first bar as
partial.
## Test plan
- `search` crate: 88 histogram tests + 9 node tests pass (includes the
new regression test); workspace clippy with CI flags passes.
- Single-node E2E: ingested 240 events at 500ms spacing over 120s;
queried with mid-bucket start/end (+17s/+107s), interval 30s. `count(*)`
= 180; buckets 26+60+60+34 = 180 (match). First bucket correctly holds
26 in-range records instead of the full 60 (old behavior summed to 214).