feat: Schedule batch update for account last_used_at
Implement deferred batch update mechanism to reduce database load:
- Add DeferredService for batching account last_used_at updates
- Add TimingWheelService for efficient recurring task scheduling
- Integrate with GatewayService and OpenAIGatewayService
- Implement BatchUpdateLastUsed repository method using CASE...WHEN SQL
- Fix golangci-lint error: Replace interface{} with any
Benefits:
- Reduces database writes by batching updates (10-second intervals)
- Improves request throughput by deferring non-critical updates
- Maintains accurate account usage tracking for scheduling
This commit is contained in:
76
backend/internal/service/deferred_service.go
Normal file
76
backend/internal/service/deferred_service.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DeferredService provides deferred batch update functionality
|
||||
type DeferredService struct {
|
||||
accountRepo AccountRepository
|
||||
timingWheel *TimingWheelService
|
||||
interval time.Duration
|
||||
|
||||
lastUsedUpdates sync.Map
|
||||
}
|
||||
|
||||
// NewDeferredService creates a new DeferredService instance
|
||||
func NewDeferredService(accountRepo AccountRepository, timingWheel *TimingWheelService, interval time.Duration) *DeferredService {
|
||||
return &DeferredService{
|
||||
accountRepo: accountRepo,
|
||||
timingWheel: timingWheel,
|
||||
interval: interval,
|
||||
}
|
||||
}
|
||||
|
||||
// Start starts the deferred service
|
||||
func (s *DeferredService) Start() {
|
||||
s.timingWheel.ScheduleRecurring("deferred:last_used", s.interval, s.flushLastUsed)
|
||||
log.Printf("[DeferredService] Started (interval: %v)", s.interval)
|
||||
}
|
||||
|
||||
// Stop stops the deferred service
|
||||
func (s *DeferredService) Stop() {
|
||||
s.timingWheel.Cancel("deferred:last_used")
|
||||
s.flushLastUsed()
|
||||
log.Printf("[DeferredService] Service stopped")
|
||||
}
|
||||
|
||||
func (s *DeferredService) ScheduleLastUsedUpdate(accountID int64) {
|
||||
s.lastUsedUpdates.Store(accountID, time.Now())
|
||||
}
|
||||
|
||||
func (s *DeferredService) flushLastUsed() {
|
||||
updates := make(map[int64]time.Time)
|
||||
s.lastUsedUpdates.Range(func(key, value any) bool {
|
||||
id, ok := key.(int64)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
ts, ok := value.(time.Time)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
updates[id] = ts
|
||||
s.lastUsedUpdates.Delete(key)
|
||||
return true
|
||||
})
|
||||
|
||||
if len(updates) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := s.accountRepo.BatchUpdateLastUsed(ctx, updates); err != nil {
|
||||
log.Printf("[DeferredService] BatchUpdateLastUsed failed (%d accounts): %v", len(updates), err)
|
||||
for id, ts := range updates {
|
||||
s.lastUsedUpdates.Store(id, ts)
|
||||
}
|
||||
} else {
|
||||
log.Printf("[DeferredService] BatchUpdateLastUsed flushed %d accounts", len(updates))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user