-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathdlq.go
More file actions
96 lines (84 loc) · 2.38 KB
/
Copy pathdlq.go
File metadata and controls
96 lines (84 loc) · 2.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
package GoEventBus
import (
"context"
"sync"
"time"
)
// DeadLetter holds a failed event and the reason it could not be processed.
type DeadLetter struct {
Event Event
Err error // handler error, or a wrapped panic value
FailedAt time.Time
Attempts int // 1 on first failure; incremented on each Replay call
}
// DeadLetterQueue is a thread-safe store for events that failed during dispatch.
// Attach one to an EventStore via store.DLQ to enable dead-letter routing.
//
// store := GoEventBus.NewEventStore(&disp, 1<<16, GoEventBus.DropOldest)
// store.DLQ = GoEventBus.NewDeadLetterQueue()
type DeadLetterQueue struct {
mu sync.Mutex
entries []DeadLetter
}
// NewDeadLetterQueue returns an empty DeadLetterQueue ready for use.
func NewDeadLetterQueue() *DeadLetterQueue {
return &DeadLetterQueue{}
}
func (q *DeadLetterQueue) add(dl DeadLetter) {
q.mu.Lock()
q.entries = append(q.entries, dl)
q.mu.Unlock()
}
// Len returns the number of dead letters currently in the queue.
func (q *DeadLetterQueue) Len() int {
q.mu.Lock()
defer q.mu.Unlock()
return len(q.entries)
}
// Entries returns a snapshot copy of all dead letters without removing them.
func (q *DeadLetterQueue) Entries() []DeadLetter {
q.mu.Lock()
defer q.mu.Unlock()
out := make([]DeadLetter, len(q.entries))
copy(out, q.entries)
return out
}
// Drain removes and returns all dead letters, leaving the queue empty.
func (q *DeadLetterQueue) Drain() []DeadLetter {
q.mu.Lock()
defer q.mu.Unlock()
out := q.entries
q.entries = nil
return out
}
// Replay re-enqueues all dead letters into store for reprocessing and calls
// store.Publish() once after. Each entry has its Attempts incremented before
// re-subscribing. Entries that fail to re-enqueue (e.g. buffer full) are kept
// in the queue. Returns the first Subscribe error encountered, or nil.
func (q *DeadLetterQueue) Replay(ctx context.Context, store *EventStore) error {
q.mu.Lock()
entries := q.entries
q.entries = nil
q.mu.Unlock()
var failed []DeadLetter
var firstErr error
for _, dl := range entries {
dl.Attempts++
if err := store.Subscribe(ctx, dl.Event); err != nil {
if firstErr == nil {
firstErr = err
}
dl.Err = err
failed = append(failed, dl)
}
}
if len(failed) > 0 {
q.mu.Lock()
q.entries = append(failed, q.entries...)
q.mu.Unlock()
}
if firstErr == nil {
store.Publish()
}
return firstErr
}