mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix(coderd): invalidate chatd provider cache on AI provider changes (#26987)
chatd subscribes to `ChatConfigEventChannel` and invalidates its provider cache on a `providers` event kind, but nothing ever published that kind. AI provider CRUD only publishes on `AIProvidersChangedChannel` (consumed by aibridged and aibridgeproxyd), so chatd's provider cache only converged via its 10 second TTL. Subscribe chatd to the same `AIProvidersChangedChannel` publish instead of adding a second publish, per the review feedback on #26207: one publish, multiple subscribers. The now-unused `ChatConfigEventProviders` kind is removed so `ChatConfigEvent` stays scoped to model configs, user prompts, and advisor config, and can't regrow a dead subscriber. Follow-up to CRF-5 from the review of #25673. Supersedes #26207. Closes CODAGT-499
This commit is contained in:
+21
-2
@@ -179,6 +179,7 @@ type Server struct {
|
||||
debugSvcInit sync.Once
|
||||
configCache *chatConfigCache
|
||||
configCacheUnsubscribe func()
|
||||
providerCacheUnsubscribe func()
|
||||
|
||||
usageTracker *workspacestats.UsageTracker
|
||||
clock quartz.Clock
|
||||
@@ -3300,8 +3301,6 @@ func New(ps pubsub.Pubsub, cfg Config) *Server {
|
||||
return
|
||||
}
|
||||
switch ev.Kind {
|
||||
case coderdpubsub.ChatConfigEventProviders:
|
||||
p.configCache.InvalidateProviders()
|
||||
case coderdpubsub.ChatConfigEventModelConfig:
|
||||
p.configCache.InvalidateModelConfig(ev.EntityID)
|
||||
case coderdpubsub.ChatConfigEventUserPrompt:
|
||||
@@ -3317,6 +3316,22 @@ func New(ps pubsub.Pubsub, cfg Config) *Server {
|
||||
p.configCacheUnsubscribe = cancelConfigSub
|
||||
}
|
||||
|
||||
cancelProviderSub, err := p.pubsub.SubscribeWithErr(
|
||||
coderdpubsub.AIProvidersChangedChannel,
|
||||
func(cbCtx context.Context, _ []byte, err error) {
|
||||
if err != nil {
|
||||
p.logger.Warn(cbCtx, "ai providers changed event error", slog.Error(err))
|
||||
return
|
||||
}
|
||||
p.configCache.InvalidateProviders()
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
p.logger.Error(ctx, "subscribe to ai providers changed events", slog.Error(err))
|
||||
} else {
|
||||
p.providerCacheUnsubscribe = cancelProviderSub
|
||||
}
|
||||
|
||||
p.ctx = ctx
|
||||
|
||||
// Spawn background goroutines that all servers need.
|
||||
@@ -4903,6 +4918,10 @@ func (p *Server) Close() error {
|
||||
p.configCacheUnsubscribe = nil
|
||||
unsub()
|
||||
}
|
||||
if unsub := p.providerCacheUnsubscribe; unsub != nil {
|
||||
p.providerCacheUnsubscribe = nil
|
||||
unsub()
|
||||
}
|
||||
if p.chatWorker != nil {
|
||||
if err := p.chatWorker.Close(); err != nil {
|
||||
p.logger.Warn(context.Background(), "failed to close chat worker", slog.Error(err))
|
||||
|
||||
@@ -14,6 +14,9 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/database/dbtestutil"
|
||||
coderdpubsub "github.com/coder/coder/v2/coderd/pubsub"
|
||||
"github.com/coder/coder/v2/coderd/x/chatd/chatprovider"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/coder/v2/testutil"
|
||||
"github.com/coder/quartz"
|
||||
@@ -1198,3 +1201,19 @@ func TestConfigCache_InvalidateAdvisorConfig_BlocksStaleInFlight(t *testing.T) {
|
||||
require.EqualValues(t, 2, third.MaxUsesPerRun)
|
||||
require.Equal(t, int32(2), store.advisorConfigCalls.Load())
|
||||
}
|
||||
|
||||
func TestConfigCache_InvalidatesProvidersOnAIProvidersChangedEvent(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
db, ps := dbtestutil.NewDB(t)
|
||||
server := newInternalTestServer(t, db, ps, chatprovider.ProviderAPIKeys{})
|
||||
|
||||
// The generation counter only advances through InvalidateProviders,
|
||||
// so this cannot false-pass via TTL expiry.
|
||||
gen := server.configCache.providersGeneration()
|
||||
require.NoError(t, ps.Publish(coderdpubsub.AIProvidersChangedChannel, nil))
|
||||
|
||||
require.Eventually(t, func() bool {
|
||||
return server.configCache.providersGeneration() > gen
|
||||
}, testutil.WaitShort, testutil.IntervalFast)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user