fn bound(
out: &mut BTreeMap<String, Series>,
dropped: &mut BTreeSet<String>,
max: usize,
)Expand description
Hold out to max_series entries by evicting its largest key.
The result is the same set sort(keys).truncate(max_series) produced, and
the argument is short enough to check: the answer is the max_series
smallest keys, and a key that belongs in it can never be the largest key of
a map that is already one over — everything else in that map is smaller, so
there are already max_series keys ahead of it. Evicting the maximum
therefore only ever discards a key the sort would have truncated. It also
cannot come back: the evicted key is greater than every key in the map, and
the map’s maximum only falls, so the same key arriving from a later block is
evicted again on sight.
The difference is when, and that is the whole point. The old map grew one
entry per distinct (name, unit, kind, temporality, monotonic, attributes)
tuple until the query finished, each carrying a rendered descriptor, a
rendered attribute object and a key concatenating both — about a kilobyte.
max_points bounded the points inside a series and nothing bounded the
series, so a query_metric with no name over a store with a request id in
a data-point attribute allocated until the process died.
The evicted key is kept, without its payload, so the response can say how
many series it is not showing. ponytail: that record is itself capped at
max_series keys, so dropped_series saturates rather than counting an
unbounded number of them exactly — remembering every key to count it is the
allocation this function exists to prevent. dropped_series == max_series
reads as “at least”. Counting evictions instead of distinct keys was the
other option and it is worse: a series above the cap is re-inserted and
re-evicted once per point, so a store with 100 series and a cap of 64 would
report tens of thousands dropped.