retention: compose the audit detail in the store with the real body count
RetainWithAudit now takes only the actor; the store builds the detail after body clearing so bodies_cleared is a real number, and still writes the row before the cap. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -346,9 +346,7 @@ func runRetain(cfg *config.Config, logger *slog.Logger) error {
|
|||||||
}
|
}
|
||||||
// The audit row is written inside the same transaction, before the cap, so
|
// The audit row is written inside the same transaction, before the cap, so
|
||||||
// the cap counts it. This is what keeps actions_log bounded across runs.
|
// the cap counts it. This is what keeps actions_log bounded across runs.
|
||||||
detail := fmt.Sprintf("bodies_cleared=%%d retention_days=%d max_rows=%d",
|
res, err := st.RetainWithAudit(ctx, policy, "scheduler")
|
||||||
cfg.RetentionDays, cfg.ActionsLogMaxRows)
|
|
||||||
res, err := st.RetainWithAudit(ctx, policy, "scheduler", detail)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,14 +49,19 @@ type RetentionResult struct {
|
|||||||
// pass never leaves the database in a mixed state. It is safe to run on a
|
// pass never leaves the database in a mixed state. It is safe to run on a
|
||||||
// schedule: every statement is idempotent.
|
// schedule: every statement is idempotent.
|
||||||
func (s *Store) Retain(ctx context.Context, p RetentionPolicy) (RetentionResult, error) {
|
func (s *Store) Retain(ctx context.Context, p RetentionPolicy) (RetentionResult, error) {
|
||||||
return s.RetainWithAudit(ctx, p, "", "")
|
return s.retain(ctx, p, false, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// RetainWithAudit is Retain plus one actions_log row (FR-13), appended inside
|
// RetainWithAudit is Retain plus one 'retain' row appended to actions_log
|
||||||
// the same transaction before the cap is applied so the audit row is itself
|
// (FR-13). The row is written inside the same transaction before the cap is
|
||||||
// counted by the cap. An empty detail writes no audit row. This keeps the
|
// applied, so the cap counts it and the log stays bounded even though every
|
||||||
// action log bounded even though every run records that it ran.
|
// run records that it ran.
|
||||||
func (s *Store) RetainWithAudit(ctx context.Context, p RetentionPolicy, actor, detail string) (RetentionResult, error) {
|
func (s *Store) RetainWithAudit(ctx context.Context, p RetentionPolicy, actor string) (RetentionResult, error) {
|
||||||
|
return s.retain(ctx, p, true, actor)
|
||||||
|
}
|
||||||
|
|
||||||
|
// retain is the shared implementation. audit=false writes no audit row.
|
||||||
|
func (s *Store) retain(ctx context.Context, p RetentionPolicy, audit bool, actor string) (RetentionResult, error) {
|
||||||
var res RetentionResult
|
var res RetentionResult
|
||||||
|
|
||||||
tx, err := s.db.BeginTx(ctx, nil)
|
tx, err := s.db.BeginTx(ctx, nil)
|
||||||
@@ -82,7 +87,9 @@ WHERE body_text IS NOT NULL AND body_text <> '' AND received_at < ?`,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if detail != "" {
|
if audit {
|
||||||
|
detail := fmt.Sprintf("retention_days=%d max_rows=%d bodies_cleared=%d",
|
||||||
|
int(p.BodyMaxAge.Hours()/24), p.ActionsLogMaxRows, res.BodiesCleared)
|
||||||
if _, err := tx.ExecContext(ctx, `
|
if _, err := tx.ExecContext(ctx, `
|
||||||
INSERT INTO actions_log (message_id, action, actor, detail)
|
INSERT INTO actions_log (message_id, action, actor, detail)
|
||||||
VALUES (NULL, 'retain', ?, ?)`, nullString(actor), detail); err != nil {
|
VALUES (NULL, 'retain', ?, ?)`, nullString(actor), detail); err != nil {
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ func TestRetainWithAuditCountsItsOwnRow(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := s.RetainWithAudit(ctx, RetentionPolicy{ActionsLogMaxRows: 3}, "scheduler", "bodies_cleared=0")
|
res, err := s.RetainWithAudit(ctx, RetentionPolicy{ActionsLogMaxRows: 3}, "scheduler")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("RetainWithAudit() error = %v", err)
|
t.Fatalf("RetainWithAudit() error = %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user