Skip to content

Commit a2d1d8f

Browse files
committed
fix(gmail): guard watch retry rollback
1 parent e5ee7cc commit a2d1d8f

4 files changed

Lines changed: 53 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## 0.23.1 - Unreleased
44

5+
### Added
6+
7+
- Gmail: add `gmail watch pull` for Pub/Sub pull subscription consumers with hook retry support. (#700) — thanks @joshp123.
8+
59
## 0.23.0 - 2026-06-09
610

711
### Added

docs/commands/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Every `gog` command has a generated docs page. The source of truth is the live CLI schema; run `make docs-commands` after changing command names, flags, help text, aliases, or arguments.
44

5-
Generated pages: 588.
5+
Generated pages: 589.
66

77
## Top-level Commands
88

internal/cmd/gmail_watch_pull.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ func (s *gmailWatchServer) processGmailWatchPayload(ctx context.Context, payload
299299
if err := s.sendHook(ctx, result); err != nil {
300300
s.warnf("watch: hook failed: %v", err)
301301
processed.HookFailed = true
302-
if restoreErr := s.restoreWatchProgressForRetry(progressBefore); restoreErr != nil {
302+
if restoreErr := s.restoreWatchProgressForRetry(progressBefore, result.HistoryID, payload.MessageID); restoreErr != nil {
303303
s.warnf("watch: failed to preserve retry state after hook failure: %v", restoreErr)
304304
}
305305
return processed, &gmailWatchHookDeliveryError{Err: err}
@@ -324,11 +324,17 @@ func (e *gmailWatchHookDeliveryError) Unwrap() error {
324324
return e.Err
325325
}
326326

327-
func (s *gmailWatchServer) restoreWatchProgressForRetry(before gmailWatchState) error {
327+
func (s *gmailWatchServer) restoreWatchProgressForRetry(before gmailWatchState, historyID, pushMessageID string) error {
328328
if s.store == nil {
329329
return nil
330330
}
331331
return s.store.Update(func(state *gmailWatchState) error {
332+
if state.HistoryID != historyID {
333+
return nil
334+
}
335+
if pushMessageID != "" && state.LastPushMessageID != pushMessageID {
336+
return nil
337+
}
332338
state.HistoryID = before.HistoryID
333339
state.LastPushMessageID = before.LastPushMessageID
334340
return nil

internal/cmd/gmail_watch_pull_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,46 @@ func TestGmailWatchPullMessage_NacksHookFailureAndPreservesProgress(t *testing.T
233233
}
234234
}
235235

236+
func TestGmailWatchRestoreProgressForRetrySkipsNewerProgress(t *testing.T) {
237+
setWatchTestConfigHome(t)
238+
239+
store, err := newGmailWatchStore("[email protected]")
240+
if err != nil {
241+
t.Fatalf("store: %v", err)
242+
}
243+
if updateErr := store.Update(func(s *gmailWatchState) error {
244+
*s = gmailWatchState{
245+
Account: "[email protected]",
246+
HistoryID: "100",
247+
LastPushMessageID: "msg-before",
248+
}
249+
return nil
250+
}); updateErr != nil {
251+
t.Fatalf("seed: %v", updateErr)
252+
}
253+
before := store.Get()
254+
if updateErr := store.Update(func(s *gmailWatchState) error {
255+
s.HistoryID = "300"
256+
s.LastPushMessageID = "msg-newer"
257+
return nil
258+
}); updateErr != nil {
259+
t.Fatalf("advance: %v", updateErr)
260+
}
261+
262+
server := &gmailWatchServer{store: store}
263+
if err := server.restoreWatchProgressForRetry(before, "200", "msg-failed"); err != nil {
264+
t.Fatalf("restore: %v", err)
265+
}
266+
267+
state := store.Get()
268+
if state.HistoryID != "300" {
269+
t.Fatalf("history id = %q", state.HistoryID)
270+
}
271+
if state.LastPushMessageID != "msg-newer" {
272+
t.Fatalf("last push message id = %q", state.LastPushMessageID)
273+
}
274+
}
275+
236276
func TestGmailWatchPullMessage_RetriesHookFailureThenAcksSuccess(t *testing.T) {
237277
server, hook, cleanup := newPullProcessorTestServer(t, http.StatusOK)
238278
defer cleanup()

0 commit comments

Comments
 (0)