MCPcopy Index your code
hub / github.com/coder/coder / Acquire

Method Acquire

coderd/aibridged/pool.go:155–238  ·  view source on GitHub ↗

Acquire retrieves or creates a [*aibridge.RequestBridge] instance per given key. Each returned [*aibridge.RequestBridge] is safe for concurrent use. Each [*aibridge.RequestBridge] is stateful because it has MCP clients which maintain sessions to the configured MCP server.

(ctx context.Context, req Request, clientFn ClientFunc, mcpProxyFactory MCPProxyBuilder)

Source from the content-addressed store, hash-verified

153// Each returned [*aibridge.RequestBridge] is safe for concurrent use.
154// Each [*aibridge.RequestBridge] is stateful because it has MCP clients which maintain sessions to the configured MCP server.
155func (p *CachedBridgePool) Acquire(ctx context.Context, req Request, clientFn ClientFunc, mcpProxyFactory MCPProxyBuilder) (_ http.Handler, outErr error) {
156 spanAttrs := []attribute.KeyValue{
157 attribute.String(tracing.InitiatorID, req.InitiatorID.String()),
158 attribute.String(tracing.APIKeyID, req.APIKeyID),
159 }
160 ctx, span := p.tracer.Start(ctx, "CachedBridgePool.Acquire", trace.WithAttributes(spanAttrs...))
161 defer tracing.EndSpanErr(span, &outErr)
162 ctx = tracing.WithRequestBridgeAttributesInContext(ctx, spanAttrs)
163
164 if err := ctx.Err(); err != nil {
165 return nil, xerrors.Errorf("acquire: %w", err)
166 }
167
168 select {
169 case <-p.shuttingDownCh:
170 return nil, xerrors.New("pool shutting down")
171 default:
172 }
173
174 // Wait for all buffered writes to be applied, otherwise multiple calls in quick succession
175 // may visit the slow path unnecessarily.
176 defer p.cache.Wait()
177
178 // Fast path.
179 cacheKey := req.InitiatorID.String() + "|" + req.APIKeyID
180 bridge, ok := p.cache.Get(cacheKey)
181 if ok && bridge != nil {
182 // TODO: future improvement:
183 // Once we can detect token expiry against an MCP server, we no longer need to let these instances
184 // expire after the original TTL; we can extend the TTL on each Acquire() call.
185 // For now, we need to let the instance expiry to keep the MCP connections fresh.
186
187 span.AddEvent("cache_hit")
188 return bridge, nil
189 }
190
191 span.AddEvent("cache_miss")
192 providerVersion := p.providerVersion.Load()
193 recorder := aibridge.NewRecorder(p.logger.Named("recorder"), p.tracer, func() (aibridge.Recorder, error) {
194 client, err := clientFn()
195 if err != nil {
196 return nil, xerrors.Errorf("acquire client: %w", err)
197 }
198
199 return &recorderTranslation{apiKeyID: req.APIKeyID, client: client}, nil
200 })
201
202 // Slow path.
203 // Creating an *aibridge.RequestBridge may take some time, so gate all subsequent callers behind the initial request and return the resulting value.
204 // TODO: track startup time since it adds latency to first request (histogram count will also help us see how often this occurs).
205 singleflightKey := cacheKey + "|" + strconv.FormatInt(providerVersion, 10)
206 instance, err, _ := p.singleflight.Do(singleflightKey, func() (*aibridge.RequestBridge, error) {
207 var (
208 mcpServers mcp.ServerProxier
209 err error
210 )
211
212 mcpServers, err = mcpProxyFactory.Build(ctx, req, p.tracer)

Calls 15

loadProvidersMethod · 0.95
EndSpanErrFunction · 0.92
NewRecorderFunction · 0.92
NewRequestBridgeFunction · 0.92
ErrMethod · 0.80
AddEventMethod · 0.80
NamedMethod · 0.80
StartMethod · 0.65
NewMethod · 0.65
WaitMethod · 0.65
GetMethod · 0.65