Skip to content

Commit d5e6666

Browse files
committed
fix: [ha] fixed node aliases
1 parent 6e63d43 commit d5e6666

4 files changed

Lines changed: 121 additions & 8 deletions

File tree

ha-raft/src/main/java/com/arcadedb/server/ha/raft/PeerAddressAllowlistFilter.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,9 @@ private void resolveNow() {
122122

123123
/**
124124
* Extracts just the host component from every entry in the ArcadeDB HA server list.
125-
* Entries follow {@code host:raftPort[:httpPort[:priority]]} or the bracketed IPv6 form.
125+
* Entries follow {@code [name@]host:raftPort[:httpPort[:priority]]} or the bracketed IPv6 form.
126+
* The optional {@code name@} prefix (e.g. {@code frankfurt@10.0.0.1:2434:2480}) is stripped
127+
* before the host is extracted, mirroring {@link RaftPeerAddressResolver#parsePeerList}.
126128
* <p>
127129
* An empty or null serverList returns an empty list rather than throwing, so callers can
128130
* decide whether a missing list is an error.
@@ -134,7 +136,15 @@ static List<String> extractPeerHosts(final String serverList) {
134136
final String[] entries = serverList.split(",");
135137
final List<String> hosts = new ArrayList<>(entries.length);
136138
for (final String entry : entries) {
137-
final String trimmed = entry.trim();
139+
String trimmed = entry.trim();
140+
if (trimmed.isEmpty())
141+
continue;
142+
143+
// Strip the optional human-readable "name@" prefix so it is not mistaken for the host.
144+
// parsePeerList allows only one '@'; here we defensively take the part after the first.
145+
final int atIdx = trimmed.indexOf('@');
146+
if (atIdx >= 0)
147+
trimmed = trimmed.substring(atIdx + 1).trim();
138148
if (trimmed.isEmpty())
139149
continue;
140150

ha-raft/src/main/java/com/arcadedb/server/ha/raft/RaftPeerAddressResolver.java

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -164,10 +164,18 @@ Found a localhost (127.0.0.1) in the server list among non-localhost servers. \
164164
}
165165

166166
/**
167-
* Determines the local peer ID. If {@code peerNames} contains an entry whose value equals the
168-
* {@code serverName}, the corresponding peer is returned. Otherwise the server name is parsed
169-
* for the numeric suffix convention {@code prefix_N} or {@code prefix-N} and used as the index
170-
* into {@code peers} (e.g. {@code arcadedb-0} or {@code ArcadeDB_0} maps to index 0).
167+
* Determines the local peer ID using three strategies, in priority order:
168+
* <ol>
169+
* <li><b>Named peer match:</b> if {@code peerNames} contains an entry whose value equals the
170+
* {@code serverName} (the {@code name@host} syntax), the corresponding peer is returned.</li>
171+
* <li><b>Hostname match:</b> if {@code serverName} equals the host component of a peer's Raft
172+
* address (e.g. server name {@code arcadesplit1} for the list entry
173+
* {@code arcadesplit1:2434:2480}, or an IP), that peer is returned. This lets a plain
174+
* {@code host:raftPort:httpPort} server list work without the {@code prefix_N} convention.</li>
175+
* <li><b>Numeric suffix:</b> the server name is parsed for the {@code prefix_N} / {@code prefix-N}
176+
* convention and used as the zero-based index into {@code peers} (e.g. {@code arcadedb-0}
177+
* or {@code ArcadeDB_0} maps to index 0).</li>
178+
* </ol>
171179
*/
172180
static RaftPeerId findLocalPeerId(final List<RaftPeer> peers, final Map<RaftPeerId, String> peerNames,
173181
final String serverName, final ArcadeDBServer server) {
@@ -177,18 +185,42 @@ static RaftPeerId findLocalPeerId(final List<RaftPeer> peers, final Map<RaftPeer
177185
return entry.getKey();
178186
}
179187

188+
// Match the server name against the host component of each peer's Raft address. Covers the
189+
// plain "host:raftPort:httpPort" (or bare-IP) server list where the node is named after its host.
190+
for (final RaftPeer peer : peers)
191+
if (serverName.equals(RaftHAServer.extractHost(peer.getAddress())))
192+
return peer.getId();
193+
180194
final int separatorIdx;
181195
try {
182196
separatorIdx = findLastSeparatorIndex(serverName);
183197
} catch (final IllegalArgumentException e) {
198+
final List<String> configuredNames = peerNames != null ? new ArrayList<>(peerNames.values()) : List.of();
199+
final List<String> peerHosts = new ArrayList<>(peers.size());
200+
for (final RaftPeer peer : peers)
201+
peerHosts.add(RaftHAServer.extractHost(peer.getAddress()));
184202
throw new IllegalArgumentException(
185-
"Server name '" + serverName + "' did not match any configured peer name and has no '_N' or '-N' suffix",
203+
"Cannot determine which cluster peer this node is: server name '" + serverName
204+
+ "' does not match any configured peer name"
205+
+ (configuredNames.isEmpty() ? "" : " (configured names: " + configuredNames + ")")
206+
+ ", does not match any host in the server list (hosts: " + peerHosts
207+
+ "), and does not end with a node index ('-N' or '_N'). Fix this in one of these ways: "
208+
+ "(1) set 'arcadedb.server.name' to the host of this node as it appears in the server list "
209+
+ "(e.g. one of " + peerHosts + "); "
210+
+ "or (2) use the 'name@host:raftPort:httpPort' syntax in the server list and set "
211+
+ "'arcadedb.server.name' to one of those names; "
212+
+ "or (3) name each node '" + serverName + "-N' (or '_N'), where N is its zero-based position "
213+
+ "in the server list (first entry = 0); for " + peers.size() + " peers the valid suffixes are -0 .. -"
214+
+ (peers.size() - 1) + ".",
186215
e);
187216
}
188217
final int index = Integer.parseInt(serverName.substring(separatorIdx + 1));
189218
if (index < 0 || index >= peers.size())
190219
throw new IllegalArgumentException(
191-
"Server index " + index + " from name '" + serverName + "' is out of range [0, " + peers.size() + ")");
220+
"Server index " + index + " parsed from node name '" + serverName + "' is out of range: the server list has "
221+
+ peers.size() + " peers, so the index must be zero-based in [0, " + peers.size() + "). "
222+
+ "For " + peers.size() + " peers name the nodes with suffixes -0 .. -" + (peers.size() - 1)
223+
+ " (the first server-list entry is index 0, not 1).");
192224

193225
return peers.get(index).getId();
194226
}

ha-raft/src/test/java/com/arcadedb/server/ha/raft/PeerAddressAllowlistFilterTest.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,27 @@ void extractPeerHostsMixedFormats() {
9898
assertThat(hosts).containsExactly("arcadedb-0", "arcadedb-1", "::1");
9999
}
100100

101+
@Test
102+
void extractPeerHostsStripsNamePrefixIPv4() {
103+
// Regression: the optional "name@" prefix must not be treated as part of the host, otherwise
104+
// the allowlist tries to DNS-resolve "arcadesplit1@10.70.2.53" and rejects all peers.
105+
final List<String> hosts = PeerAddressAllowlistFilter.extractPeerHosts(
106+
"arcadesplit1@10.70.2.53:2434:2480,arcadesplit2@10.70.2.70:2434:2480,arcadesplit3@10.70.2.40:2434:2480");
107+
assertThat(hosts).containsExactly("10.70.2.53", "10.70.2.70", "10.70.2.40");
108+
}
109+
110+
@Test
111+
void extractPeerHostsStripsNamePrefixHostname() {
112+
final List<String> hosts = PeerAddressAllowlistFilter.extractPeerHosts("frankfurt@node1:2434:2480");
113+
assertThat(hosts).containsExactly("node1");
114+
}
115+
116+
@Test
117+
void extractPeerHostsStripsNamePrefixIPv6Bracketed() {
118+
final List<String> hosts = PeerAddressAllowlistFilter.extractPeerHosts("london@[fe80::1]:2434:2480");
119+
assertThat(hosts).containsExactly("fe80::1");
120+
}
121+
101122
@Test
102123
void extractPeerHostsSkipsBlankEntries() {
103124
// Trailing comma creates an empty entry

ha-raft/src/test/java/com/arcadedb/server/ha/raft/RaftHAServerTest.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,56 @@ void findLocalPeerIdThrowsWhenSuffixOutOfRange() {
334334
.isInstanceOf(IllegalArgumentException.class);
335335
}
336336

337+
@Test
338+
void findLocalPeerIdMatchesByHostname() {
339+
// Plain "host:raftPort:httpPort" list with the node named after its host (the user's Attempt 2).
340+
// This must now resolve directly, without the prefix_N convention.
341+
final var parsed = RaftPeerAddressResolver.parsePeerList(
342+
"arcadesplit1:2434:2480,arcadesplit2:2434:2480,arcadesplit3:2434:2480", 2434);
343+
assertThat(RaftPeerAddressResolver.findLocalPeerId(parsed.peers(), parsed.peerNames(), "arcadesplit1", null))
344+
.isEqualTo(parsed.peers().get(0).getId());
345+
assertThat(RaftPeerAddressResolver.findLocalPeerId(parsed.peers(), parsed.peerNames(), "arcadesplit3", null))
346+
.isEqualTo(parsed.peers().get(2).getId());
347+
}
348+
349+
@Test
350+
void findLocalPeerIdMatchesByBareIp() {
351+
// Bare-IP server list (the user's Attempt 3) with the node named after its own IP.
352+
final var parsed = RaftPeerAddressResolver.parsePeerList(
353+
"10.70.2.53:2434:2480,10.70.2.70:2434:2480,10.70.2.40:2434:2480", 2434);
354+
assertThat(RaftPeerAddressResolver.findLocalPeerId(parsed.peers(), parsed.peerNames(), "10.70.2.70", null))
355+
.isEqualTo(parsed.peers().get(1).getId());
356+
}
357+
358+
@Test
359+
void findLocalPeerIdNoMatchErrorListsAllThreeOptions() {
360+
// A server name that matches no peer name, no host, and has no numeric suffix must produce a
361+
// message that spells out all three resolution strategies (host name, name@ syntax, -N suffix).
362+
final var parsed = RaftPeerAddressResolver.parsePeerList(
363+
"arcadesplit1:2434:2480,arcadesplit2:2434:2480,arcadesplit3:2434:2480", 2434);
364+
assertThatThrownBy(() -> RaftPeerAddressResolver.findLocalPeerId(
365+
parsed.peers(), parsed.peerNames(), "unrelated", null))
366+
.isInstanceOf(IllegalArgumentException.class)
367+
.hasMessageContaining("arcadedb.server.name")
368+
.hasMessageContaining("arcadesplit1") // listed among the valid host options
369+
.hasMessageContaining("name@host")
370+
.hasMessageContaining("zero-based");
371+
}
372+
373+
@Test
374+
void findLocalPeerIdOutOfRangeErrorIsZeroBased() {
375+
// A 3-node cluster named with 1-based suffixes (-3) is the broken workaround; the message
376+
// must steer the user to zero-based -0 .. -2.
377+
final var parsed = RaftPeerAddressResolver.parsePeerList(
378+
"host1:2434,host2:2435,host3:2436", 2434);
379+
assertThatThrownBy(() -> RaftPeerAddressResolver.findLocalPeerId(
380+
parsed.peers(), parsed.peerNames(), "arcadesplit-3", null))
381+
.isInstanceOf(IllegalArgumentException.class)
382+
.hasMessageContaining("zero-based")
383+
.hasMessageContaining("-0")
384+
.hasMessageContaining("-2");
385+
}
386+
337387
@Test
338388
void extractHostFromHostPort() {
339389
assertThat(RaftHAServer.extractHost("splitter-0.splitter.sbu.svc.cluster.local:2434"))

0 commit comments

Comments
 (0)