fix deadlocks and add recovery middleware for http server errors
All checks were successful
pedestrian-simulator / build (push) Successful in 1m1s
All checks were successful
pedestrian-simulator / build (push) Successful in 1m1s
This commit is contained in:
@@ -28,15 +28,24 @@ type StepManager struct {
|
||||
}
|
||||
|
||||
func NewStepManager(userID string) *StepManager {
|
||||
now := time.Now()
|
||||
sm := &StepManager{
|
||||
userID: userID,
|
||||
syncInterval: 15 * time.Minute,
|
||||
lastSyncTime: now.Add(-15 * time.Minute),
|
||||
nextSyncTime: now.Add(24 * time.Hour), // Don't sync unless a trip is started or loaded
|
||||
}
|
||||
sm.LoadTripState()
|
||||
return sm
|
||||
}
|
||||
|
||||
func (sm *StepManager) LoadTripState() error {
|
||||
sm.mu.Lock()
|
||||
defer sm.mu.Unlock()
|
||||
return sm.loadTripStateLocked()
|
||||
}
|
||||
|
||||
func (sm *StepManager) loadTripStateLocked() error {
|
||||
var startTime time.Time
|
||||
err := db.QueryRow(`
|
||||
SELECT start_date, start_time, start_day_initial_steps, previous_total_steps, target_total_steps, last_sync_time, next_sync_time
|
||||
@@ -47,7 +56,11 @@ func (sm *StepManager) LoadTripState() error {
|
||||
)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil // Normal for first run
|
||||
// Initialize with defaults if no trip exists
|
||||
now := time.Now()
|
||||
sm.tripState.StartDate = "" // No active trip
|
||||
sm.nextSyncTime = now.Add(24 * time.Hour)
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -74,6 +87,12 @@ func (sm *StepManager) LoadTripState() error {
|
||||
}
|
||||
|
||||
func (sm *StepManager) SaveTripState() {
|
||||
sm.mu.Lock()
|
||||
defer sm.mu.Unlock()
|
||||
sm.saveTripStateLocked()
|
||||
}
|
||||
|
||||
func (sm *StepManager) saveTripStateLocked() {
|
||||
_, err := db.Exec(`
|
||||
INSERT INTO trips (user_id, start_date, start_time, start_day_initial_steps, previous_total_steps, target_total_steps, last_sync_time, next_sync_time)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
@@ -122,7 +141,7 @@ func (sm *StepManager) StartNewTrip() {
|
||||
// On new trip, previous total is 0
|
||||
sm.previousTotalSteps = 0
|
||||
sm.targetTotalSteps = 0
|
||||
sm.SaveTripState()
|
||||
sm.saveTripStateLocked()
|
||||
|
||||
// Trigger immediate sync to set baseline
|
||||
go sm.Sync()
|
||||
@@ -153,7 +172,19 @@ func (sm *StepManager) performSync(interval time.Duration) {
|
||||
today := time.Now().Format("2006-01-02")
|
||||
|
||||
// Parse start date in local time
|
||||
start, _ := time.ParseInLocation("2006-01-02", tripStateCopy.StartDate, time.Local)
|
||||
if tripStateCopy.StartDate == "" {
|
||||
fmt.Println("[StepManager] Skipping sync: No active trip start date")
|
||||
sm.mu.Lock()
|
||||
sm.nextSyncTime = time.Now().Add(1 * time.Hour) // Check again in an hour
|
||||
sm.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
start, err := time.ParseInLocation("2006-01-02", tripStateCopy.StartDate, time.Local)
|
||||
if err != nil {
|
||||
fmt.Printf("[StepManager] ERROR: Invalid start date '%s': %v\n", tripStateCopy.StartDate, err)
|
||||
return
|
||||
}
|
||||
end, _ := time.ParseInLocation("2006-01-02", today, time.Local)
|
||||
|
||||
// Iterate from Start Date to Today
|
||||
@@ -204,7 +235,7 @@ func (sm *StepManager) performSync(interval time.Duration) {
|
||||
|
||||
// Update State
|
||||
sm.tripState.DailyCache = newDailyCache
|
||||
sm.SaveTripState()
|
||||
sm.saveTripStateLocked()
|
||||
|
||||
// Update Smoothing Targets
|
||||
sm.previousTotalSteps = sm.calculateSmoothedTokenAt(time.Now()) // Snapshot current interpolated value as new start
|
||||
@@ -239,22 +270,23 @@ func (sm *StepManager) calculateSmoothedTokenAt(t time.Time) int {
|
||||
}
|
||||
|
||||
func (sm *StepManager) GetStatus() map[string]interface{} {
|
||||
sm.mu.Lock()
|
||||
defer sm.mu.Unlock()
|
||||
|
||||
// Reload from DB to get latest sync results from other instances
|
||||
sm.LoadTripState()
|
||||
|
||||
// Auto-trigger sync if needed
|
||||
if time.Now().After(sm.nextSyncTime) {
|
||||
sm.Sync() // Sync and save to DB
|
||||
}
|
||||
|
||||
sm.mu.Lock()
|
||||
shouldSync := time.Now().After(sm.nextSyncTime)
|
||||
nextSyncMilli := sm.nextSyncTime.UnixMilli()
|
||||
currentSmoothed := sm.calculateSmoothedTokenAt(time.Now())
|
||||
sm.mu.Unlock()
|
||||
|
||||
// Auto-trigger sync if needed (out of initial lock to avoid deadlock)
|
||||
if shouldSync {
|
||||
go sm.Sync() // Async sync
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
"tripSteps": currentSmoothed,
|
||||
"nextSyncTime": sm.nextSyncTime.UnixMilli(),
|
||||
"nextSyncTime": nextSyncMilli,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user