Skip to content
Back to posts
On this page
~/posts/google

Google Calendar Sync Strategies

Full sync vs incremental sync patterns and calendar segregation logic.

Sync Parity Principle

App must show EXACTLY what Google Calendar web app shows - no more, no less.

Users only understand: “Google shows X” → “App should show X”. Any difference = bug.

Sync Modes

Full Sync (No Token)

When: First sync, or after 410 GONE error

const params = {
  showDeleted: true, // See deleted calendars
  showHidden: true, // Catch hidden primary calendars
  maxResults: 250,
};

Behavior:

  • Returns ALL calendars for account
  • Orphan detection enabled (CASE #3)
  • Primary calendar should ALWAYS be present

Incremental Sync (With Token)

When: syncToken exists in DB

const params = {
  syncToken: "<stored_token>",
  maxResults: 250,
};

Behavior:

  • Returns ONLY changed calendars since last sync
  • Google auto-includes deleted and hidden
  • deleted=true explicitly marks removed calendars
  • Orphan detection DISABLED (absence = unchanged)
  • Primary may not be present (unchanged = not returned)

API Parameters

Calendar List API

ParameterFull SyncIncrementalDescription
syncTokenOmitRequiredToken from previous sync
showDeletedtrueAuto-includedInclude deleted calendars
showHiddentrueAuto-includedInclude hidden calendars
maxResults250250Page size (max 250)

Events API

ParameterValueDescription
showHiddenInvitationsfalseDeclined events - don’t show
showDeletedtrueInclude deleted for sync
singleEventsfalseKeep recurring structure

410 GONE Error Handling

if (error.code === 410) {
  // Clear token and retry as full sync
  return this.findCalendars(userId, integrationId, undefined);
}

Occurs when: Token expired, ACL changes, server invalidation

Calendar Segregation Logic (CASE)

CASETriggerFull SyncIncremental
#1deleted=true in responseDeleteDelete
#2Calendar in responseCreate/UpdateCreate/Update
#3NOT in responseMark orphan → DeleteSKIP (unchanged)

CASE #1 Safeguard

Primary calendar CANNOT be deleted. If deleted=true for primary:

  • Contradictory data (impossible)
  • Throw exception, capture to Sentry
  • Transaction rollback

CASE #3 Safeguard

If Google primary not in full sync response but exists in DB:

  • Skip deletion (protect calendar)
  • Log ERROR + Sentry
  • Continue sync (self-healing)

App Primary vs Google Primary

ConceptFieldDescription
App Primarycalendar.primaryUser-settable, any calendar
Google Primarycalendar.isGooglePrimaryAlways user’s email calendar

Primary Reassignment

When app primary being deleted:

  1. Check if also Google primary (throw if so)
  2. Find Google primary from response
  3. Fallback: Find from DB
  4. Set as new app primary with show=true
  5. Error if no replacement found

Key Takeaways

  1. Sync parity is the #1 rule - match Google web UI exactly
  2. Full vs incremental have different orphan handling
  3. Protect primary calendar from deletion
  4. 410 GONE requires full resync with token clear

Comments

Back to posts
enko