Skip to content

Commit b341ed8

Browse files
committed
response to comments
Signed-off-by: roytman <roytman@il.ibm.com>
1 parent 9ae52a1 commit b341ed8

3 files changed

Lines changed: 19 additions & 54 deletions

File tree

cmd/epp/runner/runner.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,7 @@ func (r *Runner) registerInTreePlugins() {
581581
fwkplugin.Register(random.RandomPickerType, random.RandomPickerFactory)
582582
fwkplugin.Register(weightedrandom.WeightedRandomPickerType, weightedrandom.WeightedRandomPickerFactory)
583583
fwkplugin.Register(single.SingleProfileHandlerType, single.SingleProfileHandlerFactory)
584-
fwkplugin.Register(headerphase.HeaderPhaseProfileHandlerType, headerphase.Factory)
584+
fwkplugin.Register(headerphase.HeaderPhaseProfileHandlerType, headerphase.HeaderPhaseProfileHandlerFactory)
585585
fwkplugin.Register(disagg.DisaggHeadersHandlerType, disagg.HeadersHandlerFactory) //nolint:staticcheck // intentional: keep backward compatibility
586586
fwkplugin.Register(disagg.PrefillHeaderHandlerType, disagg.HeadersHandlerFactory) //nolint:staticcheck // intentional: keep backward compatibility
587587
fwkplugin.RegisterWithPluginDependencies(disagg.PdProfileHandlerType, disagg.PdProfileHandlerFactory, //nolint:staticcheck // intentional: keep backward compatibility

pkg/epp/framework/plugins/scheduling/profilehandler/headerphase/header_phase_profile_handler.go

Lines changed: 17 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ type parameters struct {
6262
DefaultProfile string `json:"defaultProfile"`
6363
}
6464

65-
// Factory defines the factory function for HeaderPhaseProfileHandler.
66-
func Factory(name string, rawParameters *json.Decoder, _ fwkplugin.Handle) (fwkplugin.Plugin, error) {
65+
// HeaderPhaseProfileHandlerFactory defines the factory function for HeaderPhaseProfileHandler.
66+
func HeaderPhaseProfileHandlerFactory(name string, rawParameters *json.Decoder, _ fwkplugin.Handle) (fwkplugin.Plugin, error) {
6767
params := parameters{}
6868
if rawParameters != nil {
6969
if err := rawParameters.Decode(&params); err != nil {
@@ -74,17 +74,10 @@ func Factory(name string, rawParameters *json.Decoder, _ fwkplugin.Handle) (fwkp
7474
return NewHeaderPhaseProfileHandler(params.HeaderName, params.DefaultProfile).WithName(name), nil
7575
}
7676

77-
// NewHeaderPhaseProfileHandler initializes a new HeaderPhaseProfileHandler and returns
78-
// its pointer.
79-
//
80-
// headerName is lowercased and trimmed, falling back to defaultHeaderName when that
81-
// leaves it empty: the EPP's request handler lowercases every incoming header name at
82-
// ingestion (pkg/epp/handlers/request.go), so the configured name must be normalized the
83-
// same way to match, and an empty header key would never match any request.
84-
//
85-
// defaultProfile is trimmed, falling back to defaultProfileName when that leaves it
86-
// empty. Unlike headerName, it is not case-normalized: it names a schedulingProfiles
87-
// entry, which (like the header value itself) is matched case-sensitively.
77+
// NewHeaderPhaseProfileHandler trims and defaults both arguments, additionally
78+
// lowercasing headerName to match how the EPP stores ingested header keys
79+
// (pkg/epp/handlers/request.go); defaultProfile stays case-sensitive since it names a
80+
// schedulingProfiles entry.
8881
func NewHeaderPhaseProfileHandler(headerName, defaultProfile string) *HeaderPhaseProfileHandler {
8982
headerName = strings.ToLower(strings.TrimSpace(headerName))
9083
if headerName == "" {
@@ -103,18 +96,10 @@ func NewHeaderPhaseProfileHandler(headerName, defaultProfile string) *HeaderPhas
10396
}
10497
}
10598

106-
// HeaderPhaseProfileHandler runs exactly one scheduling profile per request: the one
107-
// named by the value of a request header. This lets a single EPP instance serve several
108-
// phases of a disaggregated pipeline (e.g. encode, prefill, decode) whose caller already
109-
// knows, out of band, which phase each request is for - unlike the disagg profile
110-
// handler, which decides which profiles to run via decider plugins.
111-
//
112-
// Two fallbacks keep single-stage and header-less traffic working without a different
113-
// profile handler: with exactly one configured profile there is nothing to choose,
114-
// so that profile always runs regardless of the header (or its absence); with more than
115-
// one configured profile, a request whose header is missing or blank runs defaultProfile
116-
// instead of failing. A header naming a profile that isn't configured is still an error -
117-
// only the header's absence triggers the default, not an unrecognized value.
99+
// HeaderPhaseProfileHandler runs exactly one scheduling profile per request, named by a
100+
// request header, falling back to the sole configured profile or to defaultProfile when
101+
// there's nothing to choose or the header is missing/blank; an unrecognized header value
102+
// is still an error.
118103
type HeaderPhaseProfileHandler struct {
119104
typedName fwkplugin.TypedName
120105
headerName string
@@ -151,28 +136,13 @@ func (h *HeaderPhaseProfileHandler) noMatchError(phase string) error {
151136
return fmt.Errorf("header-phase profile handler: no scheduling profile configured for %q header value %q", h.headerName, phase)
152137
}
153138

154-
// defaultProfileNotConfiguredError explains that defaultProfile itself isn't a
155-
// configured scheduling profile. Distinct from noMatchError: this is a configuration
156-
// bug that fails every request whose phase header is missing or blank, not an ordinary
157-
// client-side unrecognized header value.
139+
// defaultProfileNotConfiguredError reports defaultProfile itself missing from
140+
// schedulingProfiles - a config bug, distinct from noMatchError's per-request issue.
158141
func (h *HeaderPhaseProfileHandler) defaultProfileNotConfiguredError() error {
159142
return fmt.Errorf("header-phase profile handler: defaultProfile %q is not a configured scheduling profile", h.defaultProfile)
160143
}
161144

162-
// Pick selects the single SchedulingProfile to run: the only configured profile when
163-
// there is just one, otherwise the one named by the request's phase header, falling
164-
// back to defaultProfile when the header is missing or blank. It returns an empty map
165-
// once that profile has run, or when no profile could be resolved. In the latter case
166-
// the scheduler's run loop (pkg/epp/scheduling.Scheduler.Schedule) stops without ever
167-
// calling ProcessResults, so the specific reason is logged here rather than returned
168-
// from ProcessResults, where it would be unreachable. The client never sees that
169-
// reason: it only gets the scheduler's generic "failed to run any scheduler profile"
170-
// error, which pkg/epp/requestcontrol/director.go maps to a 429 ResourceExhausted
171-
// response - misleading, since a malformed or missing header is a client error, not a
172-
// capacity problem. Surfacing the real reason to the client needs a
173-
// scheduler/ProfileHandler contract change and is out of scope here; the log is a
174-
// diagnostic aid for operators, not an equivalent substitute for what the caller
175-
// receives.
145+
// Pick implements fwksched.ProfileHandler.Pick; see README.md for behavior.
176146
func (h *HeaderPhaseProfileHandler) Pick(ctx context.Context, request *fwksched.InferenceRequest, profiles map[string]fwksched.SchedulerProfile,
177147
profileResults map[string]*fwksched.ProfileRunResult) map[string]fwksched.SchedulerProfile {
178148
if len(profileResults) > 0 { // the selected profile has already run
@@ -196,19 +166,14 @@ func (h *HeaderPhaseProfileHandler) Pick(ctx context.Context, request *fwksched.
196166

197167
profile, ok := profiles[resolvedPhase]
198168
if !ok {
199-
// A missing or unrecognized header value is a per-request client issue, not a
200-
// system fault - log via Info, not Error, so it doesn't page anyone or drown out
201-
// real errors. It's still a once-per-request operational signal an operator needs
202-
// to see by default, so it's unguarded rather than gated behind a verbosity level
203-
// that's off in production.
204-
// A missing header whose defaultProfile substitute also fails to resolve is a
205-
// distinct, config-time condition - it fails every header-less request, not just
206-
// an occasional bad caller - so it gets its own message rather than being
207-
// reported as an ordinary missing header.
208169
err := h.noMatchError(phase)
209170
if phase == "" {
210171
err = h.defaultProfileNotConfiguredError()
211172
}
173+
// Logged here, not returned from ProcessResults (skipped when Pick returns empty),
174+
// at Info, not Error, since a bad or missing header is a client issue an operator
175+
// still needs to see by default.
176+
// See README.md for why the client only gets a generic 429 instead of this reason.
212177
log.FromContext(ctx).Info("no scheduling profile selected for request", "error", err)
213178
return map[string]fwksched.SchedulerProfile{}
214179
}

pkg/epp/framework/plugins/scheduling/profilehandler/headerphase/header_phase_profile_handler_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ func TestHeaderPhaseProfileHandlerFactory(t *testing.T) {
123123
// (DisallowUnknownFields), so use it here too rather than a plain decoder.
124124
decoder := fwkplugin.StrictDecoder(json.RawMessage(tt.rawParameters))
125125

126-
plugin, err := Factory("custom-name", decoder, nil)
126+
plugin, err := HeaderPhaseProfileHandlerFactory("custom-name", decoder, nil)
127127
if tt.wantErr {
128128
if err == nil {
129129
t.Fatalf("Factory() expected error, got nil")

0 commit comments

Comments
 (0)