Skip to content

Commit 3174e5d

Browse files
Merge pull request #1879 from target/slack-oncall-multichan
engine/schedulemanager: fix sending messages to multiple Slack channels at the same time
2 parents e742d6d + cece534 commit 3174e5d

File tree

4 files changed

+34
-18
lines changed

4 files changed

+34
-18
lines changed

engine/schedulemanager/update.go

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"database/sql"
66
"encoding/json"
77
"fmt"
8+
"sort"
89
"time"
910

1011
"github.com/google/uuid"
@@ -234,7 +235,7 @@ func (db *DB) update(ctx context.Context) error {
234235
}
235236

236237
// Notify changed schedules
237-
needsOnCallNotification := make(map[string]uuid.UUID)
238+
needsOnCallNotification := make(map[string][]uuid.UUID)
238239
for schedID := range changedSchedules {
239240
data := scheduleData[schedID]
240241
if data == nil {
@@ -245,15 +246,15 @@ func (db *DB) update(ctx context.Context) error {
245246
continue
246247
}
247248

248-
needsOnCallNotification[schedID] = r.ChannelID
249+
needsOnCallNotification[schedID] = append(needsOnCallNotification[schedID], r.ChannelID)
249250
}
250251
}
251252

252253
for schedID, data := range scheduleData {
253254
var hadChange bool
254255
for i, r := range data.V1.OnCallNotificationRules {
255256
if r.NextNotification != nil && !r.NextNotification.After(now) {
256-
needsOnCallNotification[schedID] = r.ChannelID
257+
needsOnCallNotification[schedID] = append(needsOnCallNotification[schedID], r.ChannelID)
257258
}
258259

259260
newTime := nextOnCallNotification(now.In(tz[schedID]), r)
@@ -277,10 +278,18 @@ func (db *DB) update(ctx context.Context) error {
277278
}
278279
}
279280

280-
for schedID, chanID := range needsOnCallNotification {
281-
_, err = tx.StmtContext(ctx, db.scheduleOnCallNotification).ExecContext(ctx, uuid.New(), chanID, schedID)
282-
if err != nil {
283-
return err
281+
for schedID, chanIDs := range needsOnCallNotification {
282+
sort.Slice(chanIDs, func(i, j int) bool { return chanIDs[i].String() < chanIDs[j].String() })
283+
var lastID uuid.UUID
284+
for _, chanID := range chanIDs {
285+
if chanID == lastID {
286+
continue
287+
}
288+
lastID = chanID
289+
_, err = tx.StmtContext(ctx, db.scheduleOnCallNotification).ExecContext(ctx, uuid.New(), chanID, schedID)
290+
if err != nil {
291+
return err
292+
}
284293
}
285294
}
286295

smoketest/harness/slack.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ type SlackChannel interface {
2424
type slackServer struct {
2525
h *Harness
2626
*mockslack.Server
27-
channels map[string]*slackChannel
27+
hasFailure bool
28+
channels map[string]*slackChannel
2829
}
2930

3031
type slackChannel struct {
@@ -47,7 +48,7 @@ func (s *slackServer) WaitAndAssert() {
4748
s.h.Trigger()
4849
var hasFailure bool
4950
for _, ch := range s.channels {
50-
hasFailure = hasFailure || ch.hasUnexpectedMessages()
51+
hasFailure = s.hasFailure || hasFailure || ch.hasUnexpectedMessages()
5152
}
5253

5354
if hasFailure {
@@ -95,6 +96,7 @@ func (ch *slackChannel) ExpectMessage(keywords ...string) {
9596

9697
select {
9798
case <-timeout.C:
99+
ch.h.slack.hasFailure = true
98100
ch.h.t.Fatalf("timeout waiting for slack message: Channel=%s; ID=%s; keywords=%v\nGot: %#v", ch.name, ch.id, keywords, ch.h.slack.Messages(ch.id))
99101
default:
100102
}

smoketest/oncallnotify_test.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,18 @@ func TestOnCallNotify(t *testing.T) {
3434
3535
insert into notification_channels (id, type, name, value)
3636
values
37-
({{uuid "chan"}}, 'SLACK', '#test', {{slackChannelID "test"}});
38-
37+
({{uuid "chan1"}}, 'SLACK', '#test1', {{slackChannelID "test1"}}),
38+
({{uuid "chan2"}}, 'SLACK', '#test2', {{slackChannelID "test2"}});
39+
3940
insert into schedule_data (schedule_id, data)
4041
values
41-
({{uuid "sid"}}, '{"V1":{"OnCallNotificationRules": [{"ChannelID": {{uuidJSON "chan"}} }]}}');
42+
({{uuid "sid"}}, '{"V1":{"OnCallNotificationRules": [{"ChannelID": {{uuidJSON "chan1"}} }, {"ChannelID": {{uuidJSON "chan2"}} }]}}');
4243
`
4344
h := harness.NewHarness(t, sql, "outgoing-messages-schedule-id")
4445
defer h.Close()
4546

46-
h.Slack().Channel("test").ExpectMessage("on-call", "testschedule", "bob")
47+
h.Slack().Channel("test1").ExpectMessage("on-call", "testschedule", "bob")
48+
h.Slack().Channel("test2").ExpectMessage("on-call", "testschedule", "bob")
4749

4850
h.FastForward(time.Hour)
4951

@@ -56,5 +58,6 @@ func TestOnCallNotify(t *testing.T) {
5658
})
5759
require.NoError(t, err)
5860

59-
h.Slack().Channel("test").ExpectMessage("on-call", "testschedule", "bob", "joe")
61+
h.Slack().Channel("test1").ExpectMessage("on-call", "testschedule", "bob", "joe")
62+
h.Slack().Channel("test2").ExpectMessage("on-call", "testschedule", "bob", "joe")
6063
}

smoketest/oncallnotifytod_test.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,12 @@ func TestOnCallNotifyTOD(t *testing.T) {
2626
2727
insert into notification_channels (id, type, name, value)
2828
values
29-
({{uuid "chan"}}, 'SLACK', '#test', {{slackChannelID "test"}});
29+
({{uuid "chan1"}}, 'SLACK', '#test1', {{slackChannelID "test1"}}),
30+
({{uuid "chan2"}}, 'SLACK', '#test2', {{slackChannelID "test2"}});
3031
3132
insert into schedule_data (schedule_id, data)
3233
values
33-
({{uuid "sid"}}, '{"V1":{"OnCallNotificationRules": [{"ChannelID": {{uuidJSON "chan"}}, "Time": "00:00" }]}}');
34+
({{uuid "sid"}}, '{"V1":{"OnCallNotificationRules": [{"ChannelID": {{uuidJSON "chan1"}}, "Time": "00:00" },{"ChannelID": {{uuidJSON "chan1"}}, "Time": "01:00" },{"ChannelID": {{uuidJSON "chan2"}}, "Time": "00:00" }]}}');
3435
`
3536
h := harness.NewHarness(t, sql, "outgoing-messages-schedule-id")
3637
defer h.Close()
@@ -39,6 +40,7 @@ func TestOnCallNotifyTOD(t *testing.T) {
3940

4041
h.FastForward(24 * time.Hour)
4142

42-
h.Slack().Channel("test").ExpectMessage("on-call", "testschedule", "bob")
43-
43+
// should only send 1 message to each channel
44+
h.Slack().Channel("test1").ExpectMessage("on-call", "testschedule", "bob")
45+
h.Slack().Channel("test2").ExpectMessage("on-call", "testschedule", "bob")
4446
}

0 commit comments

Comments
 (0)