github-actions[bot] commented on code in PR #67352:
URL: https://github.com/apache/doris/pull/67352#discussion_r3930542197


##########
be/test/udf/python/python_server_test.cpp:
##########
@@ -401,19 +353,31 @@ TEST_F(PythonServerTest, 
EnsurePoolInitializedWithInvalidVersionFails) {
                 result.error().to_string().find("Timed out") != 
std::string::npos);
 }
 
-TEST_F(PythonServerTest, 
EnsurePoolInitializedReturnsImmediatelyWhenAllWorkersFail) {
+TEST_F(PythonServerTest, EnsurePoolInitializedRetriesAfterRuntimeIsRepaired) {
+    setup_doris_home();
+    config::max_python_process_num = 1;
+
+    // The first executable starts but never publishes its Flight socket. This 
reproduces a real
+    // worker-start failure and verifies that the pool does not remain stuck 
in INITIALIZING.
+    std::string python_path = 
create_fake_python_without_socket_creation("python3", "3.9.16");
+    PythonVersion version("3.9.16", test_dir_, python_path);
     PythonServerManager mgr;
-    config::max_python_process_num = 2;
 
-    PythonVersion invalid_version("3.9.16", test_dir_, test_dir_ + 
"/missing_python");
+    auto failed_result = mgr._ensure_pool_initialized(version);
+    ASSERT_FALSE(failed_result.has_value());
 
-    auto start = std::chrono::steady_clock::now();
-    auto result = mgr._ensure_pool_initialized(invalid_version);
-    auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
-            std::chrono::steady_clock::now() - start);
+    // Repair the runtime in place and retry the same version key. Production 
can hit this when an
+    // environment or server entry is fixed after a transient initialization 
failure.
+    ASSERT_EQ(create_fake_python_with_socket_creation("3.9.16"), python_path);
+    auto recovered_result = mgr._ensure_pool_initialized(version);

Review Comment:
   **[P2] Wait for the failed generation before retrying**
   
   The first caller and the detached coordinator each start their own 1 s wait, 
but the coordinator cannot acquire `versioned_pool->mutex` and start its timer 
until the caller enters `wait_for`. If the caller deadline wins under load, 
`failed_result` returns while the pool is still `INITIALIZING`. This immediate 
retry then skips worker creation; when the coordinator later resets the state 
to `UNINITIALIZED`, the retry wakes and returns another error, so the test 
fails on correct code. Synchronize on the failed generation leaving 
`INITIALIZING` before rewriting the runtime and retrying.



##########
be/test/udf/python/python_server_test.cpp:
##########
@@ -924,33 +832,74 @@ TEST_F(PythonServerTest, 
EnsurePoolInitializedForDifferentVersionsDoesNotShareVe
 
     config::max_python_process_num = 1;
 
-    std::string python39_path =
-            create_fake_python_with_delay_and_socket_creation("python3.9", 
"3.9.16", 50);
-    std::string python310_path =
-            create_fake_python_with_delay_and_socket_creation("python3.10", 
"3.10.0", 50);
+    std::string python39_path = 
create_fake_python_with_socket_creation("3.9.16");
+    std::string python310_path = test_dir_ + "/bin/python3.10";
+    ASSERT_TRUE(fs::copy_file(python39_path, python310_path));
+    fs::permissions(python310_path, fs::perms::owner_all);
 
     PythonServerManager mgr;
     PythonVersion version39("3.9.16", test_dir_, python39_path);
     PythonVersion version310("3.10.0", test_dir_, python310_path);
 
-    auto start = std::chrono::steady_clock::now();
+    struct ForkBarrier {
+        std::mutex mutex;
+        std::condition_variable cv;
+        int entries = 0;
+        bool released = false;
+    };
+    auto fork_barrier = std::make_shared<ForkBarrier>();
+    auto* sync_point = SyncPoint::get_instance();
+    Defer clear_sync_point {[fork_barrier, sync_point]() {
+        {
+            std::lock_guard lock(fork_barrier->mutex);
+            fork_barrier->released = true;
+        }
+        fork_barrier->cv.notify_all();
+        sync_point->disable_processing();
+        
sync_point->clear_call_back("PythonServerManager::fork:before_process_start");
+        sync_point->clear_call_back(
+                
"PythonServerManager::_ensure_pool_initialized:process_pool_init_timeout");
+    }};
+    // Keep callers inside initialization longer than the fork barrier. A 
manager-wide lock must
+    // therefore fail the barrier instead of serializing through the short 
BE_TEST pool timeout.
+    sync_point->set_call_back(
+            
"PythonServerManager::_ensure_pool_initialized:process_pool_init_timeout",
+            [](auto&& args) {
+                auto* timeout = 
try_any_cast<std::chrono::milliseconds*>(args.front());
+                *timeout = std::chrono::seconds(10);
+            });
+    sync_point->set_call_back(
+            "PythonServerManager::fork:before_process_start", 
[fork_barrier](auto&&) {

Review Comment:
   **[P2] Count only the two target version forks**
   
   `SyncPoint` is process-global, this callback receives no version or manager 
identity, and `_ensure_pool_initialized` detaches its slot workers without 
joining them in `shutdown()`. A worker left from an earlier manager/test can 
therefore resume here and increment `entries`; one such entry plus only one 
target version satisfies `entries >= 2`, so this test can pass even if the 
other target is still serialized behind a manager-wide lock. Pass the 
`PythonVersion` (and, if needed, a manager/pool token) through the hook and 
count/block only `version39` and `version310`.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to