```html

Fixing a Race Condition in the Booking Calendar: Jada Sailing Charter Deployment Strategy

During a recent development session on sailjada.com, we identified and resolved a critical race condition in the booking flow that allowed users to select unavailable sailing slots. This post details the technical investigation, the fix implementation, and our deployment strategy across staging and production environments.

The Problem: Race Condition in jadaOpenBook()

The issue manifested when users could interact with the booking calendar before availability data finished loading from the backend. This created a window where:

  • User clicks "Book Now" button, triggering jadaOpenBook() function
  • Modal overlay renders immediately with the calendar interface
  • Calendar becomes interactive before the fetch() call completes
  • User selects a slot that's actually already booked
  • Availability data arrives and overwrites the UI, but booking state is corrupted

This is a classic asynchronous programming pitfall where DOM state and data state become inconsistent.

Technical Investigation Process

We began by mapping the codebase structure across the sailjada.com project:

find /Users/cb/Documents/repos/sites -name "*jada*" -type d

This revealed the project layout, with the main booking logic concentrated in /Users/cb/Documents/repos/sites/sailjada.com/index.html. We then located the problematic function:

grep -n "function jadaOpenBook" /Users/cb/Documents/repos/sites/sailjada.com/index.html

Next, we searched for all pages implementing the booking flow across the site:

grep -r "jadaOpenBook" /Users/cb/Documents/repos/sites/sailjada.com

This identified 22 HTML files across multiple sections (about, contact, sd-sailing-calendar, etc.) that all reference the same booking logic. We then examined how external calendar and availability data was being fetched:

grep -n "fetch\|iframe\|GetMyBoat\|Viator\|calendar" /Users/cb/Documents/repos/sites/sailjada.com/contact/index.html

The investigation revealed that availability data was being loaded asynchronously, but the modal overlay was becoming interactive synchronously.

The Fix: Blocking Calendar Interaction Until Data Loads

The solution involved modifying the jadaOpenBook() function to implement a state machine that prevents user interaction until the availability fetch completes. Here's the approach:

  • Introduce a loading state: Set jadaBookingState.isLoading = true when the modal opens
  • Disable interactive elements: Apply CSS class jada-loading to the modal that disables pointer events
  • Block on fetch completion: Only set isLoading = false after the availability fetch promise resolves
  • Add visual feedback: Display a loading spinner or disable the calendar overlay until data arrives
  • Error handling: Implement fallback UI if the fetch fails, allowing user to retry or contact support

Rather than modifying inline scripts in each of the 22 HTML files, we created a centralized Python script to apply the fix consistently:

#!/usr/bin/env python3
import os
import re
import glob

# Find all HTML files containing jadaOpenBook
for filepath in glob.glob('/Users/cb/Documents/repos/sites/sailjada.com/**/*.html', recursive=True):
    with open(filepath, 'r') as f:
        content = f.read()
    
    if 'jadaOpenBook' not in content:
        continue
    
    # Insert blocking mechanism before availability fetch
    # Wrap the fetch call in async/await with loading state management
    modified = re.sub(
        r'(jadaOpenBook\s*=\s*function\s*\(\)\s*\{)',
        r'\1\n    jadaBookingState.isLoading = true;\n    document.querySelector(".jada-modal-overlay").classList.add("jada-loading");',
        content
    )
    
    # Add loading state reset after fetch completes
    modified = re.sub(
        r'(\.then\(response\s*=>\s*response\.json\(\))(.*?)(\})',
        r'\1\2.finally(() => { jadaBookingState.isLoading = false; document.querySelector(".jada-modal-overlay").classList.remove("jada-loading"); })\3',
        modified
    )
    
    with open(filepath, 'w') as f:
        f.write(modified)
    
    print(f"Fixed: {filepath}")

This script was applied to all 22 affected files across the sailjada.com site structure.

Infrastructure and Deployment Strategy

Staging Environment: Following the staging deployment pattern, we first deployed the fix to the staging bucket structure:

aws s3 sync /Users/cb/Documents/repos/sites/sailjada.com s3://queenofsandiego.com/_staging/sailjada/ --exclude "*" --include "*.html" --include "*.js"

This deployed to https://queenofsandiego.com/_staging/sailjada/, allowing for review and testing before production deployment.

Production Deployment: Once validated on staging, the changes were pushed to the primary production bucket:

aws s3 sync /Users/cb/Documents/repos/sites/sailjada.com s3://sailjada.com/ --exclude "*" --include "*.html" --include "*.js" --cache-control "public, max-age=3600"

This syncs to the primary S3 bucket serving sailjada.com traffic.

CloudFront Cache Invalidation: To ensure users receive the updated booking logic immediately, we invalidated the CloudFront distribution cache for all HTML paths:

aws cloudfront create-invalidation --distribution-id DISTRIBUTION_ID --paths "/*.html" "/*/index.html"

This forces CloudFront edge nodes to fetch fresh content from the S3 origin within seconds, rather than waiting for the default cache TTL to expire.

Key Decisions and Rationale

Centralized Fix Script: Rather than manually editing 22 files, we used a Python script to ensure consistency and reduce human error. This approach is maintainable and can be extended if the pattern needs adjustment.

State Machine Pattern: The jadaBookingState object acts as a single source of truth for the booking flow state. This prevents multiple simultaneous state changes and makes debugging easier.

CSS-Based Blocking: Using the jada-loading class to disable pointer events is more performant than removing event listeners, and it provides visual feedback to users that the system is loading.

Staging-First Deployment: By deploying to staging first at s3://queenofsandiego.com/_staging/sailjada/, we reduce risk of production outages and allow for QA validation.

What's Next

The immediate next steps include:

  • Monitor production CloudWatch logs for booking flow errors post