> Fetch the index of the TinyStore docs, every page as markdown, at https://tinyshed.org/tinystore/llms.txt

# TinyStore 0.7.1

TinyStore 0.7.1 came out on 6 November 2026. An aggregate given `limit` now
pages past the 1,000 series one query may find, a group at a time, so a
timer's p95 by function over a hundred functions reads in pages instead of
failing. A page's `limit` counts only buckets that hold samples, so a range
that is mostly empty no longer gives empty pages. Nothing that a program
calls changes.

- **Metrics**
  - [Aggregates past 1,000 series](#aggregates-past-1000-series): pages hold whole groups, 1,000 series at most.
  - [No empty pages](#no-empty-pages): `limit` counts buckets with samples.

## Aggregates past 1,000 series

A timer with 12 bounds writes a series for each bound, so its p95 by
function over 92 functions finds 1,104 series. Without `limit` that is still
a `limit` error. With `limit`, each page holds whole groups whose series are
1,000 at most, and the next page goes on with the next group
([#59](https://github.com/tinyshed/tinystore/issues/59)):

```ts
const page = await store.metrics.aggregate({ name: 'rpc_ms', since: '24h', op: 'p95', by: ['fn'], limit: 10_000 })

for await (const group of page) chart.appendBuckets(group.labels, group.buckets) // this page, then every page after it
```

```python
page = await store.metrics.aggregate(name="rpc_ms", since="24h", op="p95", by=["fn"], limit=10_000)

async for group in page:  # this page, then every page after it
    chart.append_buckets(group.labels, group.buckets)
```

```go
q := metrics.Query{Name: "rpc_ms", Since: 24 * time.Hour, Op: metrics.Percentile(95), By: []string{"fn"}, Limit: 10_000}
page, err := metrics.Aggregate(ctx, store, q)

for group, err := range page.All(ctx) { // this page, then every page after it
	chart.AppendBuckets(group.Labels, group.Buckets)
}
```

```rust
let day = stats.query().name("rpc_ms").since(Duration::from_hours(24)).by(&["fn"]).limit(10_000);
for group in day.aggregate(Op::Percentile(95.0))?.all() {
    chart.append_buckets(&group?); // this page, then every page after it
}
```

- Each group's buckets are the ones a call for that group alone gives: 92
  functions come as pages of 83 functions, 996 series, and then 9.

- A group with more than 1,000 series alone is still a `limit` error, since
  a bucket counts every series of its group. The error says to group `by`
  more labels or find fewer series, and no longer offers `limit`:

  ```text
  metrics: rpc_ms: a group past 1000 series, which no page splits, since a bucket counts every series of its group: group `by` more labels, or find fewer series: limit
  ```

- A bucket that alone passes a bound on a paged call says to make the
  buckets smaller, and no longer offers `limit` either.

[Aggregate in pages](https://tinyshed.org/tinystore/docs/guides/metrics/aggregates.md#aggregate-in-pages) has
the details.

## No empty pages

`limit` counts only buckets that hold samples. Before, it counted every
bucket of the range: with `since: '2h'` over an hour of data, a page spent
its `limit` on the empty hour as on the full one, and many pages came back
with no buckets at all. Now the same walk takes 552 pages of 10 buckets:

| `every: '1m'`, `limit: 10`, 92 functions, an hour of data in two | 0.7.0  | 0.7.1                      |
| ---------------------------------------------------------------- | ------ | -------------------------- |
| pages to walk                                                    | 1,114  | 552, which is 92 × 60 ÷ 10 |
| time                                                             | 1.34 s | 0.67 s                     |

Measured on Windows 11 Pro 26300, a release build, Bun 1.4.2, five runs'
median, with the script of
[#59](https://github.com/tinyshed/tinystore/issues/59). A page still holds
fewer than `limit` buckets when it is the last, when a bound stops it, when a
short gap lies inside samples dense enough to have one in every bucket, or
when a timer measured nothing in a bucket: `avg`, `max` and percentiles leave
that bucket out, though its counters have samples there.

## Fixes

- `tinystore sql --help` prints its text without the runs of spaces a lost
  line break left in it.

## Install

```sh for=bun
bun add @tinyshed/tinystore@0.7.1
npm install @tinyshed/tinystore@0.7.1
```

```sh for=python
pip install tinyshed-tinystore==0.7.1
```

```sh for=go
go get tinyshed.org/tinystore@v0.7.1
```

```sh for=rust
cargo add tinystore@0.7.1
cargo install tinystore-cli@0.7.1
```

Nothing in a store's files changes, so 0.7.0 and 0.7.1 open each other's
stores. Pages go past 1,000 series where the core runs: a program that
reaches a server gets them once the server is 0.7.1. The server image is
`ghcr.io/tinyshed/tinystore:0.7.1`.

Every commit since 0.7.0 is in the
[GitHub release](https://github.com/tinyshed/tinystore/releases/tag/v0.7.1).
