Name each database target's archive for its webhook and target (closes #376)
check / check (push) Successful in 3m16s

Each database target now has its own archive file,
archive-WEBHOOKNAME-TARGETNAME-TARGETID.db, instead of one
archive-WEBHOOKID.db per webhook. delivery.ArchiveFileName builds the
name.

A change of webhook or target name renames its archive files under the
archive writer's lock, before the new name is saved, and back again if
the save fails. Webhook edits, target edits and target creation run one
at a time, so no two of them interleave. A rename never replaces a
file, and one that fails part way moves back what it moved. Deleting a
target evicts only that target's writer. Archive files are never
deleted, and nothing looks for files under the old name.

Model: opus-5-5
This commit is contained in:
2026-10-02 10:47:27 +00:00
committed by sneak
parent b14b27b78b
commit 71762d72d9
21 changed files with 1825 additions and 663 deletions
+12 -3
View File
@@ -10,6 +10,7 @@ import (
"html/template"
"log/slog"
"net/http"
"sync"
"sync/atomic"
"github.com/prometheus/client_golang/prometheus"
@@ -65,7 +66,7 @@ type HandlersParams struct {
Session *session.Session
Middleware *middleware.Middleware
Notifier delivery.Notifier
Evictor delivery.WebhookEvictor
Archives delivery.Archives
SSRFGuard *delivery.Guard
Metrics *metrics.Set
Registry *prometheus.Registry
@@ -82,7 +83,7 @@ type Handlers struct {
session *session.Session
mw *middleware.Middleware
notifier delivery.Notifier
evictor delivery.WebhookEvictor
archives delivery.Archives
mtr *metrics.Set
templates map[string]*template.Template
@@ -91,6 +92,14 @@ type Handlers struct {
// is one delivery will actually attempt.
ssrf *delivery.Guard
// renameMu makes the webhook edit, the target edit and target
// creation run one at a time, each held from loading the stored
// names through the archive rename, the save and any move back.
// Interleaved, one could rename an archive between another's
// rename and save, leaving the file named for one edit and the
// stored names from the other.
renameMu sync.Mutex
// dummyVerifications counts the equivalent-cost verifications
// charged for usernames that do not exist. It exists so a test
// can prove that path runs without measuring wall-clock time.
@@ -134,7 +143,7 @@ func New(
s.session = params.Session
s.mw = params.Middleware
s.notifier = params.Notifier
s.evictor = params.Evictor
s.archives = params.Archives
s.mtr = params.Metrics
s.ssrf = params.SSRFGuard