Choosing a store
Ten sinks, and running more than one is the normal arrangement. Every one of them pushes: the tool is meant to run wherever it is convenient and reach its stores from there, not to be scraped. The Prometheus exporter is the one exception, and it exists because Prometheus insists.
The comparison
Section titled “The comparison”| Store | Keeps | Good for | Config |
|---|---|---|---|
| InfluxDB | the dated history | “how fast were we merging in July” | url, token, org, bucket |
| PostgreSQL | the dated history, as SQL you pipe into psql | a Grafana user with a Postgres and no InfluxDB | dialect, path |
| Graphite | the dated history | a Graphite that is already there | addr, prefix |
| Elasticsearch | the dated history, as documents | search across everything collected | url, prefix, api_key |
| Prometheus | the current value | alerting, and a number on a wall | listen, path |
| OpenTelemetry | either, depending on the backend | an existing collector pipeline | endpoint, raw |
| Loki | the events, as log lines | “what happened, in order” | url, labels, max_age |
| Telegraf | whatever its outputs keep | reaching anything Telegraf can reach | url |
| File and stdout | line protocol or JSON | a shipper you already run, and a buffer | path, format |
There is no sink for a specific hosted vendor, and that is deliberate. A managed backend is reached through one of the two sinks that exist to route onward: Telegraf, whose own outputs cover Datadog, New Relic, Wavefront, Azure Monitor and a hundred more, or OpenTelemetry, which most of them now accept directly. A sink per vendor is a key to rotate, an API to track and a test that needs a paid account, for a hop those two already make.
The thing that decides everything
Section titled “The thing that decides everything”A point carries the date the thing happened. A star is dated when it was given, a workflow run when it finished, a traffic day at that day’s own date.
InfluxDB keys a point by measurement, tag set and timestamp, so writing the same fourteen-day traffic window every six hours converges on the right answer rather than accumulating copies. That is what makes the whole backfill design work, and it is why InfluxDB is the sink that keeps history.
Prometheus cannot do that. It stamps a sample at scrape time and rejects anything meaningfully older: measured against Prometheus 3.14 with the OTLP receiver enabled and a thirty-minute out-of-order window, a sample dated two days back comes back as HTTP 400. So the exporter reduces the per-item rows to current values before serving them.
The full argument, and what the reduction does to each measurement, is on dating a point.
Only what changed is written
Section titled “Only what changed is written”A sweep offers the same history every time: the fourteen day traffic window, every open pull request, the contribution calendar. Writing it all again is harmless to what the store holds, since a row is keyed by series and timestamp and simply overwrites, and it is how a collector that was down for a day repairs itself.
It is not harmless to the store’s files. InfluxDB 3 Core writes one Parquet
file per partition per write request and never compacts them, and it refuses
any query that would open more than its file limit. Measured before the fix
below and with the limit then at ten thousand: gh_notification held barely
more than one row per Parquet file, and a query over fourteen days came back
with “Query would scan 10000 Parquet files, exceeding the file limit”.
Asking for a coarser interval does not help, because the limit counts the files the planner opens, before any aggregation. So the tool keeps a small ledger of what it has already written and sends only the points whose values have moved:
sinks: dedupe_file: /var/lib/ghchronicle/state-written.bin # default: beside state_file dedupe_horizon: 720h # forget a point nothing offers any more influxdb: dedupe: true # the default, here and for telegraf, graphite, sql and elasticsearchThe ledger stores two 64 bit hashes and a day per point, so a large account
costs a few megabytes. It is keyed by sink, so a store that was unreachable
still receives everything on its next write. Losing it, or setting
dedupe_file: off, costs one sweep of rewriting and nothing else, which is
exactly what a store that has been wiped and needs filling again wants. It is
the second file worth putting on a persistent path, next to the
state file.
A run that ends when its sweep does never opens the ledger at all. -once,
-backfill and a card render write once and exit, so there is nothing to save
and nothing to prune, and each of them offers the whole history again. That is
what a backfill is for; it is also why a scheduled -once job, which is the
shape the Action runs in, writes every point
every time. Where that matters, run the loop instead.
The sweep log says what this saved:
level=INFO msg=written sink=influxdb family=events points=0 unchanged=300If the files have already accumulated, the writer fix stops the growth but does
not remove them: raise --query-file-limit on the server, rewrite the affected
tables, or move to InfluxDB 3 Enterprise, which compacts on its own and is free
for home use.
Start here
Section titled “Start here”Running several
Section titled “Running several”Normal, and cheap: the collection happens once and the points are handed to every configured sink. The usual arrangement is one history store plus one of the current-value ones.
sinks: influxdb: url: http://localhost:8181 token: ${INFLUX_TOKEN} org: default bucket: github prometheus: listen: 127.0.0.1:9605 path: /metricsWhat every sink does with a failure
Section titled “What every sink does with a failure”A sink that fails is logged and the sweep continues; a database being down does not stop collection, and with the file sink configured the data is still on disk when it comes back. Two failures are reported specially rather than as errors, because they are partial successes:
- rejected lines, where everything parseable was written and the refused lines are logged individually with the store’s own reason.
- dropped entries, which is Loki’s age horizon leaving out what its out-of-order window would have refused, rather than losing the whole push.