Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 80 additions & 1 deletion apps/api/src/prometheus/observability-assets.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,12 +156,22 @@ function allPanels(dashboard: Dashboard): DashboardPanel[] {
}

describe('Grafana dashboard pack', () => {
// Product dashboards: every panel is scoped to a monitored connection, so
// they all carry the ds/job/connection contract. Monitor Internals is the
// Monitor's own runtime telemetry (no connection dimension) and is checked
// separately below.
const files = [
'betterdb-instance-vitals.json',
'betterdb-query-patterns.json',
'betterdb-cluster-slots.json',
'betterdb-anomalies.json',
'betterdb-persistence.json',
'betterdb-keyspace.json',
'betterdb-clients.json',
'betterdb-security.json',
'betterdb-vector.json',
];
const internalsFile = 'betterdb-monitor-internals.json';

it.each(files)('%s declares the shared contract', (file) => {
const dashboard = loadDashboard(file);
Expand Down Expand Up @@ -278,7 +288,76 @@ describe('Grafana dashboard pack', () => {
it('provisions every dashboard file in the directory', () => {
const onDisk = readdirSync(DASHBOARD_DIR).filter((file) => file.endsWith('.json'));

expect(onDisk.sort()).toEqual([...files].sort());
expect(onDisk.sort()).toEqual([...files, internalsFile].sort());
});

// Monitor Internals charts the Monitor process itself (Node.js runtime, poll
// loop, OTLP export). Those series have no `connection` label, so it carries
// only the ds/job contract — the connection-scoped rules above do not apply.
describe('Monitor Internals dashboard', () => {
it('declares the shared contract', () => {
const dashboard = loadDashboard(internalsFile);

expect(dashboard.uid).toBe(internalsFile.replace('.json', ''));
expect(dashboard.title.startsWith('BetterDB · ')).toBe(true);
expect(dashboard.schemaVersion).toBeGreaterThanOrEqual(39);
});

it('exposes a datasource variable first, then a scrape job variable', () => {
const list = loadDashboard(internalsFile).templating.list;

expect(list[0].name).toBe('ds');
expect(list[0].type).toBe('datasource');
expect(list.find((item) => item.name === 'job')?.type).toBe('query');
});

it('points every target at the datasource variable and filters by job', () => {
const targets = allPanels(loadDashboard(internalsFile)).flatMap(
(panel) => panel.targets ?? [],
);

expect(targets.length).toBeGreaterThan(0);
for (const target of targets) {
expect(target.datasource).toEqual({ type: 'prometheus', uid: '${ds}' });
expect(target.expr).toContain('job=~"$job"');
}
});

it('leaves no literal datasource uid behind', () => {
expect(readFileSync(path.join(DASHBOARD_DIR, internalsFile), 'utf8')).not.toContain(
'betterdb-prometheus',
);
});

it('only queries metrics the service registers or collects by default', () => {
const registered = registeredMetricNames();
// Node.js runtime and process metrics come from prom-client's
// collectDefaultMetrics (prefixed betterdb_), registered at startup rather
// than in the service constructor this registry snapshot is built from.
const isDefaultMetric = (name: string) =>
name.startsWith('betterdb_nodejs_') || name.startsWith('betterdb_process_');
const used = allPanels(loadDashboard(internalsFile))
.flatMap((panel) => panel.targets ?? [])
.flatMap((target) => metricNamesIn(target.expr));

expect(used.length).toBeGreaterThan(0);
for (const name of used) {
const base = name.replace(/_(bucket|sum|count)$/, '');
expect(registered.has(name) || registered.has(base) || isDefaultMetric(name)).toBe(true);
}
});

it('asks for table frames on every table panel', () => {
const tables = allPanels(loadDashboard(internalsFile)).filter(
(panel) => panel.type === 'table',
);

for (const panel of tables) {
for (const target of panel.targets ?? []) {
expect(target.format).toBe('table');
}
}
});
});
});

Expand Down
4 changes: 3 additions & 1 deletion deploy/observability/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ A hosted instance of this pack runs against the public demo workspace —
read-only Grafana at `https://demo-grafana.app.betterdb.com` and a published
scrape credential for `https://demo.app.betterdb.com/api/prometheus/metrics`
(see [docs/live-demo.md](../../docs/live-demo.md)). The dashboards there are
these JSON files, unmodified.
these JSON files, with one demo-only override applied to the deployed copies:
the `job` template variable's default is pointed at the demo's push/scrape job
so panels populate on landing. The shipped files here are unchanged.

## What's here

Expand Down
96 changes: 93 additions & 3 deletions deploy/observability/dashboards/betterdb-anomalies.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
{
"uid": "betterdb-anomalies",
"title": "BetterDB · Anomalies",
"tags": ["betterdb", "valkey"],
"tags": [
"betterdb",
"valkey"
],
"schemaVersion": 39,
"version": 1,
"refresh": "30s",
"refresh": "1m",
"time": {
"from": "now-6h",
"to": "now"
Expand Down Expand Up @@ -464,6 +467,93 @@
"x": 0,
"y": 32
}
},
{
"id": 11,
"type": "timeseries",
"title": "Correlated groups (new, 1h)",
"description": "Active groups of anomalies the correlator has linked together.",
"datasource": {
"type": "prometheus",
"uid": "${ds}"
},
"fieldConfig": {
"defaults": {
"unit": "short",
"custom": {
"fillOpacity": 10
}
},
"overrides": []
},
"targets": [
{
"refId": "A",
"datasource": {
"type": "prometheus",
"uid": "${ds}"
},
"expr": "sum by (connection, severity) (increase(betterdb_correlated_groups_total{job=~\"$job\",connection=~\"$connection\"}[1h]))",
"legendFormat": "{{connection}} {{severity}}"
}
],
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 40
}
},
{
"id": 12,
"type": "timeseries",
"title": "Correlated groups by severity",
"description": "Correlated anomaly groups split by severity.",
"datasource": {
"type": "prometheus",
"uid": "${ds}"
},
"fieldConfig": {
"defaults": {
"unit": "short",
"custom": {
"fillOpacity": 10
}
},
"overrides": []
},
"targets": [
{
"refId": "A",
"datasource": {
"type": "prometheus",
"uid": "${ds}"
},
"expr": "betterdb_correlated_groups_by_severity{job=~\"$job\",connection=~\"$connection\"}",
"legendFormat": "{{severity}}"
}
],
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 40
}
}
],
"links": [
{
"title": "BetterDB dashboards",
"type": "dashboards",
"tags": [
"betterdb"
],
"asDropdown": true,
"targetBlank": false,
"includeVars": true,
"keepTime": true,
"icon": "external link",
"keepTags": false
}
]
}
}
Loading
Loading