Analyse: Tageswerte nach 25 Monaten löschen, secure_delete, CSV-Export
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a73e0be952
commit
97890a8eee
5 files changed
+248
-3
No files matched your search
@@ -55,6 +55,7 @@ CREATE TABLE IF NOT EXISTS agg_state (
|
||||
export function openDb(path) {
|
||||
const db = new DatabaseSync(path);
|
||||
if (path !== ':memory:') db.exec('PRAGMA journal_mode = WAL');
|
||||
db.exec('PRAGMA secure_delete = ON');
|
||||
db.exec(SCHEMA);
|
||||
return db;
|
||||
}
|
||||
+32
-3
@@ -8,6 +8,17 @@ const WALL_FMT = new Intl.DateTimeFormat('en-GB', {
|
||||
const DAY_MS = 86400 * 1000;
|
||||
export const RAW_DAYS = 30;
|
||||
export const OFF_DAYS = 60;
|
||||
export const DAILY_MONTHS = 25;
|
||||
|
||||
// Berliner Tag von `now` minus DAILY_MONTHS Kalendermonate (Monatsende auf den letzten Tag des Zielmonats begrenzt).
|
||||
export function dailyCutoffDay(now) {
|
||||
const [y, m, d] = DAY_FMT.format(now).split('-').map(Number);
|
||||
const idx = y * 12 + (m - 1) - DAILY_MONTHS;
|
||||
const ty = Math.floor(idx / 12);
|
||||
const tm = idx % 12;
|
||||
const last = new Date(Date.UTC(ty, tm + 1, 0)).getUTCDate();
|
||||
return `${String(ty).padStart(4, '0')}-${String(tm + 1).padStart(2, '0')}-${String(Math.min(d, last)).padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
// Kalendertag (YYYY-MM-DD) in Europe/Berlin.
|
||||
export const dayOf = (ts) => DAY_FMT.format(ts);
|
||||
@@ -38,7 +49,7 @@ export function aggregateRows(rows) {
|
||||
return [...m].filter(([, n]) => n > 0).map(([k, n]) => { const [dim, key] = k.split('\u0000'); return [dim, key, n]; });
|
||||
}
|
||||
|
||||
export function runNightly(db, { now = Date.now() } = {}) {
|
||||
export function runNightly(db, { now = Date.now(), log = console } = {}) {
|
||||
const today = dayOf(now);
|
||||
const rawCut = now - RAW_DAYS * DAY_MS;
|
||||
const offCut = new Date(now - OFF_DAYS * DAY_MS).toISOString();
|
||||
@@ -48,7 +59,7 @@ export function runNightly(db, { now = Date.now() } = {}) {
|
||||
const insDaily = db.prepare(`INSERT INTO daily (site, day, dim, key, count) VALUES (?, ?, ?, ?, ?)
|
||||
ON CONFLICT(site, day, dim, key) DO UPDATE SET count = count + excluded.count`);
|
||||
const insDone = db.prepare('INSERT OR IGNORE INTO agg_state (site, day) VALUES (?, ?)');
|
||||
const stats = { aggregated: 0, sitesDeleted: 0 };
|
||||
const stats = { aggregated: 0, sitesDeleted: 0, dailyDeleted: 0, orphansMarked: 0, orphanRowsDeleted: 0 };
|
||||
|
||||
const tx = (fn) => {
|
||||
db.exec('BEGIN');
|
||||
@@ -85,6 +96,24 @@ export function runNightly(db, { now = Date.now() } = {}) {
|
||||
stats.sitesDeleted++;
|
||||
});
|
||||
}
|
||||
|
||||
// Tageswerte älter als 25 Monate (alle Websites)
|
||||
tx(() => {
|
||||
const cut = dailyCutoffDay(now);
|
||||
stats.dailyDeleted = Number(db.prepare('DELETE FROM daily WHERE day < ?').run(cut).changes);
|
||||
db.prepare('DELETE FROM agg_state WHERE day < ?').run(cut);
|
||||
});
|
||||
|
||||
// Ausgeschaltete Websites ohne offSince: Frist beginnt bei Entdeckung.
|
||||
stats.orphansMarked = Number(db.prepare('UPDATE sites SET offSince = ? WHERE active = 0 AND offSince IS NULL').run(new Date(now).toISOString()).changes);
|
||||
// Zeilen ohne Website-Eintrag
|
||||
tx(() => {
|
||||
for (const t of ['events_raw', 'clicks', 'daily', 'agg_state']) {
|
||||
stats.orphanRowsDeleted += Number(db.prepare(`DELETE FROM ${t} WHERE site NOT IN (SELECT siteKey FROM sites)`).run().changes);
|
||||
}
|
||||
});
|
||||
|
||||
try { db.exec('PRAGMA wal_checkpoint(TRUNCATE)'); } catch (e) { log.error('Analytics-Nachtjob: wal_checkpoint:', e.message); }
|
||||
return stats;
|
||||
}
|
||||
|
||||
@@ -113,7 +142,7 @@ export function scheduleNightly(db, { startDelayMs = 60000, log = console, now =
|
||||
const run = () => {
|
||||
try {
|
||||
const r = job(db, { now: now() });
|
||||
log.log(`Analytics-Nachtjob: ${r.aggregated} Tage verdichtet, ${r.sitesDeleted} Websites gelöscht`);
|
||||
log.log(`Analytics-Nachtjob: ${r.aggregated} Tage verdichtet, ${r.sitesDeleted} Websites gelöscht, ${r.dailyDeleted} Tageswerte > ${DAILY_MONTHS} Monate gelöscht, ${r.orphansMarked} Websites markiert, ${r.orphanRowsDeleted} verwaiste Zeilen gelöscht`);
|
||||
} catch (e) { log.error('Analytics-Nachtjob:', e.message); }
|
||||
};
|
||||
const arm = (delay, fn) => { if (!stopped) timer = timers.setTimeout(fn, delay); };
|
||||
|
||||
Reference in new issue
Block a user