Skip to content

perf: hoist DB calls out of realTimeMutex in GetAlertsForTrip#282

Merged
aaronbrethorst merged 3 commits intoOneBusAway:mainfrom
ARCoder181105:fix/realtime-db-lock-contention
Feb 4, 2026
Merged

perf: hoist DB calls out of realTimeMutex in GetAlertsForTrip#282
aaronbrethorst merged 3 commits intoOneBusAway:mainfrom
ARCoder181105:fix/realtime-db-lock-contention

Conversation

@ARCoder181105
Copy link
Contributor

Description:
This PR fixes a concurrency bug where blocking database I/O was performed while holding the global realTimeMutex.

Closes #281

Motivation:
Previously, GetAlertsForTrip acquired a read lock before querying the database. This caused the real-time updater (which requires a write lock) to starve during high read traffic or slow database queries, leading to stale vehicle data.

Changes:
Refactored GetAlertsForTrip in internal/gtfs/realtime.go to:

  1. Fetch static data (Trip, Route, Agency) from the database before acquiring the lock.
  2. Hold the RLock only during the in-memory filtering of alerts.

Verification:

  • Logic verified to ensure correct alert filtering with pre-fetched IDs.
  • Existing tests pass.

@ARCoder181105
Copy link
Contributor Author

@aaronbrethorst
The test are passing successfully, this PR is ready for review

image

Copy link
Member

@aaronbrethorst aaronbrethorst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Smart optimization, Aditya. Moving blocking database I/O outside the realTimeMutex is the right call - holding a lock during slow operations like database queries is a classic recipe for contention and stale data. The change itself is correct and safe, but there's a misleading comment that should be fixed.

Incorrect/Misleading Comment

File: internal/gtfs/realtime.go:73

The comment says:

// IMPORTANT: Caller must hold manager.RLock() before calling this method.

This comment is incorrect for this function:

  1. manager.RLock() refers to staticMutex (protects gtfsData, lastUpdated, etc.)
  2. GetAlertsForTrip doesn't access any data protected by staticMutex
  3. It only accesses:
    • manager.GtfsDB - thread-safe (Go's database/sql guarantees)
    • manager.realTimeAlerts - protected by realTimeMutex, which the function acquires internally
  4. Sibling functions GetAlertsForRoute and GetAlertsForStop have no such comment and follow the same pattern

Fix: Remove or correct the comment:

// GetAlertsForTrip returns alerts matching the trip, its route, or agency.
func (manager *Manager) GetAlertsForTrip(ctx context.Context, tripID string) []gtfs.Alert {

Good optimization. Just fix the misleading comment and this is ready to merge.

@ARCoder181105
Copy link
Contributor Author

@aaronbrethorst
This is ready for review, made the changes

Copy link
Member

@aaronbrethorst aaronbrethorst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey Aditya! This is a nice performance optimization. Reducing lock scope to exclude blocking I/O is exactly the right approach for preventing writer starvation.

Fit and Finish

1. Stale comment in related code (out of scope, but worth noting)

Location: internal/restapi/trips_helper.go:769

The GetSituationIDsForTrip function has a comment that says:

// IMPORTANT: Caller must hold manager.RLock() before calling this method.

This comment appears to be stale—none of the callers hold the lock before calling this method, and with your change, GetAlertsForTrip now manages its own locking internally. Additionally, GetSituationIDsForTrip itself does DB calls which shouldn't be done under a lock.

This is out of scope for this PR, but might be worth a quick follow-up PR to clean up that misleading comment.


Thanks for the contribution!

@aaronbrethorst aaronbrethorst merged commit a8c8918 into OneBusAway:main Feb 4, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Performance: GetAlertsForTrip holds realTimeMutex during DB I/O, blocking updates

2 participants