You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Address review: resolve events through the global index, scope by set, drop built-in auth
Query changes:
* subscriptions_latency no longer reads the events table. Events are partitioned,
and the oldest unprocessed event is known only by global position, which is not
the partition key - looking it up in events would have to visit and lock every
partition, which stops being viable well before a store reaches five figures of
partitions. The lateral now joins event_subscription_positions to
events_global_index and created_at is resolved via
EventsGlobalIndexQueries#resolve_indexes, so each event is read from its own
partition. Cost is still one index range scan per subscription and still does not
grow with the size of the backlog.
* Positions no longer come from sequences. The frontier is
max(subscription_position) from event_subscription_positions and the store head is
max(global_position) from events_global_index - both index-only scans, and neither
can report an uncommitted or run-ahead value the way a sequence can.
* The updated_at liveness filter is gone: that column has no index and can not get
one without losing HOT updates, so the condition forced a sequential scan. Reports
are instead scoped with optional, repeatable "set" query params
(?set=A&set=B), which uses idx_subscriptions_set_and_name. Left optional rather
than mandatory - a library should not force a filter - but since a set is normally
named after the application owning it, scoping is now the documented way to keep a
scrape to one application's subscriptions. Dropping the filter without a
replacement would have brought back the dead-series problem it existed to solve.
API changes:
* No built-in authentication. Protecting the endpoint is the mounting application's
business, as it already is for the Admin UI, so PG_EVENTSTORE_METRICS_TOKEN and
the bearer check are removed and the docs show wrapping the app in your own
middleware.
* The database is chosen per request with a "config" query param instead of a
predefined :metrics config, so one mounted app can serve metrics for every
configured store. Unknown or absent falls back to the default config.
* The Admin UI no longer serves these API routes; the standalone application is the
single metrics surface. Its Metrics::Helpers include and metrics_connection helper
go with them.
* Metrics::Routes is deleted. With the Admin UI no longer using it there was one
caller left, so the four routes are now declared literally in the application.
Docs:
* Corrected: event_subscription_positions is not pruned, so lag_seconds is exact
rather than a lower bound. It is now absent - not zero - when the oldest
unprocessed event no longer exists, because zero would read as "caught up".
* Rewritten from the application's perspective, without naming internal machinery.
* lag is kept as the metric name: it is the established term for this measurement.
Copy file name to clipboardExpand all lines: docs/metrics.md
+82-59Lines changed: 82 additions & 59 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -14,7 +14,7 @@ wherever the app is mounted - mounted at `/pg_eventstore/metrics` the first one
14
14
15
15
| Path | Metrics | Query cost |
16
16
|---|---|---|
17
-
|`/subscriptions/latency`| lag of every alive subscription + store positions | the only one touching the `events` table (one index hop per subscription) |
17
+
|`/subscriptions/latency`| lag of every reported subscription + store positions | the only one looking at event positions (one index range scan per subscription) |
18
18
|`/subscriptions/health`| state, lock, heartbeat age, restarts, last error age | single read of the `subscriptions` table |
19
19
|`/subscriptions/throughput`| processed events counter, handler capacity | single read of the `subscriptions` table |
20
20
|`/subscriptions`| all of the above | all of the above |
@@ -31,29 +31,15 @@ the database.
31
31
32
32
### Which subscriptions are reported
33
33
34
-
The `subscriptions` table is a registry which never garbage-collects: every handler that was ever registered keeps its
35
-
row, including handlers that were later renamed or removed. To keep dashboards meaningful, the endpoints only report
36
-
subscriptions which are either locked by a subscriptions set or were updated within the last 10 minutes. A
37
-
subscription that died *without releasing its lock* is deliberately still reported - detecting it is what
38
-
`pg_eventstore_subscription_heartbeat_age_seconds` is for.
34
+
Every subscription in the queried database, unless you narrow it down with `set` params (see
35
+
[Reporting only some subscription sets](#reporting-only-some-subscription-sets)). Note that the subscriptions registry
36
+
never removes rows, so handlers that were renamed or removed keep theirs and are reported too.
39
37
40
-
## Mounting
41
-
42
-
### Alongside the Admin UI
43
-
44
-
The Admin UI application serves the same metrics under `/metrics/*`:
45
-
46
-
```
47
-
/metrics/subscriptions
48
-
/metrics/subscriptions/latency
49
-
/metrics/subscriptions/health
50
-
/metrics/subscriptions/throughput
51
-
```
38
+
A subscription that died *without releasing its lock* is reported like any other - detecting it is what
39
+
`pg_eventstore_subscription_heartbeat_age_seconds` is for, and it is the most useful thing to alert on: neither the
40
+
state column nor the lock can be trusted to notice a process that went away.
52
41
53
-
Whatever authentication protects your Admin UI protects these paths too. This is convenient for eyeballing raw values
54
-
in the browser, but usually inconvenient for Prometheus - scrapers can not pass human-oriented authentication.
55
-
56
-
### Standalone application
42
+
## Mounting
57
43
58
44
`PgEventstore::Web::Metrics::Application` is a separate rack application designed to be a scrape target. In your
59
45
`config/routes.rb`:
@@ -72,19 +58,58 @@ require 'pg_eventstore/web'
72
58
run PgEventstore::Web::Metrics::Application
73
59
```
74
60
75
-
It supports static bearer token authentication out of the box: set the `PG_EVENTSTORE_METRICS_TOKEN` environment
76
-
variable and every request must carry an `Authorization: Bearer <token>` header. When the variable is not set the
77
-
application is open, and protecting it is your responsibility.
61
+
### Authorization
78
62
79
-
It uses the `:metrics` config when defined, with a fallback to the default config. This lets you point metrics at a
80
-
replica or restrict its pool size:
63
+
The application ships without authentication - how you protect the endpoint is up to you, exactly as it is for the
64
+
[Admin UI](admin_ui.md#authorization). Wrap it in whatever middleware your setup already uses, for example:
0 commit comments