Skip to content

Commit 8a45309

Browse files
committed
fix(oplog-collections-transformer): error on empty ALL_SITE_IDS instead of silent drop
publishUserStatus ranged ALL_SITE_IDS and, when empty/misconfigured, emitted nothing and returned nil — silently dropping every statusText change. Return a transient error when no destination resolves so the event Naks and stays visible rather than being dropped. A startup hard-fail will replace the per-event error later. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012X9qhQT4NwmCHjdndwNtFD
1 parent b34c407 commit 8a45309

2 files changed

Lines changed: 29 additions & 0 deletions

File tree

data-migration/oplog-collections-transformer/users.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ func (h *handler) publishUserStatus(ctx context.Context, account, statusText str
114114
StatusText: statusText,
115115
Timestamp: now,
116116
})
117+
sent := 0
117118
for _, dest := range h.allSiteIDs {
118119
if dest == "" {
119120
continue
@@ -128,6 +129,13 @@ func (h *handler) publishUserStatus(ctx context.Context, account, statusText str
128129
if err := h.pub.Publish(ctx, evt); err != nil {
129130
return fmt.Errorf("publish user_status_updated to %q: %w", dest, err)
130131
}
132+
sent++
133+
}
134+
if sent == 0 {
135+
// No destination sites: a statusText change would be lost. Surface a (transient) error
136+
// so the event Naks and is visible rather than silently dropped — ALL_SITE_IDS is empty
137+
// or misconfigured. (Future: hard-fail at startup instead of per-event.)
138+
return fmt.Errorf("no destination sites for user_status_updated (account %q): ALL_SITE_IDS empty", account)
131139
}
132140
return nil
133141
}

data-migration/oplog-collections-transformer/users_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,27 @@ func TestHandleUser_StatusTextUpdate_FansToAllSites(t *testing.T) {
110110
assert.ElementsMatch(t, []string{testSiteID, "s2"}, dests)
111111
}
112112

113+
func TestHandleUser_StatusTextUpdate_NoSites_ReturnsError(t *testing.T) {
114+
pub := &fakePublisher{}
115+
doc := `{"_id":"u1","username":"alice","statusText":"in a meeting"}`
116+
h := newTestHandler(pub, &fakeTarget{}, &fakeLookup{doc: []byte(doc)})
117+
h.allSiteIDs = nil // misconfigured: ALL_SITE_IDS empty
118+
119+
ev := oplogEvent{
120+
Op: "update",
121+
Collection: usersColl,
122+
EventID: "e1",
123+
DocumentKey: json.RawMessage(`{"_id":"u1"}`),
124+
UpdateDescription: json.RawMessage(`{"updatedFields":{"statusText":"in a meeting"}}`),
125+
}
126+
err := h.handleUser(context.Background(), ev)
127+
// Empty destinations must surface an error (Nak/visible), never a silent skip or drop.
128+
require.Error(t, err)
129+
assert.NotErrorIs(t, err, migration.ErrSkipped)
130+
assert.NotErrorIs(t, err, migration.ErrPoison)
131+
assert.Empty(t, pub.events)
132+
}
133+
113134
func TestHandleUser_NonStatusUpdate_NoFanout(t *testing.T) {
114135
pub := &fakePublisher{}
115136
doc := `{"_id":"u1","username":"alice","customFields":{"deptName":"NewDept"}}`

0 commit comments

Comments
 (0)