mirror of
https://github.com/nicotsx/zerobyte.git
synced 2026-04-19 14:28:54 -04:00
refactor: use backend cache in restic operations (#295)
This commit is contained in:
@@ -6,6 +6,7 @@ import { db } from "../../db/db";
|
||||
import { backupSchedulesTable, backupScheduleMirrorsTable, repositoriesTable, volumesTable } from "../../db/schema";
|
||||
import { restic } from "../../utils/restic";
|
||||
import { logger } from "../../utils/logger";
|
||||
import { cache } from "../../utils/cache";
|
||||
import { getVolumePath } from "../volumes/helpers";
|
||||
import type { CreateBackupScheduleBody, UpdateBackupScheduleBody, UpdateScheduleMirrorsBody } from "./backups.dto";
|
||||
import { toMessage } from "../../utils/errors";
|
||||
@@ -329,6 +330,8 @@ const executeBackup = async (scheduleId: number, manual = false) => {
|
||||
|
||||
const finalStatus = exitCode === 0 ? "success" : "warning";
|
||||
|
||||
cache.delByPrefix(`snapshots:${repository.id}:`);
|
||||
|
||||
const nextBackupAt = calculateNextRun(schedule.cronExpression);
|
||||
await db
|
||||
.update(backupSchedulesTable)
|
||||
@@ -488,6 +491,7 @@ const runForget = async (scheduleId: number, repositoryId?: string) => {
|
||||
const releaseLock = await repoMutex.acquireExclusive(repository.id, `forget:${scheduleId}`);
|
||||
try {
|
||||
await restic.forget(repository.config, schedule.retentionPolicy, { tag: schedule.shortId });
|
||||
cache.delByPrefix(`snapshots:${repository.id}:`);
|
||||
} finally {
|
||||
releaseLock();
|
||||
}
|
||||
@@ -619,6 +623,7 @@ const copyToMirrors = async (
|
||||
|
||||
try {
|
||||
await restic.copy(sourceRepository.config, mirror.repository.config, { tag: schedule.shortId });
|
||||
cache.delByPrefix(`snapshots:${mirror.repository.id}:`);
|
||||
} finally {
|
||||
releaseSource();
|
||||
releaseMirror();
|
||||
|
||||
@@ -7,6 +7,7 @@ import { toMessage } from "../../utils/errors";
|
||||
import { generateShortId } from "../../utils/id";
|
||||
import { restic } from "../../utils/restic";
|
||||
import { cryptoUtils } from "../../utils/crypto";
|
||||
import { cache } from "../../utils/cache";
|
||||
import { repoMutex } from "../../core/repository-mutex";
|
||||
import {
|
||||
repositoryConfigSchema,
|
||||
@@ -143,6 +144,9 @@ const deleteRepository = async (id: string) => {
|
||||
// TODO: Add cleanup logic for the actual restic repository files
|
||||
|
||||
await db.delete(repositoriesTable).where(eq(repositoriesTable.id, repository.id));
|
||||
|
||||
cache.delByPrefix(`snapshots:${repository.id}:`);
|
||||
cache.delByPrefix(`ls:${repository.id}:`);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -160,6 +164,12 @@ const listSnapshots = async (id: string, backupId?: string) => {
|
||||
throw new NotFoundError("Repository not found");
|
||||
}
|
||||
|
||||
const cacheKey = `snapshots:${repository.id}:${backupId || "all"}`;
|
||||
const cached = cache.get<Awaited<ReturnType<typeof restic.snapshots>>>(cacheKey);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const releaseLock = await repoMutex.acquireShared(repository.id, "snapshots");
|
||||
try {
|
||||
let snapshots = [];
|
||||
@@ -170,6 +180,8 @@ const listSnapshots = async (id: string, backupId?: string) => {
|
||||
snapshots = await restic.snapshots(repository.config);
|
||||
}
|
||||
|
||||
cache.set(cacheKey, snapshots);
|
||||
|
||||
return snapshots;
|
||||
} finally {
|
||||
releaseLock();
|
||||
@@ -183,6 +195,15 @@ const listSnapshotFiles = async (id: string, snapshotId: string, path?: string)
|
||||
throw new NotFoundError("Repository not found");
|
||||
}
|
||||
|
||||
const cacheKey = `ls:${repository.id}:${snapshotId}:${path || "root"}`;
|
||||
const cached = cache.get<Awaited<ReturnType<typeof restic.ls>>>(cacheKey);
|
||||
if (cached?.snapshot) {
|
||||
return {
|
||||
snapshot: cached.snapshot,
|
||||
files: cached.nodes,
|
||||
};
|
||||
}
|
||||
|
||||
const releaseLock = await repoMutex.acquireShared(repository.id, `ls:${snapshotId}`);
|
||||
try {
|
||||
const result = await restic.ls(repository.config, snapshotId, path);
|
||||
@@ -191,7 +212,7 @@ const listSnapshotFiles = async (id: string, snapshotId: string, path?: string)
|
||||
throw new NotFoundError("Snapshot not found or empty");
|
||||
}
|
||||
|
||||
return {
|
||||
const response = {
|
||||
snapshot: {
|
||||
id: result.snapshot.id,
|
||||
short_id: result.snapshot.short_id,
|
||||
@@ -201,6 +222,10 @@ const listSnapshotFiles = async (id: string, snapshotId: string, path?: string)
|
||||
},
|
||||
files: result.nodes,
|
||||
};
|
||||
|
||||
cache.set(cacheKey, result);
|
||||
|
||||
return response;
|
||||
} finally {
|
||||
releaseLock();
|
||||
}
|
||||
@@ -248,19 +273,26 @@ const getSnapshotDetails = async (id: string, snapshotId: string) => {
|
||||
throw new NotFoundError("Repository not found");
|
||||
}
|
||||
|
||||
const releaseLock = await repoMutex.acquireShared(repository.id, `snapshot_details:${snapshotId}`);
|
||||
try {
|
||||
const snapshots = await restic.snapshots(repository.config);
|
||||
const snapshot = snapshots.find((snap) => snap.id === snapshotId || snap.short_id === snapshotId);
|
||||
const cacheKey = `snapshots:${repository.id}:all`;
|
||||
let snapshots = cache.get<Awaited<ReturnType<typeof restic.snapshots>>>(cacheKey);
|
||||
|
||||
if (!snapshot) {
|
||||
throw new NotFoundError("Snapshot not found");
|
||||
if (!snapshots) {
|
||||
const releaseLock = await repoMutex.acquireShared(repository.id, `snapshot_details:${snapshotId}`);
|
||||
try {
|
||||
snapshots = await restic.snapshots(repository.config);
|
||||
cache.set(cacheKey, snapshots);
|
||||
} finally {
|
||||
releaseLock();
|
||||
}
|
||||
|
||||
return snapshot;
|
||||
} finally {
|
||||
releaseLock();
|
||||
}
|
||||
|
||||
const snapshot = snapshots.find((snap) => snap.id === snapshotId || snap.short_id === snapshotId);
|
||||
|
||||
if (!snapshot) {
|
||||
throw new NotFoundError("Snapshot not found");
|
||||
}
|
||||
|
||||
return snapshot;
|
||||
};
|
||||
|
||||
const checkHealth = async (repositoryId: string) => {
|
||||
@@ -388,6 +420,8 @@ const deleteSnapshot = async (id: string, snapshotId: string) => {
|
||||
const releaseLock = await repoMutex.acquireExclusive(repository.id, `delete:${snapshotId}`);
|
||||
try {
|
||||
await restic.deleteSnapshot(repository.config, snapshotId);
|
||||
cache.delByPrefix(`snapshots:${repository.id}:`);
|
||||
cache.delByPrefix(`ls:${repository.id}:${snapshotId}:`);
|
||||
} finally {
|
||||
releaseLock();
|
||||
}
|
||||
@@ -403,6 +437,10 @@ const deleteSnapshots = async (id: string, snapshotIds: string[]) => {
|
||||
const releaseLock = await repoMutex.acquireExclusive(repository.id, `delete:bulk`);
|
||||
try {
|
||||
await restic.deleteSnapshots(repository.config, snapshotIds);
|
||||
cache.delByPrefix(`snapshots:${repository.id}:`);
|
||||
for (const snapshotId of snapshotIds) {
|
||||
cache.delByPrefix(`ls:${repository.id}:${snapshotId}:`);
|
||||
}
|
||||
} finally {
|
||||
releaseLock();
|
||||
}
|
||||
@@ -422,6 +460,10 @@ const tagSnapshots = async (
|
||||
const releaseLock = await repoMutex.acquireExclusive(repository.id, `tag:bulk`);
|
||||
try {
|
||||
await restic.tagSnapshots(repository.config, snapshotIds, tags);
|
||||
cache.delByPrefix(`snapshots:${repository.id}:`);
|
||||
for (const snapshotId of snapshotIds) {
|
||||
cache.delByPrefix(`ls:${repository.id}:${snapshotId}:`);
|
||||
}
|
||||
} finally {
|
||||
releaseLock();
|
||||
}
|
||||
|
||||
@@ -54,6 +54,11 @@ export const createCache = (options: CacheOptions = {}) => {
|
||||
stmt.run(key);
|
||||
};
|
||||
|
||||
const delByPrefix = (prefix: string) => {
|
||||
const stmt = db.prepare("DELETE FROM cache WHERE key LIKE ?");
|
||||
stmt.run(`${prefix}%`);
|
||||
};
|
||||
|
||||
const getByPrefix = <T>(prefix: string): { key: string; value: T }[] => {
|
||||
const stmt = db.prepare("SELECT key, value, expiration FROM cache WHERE key LIKE ?");
|
||||
const rows = stmt.all(`${prefix}%`) as { key: string; value: string; expiration: number }[];
|
||||
@@ -88,6 +93,7 @@ export const createCache = (options: CacheOptions = {}) => {
|
||||
set,
|
||||
get,
|
||||
del,
|
||||
delByPrefix,
|
||||
getByPrefix,
|
||||
clear,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user