Skip to content

Commit d801bfa

Browse files
committed
CRITICAL FIX: Prevent check-in failure from seat upgrade lock contention
Flight C3GDAI's check-in FAILED because the seat upgrade function held browser_session._lock for 10+ minutes while navigating www.southwest.com. When the check-in thread (24h before departure) tried to acquire the lock, it was blocked. When it finally got the lock, the browser was on www.southwest.com (not mobile.southwest.com), causing fetch() to fail with status 0 (same-origin policy violation). Three fixes: 1. CRITICAL: Check-in proximity guard (worker.py) - attempt_seat_upgrades() now checks if ANY check-in is within 2 hours. If so, seat upgrades are PAUSED entirely. - Check-in is always the jdholtz#1 priority. Seat upgrades can wait. - Activity log shows: "Seat upgrades PAUSED: check-in for X in Y minutes" 2. HIGH: Fresh browser session before check-in (checkin_handler.py) - 30 minutes before check-in, the handler now calls browser_session.start() (not just ensure_alive()) - This creates a completely fresh browser on mobile.southwest.com - Guarantees the browser is on the correct domain with fresh WAF tokens, regardless of what seat upgrades did earlier 3. MEDIUM: Lock timeout in make_request (browser_session.py) - Changed from blocking `with self._lock:` to timed acquisition `self._lock.acquire(timeout=60)` - If the lock can't be acquired within 60 seconds, logs a warning and retries instead of blocking indefinitely - Prevents check-in from being stuck waiting for seat upgrade forever https://claude.ai/code/session_01XeexS9xn1dz8wb9K11MkpV
1 parent 2e5a98b commit d801bfa

3 files changed

Lines changed: 35 additions & 6 deletions

File tree

worker/lib/browser_session.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,8 +240,18 @@ def make_request(
240240
while attempts < max_attempts:
241241
attempts += 1
242242
try:
243-
with self._lock:
243+
# Use timeout on lock to prevent indefinite blocking
244+
# (seat upgrades can hold lock for minutes)
245+
acquired = self._lock.acquire(timeout=60)
246+
if not acquired:
247+
error_msg = "Browser session locked (another operation in progress)"
248+
logger.warning(error_msg)
249+
time.sleep(2)
250+
continue
251+
try:
244252
status, response_body = self.execute_fetch(method, url, merged_headers, body)
253+
finally:
254+
self._lock.release()
245255

246256
if status == 200:
247257
logger.debug("Browser fetch succeeded after %d attempts", attempts)

worker/lib/checkin_handler.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,13 +138,19 @@ def _wait_for_check_in(self, checkin_time: datetime) -> None:
138138
if self._stop_event.is_set():
139139
return
140140

141-
# Ensure browser session is alive before check-in
141+
# CRITICAL: Ensure browser is alive AND on mobile.southwest.com before check-in
142+
# Seat upgrades may have navigated the browser to www.southwest.com,
143+
# which causes fetch() to fail with status 0 (same-origin policy)
142144
if self.browser_session:
143145
try:
144-
self.browser_session.ensure_alive()
146+
logger.info("Preparing browser for check-in: restarting fresh session")
147+
self.browser_session.start() # Fresh browser on mobile.southwest.com
145148
self.headers = self.browser_session.headers
149+
logger.info("Browser ready for check-in with %d headers", len(self.headers))
146150
except DriverTimeoutError:
147-
logger.debug("Timeout while refreshing browser session before check-in")
151+
logger.warning("Timeout while preparing browser for check-in")
152+
except Exception as e:
153+
logger.warning("Error preparing browser for check-in: %s", e)
148154

149155
current_time = get_current_time()
150156

worker/worker.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -616,11 +616,24 @@ def check_fares(conn: sqlite3.Connection) -> None:
616616

617617
def attempt_seat_upgrades(conn: sqlite3.Connection, force_flight_id: str | None = None) -> None:
618618
"""For A-List accounts, attempt seat upgrade from 48h to 2h before departure.
619-
If force_flight_id is provided, only check that specific flight (bypasses cooldown)."""
619+
If force_flight_id is provided, only check that specific flight (bypasses cooldown).
620+
CRITICAL: Will NOT run if any check-in is imminent (within 2 hours)."""
620621
if not browser_session:
621-
add_log(conn, "Seat upgrade check: browser session not available", "warning")
622622
return
623623

624+
# CRITICAL SAFETY CHECK: Do not interfere with upcoming check-ins
625+
# Seat upgrades hold the browser lock for minutes, which blocks check-in threads
626+
now_utc = datetime.utcnow()
627+
for handler_id, handler in active_handlers.items():
628+
try:
629+
checkin_time = handler.departure_time - timedelta(days=1)
630+
seconds_until_checkin = (checkin_time - now_utc.replace(tzinfo=handler.departure_time.tzinfo) if handler.departure_time.tzinfo else checkin_time - now_utc).total_seconds()
631+
if 0 < seconds_until_checkin < 7200: # Within 2 hours
632+
add_log(conn, f"Seat upgrades PAUSED: check-in for {handler.confirmation_number} in {int(seconds_until_checkin / 60)} minutes. Check-in takes priority.", "info")
633+
return
634+
except Exception:
635+
pass
636+
624637
if force_flight_id:
625638
# Manual check for a specific flight - bypass normal query
626639
rows = conn.execute(

0 commit comments

Comments
 (0)