History

perf(api): skip inactive request interceptors

Why: - The plugin host can exist without active request interceptors. - The before-auth path still cloned large request bodies four times in that case. What: - Reuse the existing request interceptor capability detector before dispatch. - Add pointer-reuse regression coverage and allocation benchmarks. Validation: - go test ./... -count=1 - go build -o /tmp/test-output ./cmd/server - focused race test passed; the full handler race run still hits an existing stream-header race reproduced on origin/dev

Shawn G committed 4 months ago

Commit 970529b1 parent 09da52a2 files · +80 −1Browse files

Unified · Split

2 files
  1. sdk/api/handlers/handlers.go +1 −1
  2. sdk/api/handlers/handlers_interceptors_test.go +79 −0
sdk/api/handlers/handlers.go+1−1
@@ -2064,7 +2064,7 @@ func interceptStreamChunk(ctx context.Context, host PluginInterceptorHost, req p · 2063 unmodified lines
20642064
20652065 func (h *BaseAPIHandler) applyRequestInterceptorsBeforeAuth(ctx context.Context, handlerType, requestedModel string, req coreexecutor.Request, opts coreexecutor.Options, skipPluginID string) (coreexecutor.Request, coreexecutor.Options) {
20662066 host := h.interceptorHost()
2067− if host == nil {
2067+ if !requestInterceptorsEnabled(host) {
20682068 return req, opts
20692069 }
20702070 resp := interceptRequestBeforeAuth(ctx, host, pluginapi.RequestInterceptRequest{
sdk/api/handlers/handlers_interceptors_test.go+79−0
@@ -29,10 +29,18 @@ type handlerInterceptorNoStreamTestHost struct { · 28 unmodified lines
2929 *handlerInterceptorTestHost
3030 }
3131
32+type handlerInterceptorDisabledRequestTestHost struct {
33+ *handlerInterceptorTestHost
34+}
35+
3236 func (h *handlerInterceptorNoStreamTestHost) HasStreamInterceptors() bool {
3337 return false
3438 }
3539
40+func (h *handlerInterceptorDisabledRequestTestHost) HasRequestInterceptors() bool {
41+ return false
42+}
43+
3644 func (h *handlerInterceptorTestHost) InterceptRequestBeforeAuth(ctx context.Context, req pluginapi.RequestInterceptRequest) pluginapi.RequestInterceptResponse {
3745 if h != nil && h.interceptRequestBeforeAuth != nil {
3846 return h.interceptRequestBeforeAuth(ctx, req)
@@ -255,6 +263,77 @@ func TestHandlerRequestInterceptorRewritesExecutorRequest(t *testing.T) { · 216 unmodified lines
255263 }
256264 }
257265
266+func TestHandlerSkipsDisabledRequestInterceptorsWithoutCopyingPayload(t *testing.T) {
267+ payload := []byte(`{"model":"disabled-interceptor-model"}`)
268+ called := false
269+ handler := NewBaseAPIHandlers(&sdkconfig.SDKConfig{}, nil)
270+ handler.SetPluginHost(&handlerInterceptorDisabledRequestTestHost{
271+ handlerInterceptorTestHost: &handlerInterceptorTestHost{
272+ interceptRequestBeforeAuth: func(context.Context, pluginapi.RequestInterceptRequest) pluginapi.RequestInterceptResponse {
273+ called = true
274+ return pluginapi.RequestInterceptResponse{Body: []byte(`{"unexpected":true}`)}
275+ },
276+ },
277+ })
278+
279+ req := coreexecutor.Request{Model: "disabled-interceptor-model", Payload: payload}
280+ opts := coreexecutor.Options{OriginalRequest: payload}
281+ gotReq, gotOpts := handler.applyRequestInterceptorsBeforeAuth(context.Background(), "openai", req.Model, req, opts, "")
282+
283+ if called {
284+ t.Fatal("disabled request interceptor was called")
285+ }
286+ if &gotReq.Payload[0] != &payload[0] {
287+ t.Fatal("request payload was copied")
288+ }
289+ if &gotOpts.OriginalRequest[0] != &payload[0] {
290+ t.Fatal("original request was copied")
291+ }
292+}
293+
294+func BenchmarkHandlerRequestInterceptors(b *testing.B) {
295+ sizes := []struct {
296+ name string
297+ bytes int
298+ }{
299+ {name: "1KiB", bytes: 1 << 10},
300+ {name: "1MiB", bytes: 1 << 20},
301+ {name: "8MiB", bytes: 8 << 20},
302+ }
303+ hosts := []struct {
304+ name string
305+ host PluginInterceptorHost
306+ }{
307+ {
308+ name: "disabled",
309+ host: &handlerInterceptorDisabledRequestTestHost{
310+ handlerInterceptorTestHost: &handlerInterceptorTestHost{},
311+ },
312+ },
313+ {name: "active", host: &handlerInterceptorTestHost{}},
314+ }
315+
316+ for _, size := range sizes {
317+ payload := make([]byte, size.bytes)
318+ req := coreexecutor.Request{Model: "benchmark-model", Payload: payload}
319+ opts := coreexecutor.Options{OriginalRequest: payload}
320+ for _, host := range hosts {
321+ b.Run(host.name+"/"+size.name, func(b *testing.B) {
322+ handler := NewBaseAPIHandlers(&sdkconfig.SDKConfig{}, nil)
323+ handler.SetPluginHost(host.host)
324+ b.ReportAllocs()
325+ b.ResetTimer()
326+ for range b.N {
327+ gotReq, gotOpts := handler.applyRequestInterceptorsBeforeAuth(context.Background(), "openai", req.Model, req, opts, "")
328+ if len(gotReq.Payload) != size.bytes || len(gotOpts.OriginalRequest) != size.bytes {
329+ b.Fatal("request payload length changed")
330+ }
331+ }
332+ })
333+ }
334+ }
335+}
336+
258337 func TestHandlerRequestInterceptorEmptyBodyKeepsOriginalPayload(t *testing.T) {
259338 model := "handler-interceptor-empty-body-model"
260339 executor := &interceptorCaptureExecutor{}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t