Skip to content

Commit 101fc92

Browse files
author
opencode
committed
fix(testcontainers): add active rw probe to vshard storages readiness check
The readiness check from 3ad515a relied on vshard.router.info() reporting bucket.available_rw == vshard.router.bucket_count(). This is a passive view of the router-side bucket accounting and does not always match the actual rw-handshaked state of each storage in the early moments of a fresh cluster: the third CI run (run 27611921481) still failed with 'Timeout exceeded while waiting for vshard storages to complete handshake' on spring-data32.RepositoryViaJavaConfigTest, even though the previous data27/data31 classes had started successfully within the same Maven reactor. Add a second, active tier to the check: after the passive vshard.router.info() pre-check passes, perform a real read-write call (vshard.router.callrw(1, 'box.info', {})) wrapped in pcall. This is the most direct signal that the storage-side handshake has actually finished for at least one storage. A successful rw call proves that the very first CRUD call (e.g. truncate issued by @beforeeach) will not fail with VHANDSHAKE_NOT_COMPLETE. The probe is intentionally kept on a single Lua line because the testcontainers command pipe flattens newlines, which would otherwise confuse the Tarantool Lua parser around 'end' (observed in local verification: the multi-line form produced 'end expected near local'). Verified locally on 3.5.0: - spring-data31 TarantoolTemplateViaJavaConfigTest: 35/35 PASS - spring-data27 TarantoolTemplateViaJavaConfigTest: 35/35 PASS
1 parent 3ad515a commit 101fc92

1 file changed

Lines changed: 51 additions & 10 deletions

File tree

testcontainers/src/main/java/org/testcontainers/containers/cluster/vshard/VshardClusterContainer.java

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,19 @@ public class VshardClusterContainer extends GenericContainer<VshardClusterContai
8787
+ " local total = vshard.router.bucket_count();"
8888
+ " return { total, b.available_rw or 0, b.available_ro or 0,"
8989
+ " b.unreachable or 0, b.unknown or 0 }";
90+
// Actively exercises a read-write call (box.info) on a real storage through the router.
91+
// This is the most reliable signal that vshard storage-side handshakes have completed:
92+
// a successful callrw proves that at least one storage can serve a rw request, which is
93+
// exactly the precondition for CRUD operations such as truncate. Returns the call result
94+
// (or the error message) so the caller can distinguish VHANDSHAKE_NOT_COMPLETE from a
95+
// successful probe. Kept on a single Lua line because the testcontainers command pipe
96+
// flattens newlines, which would otherwise confuse the Tarantool Lua parser around `end`.
97+
protected static final String VSHARD_RW_PROBE_COMMAND =
98+
"local vshard = require('vshard'); "
99+
+ "local function probe() return vshard.router.callrw(1, 'box.info', {}) end; "
100+
+ "local ok, res = pcall(probe); "
101+
+ "if ok then return { true, '' } end; "
102+
+ "return { false, tostring(res) }";
90103
private static final ObjectMapper YAML_MAPPER = new ObjectMapper(new YAMLFactory());
91104

92105
public static final String ENV_TARANTOOL_VERSION = "TARANTOOL_VERSION";
@@ -670,25 +683,53 @@ protected boolean vshardIsBootstrapped() {
670683
}
671684

672685
/**
673-
* Returns {@code true} when {@code vshard.router.info()} reports every replica as {@code
674-
* available}, there are no unreachable or unknown buckets, and every bucket is ready for
675-
* read-write (i.e. the vshard handshake with each storage has fully completed on the storage
676-
* side, not just the network). See {@link #waitUntilVshardStoragesAreReady(int)} for rationale.
686+
* Returns {@code true} once the vshard cluster is fully ready to serve read-write requests. The
687+
* check is intentionally two-tiered:
688+
*
689+
* <ol>
690+
* <li><b>Pre-check</b> via {@code vshard.router.info()}: every replicaset has a master, every
691+
* replica is {@code available}, there are no unreachable/unknown buckets. Fails fast on
692+
* topology problems.
693+
* <li><b>Active probe</b> via {@code vshard.router.callrw(1, 'box.info', {})}: a real
694+
* read-write call routed to a storage. This is the most reliable signal that the vshard
695+
* handshake on each storage has completed; without it, the very first CRUD call (e.g. a
696+
* {@code truncate} issued by {@code @BeforeEach}) can fail with {@code
697+
* VHANDSHAKE_NOT_COMPLETE} (vshard code 40) even when the topology check passes.
698+
* </ol>
699+
*
700+
* See {@link #waitUntilVshardStoragesAreReady(int)} for rationale.
677701
*/
678702
protected boolean vshardStoragesAreReady() {
679703
try {
680-
List<?> result =
704+
List<?> preCheck =
681705
TarantoolContainerClientHelper.executeCommandDecoded(
682706
this, VSHARD_STORAGES_READY_COMMAND, null);
683-
if (result.isEmpty()) {
684-
logger().warn("Vshard storages readiness probe returned an empty response");
707+
boolean preOk = !preCheck.isEmpty() && Boolean.TRUE.equals(preCheck.get(0));
708+
if (!preOk) {
709+
logVshardStoragesStatus();
710+
return false;
711+
}
712+
// Active rw probe: succeeds only when at least one storage is fully handshaked for rw.
713+
// The Lua command returns a list {ok, msg}; the YAML decoder wraps it once more, so we
714+
// unwrap one level before inspecting the boolean.
715+
List<?> wrapped =
716+
TarantoolContainerClientHelper.executeCommandDecoded(this, VSHARD_RW_PROBE_COMMAND, null);
717+
if (wrapped.size() < 1 || !(wrapped.get(0) instanceof List)) {
718+
logger().warn("Vshard rw probe returned an unexpected response: {}", wrapped);
719+
return false;
720+
}
721+
List<?> probe = (List<?>) wrapped.get(0);
722+
if (probe.size() < 2) {
723+
logger().warn("Vshard rw probe returned a malformed payload: {}", probe);
685724
return false;
686725
}
687-
boolean ready = Boolean.TRUE.equals(result.get(0));
688-
if (!ready) {
726+
boolean rwOk = Boolean.TRUE.equals(probe.get(0));
727+
if (!rwOk) {
728+
String err = String.valueOf(probe.get(1));
729+
logger().warn("Vshard rw probe failed: {}", err);
689730
logVshardStoragesStatus();
690731
}
691-
return ready;
732+
return rwOk;
692733
} catch (Exception e) {
693734
logger().warn("Vshard storages readiness probe failed: {}", e.getMessage());
694735
return false;

0 commit comments

Comments
 (0)