juspay / juspay/decision-engine

Sticky routing: decide-time override with health veto

Open
#416 0 comments 0 reactions 1 assignee Claimed by @prajjwalkumar17 View on GitHub
Dominant language
Rust
Stars
128
Forks
36
Avg merge
1d 15h
Merged PRs (30d)
34

Description

Part of #393 (sticky routing). **Status: implemented** in #412 (commit `4d396d2`), pending review/merge.

The decide-time override runs **last** in `run_decider_flow` — after SR scoring, outage/elimination penalties, and the cost/volume post-steps — and pins the customer's highest-success-count connector for the exact `PM:PMT` combo (one `HGETALL`). It applies only when ALL of:

- merchant kill switch `sticky_routing_enabled` is on, and `paymentInfo.customerId` is present (already in the public decide-gateway contract);
- the current approach is in the **SR-selection family** — priority logic, merchant preference, downtime relabels, and hedging exploration are never overridden;
- the request's optional `stickyRouting` override isn't `false` (absent = eligible, mirroring `enableMultiObjective`);
- **health veto**: the pinned connector is in the caller's `eligibleGatewayList` (the V2 flow's entire eligibility set) and its post-elimination score is ≥ `STICKY_ROUTING_MIN_SCORE_RATIO` (default 0.5) × top score — a pin can never resurrect a connector the outage/elimination passes just buried.

Label semantics (load-bearing for scoring):
- Every applied pin reports `routing_approach: STICKY_ROUTING` (agreeing or diverging); a divergent pin additionally clears superseded multi-objective/volume-steer claims so cost analytics can't credit a pick that never reached the customer. Agree-vs-diverge stays visible in the sticky decision metrics.
- `STICKY` is admitted by the SRv3 producer-isolation and explore/exploit gates (same precedent as `SR_SELECTION_MULTI_OBJECTIVE`), so pinned outcomes keep feeding the SR windows and the health veto stays live — without this, a pinned connector's score would freeze and sticky would keep pinning a degrading connector forever.

Sticky is independent of the euclid rule store: no static-outcome gating — precedence is PL/euclid explicit orders first (they bound the candidate set), then sticky, then SR, then the default ordering (the gate admits the Default approach so a pin still wins when SR is off). Everything fails open to the plain SR pick.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.