Skip to content

fix: paginate Google incremental calendar sync - #94

Merged
wahve3 merged 1 commit into
Tymeslot:mainfrom
PixelJonas:fix/google-incremental-sync-pagination-upstream
Sep 9, 2026
Merged

fix: paginate Google incremental calendar sync#94
wahve3 merged 1 commit into
Tymeslot:mainfrom
PixelJonas:fix/google-incremental-sync-pagination-upstream

Conversation

@PixelJonas

Copy link
Copy Markdown
Contributor

What does this change?

Fixes a pagination bug in Tymeslot.Integrations.Calendar.Google.CalendarAPI.list_events_incremental/1 that caused Google Calendar incremental (delta) syncs to silently drop events and never advance the sync token when a delta response spanned more than one page.

Root cause: list_events_incremental/1 only fetched page 1 of a syncToken listing and ignored nextPageToken. Google's Calendar API only returns nextSyncToken (needed to advance the stored sync cursor) on the final page of a multi-page response. Whenever a delta spanned more than 250 events (Google's default page size), the persisted sync token was never advanced, and every subsequent periodic sync run re-fetched the exact same stale first page forever — silently dropping every changed/moved/deleted event beyond page 1.

Production evidence: a live integration had returned exactly 250 events on every 15-minute sync run for hours, with nextPageToken present and nextSyncToken absent on the raw API response. After the fix, a single sync fetched 4,465 events (the full accumulated backlog across ~18 pages) and the sync token advanced correctly.

Fix: mirrors the pagination loop that bootstrap_sync/1 in the same file already implements correctly — walks pageToken until exhausted, accumulates events, and only persists the nextSyncToken from the final page.

Tests: 2 new regression tests (single-page pass-through and multi-page accumulation with correct sync-token extraction from the final page only). Full test suite passes (19/19 in the affected test file, 12243/12246 full suite — 3 pre-existing DST-fuzzing/flaky failures confirmed unrelated).

Checklist

  • Commits are signed off with git commit -s (required — CI enforces the DCO; see CONTRIBUTING)
  • Tests pass locally (mix test), if relevant

@PixelJonas
PixelJonas force-pushed the fix/google-incremental-sync-pagination-upstream branch 2 times, most recently from 2e0e0ea to 1ffb3f8 Compare September 9, 2026 10:46
list_events_incremental/1 only fetched page 1 of a syncToken listing
and ignored nextPageToken. Google only returns nextSyncToken on the
final page, so whenever a delta spanned more than one page, the
persisted sync token was never advanced — every subsequent 15-minute
sync run re-fetched the exact same stale first page forever, silently
dropping every changed event beyond it.

Confirmed live: a production integration's incremental sync had
returned exactly 250 events (Google's default page size) on every run
for hours, with nextPageToken present and nextSyncToken absent on the
raw API response — the sync token had been stuck, never advancing.

Fix mirrors the pagination loop bootstrap_sync/1 already uses
correctly: walk pageToken until exhausted, accumulate events, and only
persist the nextSyncToken from the final page.

Assisted-by: Claude Code
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: PixelJonas <5434875+PixelJonas@users.noreply.github.com>
@PixelJonas
PixelJonas force-pushed the fix/google-incremental-sync-pagination-upstream branch from 1ffb3f8 to ccc1bd4 Compare September 9, 2026 11:16
@wahve3

wahve3 commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Confirmed and reproduced locally: reverting only the pagination loop makes the new multi-page test fail with next_sync_token: nil and page 1 events only, which is exactly the stuck-token behaviour you described. Verified the merged state against current main — full gate green in both repos, no regressions.

Thanks for the clear diagnosis and for chasing down the production evidence. Merging now.

Two small follow-ups I will take separately: fetch_incremental_page/5 and fetch_bootstrap_page/6 are now near-identical and should collapse into one paginator, and the incremental path should set maxResults the way bootstrap does.

@wahve3
wahve3 merged commit 72ab22c into Tymeslot:main Sep 9, 2026
4 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Sep 9, 2026
@PixelJonas
PixelJonas deleted the fix/google-incremental-sync-pagination-upstream branch September 9, 2026 19:03
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants