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

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" ];