server: activator replication gets its own litestream meta directory (#729)
litestream derives .<db>-litestream beside the DB; the box-wide backup replicator uses the same path over the same file, and sharing corrupts LTX state even sequentially — the fleet flip left a dozen hosts in an error loop until meta dirs were hand-cleared. SetMetaPath gives the activator a distinct .activator-<db>-litestream namespace; the class dies.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 file changed,
+9
−0
internal/activator/replica.go+9 −0
diff --git a/internal/activator/replica.go b/internal/activator/replica.go| index cf4a1d8..29d2737 100644 |
| --- a/internal/activator/replica.go |
| +++ b/internal/activator/replica.go |
| @@ -13,6 +13,7 @@ import ( |
| "context" |
| "errors" |
| "fmt" |
| + "path/filepath" |
| "sync" |
| "time" |
| @@ -156,6 +157,14 @@ func (h *HeldLease) Replicate(ctx context.Context, dbPath string) (*LeasedReplic |
| } |
| db := litestream.NewDB(dbPath) |
| + // The activator's replication state lives in its OWN meta directory — |
| + // litestream derives ".<db>-litestream" beside the DB by default, which any |
| + // other litestream process over the same file (the box-wide backup |
| + // replicator, before or after a handover) also uses. Sharing corrupts LTX |
| + // state even SEQUENTIALLY: the fleet-wide flip left a dozen hosts in an |
| + // error loop until their meta dirs were cleared by hand. A distinct |
| + // namespace ends the class. |
| + db.SetMetaPath(filepath.Join(filepath.Dir(dbPath), ".activator-"+filepath.Base(dbPath)+"-litestream")) |
| client := lss3.NewReplicaClient() |
| client.Bucket = h.leaser.Bucket |
| client.Path = h.leaser.Path |