Skip to content

[BUG] DE connector dedup drops every MCA after the first for multi-MCA merchants #13759

Description

@prajjwalkumar17

Summary

transform_de_output_for_router silently drops every merchant connector account after the first when a merchant holds several MCAs of the same connector, losing both routing targets and fallbacks.

Detail

The function builds the router-facing connector list from a Decision Engine /routing/evaluate response: evaluated_output first, then the remaining connectors from output as fallbacks, deduplicated. The dedup set was keyed on the connector name alone:

if seen.insert(eval_conn.connector) { ... }   // merchant_connector_id not part of the key

RoutableConnectorChoice carries both connector and merchant_connector_id, but only connector went into the set.

Failure case

Any rule that spans two MCAs of one connector — e.g. a volume split across two paypal MCAs. DE returns the sampled winner in evaluated_output and both arms in output; the second paypal MCA is then discarded, so the router loses its fallback and the list length disagrees with the legacy euclid result.

Found while investigating a persistent shadow-mode diff (is_equal_length=false, DE one connector vs HS two) on a sandbox profile with exactly that shape — see #13565.

Fix

Key the dedup set on the full (connector, merchant_connector_id) pair. The second loop builds the RoutableConnectorChoice before keying off it, which also removes a duplicated RoutableConnectors::from_str — the TryFrom<ConnectorInfo> impl already performs that parse with equivalent error logging.

Behaviour note: entries with merchant_connector_id: None no longer collapse into a same-named entry with Some(id). Both lists are serialized from the same ConnectorInfo objects in the same rule, so this is not expected to trigger in practice.

Why it matters

Static routing via the Decision Engine must preserve MCA-level granularity; collapsing by connector name loses both the routing target and its fallbacks for multi-MCA merchants — and generates permanent, unfixable shadow diffs that would trip the kill switch on an otherwise healthy profile.

PRs

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions