Skip to main content

Strategies

MUXPool supports four built-in strategies and a plugin interface for custom routing.

primary-secondary (default)

<strategy>primary-secondary</strategy>

Always tries the first MUX in the <muxes> list. Falls through to the next only if the first is not usable. Waits up to the remaining timeout for a usable MUX to become available.

Use this when one link is preferred and the other is a hot standby. Order the <muxes> list with primary first.

round-robin

<strategy>round-robin</strategy>

Rotates through the MUX list in order, skipping any that are not usable. A global AtomicInteger counter tracks the current position and increments on every request() call.

Use this to spread load evenly across equivalent links. Both links must be logged on and healthy for this to work well — if one is down, the pool skips it and uses the next, but the counter still advances, so the pattern is not strictly 50/50 during degradation.

round-robin-with-override

<strategy>round-robin-with-override</strategy>
<follower-override>0420 0421</follower-override>
<original-channel-field>61</original-channel-field>

Round-robin for new requests, but for follower messages (e.g., reversals) specified in <follower-override>, routes back to the exact channel that handled the original transaction.

The original channel name is read from the ISO field number specified in <original-channel-field> (field 61 in the example). This field must have been set by the application or by a participant when the original request was sent.

Use this when reversal routing must match the original channel — a common acquirer requirement.

split-by-divisor

<strategy>split-by-divisor</strategy>
<split-field>41</split-field>

Routes deterministically based on Integer.parseInt(msg.getString(splitField)) % pool.size. With field 41 (Terminal ID) as the split field, all transactions from the same terminal always go to the same MUX.

Use this when the upstream requires transaction stickiness per terminal or card range.

Custom StrategyHandler

For routing logic that doesn't fit the built-in strategies, implement MUXPool.StrategyHandler:

public class MyStrategy implements MUXPool.StrategyHandler {
@Override
public MUX getMUX(MUXPool pool, ISOMsg m, long maxWait) {
// inspect m and pick a MUX from pool.mux[]
// return null to fall back to the configured built-in strategy
return null;
}
}

Register it in the descriptor:

<mux class="org.jpos.q2.iso.MUXPool" logger="Q2" name="upstream">
<muxes>upstream-a upstream-b</muxes>
<strategy>round-robin</strategy> <!-- fallback if handler returns null -->

<strategy-handler class="com.example.MyStrategy">
<property name="some-config" value="..." />
</strategy-handler>
</mux>

If getMUX() returns null, the pool falls back to the configured <strategy>. This lets you handle special cases (e.g., route 0420 reversals by BIN range) while delegating everything else to round-robin.

Waiting for availability

All strategies share the same availability loop in nextAvailableMUX():

do {
for (int i = 0; i < mux.length; i++) {
int j = (mnumber + i) % mux.length;
if (isUsable(mux[j]))
return mux[j];
msgno.incrementAndGet();
}
ISOUtil.sleep(1000);
} while (System.currentTimeMillis() < maxWait);
return null;

If no MUX is usable, the pool sleeps 1 second and retries until the timeout deadline (maxWait = now + timeout) is reached. The caller's request() then gets null. This means a short timeout can return quickly when the pool is fully down, while a longer timeout gives time for a reconnect to complete.