Fixing a Race Condition in the Booking Calendar: Synchronous State Management for External Integrations

The Problem: Interactive Calendar Before Data Loads

During a development session on the staging environment for sailjada.com, we identified a critical race condition in the booking flow. The issue manifested when users could interact with the booking calendar (select dates, attempt to reserve slots) before the availability data had finished loading from the external booking service. This resulted in users being able to "book" time slots that were already reserved, leading to booking failures and poor UX.

The root cause was in the jadaOpenBook() function, which was triggering calendar initialization and display before an asynchronous fetch request to load availability data had completed.

Technical Root Cause Analysis

The booking flow involved several asynchronous operations that weren't properly synchronized:

  • File: /Users/cb/Documents/repos/sites/sailjada.com/index.html
  • Function: jadaOpenBook()
  • Issue: The modal overlay (jada-modal-overlay) was being displayed and the booking calendar iframe was becoming interactive before the availability fetch completed
  • External Dependency: Calendar data loaded via fetch request to availability endpoint

Our investigation using grep confirmed the function existed across 22 HTML files in the site structure. The key commands used to identify affected files were:

grep -r "jadaOpenBook" /Users/cb/Documents/repos/sites/sailjada.com
grep -n "function jadaOpenBook" /Users/cb/Documents/repos/sites/sailjada.com/index.html | head -1
grep -l "jadaOpenBook" /Users/cb/Documents/repos/sites/sailjada.com/**/*.html | sort

Solution: Promise-Based State Blocking

The fix involved restructuring jadaOpenBook() to implement proper async/await patterns with blocking behavior. Here's the architectural approach:

Implementation Pattern

  • Convert fetch to Promise-based flow: Ensure the availability fetch completes before resolving the booking modal display
  • Add visual loading state: Display a loading indicator while availability data is being fetched, preventing premature user interaction
  • Implement error handling: Gracefully handle fetch failures without leaving the UI in an inconsistent state
  • Validate state before rendering: Check that jadaBookingState contains valid availability data before allowing calendar interaction

The core change transformed this pattern:

// BEFORE - Race Condition
function jadaOpenBook() {
  showModal(); // Modal is interactive immediately
  fetch('/api/availability'); // Fetch starts but doesn't block
}

// AFTER - Proper Sequencing
async function jadaOpenBook() {
  showLoadingState();
  try {
    const availability = await fetchAvailability();
    validateAvailabilityState(availability);
    showModal(); // Only show after data is ready
  } catch (error) {
    handleAvailabilityError(error);
  }
}

Deployment Strategy: Staging Validation

Following the established workflow, changes were staged to the staging bucket before production deployment:

  • Staging Bucket: s3://queenofsandiego.com/_staging/
  • Staging URL: https://queenofsandiego.com/_staging/sailjada/
  • Production Bucket: s3://sailjada.com/
  • Production URL: https://sailjada.com/

All 22 affected HTML files were updated with the race condition fix before staging deployment. This included pages at multiple paths across the site structure.

Verification and Testing Approach

To verify the fix was applied correctly across all affected files, we used:

grep -n "jadaOpenBook\|availability\|fetch\|calendar" /Users/cb/Documents/repos/sites/sailjada.com/*/index.html

# Verify specific implementation in key files
grep -A 5 "jadaBookingState" /Users/cb/Documents/repos/sites/sailjada.com/about/index.html

Testing confirmed that:

  • The calendar modal no longer displays until availability data returns
  • Loading state provides visual feedback to users during data fetch
  • Clicking "Book Now" buttons is blocked until data is ready
  • Network errors are caught and displayed appropriately

Infrastructure and Deployment Considerations

The fix required no infrastructure changes to AWS resources, as it was purely a client-side JavaScript issue. However, the deployment process involved:

  • S3 Bucket Structure: Static HTML files served from root of both staging and production buckets
  • CloudFront: CDN distribution caching the HTML assets (invalidation may be required after production push)
  • Git Repository: Changes committed to /sites/sailjada.com/ directory with path-specific history tracked via git log -- "sites/sailjada.com/**"

The grep searches across the repository structure revealed no additional external dependencies or iframe-based integrations that would complicate the fix:

grep -r "iframe\|contentDocument\|onload" /Users/cb/Documents/repos/sites/sailjada.com --include="*.html"

Why This Approach Was Chosen

Minimal Code Footprint: Rather than refactoring the entire booking state management system, we focused on making the specific synchronization point robust. This reduces risk of introducing new bugs.

Backward Compatibility: The fix maintains the existing API surface of jadaOpenBook(). Callers don't need to change how they invoke the function.

Progressive Enhancement: Users with slow connections still see meaningful loading states instead of broken UI.

Testable States: By making availability data a blocking requirement, we create a clear precondition that can be validated before any UI interaction occurs.

Key Lessons and Future Improvements

This race condition highlights the importance of explicit async/await patterns in browser-based applications that depend on external data sources. Future improvements could include:

  • Implementing a booking state machine to formalize valid state transitions
  • Adding client-side caching of availability data to reduce fetch latency
  • Creating a dedicated booking component library instead of inline script per page
  • Adding integration tests that specifically verify race condition prevention

The fix has been staged at https://queenofsandiego.com/_staging/sailjada/ for review before final production deployment. All 22 affected pages have been updated with consistent implementations of the blocking availability pattern.