Skip to content

feat: implement thin vertical slice of system-generated notifications #13537

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 30 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
53c9cbb
feat: system-generated notifications
dannykopping Jun 11, 2024
4856aed
Fixing lint errors & minor tests
dannykopping Jun 11, 2024
cda6efb
Fixing dbauthz test
dannykopping Jun 11, 2024
86f937a
TestBufferedUpdates does not need a real db, altering test details sl…
dannykopping Jun 11, 2024
e8f1af2
Correct TestBufferedUpdates to count updated entries, use real db again
dannykopping Jun 12, 2024
a056f54
Use UUID for notifier IDs
dannykopping Jun 27, 2024
8c64d30
Small improvements from review suggestions
dannykopping Jun 27, 2024
ac149ec
Protect notifiers from modification during Stop()
dannykopping Jun 27, 2024
884fadf
Split out enqueuer as separate responsibility, get rid of singleton
dannykopping Jun 28, 2024
4e362e7
Remove unnecessary handler registry
dannykopping Jun 28, 2024
8097290
Remove unused context
dannykopping Jun 28, 2024
1b841ad
Centralise markdown rendering
dannykopping Jun 28, 2024
61f5bd6
Appease the linter
dannykopping Jun 28, 2024
3c8e33b
Only enqueue notification when not initiated by self
dannykopping Jul 1, 2024
757327c
Hide config flags which are unlikely to be modified by operators
dannykopping Jul 1, 2024
6f909ae
Remove unnecessary Labels struct
dannykopping Jul 1, 2024
36698c5
Enable experiment as safe
dannykopping Jul 1, 2024
c5701a6
Correcting bad refactor
dannykopping Jul 1, 2024
9d4c312
Initialize Enqueuer on API startup
dannykopping Jul 1, 2024
9380d8e
Only start one notifier since all dispatches are concurrent anyway
dannykopping Jul 1, 2024
4b7214d
Fix docs
dannykopping Jul 1, 2024
6679ef1
Fix lint error
dannykopping Jul 1, 2024
337997d
Merge branch 'main' of github.com:/coder/coder into dk/system-notific…
dannykopping Jul 2, 2024
ba5f7c6
Merge branch 'main' of github.com:/coder/coder into dk/system-notific…
dannykopping Jul 3, 2024
0f29293
Review feedback
dannykopping Jul 3, 2024
7c6c486
Merge branch 'main' of github.com:/coder/coder into dk/system-notific…
dannykopping Jul 4, 2024
c6e75c2
Fix lint failures
dannykopping Jul 4, 2024
aff9e6c
Review comments
dannykopping Jul 4, 2024
613e074
Avoid race by exposing number of pending updates
dannykopping Jul 4, 2024
faea7fc
Merge branch 'main' of github.com:/coder/coder into dk/system-notific…
dannykopping Jul 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Use UUID for notifier IDs
Fix test which uses coalesced fullname/username and was failing

Signed-off-by: Danny Kopping <danny@coder.com>
  • Loading branch information
dannykopping committed Jun 28, 2024
commit a056f546ad3bbde7aeb97a730a3b1f2f95adc97f
8 changes: 4 additions & 4 deletions coderd/notifications/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ func (m *Manager) loop(ctx context.Context, notifiers int) error {
for i := 0; i < notifiers; i++ {
eg.Go(func() error {
m.notifierMu.Lock()
n := newNotifier(ctx, m.cfg, i+1, m.log, m.store, m.handlers)
n := newNotifier(ctx, m.cfg, uuid.New(), m.log, m.store, m.handlers)
m.notifiers = append(m.notifiers, n)
m.notifierMu.Unlock()
return n.run(ctx, success, failure)
Expand Down Expand Up @@ -424,22 +424,22 @@ func (m *Manager) Stop(ctx context.Context) error {
}

type dispatchResult struct {
notifier int
notifier uuid.UUID
msg uuid.UUID
ts time.Time
err error
retryable bool
}

func newSuccessfulDispatch(notifier int, msg uuid.UUID) dispatchResult {
func newSuccessfulDispatch(notifier, msg uuid.UUID) dispatchResult {
return dispatchResult{
notifier: notifier,
msg: msg,
ts: time.Now(),
}
}

func newFailedDispatch(notifier int, msg uuid.UUID, err error, retryable bool) dispatchResult {
func newFailedDispatch(notifier, msg uuid.UUID, err error, retryable bool) dispatchResult {
return dispatchResult{
notifier: notifier,
msg: msg,
Expand Down
4 changes: 3 additions & 1 deletion coderd/notifications/notifications_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,8 @@ func TestWebhookDispatch(t *testing.T) {
require.Equal(t, *msgID, payload.MsgID)
require.Equal(t, payload.Payload.Labels, input)
require.Equal(t, payload.Payload.UserEmail, "bob@coder.com")
require.Equal(t, payload.Payload.UserName, "bob")
// UserName is coalesced from `name` and `username`; in this case `name` wins.
require.Equal(t, payload.Payload.UserName, "Robert McBobbington")
require.Equal(t, payload.Payload.NotificationName, "Workspace Deleted")

w.WriteHeader(http.StatusOK)
Expand Down Expand Up @@ -203,6 +204,7 @@ func TestWebhookDispatch(t *testing.T) {
_, user := coderdtest.CreateAnotherUserMutators(t, client, first.OrganizationID, nil, func(r *codersdk.CreateUserRequest) {
r.Email = "bob@coder.com"
r.Username = "bob"
r.Name = "Robert McBobbington"
})

// when
Expand Down
11 changes: 6 additions & 5 deletions coderd/notifications/notifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"sync"
"time"

"github.com/google/uuid"
"golang.org/x/sync/errgroup"
"golang.org/x/xerrors"

Expand All @@ -21,7 +22,7 @@ import (
// notifier is a consumer of the notifications_messages queue. It dequeues messages from that table and processes them
// through a pipeline of fetch -> prepare -> render -> acquire handler -> deliver.
type notifier struct {
id int
id uuid.UUID
cfg codersdk.NotificationsConfig
ctx context.Context
log slog.Logger
Expand All @@ -35,7 +36,7 @@ type notifier struct {
handlers *HandlerRegistry
}

func newNotifier(ctx context.Context, cfg codersdk.NotificationsConfig, id int, log slog.Logger, db Store, hr *HandlerRegistry) *notifier {
func newNotifier(ctx context.Context, cfg codersdk.NotificationsConfig, id uuid.UUID, log slog.Logger, db Store, hr *HandlerRegistry) *notifier {
return &notifier{
id: id,
ctx: ctx,
Expand Down Expand Up @@ -63,7 +64,7 @@ func (n *notifier) run(ctx context.Context, success chan<- dispatchResult, failu
for {
select {
case <-ctx.Done():
return xerrors.Errorf("notifier %d context canceled: %w", n.id, ctx.Err())
return xerrors.Errorf("notifier %q context canceled: %w", n.id, ctx.Err())
case <-n.quit:
return nil
default:
Expand All @@ -78,7 +79,7 @@ func (n *notifier) run(ctx context.Context, success chan<- dispatchResult, failu
// Shortcut to bail out quickly if stop() has been called or the context canceled.
select {
case <-ctx.Done():
return xerrors.Errorf("notifier %d context canceled: %w", n.id, ctx.Err())
return xerrors.Errorf("notifier %q context canceled: %w", n.id, ctx.Err())
case <-n.quit:
return nil
case <-n.tick.C:
Expand Down Expand Up @@ -137,7 +138,7 @@ func (n *notifier) fetch(ctx context.Context) ([]database.AcquireNotificationMes
msgs, err := n.store.AcquireNotificationMessages(ctx, database.AcquireNotificationMessagesParams{
Count: int32(n.cfg.LeaseCount),
MaxAttemptCount: int32(n.cfg.MaxSendAttempts),
NotifierID: int32(n.id),
NotifierID: n.id,
LeaseSeconds: int32(n.cfg.LeasePeriod.Value().Seconds()),
})
if err != nil {
Expand Down