Fixing Race Conditions in the SailJada Booking Calendar: A Multi-Site Deployment Strategy
The Problem: Calendar Interactivity Before Data Ready
During a development session on the SailJada charter booking platform, we discovered a critical race condition affecting the booking flow across staging and production environments. The issue manifested as users being able to interact with the booking calendar before availability data had finished loading from external APIs, potentially allowing them to book time slots that were already reserved.
The root cause was architectural: the jadaOpenBook() function was triggering calendar UI initialization synchronously, while availability data was being fetched asynchronously via the GetMyBoat API integration. This created a window where the DOM was interactive but the underlying data model was incomplete.
Technical Implementation: The Fix
We identified that the race condition existed across 22 HTML files in the repository structure:
/Users/cb/Documents/repos/sites/sailjada.com/
├── index.html
├── about/index.html
├── contact/index.html
├── sd-sailing-calendar/index.html
└── [19 additional files with booking flows]
The problematic code pattern looked like this:
<script>
function jadaOpenBook() {
// Calendar becomes interactive immediately
document.getElementById('jada-modal-overlay').style.display = 'block';
// Availability fetch happens asynchronously, but modal is already open
fetch('/api/availability').then(response => /* update calendar */);
}
</script>
We modified the implementation to introduce a blocking pattern where the modal remains hidden until the fetch completes:
<script>
function jadaOpenBook() {
const modalOverlay = document.getElementById('jada-modal-overlay');
const jadaCalendar = document.getElementById('jadaCalendar');
// Prevent premature interaction
modalOverlay.setAttribute('aria-busy', 'true');
modalOverlay.style.pointerEvents = 'none';
// Fetch must complete before calendar becomes interactive
return fetch('/api/availability')
.then(response => response.json())
.then(data => {
jadaCalendar.setAttribute('data-availability', JSON.stringify(data));
modalOverlay.style.display = 'block';
modalOverlay.style.pointerEvents = 'auto';
modalOverlay.removeAttribute('aria-busy');
})
.catch(error => {
console.error('Availability load failed:', error);
modalOverlay.style.display = 'none';
});
}
</script>
Why This Approach
We chose a promise-based blocking pattern rather than async/await for backward compatibility with older browsers still accessing the site. The implementation stores availability data directly in a data attribute on the calendar element, making it accessible to downstream JavaScript without requiring additional state management libraries.
The addition of aria-busy and pointer-events controls serves a dual purpose: it provides accessibility hints to screen readers while simultaneously preventing mouse clicks and keyboard interactions during the loading state.
Multi-Site Search and Discovery
Before deploying, we performed comprehensive codebase analysis to ensure we caught all affected files:
# Find all repositories with jada-related content
find /Users/cb/Documents/repos/sites -name "*jada*" -type d
# Locate all instances of the vulnerable function
grep -r "jadaOpenBook" /Users/cb/Documents/repos/sites/sailjada.com \
/Users/cb/Documents/repos/sites/sailjada.queenofsand
# Identify exact line numbers for targeted fixes
grep -n "function jadaOpenBook" /Users/cb/Documents/repos/sites/sailjada.com/index.html
# Find related calendar/booking infrastructure
grep -n "jada-modal-overlay\|availability\|jadaCalendar" \
/Users/cb/Documents/repos/sites/sailjada.com/index.html
This search strategy revealed the scope: 22 files required updates, distributed across multiple subdirectories including main pages, the dedicated calendar page, contact forms, and charter detail pages.
Staging Deployment Pipeline
Following the staging rule protocol, we deployed fixes to the staging environment first for verification before pushing to production:
# Deploy to staging bucket for review
aws s3 sync /Users/cb/Documents/repos/sites/sailjada.com/ \
s3://queenofsandiego.com/_staging/sailjada/ \
--include "*.html" \
--exclude ".git/*" \
--exclude "node_modules/*"
# Invalidate CloudFront cache for staging distribution
aws cloudfront create-invalidation \
--distribution-id [STAGING_DIST_ID] \
--paths "/*"
The staging environment at https://queenofsandiego.com/_staging/sailjada/ allowed for verification that the booking flow now properly waited for availability data before enabling user interaction.
Production Considerations
The production deployment required additional care due to the banner overlap issue mentioned in the session notes. We verified that our JavaScript changes didn't conflict with any CSS overlays or banner positioning before final deployment:
# Search for banner-related styles and scripts
grep -n "banner\|overlay\|z-index" \
/Users/cb/Documents/repos/sites/sailjada.com/index.html
# Verify no conflicts with Book Now button selectors
grep -n "Book Now\|booking-button\|cta" \
/Users/cb/Documents/repos/sites/sailjada.com/index.html
Versioning Strategy
To enable quick rollback if needed, we implemented a version comment in the modified JavaScript blocks:
<!-- jadaOpenBook race condition fix - v1.2.1 -->
<script data-version="1.2.1" data-fix="availability-blocking">
function jadaOpenBook() {
// Implementation here
}
</script>
This version string allows us to quickly identify which files have the fix applied and provides a reference point in our git history.
What's Next
Moving forward, we recommend:
- Monitoring GetMyBoat API response times to ensure the blocking doesn't create unacceptable UX delays
- Implementing a timeout fallback (e.g., after 5 seconds, show the calendar anyway with a warning message)
- Refactoring this booking logic into a dedicated JavaScript module rather than inline scripts in each HTML file
- Adding automated tests to the CI/CD pipeline to catch similar race conditions
The fix is now live on both staging and production, with version tracking in place for future maintenance.
```