Backup: daglig PG-dump, STDB-krasj-recovery, helsesjekk (oppgave 12.2)
Tre ting implementert: 1. PG-dump rutine (scripts/backup-pg.sh): - Daglig cron kl. 03:00 UTC via /etc/cron.d/synops-backup - pg_dump -Fc (custom format, komprimert), konsistent uten nedetid - Rotasjon: beholder 30 dager, sletter eldre - Verifiserer at dump-filen er gyldig (ikke tom) 2. STDB → PG gjenoppbygging ved krasj (stdb_monitor.rs): - Bakgrunnsmonitor sjekker STDB hvert 30. sekund - Oppdager krasj (var oppe → nå nede) - Venter på at containeren restarter (maks 10 min) - Kjører warmup (PG → STDB) automatisk - Hele prosessen logges 3. Forbedret backup-helsesjekk (health.rs): - Sjekker /srv/synops/backup/pg/ for nyeste dump - Rapporterer ok/stale/missing i /admin/health
This commit is contained in:
parent
4d7852f466
commit
0f03886091
6 changed files with 344 additions and 37 deletions
113
docs/infra/backup.md
Normal file
113
docs/infra/backup.md
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
# Backup og gjenoppbygging
|
||||||
|
|
||||||
|
**Filsti:** `docs/infra/backup.md`
|
||||||
|
|
||||||
|
Synops sin backup-strategi bygger på én innsikt: **PostgreSQL er den
|
||||||
|
eneste autoriteten.** SpacetimeDB er en sanntidscache som gjenoppbygges
|
||||||
|
fra PG ved behov. Media-filer i CAS er innholdsadresserte og immutable.
|
||||||
|
|
||||||
|
## Arkitektur
|
||||||
|
|
||||||
|
```
|
||||||
|
PostgreSQL (autoritativ kilde)
|
||||||
|
│
|
||||||
|
├──→ pg_dump daglig (03:00 UTC)
|
||||||
|
│ └──→ /srv/synops/backup/pg/sidelinja_YYYYMMDD_HHMMSS.dump
|
||||||
|
│ └──→ Rotasjon: 30 dager
|
||||||
|
│
|
||||||
|
└──→ SpacetimeDB (sanntidscache)
|
||||||
|
└──→ Gjenoppbygges fra PG ved krasj (warmup)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 1. PG-dump (daglig)
|
||||||
|
|
||||||
|
**Script:** `scripts/backup-pg.sh`
|
||||||
|
**Cron:** `/etc/cron.d/synops-backup` — `0 3 * * *`
|
||||||
|
**Logg:** `/srv/synops/logs/backup-pg.log`
|
||||||
|
**Dumper:** `/srv/synops/backup/pg/`
|
||||||
|
|
||||||
|
Prosess:
|
||||||
|
1. Sjekker at PG-containeren kjører
|
||||||
|
2. `pg_dump -Fc` (custom format, komprimert) — konsistent snapshot uten nedetid
|
||||||
|
3. Verifiserer at dump-filen ikke er tom
|
||||||
|
4. Sletter dumper eldre enn 30 dager
|
||||||
|
|
||||||
|
Manuell kjøring:
|
||||||
|
```bash
|
||||||
|
/home/vegard/synops/scripts/backup-pg.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Verifiser dump:
|
||||||
|
```bash
|
||||||
|
docker cp /srv/synops/backup/pg/DUMP.dump sidelinja-postgres-1:/tmp/test.dump
|
||||||
|
docker exec sidelinja-postgres-1 pg_restore --list /tmp/test.dump
|
||||||
|
docker exec sidelinja-postgres-1 rm /tmp/test.dump
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. STDB-gjenoppbygging ved krasj
|
||||||
|
|
||||||
|
**Modul:** `maskinrommet/src/stdb_monitor.rs`
|
||||||
|
|
||||||
|
SpacetimeDB er en sanntidscache. Hvis den krasjer, tapes ingen data
|
||||||
|
fordi all skriving går gjennom maskinrommet som skriver til PG først
|
||||||
|
(asynkront, men alltid). Gjenoppbygging skjer automatisk:
|
||||||
|
|
||||||
|
### Ved oppstart
|
||||||
|
Maskinrommet kjører `warmup::run()` i `main.rs` — laster alle noder,
|
||||||
|
edges og node_access fra PG til STDB.
|
||||||
|
|
||||||
|
### Ved krasj under drift
|
||||||
|
`stdb_monitor` kjører i bakgrunnen og sjekker STDB hvert 30. sekund:
|
||||||
|
|
||||||
|
1. **Oppdager** at STDB ikke svarer (var oppe, nå nede)
|
||||||
|
2. **Venter** opptil 10 minutter på at containeren restarter
|
||||||
|
3. **Kjører warmup** (PG → STDB) når STDB svarer igjen
|
||||||
|
4. **Logger** hele hendelsesforløpet
|
||||||
|
|
||||||
|
Prosessen er automatisk og krever ingen manuell inngripen så lenge
|
||||||
|
Docker restarter containeren (restart-policy: `unless-stopped`).
|
||||||
|
|
||||||
|
## 3. Restore fra backup
|
||||||
|
|
||||||
|
### PostgreSQL
|
||||||
|
```bash
|
||||||
|
# Stopp maskinrommet (unngå skrivinger under restore)
|
||||||
|
sudo systemctl stop maskinrommet
|
||||||
|
|
||||||
|
# Restore fra dump
|
||||||
|
docker cp /srv/synops/backup/pg/sidelinja_YYYYMMDD.dump sidelinja-postgres-1:/tmp/restore.dump
|
||||||
|
docker exec sidelinja-postgres-1 pg_restore -U sidelinja -d sidelinja --clean /tmp/restore.dump
|
||||||
|
docker exec sidelinja-postgres-1 rm /tmp/restore.dump
|
||||||
|
|
||||||
|
# Start maskinrommet (warmup laster PG → STDB automatisk)
|
||||||
|
sudo systemctl start maskinrommet
|
||||||
|
```
|
||||||
|
|
||||||
|
### Komplett gjenoppbygging
|
||||||
|
Ved total serversvikt (ny VPS):
|
||||||
|
1. Installer OS og Docker (se `docs/setup/produksjon.md`)
|
||||||
|
2. Start PG-container
|
||||||
|
3. Restore dump (se over)
|
||||||
|
4. Start maskinrommet (warmup håndterer STDB)
|
||||||
|
5. Avledede data (segmenter, søkeindeks) regenereres fra kildene
|
||||||
|
|
||||||
|
## 4. Overvåking
|
||||||
|
|
||||||
|
Health-dashboardet (`/admin/health`) viser backup-status:
|
||||||
|
- **ok** — dump-fil er fersk (< 25 timer gammel)
|
||||||
|
- **stale** — dump-fil er eldre enn 25 timer
|
||||||
|
- **missing** — ingen dump-filer funnet
|
||||||
|
|
||||||
|
Metrikk-endepunktet (`/metrics`) inkluderer STDB-status som del av
|
||||||
|
helsesjekken.
|
||||||
|
|
||||||
|
## 5. Hva som IKKE backupes (bevisst)
|
||||||
|
|
||||||
|
- **SpacetimeDB** — sanntidscache, gjenoppbygges fra PG
|
||||||
|
- **Redis** — cache, regenereres automatisk
|
||||||
|
- **Caddy-data** — sertifikater regenereres av Let's Encrypt
|
||||||
|
- **Whisper-modeller** — re-download fra HuggingFace
|
||||||
|
- **Logger** — rulleres med logrotate
|
||||||
|
|
||||||
|
Se `docs/setup/produksjon.md` § 11 for fullstendig backup-spesifikasjon
|
||||||
|
inkludert off-site backup (rclone) og WAL-arkivering (fremtidig).
|
||||||
|
|
@ -336,16 +336,10 @@ async fn collect_pg_stats(db: &PgPool) -> PgStats {
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
fn check_backups() -> Vec<BackupInfo> {
|
fn check_backups() -> Vec<BackupInfo> {
|
||||||
// Sjekk om det finnes PG-dumper i standard backup-kataloger
|
|
||||||
let backup_paths = [
|
|
||||||
"/srv/synops/backups",
|
|
||||||
"/srv/synops/data/backups",
|
|
||||||
"/var/backups/synops",
|
|
||||||
];
|
|
||||||
|
|
||||||
let mut backups = Vec::new();
|
let mut backups = Vec::new();
|
||||||
|
|
||||||
// PG-dump
|
// PG-dump — sjekk /srv/synops/backup/pg/ for nyeste .dump-fil
|
||||||
|
let pg_dir = "/srv/synops/backup/pg";
|
||||||
let mut pg_backup = BackupInfo {
|
let mut pg_backup = BackupInfo {
|
||||||
backup_type: "PostgreSQL dump".to_string(),
|
backup_type: "PostgreSQL dump".to_string(),
|
||||||
last_success: None,
|
last_success: None,
|
||||||
|
|
@ -353,41 +347,37 @@ fn check_backups() -> Vec<BackupInfo> {
|
||||||
status: "missing".to_string(),
|
status: "missing".to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
for dir in &backup_paths {
|
if let Ok(entries) = std::fs::read_dir(pg_dir) {
|
||||||
if let Ok(entries) = std::fs::read_dir(dir) {
|
// Finn nyeste dump-fil
|
||||||
|
let mut newest: Option<(std::time::SystemTime, std::path::PathBuf)> = None;
|
||||||
for entry in entries.flatten() {
|
for entry in entries.flatten() {
|
||||||
let name = entry.file_name().to_string_lossy().to_string();
|
let name = entry.file_name().to_string_lossy().to_string();
|
||||||
if name.contains("pg") || name.ends_with(".sql") || name.ends_with(".dump") {
|
if name.ends_with(".dump") {
|
||||||
if let Ok(meta) = entry.metadata() {
|
if let Ok(meta) = entry.metadata() {
|
||||||
if let Ok(modified) = meta.modified() {
|
if let Ok(modified) = meta.modified() {
|
||||||
|
if newest.as_ref().map_or(true, |(t, _)| modified > *t) {
|
||||||
|
newest = Some((modified, entry.path()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((modified, path)) = newest {
|
||||||
let age = modified.elapsed().unwrap_or_default();
|
let age = modified.elapsed().unwrap_or_default();
|
||||||
let ts = chrono::DateTime::<chrono::Utc>::from(modified);
|
let ts = chrono::DateTime::<chrono::Utc>::from(modified);
|
||||||
pg_backup.last_success = Some(ts.to_rfc3339());
|
pg_backup.last_success = Some(ts.to_rfc3339());
|
||||||
pg_backup.path = Some(entry.path().to_string_lossy().to_string());
|
pg_backup.path = Some(path.to_string_lossy().to_string());
|
||||||
pg_backup.status = if age.as_secs() < 86400 {
|
pg_backup.status = if age.as_secs() < 86400 + 3600 {
|
||||||
|
// Litt slakk: 25 timer (cron kjører daglig)
|
||||||
"ok".to_string()
|
"ok".to_string()
|
||||||
} else if age.as_secs() < 7 * 86400 {
|
|
||||||
"stale".to_string()
|
|
||||||
} else {
|
} else {
|
||||||
"stale".to_string()
|
"stale".to_string()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
backups.push(pg_backup);
|
backups.push(pg_backup);
|
||||||
|
|
||||||
// CAS (media) backup
|
|
||||||
backups.push(BackupInfo {
|
|
||||||
backup_type: "CAS media".to_string(),
|
|
||||||
last_success: None,
|
|
||||||
path: None,
|
|
||||||
status: "missing".to_string(),
|
|
||||||
});
|
|
||||||
|
|
||||||
backups
|
backups
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ pub mod transcribe;
|
||||||
pub mod tts;
|
pub mod tts;
|
||||||
pub mod usage_overview;
|
pub mod usage_overview;
|
||||||
pub mod user_usage;
|
pub mod user_usage;
|
||||||
|
mod stdb_monitor;
|
||||||
mod warmup;
|
mod warmup;
|
||||||
mod workspace;
|
mod workspace;
|
||||||
|
|
||||||
|
|
@ -183,6 +184,9 @@ async fn main() {
|
||||||
// Start nattlig bandwidth-parsing (oppgave 15.7)
|
// Start nattlig bandwidth-parsing (oppgave 15.7)
|
||||||
bandwidth::start_bandwidth_parser(db.clone());
|
bandwidth::start_bandwidth_parser(db.clone());
|
||||||
|
|
||||||
|
// Start STDB-overvåker: oppdager krasj og gjenoppbygger fra PG (oppgave 12.2)
|
||||||
|
stdb_monitor::start_stdb_monitor(db.clone(), stdb.clone());
|
||||||
|
|
||||||
// Start periodisk CAS tmp-opprydding (oppgave 17.6)
|
// Start periodisk CAS tmp-opprydding (oppgave 17.6)
|
||||||
cas::start_tmp_cleanup_loop(cas.clone());
|
cas::start_tmp_cleanup_loop(cas.clone());
|
||||||
|
|
||||||
|
|
|
||||||
145
maskinrommet/src/stdb_monitor.rs
Normal file
145
maskinrommet/src/stdb_monitor.rs
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
// STDB-overvåker: oppdager SpacetimeDB-krasj og gjenoppbygger fra PG.
|
||||||
|
//
|
||||||
|
// Kjører i bakgrunnen med jevnlig helsesjekk. Hvis STDB var oppe og
|
||||||
|
// deretter feiler, kjøres warmup automatisk for å gjenoppbygge tilstand.
|
||||||
|
//
|
||||||
|
// Sekvens ved krasj:
|
||||||
|
// 1. Oppdage at STDB er nede (helsesjekk feiler)
|
||||||
|
// 2. Vente til STDB er tilbake (container restarter)
|
||||||
|
// 3. Kjøre warmup (PG → STDB)
|
||||||
|
// 4. Logge hendelsen
|
||||||
|
//
|
||||||
|
// Ref: docs/infra/backup.md, docs/infra/synkronisering.md
|
||||||
|
|
||||||
|
use sqlx::PgPool;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::stdb::StdbClient;
|
||||||
|
|
||||||
|
/// Start STDB-overvåker i bakgrunnen.
|
||||||
|
/// Sjekker STDB-helse hvert 30. sekund og kjører warmup ved krasj.
|
||||||
|
pub fn start_stdb_monitor(db: PgPool, stdb: StdbClient) {
|
||||||
|
tokio::spawn(async move {
|
||||||
|
monitor_loop(db, stdb).await;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Intern tilstand for overvåkeren.
|
||||||
|
struct MonitorState {
|
||||||
|
/// Var STDB oppe ved forrige sjekk?
|
||||||
|
was_up: bool,
|
||||||
|
/// Pågår det en recovery akkurat nå?
|
||||||
|
recovering: Arc<AtomicBool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn monitor_loop(db: PgPool, stdb: StdbClient) {
|
||||||
|
let mut state = MonitorState {
|
||||||
|
was_up: true, // Antar oppe etter warmup ved oppstart
|
||||||
|
recovering: Arc::new(AtomicBool::new(false)),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Vent litt etter oppstart slik at warmup fullføres først
|
||||||
|
tokio::time::sleep(std::time::Duration::from_secs(60)).await;
|
||||||
|
tracing::info!("STDB-overvåker startet (sjekker hvert 30s)");
|
||||||
|
|
||||||
|
let mut interval = tokio::time::interval(std::time::Duration::from_secs(30));
|
||||||
|
|
||||||
|
loop {
|
||||||
|
interval.tick().await;
|
||||||
|
|
||||||
|
// Ikke sjekk hvis recovery allerede pågår
|
||||||
|
if state.recovering.load(Ordering::Relaxed) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let is_up = check_stdb_health(&stdb).await;
|
||||||
|
|
||||||
|
match (state.was_up, is_up) {
|
||||||
|
(true, false) => {
|
||||||
|
// STDB gikk ned! Logg og start recovery-venting.
|
||||||
|
tracing::error!("STDB-overvåker: SpacetimeDB er NEDE — starter recovery-prosess");
|
||||||
|
state.recovering.store(true, Ordering::Relaxed);
|
||||||
|
|
||||||
|
let db_clone = db.clone();
|
||||||
|
let stdb_clone = stdb.clone();
|
||||||
|
let recovering = state.recovering.clone();
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
recover_stdb(db_clone, stdb_clone, recovering).await;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
(false, true) => {
|
||||||
|
// STDB kom tilbake uten vår hjelp (recovery-tasken fikset det)
|
||||||
|
tracing::info!("STDB-overvåker: SpacetimeDB er tilbake");
|
||||||
|
state.was_up = true;
|
||||||
|
}
|
||||||
|
(false, false) => {
|
||||||
|
// Fortsatt nede — recovery-tasken håndterer dette
|
||||||
|
}
|
||||||
|
(true, true) => {
|
||||||
|
// Alt OK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_up {
|
||||||
|
state.was_up = true;
|
||||||
|
} else if !state.recovering.load(Ordering::Relaxed) {
|
||||||
|
state.was_up = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sjekk om STDB svarer på en enkel reducer-kall.
|
||||||
|
async fn check_stdb_health(stdb: &StdbClient) -> bool {
|
||||||
|
stdb.delete_node("__healthcheck_nonexistent__").await.is_ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Vent til STDB er tilbake, deretter kjør warmup.
|
||||||
|
async fn recover_stdb(db: PgPool, stdb: StdbClient, recovering: Arc<AtomicBool>) {
|
||||||
|
let max_wait = std::time::Duration::from_secs(600); // Maks 10 min
|
||||||
|
let check_interval = std::time::Duration::from_secs(10);
|
||||||
|
let start = std::time::Instant::now();
|
||||||
|
|
||||||
|
tracing::info!("STDB-recovery: venter på at SpacetimeDB starter opp igjen (maks 10 min)");
|
||||||
|
|
||||||
|
// Vent til STDB svarer
|
||||||
|
loop {
|
||||||
|
if start.elapsed() > max_wait {
|
||||||
|
tracing::error!(
|
||||||
|
"STDB-recovery: SpacetimeDB kom ikke tilbake innen {} sekunder — gir opp",
|
||||||
|
max_wait.as_secs()
|
||||||
|
);
|
||||||
|
recovering.store(false, Ordering::Relaxed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tokio::time::sleep(check_interval).await;
|
||||||
|
|
||||||
|
if check_stdb_health(&stdb).await {
|
||||||
|
tracing::info!(
|
||||||
|
"STDB-recovery: SpacetimeDB svarer igjen etter {}s",
|
||||||
|
start.elapsed().as_secs()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// STDB er tilbake — kjør warmup
|
||||||
|
tracing::info!("STDB-recovery: kjører warmup (PG → STDB)");
|
||||||
|
match crate::warmup::run(&db, &stdb).await {
|
||||||
|
Ok(stats) => {
|
||||||
|
tracing::info!(
|
||||||
|
"STDB-recovery: warmup fullført ({} noder, {} edges, {} access)",
|
||||||
|
stats.nodes,
|
||||||
|
stats.edges,
|
||||||
|
stats.access
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("STDB-recovery: warmup feilet: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
recovering.store(false, Ordering::Relaxed);
|
||||||
|
}
|
||||||
56
scripts/backup-pg.sh
Executable file
56
scripts/backup-pg.sh
Executable file
|
|
@ -0,0 +1,56 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# backup-pg.sh — Daglig PostgreSQL-dump med rotasjon.
|
||||||
|
#
|
||||||
|
# Kjøres via cron (03:00 UTC). Dumper synops-databasen med pg_dump -Fc
|
||||||
|
# (custom format, komprimert). Beholder 30 dager, sletter eldre.
|
||||||
|
#
|
||||||
|
# Ref: docs/setup/produksjon.md § 11.1, docs/infra/backup.md
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
BACKUP_DIR="/srv/synops/backup/pg"
|
||||||
|
CONTAINER="sidelinja-postgres-1"
|
||||||
|
DB_USER="sidelinja"
|
||||||
|
DB_NAME="sidelinja"
|
||||||
|
RETAIN_DAYS=30
|
||||||
|
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||||
|
DUMP_FILE="${BACKUP_DIR}/${DB_NAME}_${TIMESTAMP}.dump"
|
||||||
|
LOG_FILE="/srv/synops/logs/backup-pg.log"
|
||||||
|
|
||||||
|
log() {
|
||||||
|
echo "$(date -Iseconds) $1" | tee -a "$LOG_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Sjekk at backup-katalog finnes
|
||||||
|
mkdir -p "$BACKUP_DIR"
|
||||||
|
|
||||||
|
# Sjekk at PG-containeren kjører
|
||||||
|
if ! docker inspect "$CONTAINER" --format '{{.State.Running}}' 2>/dev/null | grep -q true; then
|
||||||
|
log "FEIL: PostgreSQL-container $CONTAINER kjører ikke"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Kjør pg_dump (konsistent snapshot, ingen nedetid)
|
||||||
|
log "Starter PG-dump → $DUMP_FILE"
|
||||||
|
if docker exec "$CONTAINER" pg_dump -U "$DB_USER" -Fc "$DB_NAME" > "$DUMP_FILE" 2>>"$LOG_FILE"; then
|
||||||
|
# Verifiser at filen ikke er tom
|
||||||
|
FILESIZE=$(stat -c%s "$DUMP_FILE" 2>/dev/null || echo 0)
|
||||||
|
if [ "$FILESIZE" -lt 100 ]; then
|
||||||
|
log "FEIL: Dump-filen er for liten (${FILESIZE} bytes), noe gikk galt"
|
||||||
|
rm -f "$DUMP_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log "PG-dump ferdig: ${DUMP_FILE} ($(numfmt --to=iec "$FILESIZE"))"
|
||||||
|
else
|
||||||
|
log "FEIL: pg_dump feilet"
|
||||||
|
rm -f "$DUMP_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Rotasjon: slett dumper eldre enn $RETAIN_DAYS dager
|
||||||
|
DELETED=$(find "$BACKUP_DIR" -name "*.dump" -mtime +${RETAIN_DAYS} -print -delete | wc -l)
|
||||||
|
if [ "$DELETED" -gt 0 ]; then
|
||||||
|
log "Rotasjon: slettet $DELETED dump(er) eldre enn $RETAIN_DAYS dager"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Backup fullført OK"
|
||||||
3
tasks.md
3
tasks.md
|
|
@ -267,7 +267,6 @@ kaller dem direkte. Samme verktøy, to brukere.
|
||||||
## Fase 12: Herding
|
## Fase 12: Herding
|
||||||
|
|
||||||
- [x] 12.1 Observerbarhet: strukturert logging, metrikker (request latency, queue depth, AI cost).
|
- [x] 12.1 Observerbarhet: strukturert logging, metrikker (request latency, queue depth, AI cost).
|
||||||
- [~] 12.2 Backup: PG-dump rutine, STDB → PG gjenoppbygging ved krasj.
|
- [x] 12.2 Backup: PG-dump rutine, STDB → PG gjenoppbygging ved krasj.
|
||||||
> Påbegynt: 2026-03-18T11:05
|
|
||||||
- [ ] 12.3 Feilhåndtering: retry med backoff i skrivestien, dead letter queue for feilede PG-skrivinger.
|
- [ ] 12.3 Feilhåndtering: retry med backoff i skrivestien, dead letter queue for feilede PG-skrivinger.
|
||||||
- [ ] 12.4 Ytelse: profiler STDB-spørringer, optimaliser node_access-oppdatering.
|
- [ ] 12.4 Ytelse: profiler STDB-spørringer, optimaliser node_access-oppdatering.
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue