overview: one endpoint, live projections and a response cache (m36)
This commit is contained in:
+70
-201
@@ -422,139 +422,29 @@ paths:
|
||||
"503":
|
||||
$ref: "#/components/responses/Unavailable"
|
||||
|
||||
/api/stats:
|
||||
/api/overview:
|
||||
get:
|
||||
summary: Totals for a period
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/Period"
|
||||
responses:
|
||||
"200":
|
||||
description: Totals over the period's UTC-aligned window.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/StatsTotals"
|
||||
"400":
|
||||
$ref: "#/components/responses/BadRequest"
|
||||
"401":
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
"429":
|
||||
$ref: "#/components/responses/RateLimited"
|
||||
"500":
|
||||
$ref: "#/components/responses/Internal"
|
||||
"503":
|
||||
$ref: "#/components/responses/Unavailable"
|
||||
|
||||
/api/stats/timeseries:
|
||||
get:
|
||||
summary: Bucketed counts for a period
|
||||
summary: Everything the Overview page draws, for one period
|
||||
description: |
|
||||
Fixed-width UTC buckets covering the same window `/api/stats`
|
||||
reports for the period: 1h into 60 one-minute buckets, 24h into 48
|
||||
half-hour buckets, 7d into 168 one-hour buckets, 30d into 120
|
||||
six-hour buckets. Empty buckets are zero-filled.
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/Period"
|
||||
responses:
|
||||
"200":
|
||||
description: The bucket series.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/StatsTimeseries"
|
||||
"400":
|
||||
$ref: "#/components/responses/BadRequest"
|
||||
"401":
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
"429":
|
||||
$ref: "#/components/responses/RateLimited"
|
||||
"500":
|
||||
$ref: "#/components/responses/Internal"
|
||||
"503":
|
||||
$ref: "#/components/responses/Unavailable"
|
||||
One response over one read transaction: the period's totals, its
|
||||
fixed-width UTC buckets, the per-client series, the query-type
|
||||
breakdown and the answering-route breakdown, plus the coverage
|
||||
watermark judged against the same window. The panels therefore describe
|
||||
one database state rather than five, so the breakdowns sum to the
|
||||
totals on a quiet box.
|
||||
|
||||
/api/stats/types:
|
||||
get:
|
||||
summary: Query-type breakdown for a period
|
||||
description: |
|
||||
How many queries of each DNS type the period's window holds, over the
|
||||
same UTC-aligned window `/api/stats` reports for. Rows carry the numeric
|
||||
type only: the type-name table lives in the admin, and a second copy
|
||||
here would drift out of agreement with it. `qtype` is nullable in the
|
||||
query log, so the rows that carry no type group into a row of their own
|
||||
rather than vanishing from a breakdown that claims to add up. Ordered by
|
||||
count descending, then type ascending with the null row last. Types
|
||||
absent from the window are absent from the list.
|
||||
Buckets are UTC-aligned and zero-filled: 1h into 60 one-minute buckets,
|
||||
24h into 48 half-hour buckets, 7d into 168 one-hour buckets, 30d into
|
||||
120 six-hour buckets. The last bucket is the one in progress.
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/Period"
|
||||
responses:
|
||||
"200":
|
||||
description: The type breakdown.
|
||||
description: The period's overview.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/StatsTypes"
|
||||
"400":
|
||||
$ref: "#/components/responses/BadRequest"
|
||||
"401":
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
"429":
|
||||
$ref: "#/components/responses/RateLimited"
|
||||
"500":
|
||||
$ref: "#/components/responses/Internal"
|
||||
"503":
|
||||
$ref: "#/components/responses/Unavailable"
|
||||
|
||||
/api/stats/routes:
|
||||
get:
|
||||
summary: How the period's queries were answered
|
||||
description: |
|
||||
A breakdown by answering route over the same window `/api/stats`
|
||||
reports for. `source` is the answering resolver's identity — the
|
||||
upstream url on `upstream` rows, the zone on `forward_zone` rows, null
|
||||
on every other kind and on rows whose identity the log did not record.
|
||||
It is not the blocklist a block came from. Ordered by count descending,
|
||||
then route ascending, then source ascending with nulls last.
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/Period"
|
||||
responses:
|
||||
"200":
|
||||
description: The route breakdown.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/StatsRoutes"
|
||||
"400":
|
||||
$ref: "#/components/responses/BadRequest"
|
||||
"401":
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
"429":
|
||||
$ref: "#/components/responses/RateLimited"
|
||||
"500":
|
||||
$ref: "#/components/responses/Internal"
|
||||
"503":
|
||||
$ref: "#/components/responses/Unavailable"
|
||||
|
||||
/api/stats/clients:
|
||||
get:
|
||||
summary: Per-client bucketed counts for a period
|
||||
description: |
|
||||
One zero-filled series per client, bucketed exactly like
|
||||
`/api/stats/timeseries` so the two charts share an x-axis. The eight
|
||||
clients with the most queries in the window are named, ranked by count
|
||||
descending then address ascending; every other client sums into
|
||||
`other`, which is always present and always holds one entry per bucket
|
||||
in the window — including when `clients` is empty, when no client fell
|
||||
outside the named eight, and when the window holds no queries at all.
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/Period"
|
||||
responses:
|
||||
"200":
|
||||
description: The per-client series.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/StatsClients"
|
||||
$ref: "#/components/schemas/Overview"
|
||||
"400":
|
||||
$ref: "#/components/responses/BadRequest"
|
||||
"401":
|
||||
@@ -2316,31 +2206,6 @@ components:
|
||||
type: integer
|
||||
description: How many resolved events the purge removed; zero when there were none.
|
||||
|
||||
StatsTotals:
|
||||
type: object
|
||||
required: [period, since, until, queries, blocked, clients, avg_response_time_us, coverage]
|
||||
properties:
|
||||
period:
|
||||
type: string
|
||||
enum: [1h, 24h, 7d, 30d]
|
||||
since:
|
||||
type: integer
|
||||
description: Window start, unix seconds, inclusive.
|
||||
until:
|
||||
type: integer
|
||||
description: Window end, unix seconds, exclusive.
|
||||
queries: { type: integer }
|
||||
blocked: { type: integer }
|
||||
clients:
|
||||
type: integer
|
||||
description: Distinct client addresses in the window.
|
||||
avg_response_time_us:
|
||||
type: integer
|
||||
nullable: true
|
||||
description: Null when no query in the window recorded a time.
|
||||
coverage:
|
||||
$ref: "#/components/schemas/Coverage"
|
||||
|
||||
Bucket:
|
||||
type: object
|
||||
required: [ts, queries, blocked, cached]
|
||||
@@ -2352,23 +2217,6 @@ components:
|
||||
blocked: { type: integer }
|
||||
cached: { type: integer }
|
||||
|
||||
StatsTimeseries:
|
||||
type: object
|
||||
required: [period, since, until, bucket_seconds, buckets, coverage]
|
||||
properties:
|
||||
period:
|
||||
type: string
|
||||
enum: [1h, 24h, 7d, 30d]
|
||||
since: { type: integer }
|
||||
until: { type: integer }
|
||||
bucket_seconds: { type: integer }
|
||||
buckets:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Bucket"
|
||||
coverage:
|
||||
$ref: "#/components/schemas/Coverage"
|
||||
|
||||
TypeCount:
|
||||
type: object
|
||||
required: [qtype, count]
|
||||
@@ -2381,22 +2229,6 @@ components:
|
||||
recorded no type, not an absent row.
|
||||
count: { type: integer }
|
||||
|
||||
StatsTypes:
|
||||
type: object
|
||||
required: [period, since, until, types, coverage]
|
||||
properties:
|
||||
period:
|
||||
type: string
|
||||
enum: [1h, 24h, 7d, 30d]
|
||||
since: { type: integer }
|
||||
until: { type: integer }
|
||||
types:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/TypeCount"
|
||||
coverage:
|
||||
$ref: "#/components/schemas/Coverage"
|
||||
|
||||
RouteCount:
|
||||
type: object
|
||||
required: [route, source, count]
|
||||
@@ -2412,22 +2244,6 @@ components:
|
||||
row recorded no identity.
|
||||
count: { type: integer }
|
||||
|
||||
StatsRoutes:
|
||||
type: object
|
||||
required: [period, since, until, routes, coverage]
|
||||
properties:
|
||||
period:
|
||||
type: string
|
||||
enum: [1h, 24h, 7d, 30d]
|
||||
since: { type: integer }
|
||||
until: { type: integer }
|
||||
routes:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/RouteCount"
|
||||
coverage:
|
||||
$ref: "#/components/schemas/Coverage"
|
||||
|
||||
ClientSeries:
|
||||
type: object
|
||||
required: [client, buckets]
|
||||
@@ -2443,18 +2259,47 @@ components:
|
||||
items:
|
||||
type: integer
|
||||
|
||||
StatsClients:
|
||||
OverviewTotals:
|
||||
type: object
|
||||
required: [period, since, until, bucket_seconds, clients, other, coverage]
|
||||
required: [queries, blocked, clients, avg_response_time_us]
|
||||
properties:
|
||||
queries: { type: integer }
|
||||
blocked: { type: integer }
|
||||
clients:
|
||||
type: integer
|
||||
description: Distinct client addresses in the window.
|
||||
avg_response_time_us:
|
||||
type: integer
|
||||
nullable: true
|
||||
description: Null when no query in the window recorded a time.
|
||||
|
||||
Overview:
|
||||
type: object
|
||||
required: [period, since, until, bucket_seconds, totals, buckets, clients, other, types, routes, coverage]
|
||||
properties:
|
||||
period:
|
||||
type: string
|
||||
enum: [1h, 24h, 7d, 30d]
|
||||
since: { type: integer }
|
||||
until: { type: integer }
|
||||
since:
|
||||
type: integer
|
||||
description: Window start, unix seconds, inclusive.
|
||||
until:
|
||||
type: integer
|
||||
description: Window end, unix seconds, exclusive.
|
||||
bucket_seconds: { type: integer }
|
||||
totals:
|
||||
$ref: "#/components/schemas/OverviewTotals"
|
||||
buckets:
|
||||
type: array
|
||||
description: One entry per bucket in the window, zero-filled.
|
||||
items:
|
||||
$ref: "#/components/schemas/Bucket"
|
||||
clients:
|
||||
type: array
|
||||
description: |
|
||||
The eight clients with the most queries in the window, ranked by
|
||||
count descending then address ascending. Every other client sums
|
||||
into `other`.
|
||||
items:
|
||||
$ref: "#/components/schemas/ClientSeries"
|
||||
other:
|
||||
@@ -2466,6 +2311,30 @@ components:
|
||||
eight, and when the window holds no queries at all.
|
||||
items:
|
||||
type: integer
|
||||
types:
|
||||
type: array
|
||||
description: |
|
||||
How many queries of each DNS type the window holds. Rows carry the
|
||||
numeric type only: the type-name table lives in the admin, and a
|
||||
second copy here would drift out of agreement with it. `qtype` is
|
||||
nullable in the query log, so the rows that carry no type group
|
||||
into a row of their own rather than vanishing from a breakdown that
|
||||
claims to add up. Ordered by count descending, then type ascending
|
||||
with the null row last. Types absent from the window are absent
|
||||
from the list.
|
||||
items:
|
||||
$ref: "#/components/schemas/TypeCount"
|
||||
routes:
|
||||
type: array
|
||||
description: |
|
||||
A breakdown by answering route. `source` is the answering
|
||||
resolver's identity — the upstream url on `upstream` rows, the zone
|
||||
on `forward_zone` rows, null on every other kind and on rows whose
|
||||
identity the log did not record. It is not the blocklist a block
|
||||
came from. Ordered by count descending, then route ascending, then
|
||||
source ascending with nulls last.
|
||||
items:
|
||||
$ref: "#/components/schemas/RouteCount"
|
||||
coverage:
|
||||
$ref: "#/components/schemas/Coverage"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user