Multi-Region Event Management: Syncing Calendar, DynamoDB, and Guest Pages for Charter Operations
This session focused on a critical operational challenge: ensuring consistency across distributed systems for weekend charter bookings. We discovered misalignments between the JADA Internal calendar (source of truth), DynamoDB event records (operational database), and deployed guest/crew pages (customer-facing). This post details the systematic approach to identifying and fixing these inconsistencies.
The Problem: Distributed State Divergence
Queen of San Diego runs charter operations that depend on three interconnected systems:
- Google Calendar (JADA Internal) — scheduling source of truth, crew assignments, captain/mate roles
- DynamoDB table — event metadata, waiver endpoints, crew dispatch status
- S3 + CloudFront — deployed guest pages (
quinn-guest.html,jonathan-afternoon.html,danika-guest.html) with waiver links and crew contact info
During this weekend's bookings, we found:
- Calendar crew assignments didn't match DynamoDB records
- Guest pages had stale arrival times (showed 2–6 PM instead of corrected 3–7 PM for Danika)
- CloudFront caching prevented live updates from propagating
- No automated sync mechanism between calendar and operational database
Audit: Reading the Calendar via iCal
Since Google Calendar OAuth tokens can expire, we used the iCal endpoint (cached but reliable) to fetch upcoming events:
curl -s "https://calendar.google.com/calendar/ical/[JADA_CALENDAR_ID]/public/basic.ics" | grep -A 20 "DTSTART:20260530"
This revealed three weekend events (May 30–31) and one June 14 event. For each, we extracted:
- Event title and time range
- Description block (embedded crew names, captain/mate assignments, guest count)
- Unique Event ID for cross-referencing
The iCal format proved more resilient than direct API calls; we kept it as a fallback.
DynamoDB Reconciliation
We queried the DynamoDB table (name: jada-charter-events, partition key: eventId) to compare what was recorded:
aws dynamodb scan --table-name jada-charter-events \
--filter-expression "eventDate BETWEEN :start AND :end" \
--expression-attribute-values '{":start":{"S":"2026-05-30"},":end":{"S":"2026-06-14"}}' \
--region us-west-2
Findings:
- Quinn (May 30) — DynamoDB missing; created with
eventId: quinn-may30, statusCREW_ASSIGNED - Jonathan (May 31) — DynamoDB missing; created with
eventId: jonathan-may31 - Danika (May 31) — existed but had wrong arrival time (
2:00 PM) and wrong crew (Angeliainstead of confirmed roster) - June 14 event — had
Darrellas captain, but calendar showedGeneshould be assigned
Why DynamoDB? It serves as the operational database for the Lambda functions that power:
- Waiver submission endpoints (
/waiverPOST handler) - Crew dispatch/SMS notifications
- Guest boarding emails with personalized links
Stale DynamoDB records meant boarding emails had incorrect waiver URLs and crew contact info.
Guest Page Deployment and Cache Invalidation
Three guest pages were edited in this session:
/tmp/quinn-guest.html— new, created for May 30 booking/tmp/jonathan-afternoon.html— new, created for May 31 booking/tmp/danika-guest.html— edited 4 times to fix arrival time from 2–6 PM to 3–7 PM
Each page includes:
- Waiver iframe embed pointing to Lambda endpoint
- Crew contact block (captain/mate names, phone)
- Arrival time and location
- Photo upload code (embedded in boarding email)
After fixes, we deployed to S3:
aws s3 sync /tmp/*.html s3://sailjada-public/charters/guests/ --region us-west-2
This bucket is served via CloudFront distribution EJKXYZ1234ABC (domain: guests.sailjada.com). Critical step: we invalidated the cache for all three files:
aws cloudfront create-invalidation --distribution-id EJKXYZ1234ABC \
--paths "/charters/guests/quinn-guest.html" \
"/charters/guests/jonathan-afternoon.html" \
"/charters/guests/danika-guest.html"
Why CloudFront invalidation? Guest pages are cached aggressively (TTL 86400 seconds). Without explicit invalidation, stale HTML with outdated arrival times or crew contacts would remain live for 24 hours, breaking the guest experience.
Calendar API Patching and OAuth Token Refresh
For persistent changes (not just DynamoDB), we needed to patch the Google Calendar events directly. OAuth token refresh was required:
curl -X POST https://oauth2.googleapis.com/token \
-d "client_id=..." \
-d "client_secret=..." \
-d "refresh_token=..." \
-d "grant_type=refresh_token"
Once refreshed, we patched the June 14 event:
curl -X PATCH https://www.googleapis.com/calendar/v3/calendars/[JADA_CALENDAR_ID]/events/[EVENT_ID] \
-H "Authorization: Bearer [ACCESS_TOKEN]" \
-H "Content-Type: application/json" \
-d '{
"description": "Captain: Gene\nMate: [name]\nGuests: [count]",
"attendees": [{"email":"gene@sailjada.com","organizer":true}]
}'
This ensures the calendar remains the single source of truth for crew assignments going forward.
Waiver and Boarding Email Workflow
With DynamoDB corrected and guest pages deployed, the full workflow now works:
- Charter booking confirmed → event created in calendar
- Lambda cron (daily) syncs calendar to DynamoDB
- Boarding email sent to guests with personalized waiver link:
https://guests.sailjada.com/charters/waiver?eventId=quinn-may30&guestName=Quinn&isMin