Watch
0
0
Fork
You've already forked hyperhive
0

export the swarm cache's size from atticd's database to the attic dashboard

This commit is contained in:
atlas 2026-11-01 23:44:54 +01:00
commit c0429bf617
2 changed files with 291 additions and 1 deletions

View file

@ -102,6 +102,16 @@ let
forwarderUnit = "swarm-journal-forwarder-oidc";
# In the host netns: distinct from every other collector's there.
forwarderTelemetryPort = 8903;
# The forwarder's OTLP/HTTP metrics receiver, which the cache-size exporter
# pushes to. Not 4318: the hive collector holds that in the host netns.
forwarderOtlpPort = 8905;
# The exporter's unit and query. `V` is attic's stored value for a valid
# chunk (`ChunkState::Valid`) and for a valid NAR (`NarState::Valid`).
# `file_size` is the compressed size on disk, and each chunk is one row
# however many NARs reference it.
sizeUnit = "swarm-attic-cache-size";
databaseUri = "file:${stateDir}/server.db?mode=ro";
forwarderCredential = id: "/run/credentials/${forwarderUnit}.service/${id}";
# The cache's store identity, handed into the container as system credentials
# for the forwarder's fetch unit. The host units keep reading the same files.
@ -418,6 +428,7 @@ in
clientId = forwarderClientId;
tokenUrl = "${toString autheliaCfg.url}/api/oidc/token";
telemetryPort = forwarderTelemetryPort;
otlpMetricsPort = forwarderOtlpPort;
store = {
package = baoDeploy.package;
addr = "https://${baoCfg.domain}:${toString baoCfg.port}";
@ -433,6 +444,86 @@ in
};
systemd.services.${forwarderUnit}.serviceConfig.LoadCredential = lib.attrNames forwarderStoreFiles;
# Reads atticd's database and pushes the cache's size to the
# forwarder. It runs as atticd's dynamic user, shared by name, because
# the state directory is mode 0700. With atticd stopped the name gets
# another uid and the read fails. `-readonly` and `mode=ro` leave
# the database to atticd, and `.timeout` waits out its writes.
systemd.services.${sizeUnit} = {
description = "push the swarm cache's size, read from atticd's database, to the swarm collector";
wants = [ "${unit}.service" ];
after = [
"${unit}.service"
"opentelemetry-collector.service"
];
path = [
pkgs.coreutils
pkgs.curl
pkgs.sqlite
];
serviceConfig = {
Type = "oneshot";
User = unit;
DynamicUser = true;
StateDirectory = stateName;
SyslogIdentifier = sizeUnit;
UMask = "0077";
CapabilityBoundingSet = [ "" ];
NoNewPrivileges = true;
PrivateDevices = true;
PrivateTmp = true;
ProtectSystem = "strict";
ProtectHome = true;
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectControlGroups = true;
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
"AF_UNIX"
];
RestrictNamespaces = true;
RestrictRealtime = true;
LockPersonality = true;
SystemCallArchitectures = "native";
};
script = ''
set -euo pipefail
query() {
sqlite3 -readonly -cmd '.timeout 5000' ${lib.escapeShellArg databaseUri} "$1"
}
bytes="$(query "SELECT COALESCE(SUM(file_size), 0) FROM chunk WHERE state = 'V'")"
nars="$(query "SELECT COUNT(*) FROM nar WHERE state = 'V'")"
case "$bytes$nars" in
"" | *[!0-9]*)
echo "the database answered a non-number: bytes='$bytes' nars='$nars'" >&2
exit 1
;;
esac
now="$(date +%s%N)"
gauge() {
printf '{"name":"%s","gauge":{"dataPoints":[{"asInt":"%s","timeUnixNano":"%s"}]}}' "$1" "$2" "$now"
}
printf '{"resourceMetrics":[{"resource":{"attributes":[{"key":"service.name","value":{"stringValue":"%s"}}]},"scopeMetrics":[{"metrics":[%s,%s]}]}]}' \
${lib.escapeShellArg machine} \
"$(gauge hyperhive.attic.cache_bytes "$bytes")" \
"$(gauge hyperhive.attic.cache_nars "$nars")" |
curl --fail --silent --show-error --max-time 20 \
-H 'Content-Type: application/json' --data-binary @- \
http://127.0.0.1:${toString forwarderOtlpPort}/v1/metrics
'';
};
systemd.timers.${sizeUnit} = {
wantedBy = [ "timers.target" ];
timerConfig = {
OnBootSec = "2min";
OnUnitActiveSec = "5min";
};
};
systemd.services.${unit} = {
description = "attic binary cache server";
wantedBy = [ "multi-user.target" ];

View file

@ -806,6 +806,178 @@
"sort": "desc"
}
}
},
{
"id": 30,
"type": "row",
"title": "Cache size",
"collapsed": false,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 53
},
"panels": []
},
{
"id": 31,
"type": "stat",
"title": "Cache size",
"description": "Compressed bytes of valid chunks, each counted once. Source: cache_bytes gauge.",
"datasource": {
"type": "prometheus",
"uid": "@datasourceUid@"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 0,
"y": 54
},
"targets": [
{
"refId": "A",
"datasource": {
"type": "prometheus",
"uid": "@datasourceUid@"
},
"expr": "max(last_over_time({__name__=\"hyperhive.attic.cache_bytes\"}[15m]))",
"instant": true,
"legendFormat": "size"
}
],
"fieldConfig": {
"defaults": {
"unit": "bytes",
"decimals": 1,
"color": {
"mode": "fixed",
"fixedColor": "blue"
},
"mappings": []
},
"overrides": []
},
"options": {
"graphMode": "none",
"colorMode": "value",
"textMode": "value",
"justifyMode": "auto",
"reduceOptions": {
"calcs": ["lastNotNull"],
"fields": "",
"values": false
}
}
},
{
"id": 32,
"type": "stat",
"title": "Store paths cached",
"description": "Store paths (NARs) the cache holds as valid. Source: cache_nars gauge.",
"datasource": {
"type": "prometheus",
"uid": "@datasourceUid@"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 6,
"y": 54
},
"targets": [
{
"refId": "A",
"datasource": {
"type": "prometheus",
"uid": "@datasourceUid@"
},
"expr": "max(last_over_time({__name__=\"hyperhive.attic.cache_nars\"}[15m]))",
"instant": true,
"legendFormat": "store paths"
}
],
"fieldConfig": {
"defaults": {
"unit": "short",
"decimals": 0,
"color": {
"mode": "fixed",
"fixedColor": "blue"
},
"mappings": []
},
"overrides": []
},
"options": {
"graphMode": "none",
"colorMode": "value",
"textMode": "value",
"justifyMode": "auto",
"reduceOptions": {
"calcs": ["lastNotNull"],
"fields": "",
"values": false
}
}
},
{
"id": 33,
"type": "timeseries",
"title": "Cache size over time",
"description": "Compressed bytes of valid chunks; garbage collection lowers it. Source: cache_bytes gauge.",
"datasource": {
"type": "prometheus",
"uid": "@datasourceUid@"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 54
},
"targets": [
{
"refId": "A",
"datasource": {
"type": "prometheus",
"uid": "@datasourceUid@"
},
"expr": "max({__name__=\"hyperhive.attic.cache_bytes\"})",
"legendFormat": "size"
}
],
"fieldConfig": {
"defaults": {
"unit": "bytes",
"decimals": 1,
"color": {
"mode": "palette-classic"
},
"custom": {
"drawStyle": "line",
"lineWidth": 1,
"fillOpacity": 10,
"showPoints": "never",
"spanNulls": false,
"axisSoftMin": 0
}
},
"overrides": []
},
"options": {
"legend": {
"displayMode": "list",
"placement": "bottom",
"showLegend": true,
"calcs": []
},
"tooltip": {
"mode": "multi",
"sort": "desc"
}
}
}
]
}