From b6c0706e30afcb3bebcd27441cb1bce1d45178ce Mon Sep 17 00:00:00 2001 From: Pluviobyte Date: Wed, 3 Jun 2026 14:18:12 +0800 Subject: [PATCH] fix: sync scheduler snapshots after account state clears --- backend/internal/repository/account_repo.go | 3 ++ .../account_repo_integration_test.go | 37 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/backend/internal/repository/account_repo.go b/backend/internal/repository/account_repo.go index bc970f7607..f149116b3e 100644 --- a/backend/internal/repository/account_repo.go +++ b/backend/internal/repository/account_repo.go @@ -1141,6 +1141,7 @@ func (r *accountRepository) SetOverloaded(ctx context.Context, id int64, until t if err := enqueueSchedulerOutbox(ctx, r.sql, service.SchedulerOutboxEventAccountChanged, &id, nil, nil); err != nil { logger.LegacyPrintf("repository.account", "[SchedulerOutbox] enqueue overload failed: account=%d err=%v", id, err) } + r.syncSchedulerAccountSnapshot(ctx, id) return nil } @@ -1179,6 +1180,7 @@ func (r *accountRepository) ClearTempUnschedulable(ctx context.Context, id int64 if err := enqueueSchedulerOutbox(ctx, r.sql, service.SchedulerOutboxEventAccountChanged, &id, nil, nil); err != nil { logger.LegacyPrintf("repository.account", "[SchedulerOutbox] enqueue clear temp unschedulable failed: account=%d err=%v", id, err) } + r.syncSchedulerAccountSnapshot(ctx, id) return nil } @@ -1244,6 +1246,7 @@ func (r *accountRepository) ClearModelRateLimits(ctx context.Context, id int64) if err := enqueueSchedulerOutbox(ctx, r.sql, service.SchedulerOutboxEventAccountChanged, &id, nil, nil); err != nil { logger.LegacyPrintf("repository.account", "[SchedulerOutbox] enqueue clear model rate limit failed: account=%d err=%v", id, err) } + r.syncSchedulerAccountSnapshot(ctx, id) return nil } diff --git a/backend/internal/repository/account_repo_integration_test.go b/backend/internal/repository/account_repo_integration_test.go index 9e15047c7f..c4d65a665c 100644 --- a/backend/internal/repository/account_repo_integration_test.go +++ b/backend/internal/repository/account_repo_integration_test.go @@ -670,6 +670,8 @@ func (s *AccountRepoSuite) TestBulkUpdate_SyncSchedulerSnapshotOnDisabled() { func (s *AccountRepoSuite) TestSetOverloaded() { account := mustCreateAccount(s.T(), s.client, &service.Account{Name: "acc-over"}) until := time.Date(2025, 6, 15, 12, 0, 0, 0, time.UTC) + cacheRecorder := &schedulerCacheRecorder{} + s.repo.schedulerCache = cacheRecorder s.Require().NoError(s.repo.SetOverloaded(s.ctx, account.ID, until)) @@ -677,6 +679,10 @@ func (s *AccountRepoSuite) TestSetOverloaded() { s.Require().NoError(err) s.Require().NotNil(got.OverloadUntil) s.Require().WithinDuration(until, *got.OverloadUntil, time.Second) + s.Require().Len(cacheRecorder.setAccounts, 1) + s.Require().Equal(account.ID, cacheRecorder.setAccounts[0].ID) + s.Require().NotNil(cacheRecorder.setAccounts[0].OverloadUntil) + s.Require().WithinDuration(until, *cacheRecorder.setAccounts[0].OverloadUntil, time.Second) } func (s *AccountRepoSuite) TestSetRateLimited() { @@ -732,11 +738,42 @@ func (s *AccountRepoSuite) TestTempUnschedulableFieldsLoadedByGetByIDAndGetByIDs s.Require().WithinDuration(until, *gotByIDs[1].TempUnschedulableUntil, time.Second) s.Require().Equal(reason, gotByIDs[1].TempUnschedulableReason) + cacheRecorder := &schedulerCacheRecorder{} + s.repo.schedulerCache = cacheRecorder + s.Require().NoError(s.repo.ClearTempUnschedulable(s.ctx, acc1.ID)) cleared, err := s.repo.GetByID(s.ctx, acc1.ID) s.Require().NoError(err) s.Require().Nil(cleared.TempUnschedulableUntil) s.Require().Equal("", cleared.TempUnschedulableReason) + s.Require().Len(cacheRecorder.setAccounts, 1) + s.Require().Equal(acc1.ID, cacheRecorder.setAccounts[0].ID) + s.Require().Nil(cacheRecorder.setAccounts[0].TempUnschedulableUntil) + s.Require().Equal("", cacheRecorder.setAccounts[0].TempUnschedulableReason) +} + +func (s *AccountRepoSuite) TestClearModelRateLimits_SyncsSchedulerSnapshot() { + account := mustCreateAccount(s.T(), s.client, &service.Account{ + Name: "acc-clear-model-rate", + Extra: map[string]any{ + "model_rate_limits": map[string]any{ + "claude-sonnet-4-5": map[string]any{ + "rate_limit_reset_at": "2026-06-03T10:00:00Z", + }, + }, + }, + }) + cacheRecorder := &schedulerCacheRecorder{} + s.repo.schedulerCache = cacheRecorder + + s.Require().NoError(s.repo.ClearModelRateLimits(s.ctx, account.ID)) + + got, err := s.repo.GetByID(s.ctx, account.ID) + s.Require().NoError(err) + s.Require().NotContains(got.Extra, "model_rate_limits") + s.Require().Len(cacheRecorder.setAccounts, 1) + s.Require().Equal(account.ID, cacheRecorder.setAccounts[0].ID) + s.Require().NotContains(cacheRecorder.setAccounts[0].Extra, "model_rate_limits") } // --- UpdateLastUsed ---