|
82 | 82 | * advertised address points back at itself, so a single-broker embedded cluster needs no special config |
83 | 83 | * (its own ephemeral port is discovered from {@code bootstrapServers()}). |
84 | 84 | * |
| 85 | + * <p><b>Single broker only.</b> Because every advertised broker and coordinator address is rewritten to this |
| 86 | + * one proxy — which forwards to a single upstream broker — the proxy supports exactly one broker. A |
| 87 | + * multi-broker bootstrap would silently funnel every partition and coordinator to the first broker, producing |
| 88 | + * {@code NOT_LEADER_OR_FOLLOWER}/{@code NOT_COORDINATOR} retry loops, so {@link #inFrontOf(String)} rejects a |
| 89 | + * bootstrap that lists more than one server. Front a single-broker {@code EmbeddedKafkaCluster}, or pass one |
| 90 | + * broker's address (e.g. {@code cluster.bootstrapServers().split(",")[0]}). |
| 91 | + * |
85 | 92 | * <p>Determinism: {@code once()}/{@code onCall(n)}/{@code times(n)} are deterministic and safe for |
86 | 93 | * assertions; {@code withProbability(p)} is chaos-mode only. The proxy never closes sockets unless a |
87 | 94 | * {@code disconnectOn(...)} rule fires, so it is not itself a source of flakiness. |
@@ -140,13 +147,27 @@ public final class KafkaProtocolFaultProxy implements AutoCloseable { |
140 | 147 | private volatile int proxyPort; |
141 | 148 |
|
142 | 149 | private KafkaProtocolFaultProxy(final String targetBootstrap) { |
143 | | - final String hostPort = targetBootstrap.split(",")[0].trim(); |
| 150 | + final String[] servers = targetBootstrap.split(","); |
| 151 | + // Routing rewrites all addresses to this proxy (one upstream broker), so a multi-broker bootstrap |
| 152 | + // would funnel every partition and coordinator to the first broker |
| 153 | + // causing NOT_LEADER / NOT_COORDINATOR loops. |
| 154 | + if (servers.length > 1) { |
| 155 | + throw new IllegalArgumentException( |
| 156 | + "KafkaProtocolFaultProxy proxies a single broker, but the bootstrap '" + targetBootstrap |
| 157 | + + "' lists " + servers.length + " servers"); |
| 158 | + } |
| 159 | + final String hostPort = servers[0].trim(); |
144 | 160 | final int idx = hostPort.lastIndexOf(':'); |
145 | 161 | this.targetHost = hostPort.substring(0, idx); |
146 | 162 | this.targetPort = Integer.parseInt(hostPort.substring(idx + 1)); |
147 | 163 | } |
148 | 164 |
|
149 | | - /** Create and start a proxy in front of the given broker bootstrap address. */ |
| 165 | + /** |
| 166 | + * Create and start a proxy in front of the given broker bootstrap address. |
| 167 | + * |
| 168 | + * @param targetBootstrap a single broker's {@code host:port} — the proxy supports one broker only |
| 169 | + * @throws IllegalArgumentException if {@code targetBootstrap} lists more than one server |
| 170 | + */ |
150 | 171 | public static KafkaProtocolFaultProxy inFrontOf(final String targetBootstrap) { |
151 | 172 | final KafkaProtocolFaultProxy proxy = new KafkaProtocolFaultProxy(targetBootstrap); |
152 | 173 | try { |
|
0 commit comments