Raw
 1package storage
 2
 3import (
 4	"context"
 5	"fmt"
 6	"time"
 7)
 8
 9const maxAuditPruneBatch = 100000
10
11var afterAuditPruneBatch = func(int64) {}
12
13func (s *Store) PruneAudit(ctx context.Context, before time.Time, batch int, pause time.Duration) (int64, error) {
14	const operation = "prune audit"
15	if err := s.checkOpen(operation); err != nil {
16		return 0, err
17	}
18	if batch < 1 || batch > maxAuditPruneBatch {
19		return 0, invalid(operation, fmt.Errorf("batch must be between 1 and %d", maxAuditPruneBatch))
20	}
21	var total int64
22	for {
23		n, err := s.pruneAuditBatch(ctx, timeToInt(before), batch)
24		total += n
25		if err != nil {
26			return total, err
27		}
28		afterAuditPruneBatch(n)
29		if n < int64(batch) {
30			return total, nil
31		}
32		if pause > 0 {
33			t := time.NewTimer(pause)
34			select {
35			case <-ctx.Done():
36				t.Stop()
37				return total, ctx.Err()
38			case <-t.C:
39			}
40		} else if err := ctx.Err(); err != nil {
41			return total, err
42		}
43	}
44}
45
46func (s *Store) pruneAuditBatch(ctx context.Context, before int64, batch int) (int64, error) {
47	const operation = "prune audit batch"
48	tx, err := s.sql.BeginTx(ctx, nil)
49	if err != nil {
50		return 0, classify("begin "+operation, err)
51	}
52	defer tx.Rollback()
53	result, err := tx.ExecContext(ctx, `
54DELETE FROM audit_log WHERE id IN (
55    SELECT id FROM audit_log WHERE occurred_at < ? ORDER BY occurred_at, id LIMIT ?
56)`, before, batch)
57	if err != nil {
58		return 0, classify(operation, err)
59	}
60	n, err := result.RowsAffected()
61	if err != nil {
62		return 0, classify(operation, err)
63	}
64	if err := tx.Commit(); err != nil {
65		return 0, classify("commit "+operation, err)
66	}
67	return n, nil
68}