Skip to content

Commit

Permalink
A small fix to avoid deadlock that can happen as mentioned in issue p…
Browse files Browse the repository at this point in the history
…rometheus#3682

Signed-off-by: Anand Rajagopal <[email protected]>
  • Loading branch information
rajagopalanand committed Feb 8, 2024
1 parent d4deb6d commit 0db2e83
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 4 deletions.
2 changes: 1 addition & 1 deletion provider/mem/mem.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,9 @@ func max(a, b int) int {
// resolved and successfully notified about.
// They are not guaranteed to be in chronological order.
func (a *Alerts) Subscribe() provider.AlertIterator {

a.mtx.Lock()
defer a.mtx.Unlock()

var (
done = make(chan struct{})
alerts = a.alerts.List()
Expand Down
57 changes: 57 additions & 0 deletions provider/mem/mem_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,63 @@ func TestAlertsSubscribePutStarvation(t *testing.T) {
}
}

func TestDeadLock(t *testing.T) {
t0 := time.Now()
t1 := t0.Add(5 * time.Second)

marker := types.NewMarker(prometheus.NewRegistry())
// Run gc every 5 milliseconds to increase the possibility of a deadlock with Subscribe()
alerts, err := NewAlerts(context.Background(), marker, 5*time.Millisecond, noopCallback{}, log.NewNopLogger(), nil)
if err != nil {
t.Fatal(err)
}
alertsToInsert := []*types.Alert{}
for i := 0; i < 200+1; i++ {
alertsToInsert = append(alertsToInsert, &types.Alert{
Alert: model.Alert{
// Make sure the fingerprints differ
Labels: model.LabelSet{"iteration": model.LabelValue(strconv.Itoa(i))},
Annotations: model.LabelSet{"foo": "bar"},
StartsAt: t0,
EndsAt: t1,
GeneratorURL: "http://example.com/prometheus",
},
UpdatedAt: t0,
Timeout: false,
})
}

if err := alerts.Put(alertsToInsert...); err != nil {
t.Fatal("Unable to add alerts")
}
done := make(chan bool)

// call subscribe repeatedly in a goroutine to increase
// the possibility of a deadlock occurring
go func() {
tick := time.NewTicker(10 * time.Millisecond)
defer tick.Stop()
stopAfter := time.After(1 * time.Second)
for {
select {
case <-tick.C:
alerts.Subscribe()
case <-stopAfter:
done <- true
break
}
}
}()

select {
case <-done:
// no deadlock
alerts.Close()
case <-time.After(10 * time.Second):
t.Error("Deadlock detected")
}
}

func TestAlertsPut(t *testing.T) {
marker := types.NewMarker(prometheus.NewRegistry())
alerts, err := NewAlerts(context.Background(), marker, 30*time.Minute, noopCallback{}, log.NewNopLogger(), nil)
Expand Down
6 changes: 3 additions & 3 deletions store/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,16 +71,16 @@ func (a *Alerts) Run(ctx context.Context, interval time.Duration) {

func (a *Alerts) gc() {
a.Lock()
defer a.Unlock()

var resolved []*types.Alert
defer a.cb(resolved)
defer a.Unlock()
for fp, alert := range a.c {
if alert.Resolved() {
delete(a.c, fp)
resolved = append(resolved, alert)
}
}
a.cb(resolved)

}

// Get returns the Alert with the matching fingerprint, or an error if it is
Expand Down

0 comments on commit 0db2e83

Please sign in to comment.