# BDN metrics and observability

> How the Usage & billing screen derives tx_sent, fan-out, land success, land latency percentiles, block compression, short-id table size, and get-txs recovery.

The Usage & billing screen is the metering view for the FirstByte
BDN transport. Despite the procedure name, it reports **no cost fields** —
every number on the screen is a transport counter or a transport-quality
statistic. The product being metered is the observability itself.

The whole screen is backed by one call:

```ts
const { data: b } = trpc.billing.usage.useQuery();
```

The response carries a `period` string (rendered in the page subtitle as
`Metered transport · {period}`), the four meter-strip counters, the land
and block statistics, a short land series for the chart, and a recent
error list.

## What billing.usage returns

| Field             | Where it renders                      | Meaning                                                     |
| ----------------- | ------------------------------------- | ----------------------------------------------------------- |
| `period`          | Page subtitle                         | The metering period the counters below are scoped to.        |
| `txSent`          | Meter card `tx_sent`                  | Transactions ingested and fanned out.                        |
| `txBytes`         | Meter card `tx_bytes`                 | Raw transaction volume in bytes.                             |
| `fanout`          | Meter card `fanout`                   | The `bdn.tx_fanout` counter.                                 |
| `landed`          | Meter card `landed`, chart total      | Transactions that landed.                                    |
| `landFail`        | Meter card `landed` sub-label         | Land failures, shown as `{landFail} failed`.                 |
| `landedSeries`    | Lands bar chart                       | Per-tick land counts for the recent window.                  |
| `landSuccessPct`  | Stat row `Land success`               | Land success percentage; `null` renders as `—`.              |
| `landP50`         | Stat row `Land latency p50`           | Median land latency, milliseconds.                           |
| `landP95`         | Stat row `Land latency p95`           | 95th-percentile land latency, milliseconds.                  |
| `compressionPct`  | Stat row `Block compression`          | Block compression percentage.                                |
| `shortIdTable`    | Stat row `short-id table`             | Current short-id table size.                                 |
| `blocks`          | Stat row `Blocks published`           | Blocks published over the period.                            |
| `getTxHitPct`     | Stat row `get-txs hit rate`           | Percentage of referenced txs resolved locally from short ids.|
| `gettxRecovered`  | Stat row `get-txs recovered`          | Txs recovered by an explicit get-txs round trip.             |
| `rxMissing`       | Stat row `get-txs recovered`          | Misses that triggered recovery, rendered as `/ {rxMissing} miss`. |
| `errors[]`        | Recent errors table                   | `{ ms, cluster, msg }` per row.                              |

## The meter strip: tx_sent, tx_bytes, fanout, landed

The four cards across the top are raw counters for the metering `period`.
They are cumulative for that period, not windowed like the chart below them.

- **`tx_sent`** — sub-labelled `ingested → fan-out`. This counts transactions
  the BDN accepted from your submit path and handed to fan-out. It is the
  denominator you reach for first when a downstream number looks low: if
  `tx_sent` is flat, nothing reached the network and the problem is on the
  submit side, not the relay side.
- **`tx_bytes`** — sub-labelled `raw tx volume`. Payload bytes for those
  transactions. Compare against `tx_sent` to get an average transaction size;
  a sudden jump in bytes without a jump in count means larger payloads, which
  is what drives block compression and short-id behaviour further down the page.
- **`fanout`** — the `bdn.tx_fanout` counter. This is the propagation multiplier:
  how many peer deliveries the BDN performed for the transactions it ingested.
  `fanout` near zero while `tx_sent` climbs means the node ingested traffic but
  did not propagate it.
- **`landed`** — transactions that landed, with `landFail` printed underneath as
  `N failed`. These two are the numerator and the failure count behind the land
  success percentage.

## Land success and land failures

`landSuccessPct` is reported by the procedure as its own field rather than being
computed in the browser, and it is nullable. When it is `null` the console prints
an em dash — that is the "nothing to measure yet" state, not zero percent. A
freshly provisioned tenant that has submitted nothing shows `—` here while
`landed` and `landFail` both sit at zero.

Read `landSuccessPct` together with the `landFail` count on the meter card and
with the error feed at the bottom of the page. A falling success percentage
tells you the ratio moved; the error feed tells you which cluster and which
error moved it.

## Land latency: what is timed and where percentiles come from

Two percentiles are exposed, `landP50` and `landP95`, both in milliseconds and
both rendered directly from the fields — the console does no bucketing of its
own. `landP95` is tinted with the warning colour on the screen so that tail
latency is visually separable from the median at a glance.

Use them as a pair:

- p50 and p95 close together means a uniform path — the transport is behaving
  consistently for every submission.
- p50 stable while p95 climbs means a tail problem. A subset of submissions is
  taking a slower route or retrying; the aggregate count in `landed` will not
  show it, which is exactly why the percentile split exists on this screen.
- both climbing together points at the whole path, not a subset.

There is no configured target on this screen. What counts as good is a property
of your own network position and the chains you submit to, so compare against
your own baseline rather than an absolute.

## Lands over the recent window

The bar chart is titled **Lands · recent window** and is subtitled
`per stats tick (~2s)`. Each bar in `landedSeries` is one stats tick, and the
chart's axis labels run `−2m` on the left to `now` on the right — so the chart
covers roughly the last two minutes of land activity, independent of the
`period` that scopes the meter cards above it.

The card's right-hand corner shows `{landed} total`, which is the period
counter, not the sum of the bars. Do not expect the bars to add up to it.

When `landedSeries` is empty the chart is replaced by an empty state:
*"No throughput yet — Submit a transaction to see live land throughput."*
That is the normal state before your first submission.

## Block publication: compression, short-id table, get-txs recovery

The lower four stat rows describe the block side of the BDN rather than the
transaction side.

- **`compressionPct`** — how much smaller published blocks are than their
  uncompressed form. Compression on the BDN comes from replacing transactions
  the peer already holds with short ids, so this number is a direct consequence
  of how well the short-id table is populated.
- **`shortIdTable`** — the current size of the short-id table. This is the
  working set of transactions that can be referenced by id instead of shipped
  in full. A small table and a low `compressionPct` usually move together.
- **`blocks`** — blocks published over the period. This is the denominator for
  reasoning about the compression and get-txs numbers; a compression percentage
  computed over a handful of blocks is not yet meaningful.
- **`getTxHitPct`** — the hit rate when resolving short ids in a received block.
  A hit means the referenced transaction was already held locally and no round
  trip was needed.
- **`gettxRecovered` / `rxMissing`** — rendered together as
  `{gettxRecovered} / {rxMissing} miss`. `rxMissing` counts short ids that
  missed the local table; `gettxRecovered` counts how many of those were then
  recovered with an explicit get-txs request. The gap between the two is the
  set of misses that were not recovered.

Read these as a chain: a shrinking `shortIdTable` drives `getTxHitPct` down,
which drives `rxMissing` up, which drives get-txs round trips, which is the work
that `compressionPct` was meant to avoid.

## Reading the error feed

The bottom card, **Recent errors**, is subtitled `land / submit failures across
nodes`. It is an observability feed, not a billing artifact. Each row has three
columns:

| Column    | Field     | Notes                                                        |
| --------- | --------- | ------------------------------------------------------------ |
| `When`    | `e.ms`    | Epoch milliseconds, rendered as a local time-of-day string.   |
| `Cluster` | `e.cluster` | The cluster the failure was observed on.                    |
| `Error`   | `e.msg`   | Truncated for display; long messages are cut short in the table. |

Because the message column is truncated in the console, treat it as a
classifier — enough to tell whether failures are all the same error or a mix —
and go to your logs for the full text.

The `Cluster` column is what makes this feed actionable: failures concentrated
on one cluster point at that cluster's connectivity or peering, while failures
spread evenly across clusters point at the submitted transactions themselves.

When `errors` is empty the card shows *"No recent errors — Land + submit paths
healthy."*

## Aggregation window and stats tick

There are three distinct time bases on this one screen, and mixing them up is
the most common misreading:

| Element                           | Time base                                   |
| --------------------------------- | ------------------------------------------- |
| Meter strip (`tx_sent`, `tx_bytes`, `fanout`, `landed`) | The metering `period` shown in the page subtitle. |
| Lands chart (`landedSeries`)      | One bar per stats tick (~2s), spanning about the last two minutes. |
| Transport observability stat rows | Live BDN metrics, as labelled on the card.  |
| Recent errors                     | Event timestamps, most recent first.        |

The stats tick is also the practical refresh granularity for anything derived
from live BDN metrics: a change you make to your submit path will not appear in
the chart faster than the next tick, and a single tick of data is not enough to
move a percentile.

## Related

- [Telemetry](/platform/telemetry) — metric, trace, and record streams emitted platform-wide
- [Telephony Metrics](/glossary/metrics) — how percentile and success-ratio metrics are read elsewhere in the platform
