2121import org .slf4j .LoggerFactory ;
2222import org .testcontainers .containers .GenericContainer ;
2323import org .testcontainers .containers .wait .strategy .Wait ;
24+ import org .testcontainers .containers .wait .strategy .WaitAllStrategy ;
2425import org .testcontainers .utility .DockerImageName ;
2526
2627import java .time .Duration ;
@@ -93,16 +94,22 @@ private void configureContainer() {
9394 waitingFor (Wait .forLogMessage (".*Hive container ready \\ (pre-initialized\\ )!.*" , 1 )
9495 .withStartupTimeout (Duration .ofMinutes (2 )));
9596 } else if (useFallbackImage ) {
96- // Fallback to apache/hive:3.1.3 - wait for HiveServer2 to be ready
97- // This image uses a different startup sequence
98- waitingFor (Wait .forLogMessage (".*Starting HiveServer2.*" , 1 )
99- .withStartupTimeout (Duration .ofMinutes (5 )));
97+ // Fallback to apache/hive:3.1.3 - wait for both metastore port AND HiveServer2 to be ready
98+ // The metastore must be accepting connections before Drill can use it
99+ WaitAllStrategy waitStrategy = new WaitAllStrategy ()
100+ .withStrategy (Wait .forListeningPort ()) // Wait for metastore port 9083 to be listening
101+ .withStrategy (Wait .forLogMessage (".*Starting HiveServer2.*" , 1 ))
102+ .withStartupTimeout (Duration .ofMinutes (10 )); // Allow up to 10 minutes for full startup
103+ waitingFor (waitStrategy );
100104 } else {
101- // Custom image: wait for both HiveServer2 to start AND test data to be initialized
105+ // Custom image: wait for metastore port AND test data initialization message
102106 // Allow up to 20 minutes: Metastore + HiveServer2 startup (~5-10 min) + data initialization (~5-10 min)
103107 // This is only on first run; container reuse makes subsequent tests fast (~1 second)
104- waitingFor (Wait .forLogMessage (".*Test data loaded and ready for queries.*" , 1 )
105- .withStartupTimeout (Duration .ofMinutes (20 )));
108+ WaitAllStrategy waitStrategy = new WaitAllStrategy ()
109+ .withStrategy (Wait .forListeningPort ()) // Wait for metastore port 9083 to be listening
110+ .withStrategy (Wait .forLogMessage (".*Test data loaded and ready for queries.*" , 1 ))
111+ .withStartupTimeout (Duration .ofMinutes (20 ));
112+ waitingFor (waitStrategy );
106113 }
107114
108115 // Enable reuse for faster test execution
@@ -175,6 +182,11 @@ private static HiveContainer tryStartContainer(String imageName, boolean isFallb
175182 System .out .println ("Pulling Docker image and starting container..." );
176183 long startTime = System .currentTimeMillis ();
177184 container .start ();
185+
186+ // Additional wait for metastore to stabilize
187+ System .out .println ("Waiting for metastore to stabilize..." );
188+ waitForMetastoreReady (container );
189+
178190 long elapsedSeconds = (System .currentTimeMillis () - startTime ) / 1000 ;
179191
180192 System .out .println ("========================================" );
@@ -192,6 +204,39 @@ private static HiveContainer tryStartContainer(String imageName, boolean isFallb
192204 return container ;
193205 }
194206
207+ /**
208+ * Waits for the metastore to be fully ready by attempting socket connections.
209+ * This ensures the Thrift service is actually accepting connections.
210+ */
211+ private static void waitForMetastoreReady (HiveContainer container ) {
212+ int maxAttempts = 30 ;
213+ int attemptDelayMs = 2000 ;
214+ String host = container .getHost ();
215+ int port = container .getMetastorePort ();
216+
217+ for (int i = 1 ; i <= maxAttempts ; i ++) {
218+ try (java .net .Socket socket = new java .net .Socket ()) {
219+ socket .connect (new java .net .InetSocketAddress (host , port ), 1000 );
220+ // Connection successful - wait a bit more for service to fully initialize
221+ Thread .sleep (3000 );
222+ System .out .println ("Metastore is ready and accepting connections" );
223+ return ;
224+ } catch (Exception e ) {
225+ if (i < maxAttempts ) {
226+ logger .debug ("Metastore not ready yet (attempt {}/{}): {}" , i , maxAttempts , e .getMessage ());
227+ try {
228+ Thread .sleep (attemptDelayMs );
229+ } catch (InterruptedException ie ) {
230+ Thread .currentThread ().interrupt ();
231+ throw new RuntimeException ("Interrupted while waiting for metastore" , ie );
232+ }
233+ } else {
234+ throw new RuntimeException ("Metastore failed to become ready after " + maxAttempts + " attempts" , e );
235+ }
236+ }
237+ }
238+ }
239+
195240 /**
196241 * Gets the JDBC URL for connecting to HiveServer2.
197242 *
0 commit comments