This is an automated email from the ASF dual-hosted git repository.

fresh-borzoni pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fluss-rust.git


The following commit(s) were added to refs/heads/main by this push:
     new 9f69ae46 [tests] replace fixed tokio::time::sleep workarounds with 
wait_for_table_ready helper. (#564)
9f69ae46 is described below

commit 9f69ae46529d8d82b65fd74dcd00315b02074ab8
Author: slfan1989 <[email protected]>
AuthorDate: Thu May 28 15:54:53 2026 +0800

    [tests] replace fixed tokio::time::sleep workarounds with 
wait_for_table_ready helper. (#564)
    
    * [tests] replace fixed tokio::time::sleep workarounds with 
wait_for_table_ready helper.
    
    * [tests] replace fixed tokio::time::sleep workarounds with 
wait_for_table_ready helper.
    
    * [tests] replace fixed tokio::time::sleep workarounds with 
wait_for_table_ready helper.
---
 crates/fluss/tests/integration/log_table.rs        |  11 +-
 .../tests/integration/record_batch_log_reader.rs   |  13 +-
 crates/fluss/tests/integration/utils.rs            | 157 +++++++++++++++++----
 3 files changed, 142 insertions(+), 39 deletions(-)

diff --git a/crates/fluss/tests/integration/log_table.rs 
b/crates/fluss/tests/integration/log_table.rs
index 8bad7398..e2377e1d 100644
--- a/crates/fluss/tests/integration/log_table.rs
+++ b/crates/fluss/tests/integration/log_table.rs
@@ -22,7 +22,7 @@ mod table_test {
         ColumnPlan, array_dt_basics_columns, as_row_type, create_partitions, 
create_table,
         dt_array_int, dt_map_string_int, dt_row_seq_label, 
extract_ids_from_batches,
         get_shared_cluster, make_int_array, make_string_array, 
map_dt_basics_columns,
-        row_dt_basics_columns, scalar_dt_columns,
+        row_dt_basics_columns, scalar_dt_columns, wait_for_partitions_ready, 
wait_for_table_ready,
     };
     use arrow::array::record_batch;
     use fluss::client::{EARLIEST_OFFSET, FlussTable, TableScan};
@@ -169,8 +169,7 @@ mod table_test {
 
         create_table(&admin, &table_path, &table_descriptor).await;
 
-        // Wait for table to be fully initialized
-        tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
+        wait_for_table_ready(&admin, &table_path).await;
 
         // Test earliest offset (should be 0 for empty table)
         let earliest_offsets = admin
@@ -475,7 +474,7 @@ mod table_test {
             &TableDescriptor::builder().schema(schema).build().unwrap(),
         )
         .await;
-        tokio::time::sleep(Duration::from_secs(1)).await;
+        wait_for_table_ready(&admin, &table_path).await;
 
         let table = connection.get_table(&table_path).await.unwrap();
         let scanner = 
table.new_scan().create_record_batch_log_scanner().unwrap();
@@ -595,8 +594,8 @@ mod table_test {
         // Create partitions
         create_partitions(&admin, &table_path, "region", &["US", "EU"]).await;
 
-        // Wait for partitions to be available
-        tokio::time::sleep(Duration::from_secs(2)).await;
+        // Wait for partition bucket leaders to be available.
+        wait_for_partitions_ready(&admin, &table_path, &["US", "EU"]).await;
 
         let table = connection
             .get_table(&table_path)
diff --git a/crates/fluss/tests/integration/record_batch_log_reader.rs 
b/crates/fluss/tests/integration/record_batch_log_reader.rs
index 13836453..cf89c065 100644
--- a/crates/fluss/tests/integration/record_batch_log_reader.rs
+++ b/crates/fluss/tests/integration/record_batch_log_reader.rs
@@ -21,6 +21,7 @@
 mod reader_test {
     use crate::integration::utils::{
         create_partitions, create_table, extract_ids_from_batches, 
get_shared_cluster,
+        wait_for_partitions_ready, wait_for_table_buckets_ready, 
wait_for_table_ready,
     };
     use arrow::array::record_batch;
     use fluss::client::{EARLIEST_OFFSET, FlussConnection, 
RecordBatchLogReader};
@@ -48,7 +49,7 @@ mod reader_test {
             .build()
             .expect("Failed to build table");
         create_table(&admin, &table_path, &table_descriptor).await;
-        tokio::time::sleep(Duration::from_secs(1)).await;
+        wait_for_table_ready(&admin, &table_path).await;
 
         let table = connection
             .get_table(&table_path)
@@ -121,7 +122,7 @@ mod reader_test {
             .build()
             .expect("Failed to build table");
         create_table(&admin, &table_path, &table_descriptor).await;
-        tokio::time::sleep(Duration::from_secs(1)).await;
+        wait_for_table_ready(&admin, &table_path).await;
 
         let table = connection
             .get_table(&table_path)
@@ -189,7 +190,7 @@ mod reader_test {
             .build()
             .expect("Failed to build table");
         create_table(&admin, &table_path, &table_descriptor).await;
-        tokio::time::sleep(Duration::from_secs(1)).await;
+        wait_for_table_ready(&admin, &table_path).await;
 
         let table = connection
             .get_table(&table_path)
@@ -284,7 +285,7 @@ mod reader_test {
             .build()
             .expect("Failed to build table");
         create_table(&admin, &table_path, &table_descriptor).await;
-        tokio::time::sleep(Duration::from_secs(1)).await;
+        wait_for_table_buckets_ready(&admin, &table_path, &[0, 1]).await;
 
         let table = connection
             .get_table(&table_path)
@@ -380,7 +381,7 @@ mod reader_test {
             .expect("Failed to build table");
 
         create_table(&admin, &table_path, &table_descriptor).await;
-        tokio::time::sleep(Duration::from_secs(1)).await;
+        wait_for_table_ready(&admin, &table_path).await;
 
         let table = connection
             .get_table(&table_path)
@@ -453,7 +454,7 @@ mod reader_test {
 
         create_table(&admin, &table_path, &table_descriptor).await;
         create_partitions(&admin, &table_path, "region", &["US", "EU"]).await;
-        tokio::time::sleep(Duration::from_secs(2)).await;
+        wait_for_partitions_ready(&admin, &table_path, &["US", "EU"]).await;
 
         let table = connection
             .get_table(&table_path)
diff --git a/crates/fluss/tests/integration/utils.rs 
b/crates/fluss/tests/integration/utils.rs
index 934b6626..2ebe31d9 100644
--- a/crates/fluss/tests/integration/utils.rs
+++ b/crates/fluss/tests/integration/utils.rs
@@ -24,7 +24,9 @@ use fluss::metadata::{
 use fluss::record::ScanBatch;
 use fluss::row::FlussArray;
 use fluss::row::binary_array::FlussArrayWriter;
+use fluss::rpc::message::OffsetSpec;
 use std::collections::HashMap;
+use std::future::Future;
 use std::sync::Arc;
 use std::sync::LazyLock;
 use std::time::Duration;
@@ -100,6 +102,110 @@ pub async fn create_table(
         .expect("Failed to create table");
 }
 
+const READINESS_TIMEOUT: Duration = Duration::from_secs(30);
+const READINESS_POLL_INTERVAL: Duration = Duration::from_millis(200);
+
+async fn poll_until<F, Fut>(
+    timeout: Duration,
+    interval: Duration,
+    timeout_message: String,
+    mut probe: F,
+) where
+    F: FnMut() -> Fut,
+    Fut: Future<Output = Result<(), String>>,
+{
+    let start = std::time::Instant::now();
+
+    loop {
+        match probe().await {
+            Ok(()) => return,
+            Err(err) => {
+                if start.elapsed() >= timeout {
+                    panic!(
+                        "{timeout_message} after {} seconds. Last error: 
{err}",
+                        timeout.as_secs()
+                    );
+                }
+            }
+        }
+
+        tokio::time::sleep(interval).await;
+    }
+}
+
+/// Waits until the default bucket of a non-partitioned table can serve offset 
requests.
+///
+/// Newly-created tables may not have bucket leaders immediately. Polling list 
offsets avoids
+/// fixed sleeps that are either flaky on slow CI or waste time when the 
cluster is ready sooner.
+pub async fn wait_for_table_ready(admin: &FlussAdmin, table_path: &TablePath) {
+    wait_for_table_buckets_ready(admin, table_path, &[0]).await;
+}
+
+/// Waits until the specified buckets of a non-partitioned table can serve 
offset requests.
+pub async fn wait_for_table_buckets_ready(
+    admin: &FlussAdmin,
+    table_path: &TablePath,
+    buckets: &[i32],
+) {
+    poll_until(
+        READINESS_TIMEOUT,
+        READINESS_POLL_INTERVAL,
+        format!("Timed out waiting for table '{table_path}' buckets 
{buckets:?} to become ready"),
+        || async {
+            admin
+                .list_offsets(table_path, buckets, OffsetSpec::Latest)
+                .await
+                .map(|_| ())
+                .map_err(|err| format!("{err:?}"))
+        },
+    )
+    .await;
+}
+
+/// Waits until all listed partition values can serve offset requests for the 
default bucket.
+pub async fn wait_for_partitions_ready(
+    admin: &FlussAdmin,
+    table_path: &TablePath,
+    partition_values: &[&str],
+) {
+    for partition_value in partition_values {
+        wait_for_partition_ready(admin, table_path, partition_value).await;
+    }
+}
+
+/// Waits until one partition value can serve offset requests for the default 
bucket.
+pub async fn wait_for_partition_ready(
+    admin: &FlussAdmin,
+    table_path: &TablePath,
+    partition_value: &str,
+) {
+    wait_for_partition_buckets_ready(admin, table_path, partition_value, 
&[0]).await;
+}
+
+/// Waits until the specified buckets of a partition can serve offset requests.
+pub async fn wait_for_partition_buckets_ready(
+    admin: &FlussAdmin,
+    table_path: &TablePath,
+    partition_value: &str,
+    buckets: &[i32],
+) {
+    poll_until(
+        READINESS_TIMEOUT,
+        READINESS_POLL_INTERVAL,
+        format!(
+            "Timed out waiting for table '{table_path}' partition 
'{partition_value}' buckets {buckets:?} to become ready"
+        ),
+        || async {
+            admin
+                .list_partition_offsets(table_path, partition_value, buckets, 
OffsetSpec::Latest)
+                .await
+                .map(|_| ())
+                .map_err(|err| format!("{err:?}"))
+        },
+    )
+    .await;
+}
+
 pub fn make_string_array(values: &[Option<&str>]) -> FlussArray {
     let mut writer = FlussArrayWriter::new(values.len(), &DataTypes::string());
     for (idx, value) in values.iter().enumerate() {
@@ -141,38 +247,35 @@ pub fn extract_ids_from_batches(batches: &[ScanBatch]) -> 
Vec<i32> {
 
 /// Similar to wait_for_cluster_ready but connects with SASL credentials.
 pub async fn wait_for_cluster_ready_with_sasl(cluster: &FlussTestingCluster) {
-    let timeout = Duration::from_secs(30);
-    let poll_interval = Duration::from_millis(500);
-    let start = std::time::Instant::now();
-
     let (username, password) = cluster
         .sasl_users()
         .first()
         .expect("SASL cluster must have at least one user");
 
-    loop {
-        let connection = cluster
-            .get_fluss_connection_with_sasl(username, password)
-            .await;
-        if connection
-            .get_metadata()
-            .get_cluster()
-            .get_one_available_server()
-            .is_some()
-        {
-            return;
-        }
-
-        if start.elapsed() >= timeout {
-            panic!(
-                "SASL server readiness check timed out after {} seconds. \
-                 CoordinatorEventProcessor may not be initialized or 
TabletServer may not be available.",
-                timeout.as_secs()
-            );
-        }
-
-        tokio::time::sleep(poll_interval).await;
-    }
+    poll_until(
+        Duration::from_secs(30),
+        Duration::from_millis(500),
+        "SASL server readiness check timed out".to_string(),
+        || async {
+            let connection = cluster
+                .get_fluss_connection_with_sasl(username, password)
+                .await;
+            if connection
+                .get_metadata()
+                .get_cluster()
+                .get_one_available_server()
+                .is_some()
+            {
+                Ok(())
+            } else {
+                Err(
+                    "CoordinatorEventProcessor may not be initialized or 
TabletServer may not be available"
+                        .to_string(),
+                )
+            }
+        },
+    )
+    .await;
 }
 
 /// Creates partitions for a partitioned table.

Reply via email to