@@ -216,6 +216,61 @@ TEST_F(GlobalIndexFileManagerTest, TestCheckpointIOAndRestart) {
216216 ASSERT_TRUE (exists);
217217}
218218
219+ TEST_F (GlobalIndexFileManagerTest, TestExternalCheckpointIOAndRestart) {
220+ std::string external_path = " file:" + PathUtil::JoinPath (dir_->Str (), " external-index" );
221+ ASSERT_OK_AND_ASSIGN (
222+ path_factory_,
223+ FileStorePathFactory::Create (
224+ dir_->Str (), arrow::schema ({}), /* partition_keys=*/ {}, /* default_part_value=*/ " " ,
225+ /* identifier=*/ " mock" , /* data_file_prefix=*/ " data-" ,
226+ /* legacy_partition_name_enabled=*/ true , /* external_paths=*/ {}, external_path,
227+ /* index_file_in_data_file_dir=*/ false , GetDefaultPool ()));
228+ ASSERT_OK_AND_ASSIGN (std::shared_ptr<GlobalIndexFileManager> manager, CreateManager ());
229+ std::string directory = PathUtil::JoinPath (external_path, " checkpoint/task-1" );
230+ std::string file_name = " lumina-global-index-vector-10-20-0.index.ckpt" ;
231+ std::string local_path =
232+ PathUtil::JoinPath (dir_->Str (), " index/checkpoint/task-1/" + file_name);
233+ ASSERT_OK (fs_->WriteFile (local_path, " local" , /* overwrite=*/ false ));
234+ ASSERT_OK_AND_ASSIGN (bool exists, manager->CheckpointExists ());
235+ ASSERT_FALSE (exists);
236+ ASSERT_OK_AND_ASSIGN (std::unique_ptr<OutputStream> output,
237+ manager->CreateCheckpointOutputStream ());
238+ ASSERT_OK_AND_ASSIGN (int64_t written, output->Write (" external" , 8 ));
239+ ASSERT_EQ (written, 8 );
240+ ASSERT_OK (output->Close ());
241+ ASSERT_OK_AND_ASSIGN (exists, fs_->Exists (PathUtil::JoinPath (directory, file_name)));
242+ ASSERT_TRUE (exists);
243+
244+ ASSERT_OK_AND_ASSIGN (std::shared_ptr<GlobalIndexFileManager> restarted, CreateManager ());
245+ ASSERT_OK_AND_ASSIGN (exists, restarted->CheckpointExists ());
246+ ASSERT_TRUE (exists);
247+ ASSERT_OK_AND_ASSIGN (std::unique_ptr<InputStream> input,
248+ restarted->OpenCheckpointInputStream ());
249+ char buffer[8 ];
250+ ASSERT_OK_AND_ASSIGN (int64_t read, input->Read (buffer, sizeof (buffer)));
251+ ASSERT_EQ (read, 8 );
252+ ASSERT_EQ (std::string (buffer, sizeof (buffer)), " external" );
253+ ASSERT_OK (input->Close ());
254+ ASSERT_OK_AND_ASSIGN (std::string next_name, CreateCheckpointFile (restarted));
255+ ASSERT_EQ (next_name, " lumina-global-index-vector-10-20-1.index.ckpt" );
256+
257+ std::vector<std::string> retained_paths = {
258+ local_path, PathUtil::JoinPath (external_path, " checkpoint/task-2/" + file_name),
259+ PathUtil::JoinPath (directory, " lumina-global-index-other-10-20-0.index.ckpt" ),
260+ manager->ToPath (" retained.index" )};
261+ for (size_t i = 1 ; i < retained_paths.size (); ++i) {
262+ ASSERT_OK (fs_->WriteFile (retained_paths[i], " retained" , /* overwrite=*/ false ));
263+ }
264+ ASSERT_OK (restarted->DeleteCheckpoint ());
265+ ASSERT_OK_AND_ASSIGN (exists, restarted->CheckpointExists ());
266+ ASSERT_FALSE (exists);
267+ ASSERT_OK (restarted->DeleteCheckpoint ());
268+ for (const std::string& path : retained_paths) {
269+ ASSERT_OK_AND_ASSIGN (exists, fs_->Exists (path));
270+ ASSERT_TRUE (exists) << path;
271+ }
272+ }
273+
219274TEST_F (GlobalIndexFileManagerTest, TestInitializationFailureCanRetry) {
220275 ASSERT_OK_AND_ASSIGN (std::shared_ptr<GlobalIndexFileManager> manager, CreateManager ());
221276 fs_->fail_list_ = true ;
@@ -357,6 +412,54 @@ TEST_F(GlobalIndexFileManagerTest, TestDeleteTaskCheckpoint) {
357412 ASSERT_OK (GlobalIndexWriteTask::DeleteCheckpoint (dir_->Str (), " task-1" , /* options=*/ {}, fs_));
358413}
359414
415+ TEST_F (GlobalIndexFileManagerTest, TestDeleteExternalTaskCheckpointFromSchemaAndOverrides) {
416+ std::string schema_path = " file:" + PathUtil::JoinPath (dir_->Str (), " schema-index" );
417+ std::string override_path = " file:" + PathUtil::JoinPath (dir_->Str (), " override-index" );
418+ ASSERT_OK (CreateSchema ({{Options::GLOBAL_INDEX_EXTERNAL_PATH , " " + schema_path + " / " }}));
419+ std::string local_directory = PathUtil::JoinPath (dir_->Str (), " index/checkpoint/task-1" );
420+ std::string schema_directory = PathUtil::JoinPath (schema_path, " checkpoint/task-1" );
421+ std::string override_directory = PathUtil::JoinPath (override_path, " checkpoint/task-1" );
422+ for (const std::string& directory : {local_directory, schema_directory, override_directory}) {
423+ ASSERT_OK (fs_->WriteFile (PathUtil::JoinPath (directory, " nested/unfinished-checkpoint" ),
424+ " checkpoint" , /* overwrite=*/ false ));
425+ }
426+ std::vector<std::string> retained_paths = {
427+ PathUtil::JoinPath (schema_path, " checkpoint/task-2/checkpoint" ),
428+ PathUtil::JoinPath (override_path, " checkpoint/task-1-extended/checkpoint" ),
429+ PathUtil::JoinPath (schema_path, " retained.index" ),
430+ PathUtil::JoinPath (override_path, " retained.index" )};
431+ for (const std::string& path : retained_paths) {
432+ ASSERT_OK (fs_->WriteFile (path, " retained" , /* overwrite=*/ false ));
433+ }
434+ std::map<std::string, std::string> overrides = {
435+ {Options::GLOBAL_INDEX_EXTERNAL_PATH , " " + override_path + " / " }};
436+ ASSERT_OK (GlobalIndexWriteTask::DeleteCheckpoint (dir_->Str (), " task-1" , overrides, fs_));
437+ ASSERT_OK_AND_ASSIGN (bool exists, fs_->Exists (override_directory));
438+ ASSERT_FALSE (exists);
439+ ASSERT_OK_AND_ASSIGN (exists, fs_->Exists (schema_directory));
440+ ASSERT_TRUE (exists);
441+ ASSERT_OK_AND_ASSIGN (exists, fs_->Exists (local_directory));
442+ ASSERT_TRUE (exists);
443+ ASSERT_EQ (fs_->delete_paths_ , std::vector<std::string>({override_directory}));
444+ ASSERT_OK (GlobalIndexWriteTask::DeleteCheckpoint (dir_->Str (), " task-1" , overrides, fs_));
445+
446+ ASSERT_OK (GlobalIndexWriteTask::DeleteCheckpoint (dir_->Str (), " task-1" , /* options=*/ {}));
447+ ASSERT_OK_AND_ASSIGN (exists, fs_->Exists (schema_directory));
448+ ASSERT_FALSE (exists);
449+ ASSERT_OK_AND_ASSIGN (exists, fs_->Exists (local_directory));
450+ ASSERT_TRUE (exists);
451+ ASSERT_OK (GlobalIndexWriteTask::DeleteCheckpoint (dir_->Str (), " task-1" , /* options=*/ {}));
452+ for (const std::string& path : retained_paths) {
453+ ASSERT_OK_AND_ASSIGN (exists, fs_->Exists (path));
454+ ASSERT_TRUE (exists) << path;
455+ }
456+
457+ ASSERT_OK (GlobalIndexWriteTask::DeleteCheckpoint (
458+ dir_->Str (), " task-1" , {{Options::GLOBAL_INDEX_EXTERNAL_PATH , " " }}, fs_));
459+ ASSERT_OK_AND_ASSIGN (exists, fs_->Exists (local_directory));
460+ ASSERT_FALSE (exists);
461+ }
462+
360463TEST_F (GlobalIndexFileManagerTest, TestDeleteTaskCheckpointWithoutCustomFileSystem) {
361464 ASSERT_OK (CreateSchema ());
362465 ASSERT_NOK_WITH_MSG (GlobalIndexWriteTask::DeleteCheckpoint (
@@ -376,12 +479,13 @@ TEST_F(GlobalIndexFileManagerTest, TestDeleteTaskCheckpointWithoutCustomFileSyst
376479}
377480
378481TEST_F (GlobalIndexFileManagerTest, TestDeleteTaskCheckpointRejectsInvalidTaskId) {
482+ ASSERT_OK (CreateSchema ());
379483 for (const std::string& invalid : std::vector<std::string>{" " , " ." , " .." , " " , " a/b" , " a\\ b" ,
380484 " a\n b" , std::string (" a\0 b" , 3 )}) {
381- ASSERT_NOK (
382- GlobalIndexWriteTask::DeleteCheckpoint (dir_->Str (), invalid, /* options=*/ {}, fs_));
485+ ASSERT_NOK_WITH_MSG (
486+ GlobalIndexWriteTask::DeleteCheckpoint (dir_->Str (), invalid, /* options=*/ {}, fs_),
487+ " checkpoint task id" );
383488 }
384- ASSERT_EQ (fs_->list_count_ , 0 );
385489 ASSERT_TRUE (fs_->delete_paths_ .empty ());
386490}
387491
0 commit comments