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


##########
be/src/exec/spill/spill_file.cpp:
##########
@@ -57,8 +69,15 @@ void SpillFile::gc() {
                                                    _spill_dir, 
status.to_string());
         }
     }
-    // decrease spill data usage anyway, since in ~QueryContext() spill data 
of the query will be
-    // clean up as a last resort
+
+    status = _data_dir->delete_empty_spill_query_dir(_spill_query_dir);

Review Comment:
   [P2] Handle nested spill paths when pruning the query directory
   
   The new contract only requires the relative path to start with a query-id 
component, and `SpillRepartitioner::create_output_spill_files` already accepts 
prefixes containing `/` (for example, `RouteBlockConservesTotalRowCount` writes 
`test/route_block`). That creates `<root>/<query-id>/test/route_block_sub...`. 
This GC removes only the leaf and then immediately calls 
`rmdir(<root>/<query-id>)`, so the empty `test` directory makes the call return 
`DIRECTORY_NOT_EMPTY` and the query directory is still leaked. Please either 
reject nested suffixes at creation or prune empty ancestors up to 
`_spill_query_dir` under the same synchronization, and cover this layout in the 
GC test.



##########
be/test/vec/spill/spill_file_test.cpp:
##########
@@ -887,6 +888,101 @@ TEST_F(SpillFileTest, GCCleansUpFiles) {
     auto st = io::global_local_filesystem()->exists(spill_file_dir, &exists);
     ASSERT_TRUE(st.ok());
     ASSERT_FALSE(exists);
+    st = io::global_local_filesystem()->exists(query_dir, &exists);
+    ASSERT_TRUE(st.ok());
+    ASSERT_FALSE(exists);
+    st = io::global_local_filesystem()->exists(spill_root, &exists);
+    ASSERT_TRUE(st.ok());
+    ASSERT_TRUE(exists);
+}
+
+TEST_F(SpillFileTest, GCCleansUpEmptyQueryDirectoryWithoutDeletingSibling) {
+    SpillFileSPtr first_spill_file;
+    auto st = _create_and_write_spill_file("test_query/gc_first", 
first_spill_file);
+    ASSERT_TRUE(st.ok()) << st;
+    SpillFileSPtr second_spill_file;
+    st = _create_and_write_spill_file("test_query/gc_second", 
second_spill_file);
+    ASSERT_TRUE(st.ok()) << st;
+
+    auto query_dir = _data_dir_ptr->get_spill_data_path("test_query");
+    auto spill_root = _data_dir_ptr->get_spill_data_path();
+    auto first_spill_file_dir = query_dir + "/gc_first";
+    auto second_spill_file_dir = query_dir + "/gc_second";
+    auto second_part_file = second_spill_file_dir + "/0";
+
+    first_spill_file->gc();
+
+    bool exists = false;
+    st = io::global_local_filesystem()->exists(first_spill_file_dir, &exists);
+    ASSERT_TRUE(st.ok());
+    ASSERT_FALSE(exists);
+    st = io::global_local_filesystem()->exists(query_dir, &exists);
+    ASSERT_TRUE(st.ok());
+    ASSERT_TRUE(exists);
+    st = io::global_local_filesystem()->exists(second_spill_file_dir, &exists);
+    ASSERT_TRUE(st.ok());
+    ASSERT_TRUE(exists);
+    st = io::global_local_filesystem()->exists(second_part_file, &exists);
+    ASSERT_TRUE(st.ok());
+    ASSERT_TRUE(exists);
+
+    // Repeated cleanup must not remove a sibling spill file.
+    first_spill_file->gc();
+    st = io::global_local_filesystem()->exists(second_part_file, &exists);
+    ASSERT_TRUE(st.ok());
+    ASSERT_TRUE(exists);
+
+    second_spill_file->gc();
+    st = io::global_local_filesystem()->exists(second_spill_file_dir, &exists);
+    ASSERT_TRUE(st.ok());
+    ASSERT_FALSE(exists);
+    st = io::global_local_filesystem()->exists(query_dir, &exists);
+    ASSERT_TRUE(st.ok());
+    ASSERT_FALSE(exists);
+    st = io::global_local_filesystem()->exists(spill_root, &exists);
+    ASSERT_TRUE(st.ok());
+    ASSERT_TRUE(exists);
+}
+
+TEST_F(SpillFileTest, LazySpillFileRecreatesCleanedQueryDirectory) {

Review Comment:
   [P2] Exercise the mkdir/rmdir race deterministically
   
   This test recreates the query directory only after `first_spill_file->gc()` 
has completed, so it still passes if `_spill_dir_mutex` is removed. The mutex 
is the key part of the fix—the unsafe interleaving is parent `rmdir` racing 
recursive child `mkdir`—but neither added test overlaps those operations. 
Please add a deterministic concurrent BE-UT (for example with sync 
points/barriers around the two `SpillDataDir` methods) that forces the 
interleaving and verifies the writer's part survives and the final empty query 
directory is removed.



-- 
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