Rotate a database target's archive monthly, daily or hourly (closes #379)
check / check (push) Successful in 3m28s

A database target's rotation (none, monthly, daily or hourly) puts the
UTC period of each event's receive time in its archive file name, so
each file holds exactly its period's events. It is on the new webhook
page, the add target form and the target edit form, and shown in the
target list.

Renames move every one of a target's files and move them back if one
fails. The sweep prunes every file, one at a time under the target's
lock, and deletes a rotated file it leaves empty. Download lists the
files, then opens one at a time, oldest first, finding each again under
the target's current names, and gives each row its period. The target
list names the current file and totals the size of all of them.

Model: opus-5-5
This commit is contained in:
2026-10-03 02:06:19 +00:00
parent ea8384f4a2
commit ed344ecc66
36 changed files with 2125 additions and 406 deletions
+11 -1
View File
@@ -107,7 +107,8 @@ func TestPausedTarget_ShownUntilBreakerCloses(t *testing.T) {
assert.Contains(t, list, "t-http http Active Edit Deactivate Delete "+
"Deliveries Paused: held while one delivery tests whether the "+
"target has recovered")
assert.NotContains(t, list, "UTC")
// Not the whole list: the add target form above the rows says UTC.
assert.NotContains(t, targetRow(list, "t-http", "t-log"), "UTC")
assertRetryingNotWaiting(t, h, sess, wh.ID, retrying, backedOff)
@@ -119,6 +120,15 @@ func TestPausedTarget_ShownUntilBreakerCloses(t *testing.T) {
assertRetryingNotWaiting(t, h, sess, wh.ID, retrying, backedOff)
}
// targetRow returns the row of the target named name in a targetList:
// from its name to the name of the target listed after it, next.
func targetRow(list, name, next string) string {
_, row, _ := strings.Cut(list, name+" ")
row, _, _ = strings.Cut(row, next+" ")
return row
}
// assertRetryingNotWaiting checks that the event log and each event's
// page show the http target's delivery of the event as retrying, and
// none of them as waiting.