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

lxy-9602 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/paimon-cpp.git


The following commit(s) were added to refs/heads/main by this push:
     new 9509811d feat(global-index): support checkpoints for Lumina index 
builds (#382)
9509811d is described below

commit 9509811dc53a6cbecb35f59a06f43a5263391442
Author: lszskye <[email protected]>
AuthorDate: Wed Sep 23 17:45:42 2026 -0700

    feat(global-index): support checkpoints for Lumina index builds (#382)
---
 .../paimon/global_index/global_index_write_task.h  |  12 +-
 include/paimon/global_index/global_indexer.h       |   5 +
 .../io/global_index_checkpoint_file_manager.h      |  53 +++
 src/paimon/CMakeLists.txt                          |   1 +
 .../bitmap/bitmap_global_index_test.cpp            |   6 +-
 .../global_index/global_indexer_factory_test.cpp   |   1 +
 .../rangebitmap/range_bitmap_global_index_test.cpp |   6 +-
 .../core/global_index/global_index_file_manager.h  | 123 +++++-
 .../global_index_file_manager_test.cpp             | 294 ++++++++++++++
 .../core/global_index/global_index_scan_impl.cpp   |   4 +-
 .../core/global_index/global_index_write_task.cpp  |  37 +-
 .../core/index/index_checkpoint_path_factory.h     |  43 ++
 .../index/pksorted/pk_sorted_index_builder.cpp     |   3 +-
 src/paimon/core/utils/file_store_path_factory.cpp  |  73 ++++
 src/paimon/core/utils/file_store_path_factory.h    |   5 +
 .../core/utils/file_store_path_factory_test.cpp    | 127 ++++++
 .../lucene/lucene_global_index_test.cpp            |   6 +-
 src/paimon/global_index/lumina/CMakeLists.txt      |   4 +-
 .../lumina/lumina_checkpoint_manager.cpp           |  55 +++
 .../lumina/lumina_checkpoint_manager.h             |  45 +++
 .../global_index/lumina/lumina_global_index.cpp    | 150 +++++--
 .../global_index/lumina/lumina_global_index.h      |  36 +-
 .../lumina/lumina_global_index_test.cpp            | 449 ++++++++++++++++++++-
 .../tantivy/tantivy_equivalence_test.cpp           |   6 +-
 .../tantivy/tantivy_filter_limit_test.cpp          |   3 +-
 .../global_index/tantivy/tantivy_index_test.cpp    |   6 +-
 .../tantivy/tantivy_java_compat_test.cpp           |   6 +-
 .../tantivy/tantivy_lucene_coexist_test.cpp        |   6 +-
 .../tantivy/tantivy_streaming_test.cpp             |   6 +-
 .../global_index/tantivy/tantivy_writer_test.cpp   |   6 +-
 test/inte/global_index_test.cpp                    |  94 ++++-
 31 files changed, 1560 insertions(+), 111 deletions(-)

diff --git a/include/paimon/global_index/global_index_write_task.h 
b/include/paimon/global_index/global_index_write_task.h
index 33760405..391c02ca 100644
--- a/include/paimon/global_index/global_index_write_task.h
+++ b/include/paimon/global_index/global_index_write_task.h
@@ -21,6 +21,7 @@
 
 #include <map>
 #include <memory>
+#include <optional>
 #include <string>
 
 #include "paimon/global_index/indexed_split.h"
@@ -45,7 +46,13 @@ class PAIMON_EXPORT GlobalIndexWriteTask {
     ///                     The range must be fully contained within the data 
covered
     ///                     by the given `indexed_split`.
     /// @param options      Index-specific configuration (e.g., false positive 
rate for bloom
-    /// filters).
+    ///                     filters).
+    /// @param task_id      When checkpoints are enabled, the caller must 
provide a non-empty task
+    ///                     identifier that uniquely identifies an index build 
task. Reuse it when
+    ///                     retrying the same build. If the source data, build 
configuration, or
+    ///                     build source code changes, the caller must use a 
new identifier;
+    ///                     otherwise, the index build may fail. Pass nullopt 
when checkpoints are
+    ///                     disabled. Index types without checkpoint support 
ignore this value.
     /// @param pool         Memory pool for temporary allocations during index 
construction.
     ///                     If `nullptr`, the system's default memory pool 
will be used.
     /// @param file_system  Specifies the file system for file operations.
@@ -55,7 +62,8 @@ class PAIMON_EXPORT GlobalIndexWriteTask {
     static Result<std::shared_ptr<CommitMessage>> WriteIndex(
         const std::string& table_path, const std::string& field_name, const 
std::string& index_type,
         const std::shared_ptr<IndexedSplit>& indexed_split,
-        const std::map<std::string, std::string>& options, const 
std::shared_ptr<MemoryPool>& pool,
+        const std::map<std::string, std::string>& options,
+        const std::optional<std::string>& task_id, const 
std::shared_ptr<MemoryPool>& pool,
         const std::shared_ptr<FileSystem>& file_system = nullptr);
 };
 
diff --git a/include/paimon/global_index/global_indexer.h 
b/include/paimon/global_index/global_indexer.h
index 4da6293f..5be789ac 100644
--- a/include/paimon/global_index/global_indexer.h
+++ b/include/paimon/global_index/global_indexer.h
@@ -70,6 +70,11 @@ class PAIMON_EXPORT GlobalIndexer {
         ::ArrowSchema* arrow_schema, const 
std::shared_ptr<GlobalIndexFileReader>& file_reader,
         const std::vector<GlobalIndexIOMeta>& files,
         const std::shared_ptr<MemoryPool>& pool) const = 0;
+
+    /// Whether this indexer supports checkpointing an index build.
+    virtual bool SupportsCheckpoint() const {
+        return false;
+    }
 };
 
 }  // namespace paimon
diff --git 
a/include/paimon/global_index/io/global_index_checkpoint_file_manager.h 
b/include/paimon/global_index/io/global_index_checkpoint_file_manager.h
new file mode 100644
index 00000000..7a07b9ae
--- /dev/null
+++ b/include/paimon/global_index/io/global_index_checkpoint_file_manager.h
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "paimon/result.h"
+#include "paimon/status.h"
+#include "paimon/visibility.h"
+
+namespace paimon {
+class InputStream;
+class OutputStream;
+
+/// Abstract interface for managing checkpoints belonging to one global index 
build identity.
+class PAIMON_EXPORT GlobalIndexCheckpointFileManager {
+ public:
+    virtual ~GlobalIndexCheckpointFileManager() = default;
+
+    /// Returns whether checkpoint storage is configured, without accessing 
storage.
+    virtual bool SupportsCheckpoint() const = 0;
+
+    /// Creates a new checkpoint file and opens it for writing.
+    virtual Result<std::unique_ptr<OutputStream>> 
CreateCheckpointOutputStream() const = 0;
+
+    /// Opens the matching checkpoint with the largest numeric id for reading.
+    virtual Result<std::unique_ptr<InputStream>> OpenCheckpointInputStream() 
const = 0;
+
+    /// Returns whether the checkpoint file exists.
+    virtual Result<bool> CheckpointExists() const = 0;
+
+    /// Deletes all matching checkpoint files. Deleting missing checkpoints 
succeeds.
+    virtual Status DeleteCheckpoint() const = 0;
+};
+
+}  // namespace paimon
diff --git a/src/paimon/CMakeLists.txt b/src/paimon/CMakeLists.txt
index ffd96c70..6fc99343 100644
--- a/src/paimon/CMakeLists.txt
+++ b/src/paimon/CMakeLists.txt
@@ -822,6 +822,7 @@ if(PAIMON_BUILD_TESTS)
                     core/io/file_index_evaluator_test.cpp
                     core/io/single_file_writer_test.cpp
                     core/io/rolling_blob_file_writer_test.cpp
+                    core/global_index/global_index_file_manager_test.cpp
                     core/global_index/global_index_evaluator_impl_test.cpp
                     core/global_index/indexed_split_test.cpp
                     core/manifest/file_source_test.cpp
diff --git a/src/paimon/common/global_index/bitmap/bitmap_global_index_test.cpp 
b/src/paimon/common/global_index/bitmap/bitmap_global_index_test.cpp
index 80e023f9..6e1ad44a 100644
--- a/src/paimon/common/global_index/bitmap/bitmap_global_index_test.cpp
+++ b/src/paimon/common/global_index/bitmap/bitmap_global_index_test.cpp
@@ -67,7 +67,8 @@ class BitmapGlobalIndexTest : public ::testing::Test {
         auto global_index = std::make_shared<BitmapGlobalIndex>(file_index);
 
         auto path_factory = std::make_shared<MockIndexPathFactory>(index_root);
-        auto file_writer = std::make_shared<GlobalIndexFileManager>(fs_, 
path_factory);
+        auto file_writer = std::make_shared<GlobalIndexFileManager>(
+            fs_, path_factory, /*checkpoint_path_factory=*/nullptr);
 
         PAIMON_ASSIGN_OR_RAISE(
             std::shared_ptr<GlobalIndexWriter> global_writer,
@@ -112,7 +113,8 @@ class BitmapGlobalIndexTest : public ::testing::Test {
         auto global_index = std::make_shared<BitmapGlobalIndex>(file_index);
 
         auto path_factory = std::make_shared<MockIndexPathFactory>(index_root);
-        auto file_reader = std::make_shared<GlobalIndexFileManager>(fs_, 
path_factory);
+        auto file_reader = std::make_shared<GlobalIndexFileManager>(
+            fs_, path_factory, /*checkpoint_path_factory=*/nullptr);
         EXPECT_OK_AND_ASSIGN(
             auto global_index_reader,
             global_index->CreateReader(CreateArrowSchema(type).get(), 
file_reader, {meta}, pool_));
diff --git a/src/paimon/common/global_index/global_indexer_factory_test.cpp 
b/src/paimon/common/global_index/global_indexer_factory_test.cpp
index c03fec58..30c58b83 100644
--- a/src/paimon/common/global_index/global_indexer_factory_test.cpp
+++ b/src/paimon/common/global_index/global_indexer_factory_test.cpp
@@ -39,6 +39,7 @@ TEST(GlobalIndexerFactoryTest, 
TestLegacyBitmapEnabledForTesting) {
     ASSERT_OK_AND_ASSIGN(std::unique_ptr<GlobalIndexer> indexer,
                          GlobalIndexerFactory::Get("bitmap", options));
     ASSERT_TRUE(dynamic_cast<BitmapGlobalIndex*>(indexer.get()));
+    ASSERT_FALSE(indexer->SupportsCheckpoint());
 }
 
 TEST(GlobalIndexerFactoryTest, TestNonExist) {
diff --git 
a/src/paimon/common/global_index/rangebitmap/range_bitmap_global_index_test.cpp 
b/src/paimon/common/global_index/rangebitmap/range_bitmap_global_index_test.cpp
index d19622ad..c198fbf7 100644
--- 
a/src/paimon/common/global_index/rangebitmap/range_bitmap_global_index_test.cpp
+++ 
b/src/paimon/common/global_index/rangebitmap/range_bitmap_global_index_test.cpp
@@ -66,7 +66,8 @@ class RangeBitmapGlobalIndexTest : public ::testing::Test {
         auto global_index = 
std::make_shared<RangeBitmapGlobalIndex>(file_index);
 
         auto path_factory = std::make_shared<MockIndexPathFactory>(index_root);
-        auto file_writer = std::make_shared<GlobalIndexFileManager>(fs_, 
path_factory);
+        auto file_writer = std::make_shared<GlobalIndexFileManager>(
+            fs_, path_factory, /*checkpoint_path_factory=*/nullptr);
 
         PAIMON_ASSIGN_OR_RAISE(
             std::shared_ptr<GlobalIndexWriter> global_writer,
@@ -109,7 +110,8 @@ class RangeBitmapGlobalIndexTest : public ::testing::Test {
         auto global_index = 
std::make_shared<RangeBitmapGlobalIndex>(file_index);
 
         auto path_factory = std::make_shared<MockIndexPathFactory>(index_root);
-        auto file_reader = std::make_shared<GlobalIndexFileManager>(fs_, 
path_factory);
+        auto file_reader = std::make_shared<GlobalIndexFileManager>(
+            fs_, path_factory, /*checkpoint_path_factory=*/nullptr);
         EXPECT_OK_AND_ASSIGN(
             auto global_index_reader,
             global_index->CreateReader(CreateArrowSchema(type).get(), 
file_reader, {meta}, pool_));
diff --git a/src/paimon/core/global_index/global_index_file_manager.h 
b/src/paimon/core/global_index/global_index_file_manager.h
index 3db9965b..8ca5ebf7 100644
--- a/src/paimon/core/global_index/global_index_file_manager.h
+++ b/src/paimon/core/global_index/global_index_file_manager.h
@@ -19,22 +19,37 @@
 
 #pragma once
 
+#include <algorithm>
+#include <cstdint>
+#include <limits>
 #include <memory>
+#include <optional>
 #include <string>
+#include <utility>
+#include <vector>
 
+#include "paimon/common/utils/path_util.h"
 #include "paimon/common/utils/uuid.h"
+#include "paimon/core/index/index_checkpoint_path_factory.h"
 #include "paimon/core/index/index_path_factory.h"
 #include "paimon/fs/file_system.h"
+#include "paimon/global_index/io/global_index_checkpoint_file_manager.h"
 #include "paimon/global_index/io/global_index_file_reader.h"
 #include "paimon/global_index/io/global_index_file_writer.h"
 
 namespace paimon {
 /// Helper class for managing global index files.
-class GlobalIndexFileManager : public GlobalIndexFileReader, public 
GlobalIndexFileWriter {
+/// Checkpoint storage is optional and is never accessed by construction or 
ordinary index I/O.
+class GlobalIndexFileManager : public GlobalIndexFileReader,
+                               public GlobalIndexFileWriter,
+                               public GlobalIndexCheckpointFileManager {
  public:
     GlobalIndexFileManager(const std::shared_ptr<FileSystem>& fs,
-                           const std::shared_ptr<IndexPathFactory>& 
path_factory)
-        : fs_(fs), path_factory_(path_factory) {}
+                           const std::shared_ptr<IndexPathFactory>& 
path_factory,
+                           std::unique_ptr<IndexCheckpointPathFactory> 
checkpoint_path_factory)
+        : fs_(fs),
+          path_factory_(path_factory),
+          checkpoint_path_factory_(std::move(checkpoint_path_factory)) {}
 
     Result<std::unique_ptr<InputStream>> GetInputStream(
         const std::string& file_path) const override {
@@ -71,8 +86,110 @@ class GlobalIndexFileManager : public 
GlobalIndexFileReader, public GlobalIndexF
         return path_factory_->IsExternalPath();
     }
 
+    bool SupportsCheckpoint() const override {
+        return checkpoint_path_factory_ != nullptr;
+    }
+
+    Result<std::unique_ptr<OutputStream>> CreateCheckpointOutputStream() const 
override {
+        if (!SupportsCheckpoint()) {
+            return Status::Invalid("global index checkpoint storage is not 
configured");
+        }
+        PAIMON_ASSIGN_OR_RAISE(int64_t file_id, NextCheckpointFileId());
+        
PAIMON_RETURN_NOT_OK(fs_->Mkdirs(checkpoint_path_factory_->GetDirectoryPath()));
+        return fs_->Create(checkpoint_path_factory_->NewPath(file_id), 
/*overwrite=*/false);
+    }
+
+    Result<std::unique_ptr<InputStream>> OpenCheckpointInputStream() const 
override {
+        if (!SupportsCheckpoint()) {
+            return Status::Invalid("global index checkpoint storage is not 
configured");
+        }
+        PAIMON_ASSIGN_OR_RAISE(std::optional<CheckpointFile> checkpoint_file,
+                               LatestCheckpointFile());
+        if (!checkpoint_file) {
+            return Status::NotExist("global index checkpoint file does not 
exist");
+        }
+        return fs_->Open(checkpoint_file->path);
+    }
+
+    Result<bool> CheckpointExists() const override {
+        if (!SupportsCheckpoint()) {
+            return Status::Invalid("global index checkpoint storage is not 
configured");
+        }
+        PAIMON_ASSIGN_OR_RAISE(std::optional<CheckpointFile> checkpoint_file,
+                               LatestCheckpointFile());
+        return checkpoint_file.has_value();
+    }
+
+    Status DeleteCheckpoint() const override {
+        if (!SupportsCheckpoint()) {
+            return Status::Invalid("global index checkpoint storage is not 
configured");
+        }
+        PAIMON_ASSIGN_OR_RAISE(std::vector<CheckpointFile> checkpoint_files, 
ListCheckpointFiles());
+        Status first_error = Status::OK();
+        for (const CheckpointFile& checkpoint_file : checkpoint_files) {
+            Status status = fs_->Delete(checkpoint_file.path, 
/*recursive=*/false);
+            if (!status.ok() && first_error.ok()) {
+                first_error = std::move(status);
+            }
+        }
+        return first_error;
+    }
+
  private:
+    struct CheckpointFile {
+        int64_t id;
+        std::string path;
+    };
+
+    Result<int64_t> NextCheckpointFileId() const {
+        if (!last_checkpoint_file_id_) {
+            PAIMON_ASSIGN_OR_RAISE(std::optional<CheckpointFile> 
checkpoint_file,
+                                   LatestCheckpointFile());
+            last_checkpoint_file_id_ = checkpoint_file ? checkpoint_file->id : 
-1;
+        }
+        if (last_checkpoint_file_id_.value() == 
std::numeric_limits<int64_t>::max()) {
+            return Status::Invalid("checkpoint file id exceeds int64 max");
+        }
+        return ++last_checkpoint_file_id_.value();
+    }
+
+    Result<std::vector<CheckpointFile>> ListCheckpointFiles() const {
+        std::vector<BasicFileStatus> file_statuses;
+        PAIMON_RETURN_NOT_OK(
+            fs_->ListDir(checkpoint_path_factory_->GetDirectoryPath(), 
&file_statuses));
+        std::vector<CheckpointFile> checkpoint_files;
+        for (const BasicFileStatus& file_status : file_statuses) {
+            if (file_status.IsDir()) {
+                continue;
+            }
+            std::string file_name = PathUtil::GetName(file_status.GetPath());
+            std::optional<int64_t> id = 
checkpoint_path_factory_->GetCheckpointId(file_name);
+            if (!id) {
+                continue;
+            }
+            checkpoint_files.push_back(
+                CheckpointFile{id.value(), 
checkpoint_path_factory_->ToPath(file_name)});
+        }
+        return checkpoint_files;
+    }
+
+    Result<std::optional<CheckpointFile>> LatestCheckpointFile() const {
+        PAIMON_ASSIGN_OR_RAISE(std::vector<CheckpointFile> checkpoint_files, 
ListCheckpointFiles());
+        if (checkpoint_files.empty()) {
+            return std::optional<CheckpointFile>();
+        }
+        CheckpointFile latest =
+            *std::max_element(checkpoint_files.begin(), checkpoint_files.end(),
+                              [](const CheckpointFile& left, const 
CheckpointFile& right) {
+                                  return left.id < right.id;
+                              });
+        return std::optional<CheckpointFile>(std::move(latest));
+    }
+
     std::shared_ptr<FileSystem> fs_;
     std::shared_ptr<IndexPathFactory> path_factory_;
+    std::unique_ptr<IndexCheckpointPathFactory> checkpoint_path_factory_;
+    // Historical ids are loaded only on the first successful file id 
allocation scan.
+    mutable std::optional<int64_t> last_checkpoint_file_id_;
 };
 }  // namespace paimon
diff --git a/src/paimon/core/global_index/global_index_file_manager_test.cpp 
b/src/paimon/core/global_index/global_index_file_manager_test.cpp
new file mode 100644
index 00000000..ab27ad2e
--- /dev/null
+++ b/src/paimon/core/global_index/global_index_file_manager_test.cpp
@@ -0,0 +1,294 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "paimon/core/global_index/global_index_file_manager.h"
+
+#include <limits>
+#include <set>
+#include <vector>
+
+#include "arrow/type.h"
+#include "fmt/format.h"
+#include "gtest/gtest.h"
+#include "paimon/common/utils/string_utils.h"
+#include "paimon/core/utils/file_store_path_factory.h"
+#include "paimon/fs/local/local_file_system.h"
+#include "paimon/testing/utils/testharness.h"
+#include "paimon/utils/range.h"
+
+namespace paimon::test {
+
+class GlobalIndexFileManagerTest : public ::testing::Test {
+ public:
+    class TrackingFileSystem : public LocalFileSystem {
+     public:
+        Status ListDir(const std::string& directory,
+                       std::vector<BasicFileStatus>* status_list) const 
override {
+            ++list_count_;
+            if (fail_list_) {
+                return Status::IOError("checkpoint list failed");
+            }
+            return LocalFileSystem::ListDir(directory, status_list);
+        }
+        bool fail_list_ = false;
+        mutable int32_t list_count_ = 0;
+    };
+
+    void SetUp() override {
+        dir_ = UniqueTestDirectory::Create();
+        ASSERT_TRUE(dir_);
+        ASSERT_OK_AND_ASSIGN(
+            path_factory_,
+            FileStorePathFactory::Create(
+                dir_->Str(), arrow::schema({}), /*partition_keys=*/{}, 
/*default_part_value=*/"",
+                /*identifier=*/"mock", /*data_file_prefix=*/"data-",
+                /*legacy_partition_name_enabled=*/true, /*external_paths=*/{},
+                /*global_index_external_path=*/std::nullopt,
+                /*index_file_in_data_file_dir=*/false, GetDefaultPool()));
+    }
+
+    Result<std::shared_ptr<GlobalIndexFileManager>> CreateManager() const {
+        PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<IndexCheckpointPathFactory> 
checkpoint_path_factory,
+                               
path_factory_->CreateGlobalIndexCheckpointPathFactory(
+                                   "lumina", "vector", Range(10, 20), 
"task-1"));
+        return std::make_shared<GlobalIndexFileManager>(
+            fs_, path_factory_->CreateGlobalIndexFileFactory(), 
std::move(checkpoint_path_factory));
+    }
+
+    Result<std::string> CreateCheckpointFile(
+        const std::shared_ptr<GlobalIndexFileManager>& manager) const {
+        PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<OutputStream> output,
+                               manager->CreateCheckpointOutputStream());
+        PAIMON_ASSIGN_OR_RAISE(std::string uri, output->GetUri());
+        PAIMON_RETURN_NOT_OK(output->Close());
+        return PathUtil::GetName(uri);
+    }
+
+    std::shared_ptr<TrackingFileSystem> fs_ = 
std::make_shared<TrackingFileSystem>();
+    std::unique_ptr<UniqueTestDirectory> dir_;
+    std::shared_ptr<FileStorePathFactory> path_factory_;
+};
+
+TEST_F(GlobalIndexFileManagerTest, TestIndexIOWithoutCheckpointAccess) {
+    fs_->fail_list_ = true;
+    ASSERT_OK_AND_ASSIGN(std::shared_ptr<GlobalIndexFileManager> manager, 
CreateManager());
+    std::shared_ptr<GlobalIndexFileWriter> writer = manager;
+    std::shared_ptr<GlobalIndexFileReader> reader = manager;
+    
ASSERT_TRUE(std::dynamic_pointer_cast<GlobalIndexCheckpointFileManager>(writer));
+    
ASSERT_TRUE(std::dynamic_pointer_cast<GlobalIndexCheckpointFileManager>(reader));
+    ASSERT_TRUE(manager->SupportsCheckpoint());
+    auto plain_manager = std::make_shared<GlobalIndexFileManager>(
+        fs_, path_factory_->CreateGlobalIndexFileFactory(), 
/*checkpoint_path_factory=*/nullptr);
+    
ASSERT_TRUE(std::dynamic_pointer_cast<GlobalIndexCheckpointFileManager>(plain_manager));
+    ASSERT_FALSE(plain_manager->SupportsCheckpoint());
+
+    ASSERT_OK_AND_ASSIGN(std::string name, writer->NewFileName("lumina"));
+    ASSERT_TRUE(StringUtils::EndsWith(name, ".index"));
+    ASSERT_EQ(writer->ToPath(name), PathUtil::JoinPath(dir_->Str(), "index/" + 
name));
+    ASSERT_OK_AND_ASSIGN(std::unique_ptr<OutputStream> output, 
writer->NewOutputStream(name));
+    ASSERT_OK_AND_ASSIGN(int64_t written, output->Write("index", 5));
+    ASSERT_EQ(written, 5);
+    ASSERT_OK(output->Close());
+    ASSERT_OK_AND_ASSIGN(int64_t size, writer->GetFileSize(name));
+    ASSERT_EQ(size, 5);
+    ASSERT_OK_AND_ASSIGN(std::unique_ptr<InputStream> input,
+                         reader->GetInputStream(writer->ToPath(name)));
+    char buffer[5];
+    ASSERT_OK_AND_ASSIGN(int64_t read, input->Read(buffer, sizeof(buffer)));
+    ASSERT_EQ(read, 5);
+    ASSERT_EQ(std::string(buffer, sizeof(buffer)), "index");
+    ASSERT_OK(input->Close());
+    ASSERT_EQ(fs_->list_count_, 0);
+    ASSERT_OK_AND_ASSIGN(bool exists,
+                         fs_->Exists(PathUtil::JoinPath(dir_->Str(), 
"index/checkpoint")));
+    ASSERT_FALSE(exists);
+}
+
+TEST_F(GlobalIndexFileManagerTest, TestCheckpointNotConfigured) {
+    fs_->fail_list_ = true;
+    GlobalIndexFileManager manager(fs_, 
path_factory_->CreateGlobalIndexFileFactory(),
+                                   /*checkpoint_path_factory=*/nullptr);
+    ASSERT_FALSE(manager.SupportsCheckpoint());
+    ASSERT_NOK_WITH_MSG(manager.CreateCheckpointOutputStream(),
+                        "checkpoint storage is not configured");
+    ASSERT_NOK_WITH_MSG(manager.OpenCheckpointInputStream(),
+                        "checkpoint storage is not configured");
+    ASSERT_NOK_WITH_MSG(manager.CheckpointExists(), "checkpoint storage is not 
configured");
+    ASSERT_NOK_WITH_MSG(manager.DeleteCheckpoint(), "checkpoint storage is not 
configured");
+    ASSERT_EQ(fs_->list_count_, 0);
+    ASSERT_OK_AND_ASSIGN(bool exists,
+                         fs_->Exists(PathUtil::JoinPath(dir_->Str(), 
"index/checkpoint")));
+    ASSERT_FALSE(exists);
+}
+
+TEST_F(GlobalIndexFileManagerTest, TestCheckpointIOAndRestart) {
+    ASSERT_OK_AND_ASSIGN(std::shared_ptr<GlobalIndexFileManager> manager, 
CreateManager());
+    ASSERT_EQ(fs_->list_count_, 0);
+    std::string directory = PathUtil::JoinPath(dir_->Str(), 
"index/checkpoint");
+    std::string prefix = "lumina-global-index-vector-10-20-task-1-";
+    std::string previous_path = PathUtil::JoinPath(directory, prefix + 
"9.index.ckpt");
+    // The latest sequence must be discovered on first file name allocation, 
not at construction.
+    ASSERT_OK(fs_->WriteFile(previous_path, "old", /*overwrite=*/false));
+    ASSERT_OK_AND_ASSIGN(std::string name, CreateCheckpointFile(manager));
+    ASSERT_TRUE(StringUtils::StartsWith(name, prefix));
+    ASSERT_TRUE(StringUtils::EndsWith(name, "-10.index.ckpt"));
+    ASSERT_OK_AND_ASSIGN(std::unique_ptr<OutputStream> output,
+                         manager->CreateCheckpointOutputStream());
+    ASSERT_OK_AND_ASSIGN(int64_t written, output->Write("latest", 6));
+    ASSERT_EQ(written, 6);
+    ASSERT_OK(output->Close());
+
+    ASSERT_OK_AND_ASSIGN(std::shared_ptr<GlobalIndexFileManager> restarted, 
CreateManager());
+    ASSERT_OK_AND_ASSIGN(bool exists, restarted->CheckpointExists());
+    ASSERT_TRUE(exists);
+    ASSERT_OK_AND_ASSIGN(std::unique_ptr<InputStream> input,
+                         restarted->OpenCheckpointInputStream());
+    char buffer[6];
+    ASSERT_OK_AND_ASSIGN(int64_t read, input->Read(buffer, sizeof(buffer)));
+    ASSERT_EQ(read, 6);
+    ASSERT_EQ(std::string(buffer, sizeof(buffer)), "latest");
+    ASSERT_OK(input->Close());
+    ASSERT_OK_AND_ASSIGN(name, CreateCheckpointFile(restarted));
+    ASSERT_TRUE(StringUtils::EndsWith(name, "-12.index.ckpt"));
+
+    std::string other_task_path =
+        PathUtil::JoinPath(directory, 
"lumina-global-index-vector-10-20-task-2-99.index.ckpt");
+    std::string index_path = manager->ToPath("retained.index");
+    ASSERT_OK(fs_->WriteFile(other_task_path, "other", /*overwrite=*/false));
+    ASSERT_OK(fs_->WriteFile(index_path, "index", /*overwrite=*/false));
+    ASSERT_OK(restarted->DeleteCheckpoint());
+    ASSERT_OK_AND_ASSIGN(exists, restarted->CheckpointExists());
+    ASSERT_FALSE(exists);
+    ASSERT_NOK_WITH_MSG(restarted->OpenCheckpointInputStream(), "checkpoint 
file does not exist");
+    ASSERT_OK(restarted->DeleteCheckpoint());
+    ASSERT_OK_AND_ASSIGN(exists, fs_->Exists(previous_path));
+    ASSERT_FALSE(exists);
+    ASSERT_OK_AND_ASSIGN(exists, fs_->Exists(other_task_path));
+    ASSERT_TRUE(exists);
+    ASSERT_OK_AND_ASSIGN(exists, fs_->Exists(index_path));
+    ASSERT_TRUE(exists);
+}
+
+TEST_F(GlobalIndexFileManagerTest, TestInitializationFailureCanRetry) {
+    ASSERT_OK_AND_ASSIGN(std::shared_ptr<GlobalIndexFileManager> manager, 
CreateManager());
+    fs_->fail_list_ = true;
+    ASSERT_TRUE(manager->SupportsCheckpoint());
+    ASSERT_EQ(fs_->list_count_, 0);
+    ASSERT_NOK_WITH_MSG(CreateCheckpointFile(manager), "checkpoint list 
failed");
+    ASSERT_NOK_WITH_MSG(manager->CreateCheckpointOutputStream(), "checkpoint 
list failed");
+    ASSERT_NOK_WITH_MSG(manager->OpenCheckpointInputStream(), "checkpoint list 
failed");
+    ASSERT_NOK_WITH_MSG(manager->CheckpointExists(), "checkpoint list failed");
+    ASSERT_NOK_WITH_MSG(manager->DeleteCheckpoint(), "checkpoint list failed");
+    fs_->fail_list_ = false;
+    ASSERT_OK_AND_ASSIGN(std::string first, CreateCheckpointFile(manager));
+    ASSERT_TRUE(StringUtils::EndsWith(first, "-0.index.ckpt"));
+    ASSERT_OK_AND_ASSIGN(std::string second, CreateCheckpointFile(manager));
+    ASSERT_TRUE(StringUtils::EndsWith(second, "-1.index.ckpt"));
+    ASSERT_EQ(fs_->list_count_, 6);
+}
+
+TEST_F(GlobalIndexFileManagerTest, TestLatestCheckpointUsesNumericId) {
+    ASSERT_OK_AND_ASSIGN(std::shared_ptr<GlobalIndexFileManager> manager, 
CreateManager());
+    std::string directory = PathUtil::JoinPath(dir_->Str(), 
"index/checkpoint");
+    std::string prefix = "lumina-global-index-vector-10-20-task-1-";
+    ASSERT_OK(fs_->WriteFile(PathUtil::JoinPath(directory, prefix + 
"9.index.ckpt"), "older",
+                             /*overwrite=*/false));
+    ASSERT_OK(fs_->WriteFile(PathUtil::JoinPath(directory, prefix + 
"10.index.ckpt"), "newer",
+                             /*overwrite=*/false));
+    ASSERT_OK_AND_ASSIGN(std::unique_ptr<InputStream> input, 
manager->OpenCheckpointInputStream());
+    char buffer[5];
+    ASSERT_OK_AND_ASSIGN(int64_t read, input->Read(buffer, sizeof(buffer)));
+    ASSERT_EQ(read, 5);
+    ASSERT_EQ(std::string(buffer, sizeof(buffer)), "newer");
+    ASSERT_OK(input->Close());
+}
+
+TEST_F(GlobalIndexFileManagerTest, TestReadsDoNotInitializeFileId) {
+    ASSERT_OK_AND_ASSIGN(std::shared_ptr<GlobalIndexFileManager> manager, 
CreateManager());
+    ASSERT_OK_AND_ASSIGN(bool exists, manager->CheckpointExists());
+    ASSERT_FALSE(exists);
+    std::string directory = PathUtil::JoinPath(dir_->Str(), 
"index/checkpoint");
+    std::string prefix = "lumina-global-index-vector-10-20-task-1-";
+    ASSERT_OK(fs_->WriteFile(PathUtil::JoinPath(directory, prefix + 
"9.index.ckpt"), "old",
+                             /*overwrite=*/false));
+    ASSERT_OK_AND_ASSIGN(std::unique_ptr<InputStream> input, 
manager->OpenCheckpointInputStream());
+    ASSERT_OK(input->Close());
+    ASSERT_EQ(fs_->list_count_, 2);
+
+    ASSERT_OK(fs_->WriteFile(PathUtil::JoinPath(directory, prefix + 
"19.index.ckpt"), "latest",
+                             /*overwrite=*/false));
+    ASSERT_OK_AND_ASSIGN(std::string first, CreateCheckpointFile(manager));
+    ASSERT_TRUE(StringUtils::EndsWith(first, "-20.index.ckpt"));
+    ASSERT_EQ(fs_->list_count_, 3);
+    ASSERT_OK_AND_ASSIGN(std::string second, CreateCheckpointFile(manager));
+    ASSERT_TRUE(StringUtils::EndsWith(second, "-21.index.ckpt"));
+    ASSERT_EQ(fs_->list_count_, 3);
+}
+
+TEST_F(GlobalIndexFileManagerTest, TestFileIdOverflow) {
+    ASSERT_OK_AND_ASSIGN(std::shared_ptr<GlobalIndexFileManager> manager, 
CreateManager());
+    std::string directory = PathUtil::JoinPath(dir_->Str(), 
"index/checkpoint");
+    std::string prefix = "lumina-global-index-vector-10-20-task-1-";
+    int64_t max_id = std::numeric_limits<int64_t>::max();
+    ASSERT_OK(fs_->WriteFile(
+        PathUtil::JoinPath(directory, fmt::format("{}{}.index.ckpt", prefix, 
max_id - 1)), "old",
+        /*overwrite=*/false));
+    ASSERT_OK_AND_ASSIGN(std::string name, CreateCheckpointFile(manager));
+    ASSERT_TRUE(StringUtils::EndsWith(name, fmt::format("-{}.index.ckpt", 
max_id)));
+    ASSERT_NOK_WITH_MSG(CreateCheckpointFile(manager), "checkpoint file id 
exceeds int64 max");
+    ASSERT_NOK_WITH_MSG(manager->CreateCheckpointOutputStream(),
+                        "checkpoint file id exceeds int64 max");
+    ASSERT_EQ(fs_->list_count_, 1);
+
+    ASSERT_OK_AND_ASSIGN(std::shared_ptr<GlobalIndexFileManager> restarted, 
CreateManager());
+    ASSERT_EQ(fs_->list_count_, 1);
+    ASSERT_NOK_WITH_MSG(CreateCheckpointFile(restarted), "checkpoint file id 
exceeds int64 max");
+    ASSERT_NOK_WITH_MSG(CreateCheckpointFile(restarted), "checkpoint file id 
exceeds int64 max");
+    ASSERT_EQ(fs_->list_count_, 2);
+    ASSERT_OK_AND_ASSIGN(std::unique_ptr<InputStream> input,
+                         restarted->OpenCheckpointInputStream());
+    ASSERT_OK(input->Close());
+    ASSERT_OK(restarted->DeleteCheckpoint());
+}
+
+TEST_F(GlobalIndexFileManagerTest, TestSerialAllocations) {
+    ASSERT_OK_AND_ASSIGN(std::shared_ptr<GlobalIndexFileManager> manager, 
CreateManager());
+    std::vector<std::string> names(8);
+    for (size_t i = 0; i < names.size(); ++i) {
+        ASSERT_OK_AND_ASSIGN(names[i], CreateCheckpointFile(manager));
+        ASSERT_TRUE(StringUtils::EndsWith(names[i], 
fmt::format("-{}.index.ckpt", i)));
+    }
+    ASSERT_EQ(fs_->list_count_, 1);
+    ASSERT_EQ(std::set<std::string>(names.begin(), names.end()).size(), 
names.size());
+    for (const std::string& name : names) {
+        ASSERT_FALSE(name.empty());
+    }
+}
+
+TEST_F(GlobalIndexFileManagerTest, TestDeleteDoesNotResetFileId) {
+    ASSERT_OK_AND_ASSIGN(std::shared_ptr<GlobalIndexFileManager> manager, 
CreateManager());
+    ASSERT_OK_AND_ASSIGN(std::string first, CreateCheckpointFile(manager));
+    ASSERT_TRUE(StringUtils::EndsWith(first, "-0.index.ckpt"));
+    ASSERT_OK(manager->DeleteCheckpoint());
+    ASSERT_OK_AND_ASSIGN(std::string second, CreateCheckpointFile(manager));
+    ASSERT_TRUE(StringUtils::EndsWith(second, "-1.index.ckpt"));
+    ASSERT_EQ(fs_->list_count_, 2);
+}
+
+}  // namespace paimon::test
diff --git a/src/paimon/core/global_index/global_index_scan_impl.cpp 
b/src/paimon/core/global_index/global_index_scan_impl.cpp
index e4b3e7b7..c38c5037 100644
--- a/src/paimon/core/global_index/global_index_scan_impl.cpp
+++ b/src/paimon/core/global_index/global_index_scan_impl.cpp
@@ -43,8 +43,8 @@ GlobalIndexScanImpl::GlobalIndexScanImpl(const 
std::shared_ptr<TableSchema>& tab
     : pool_(pool),
       table_schema_(table_schema),
       options_(options),
-      index_file_manager_(
-          std::make_shared<GlobalIndexFileManager>(options.GetFileSystem(), 
path_factory)),
+      index_file_manager_(std::make_shared<GlobalIndexFileManager>(
+          options.GetFileSystem(), path_factory, 
/*checkpoint_path_factory=*/nullptr)),
       index_metas_(std::move(index_metas)),
       executor_(executor) {}
 
diff --git a/src/paimon/core/global_index/global_index_write_task.cpp 
b/src/paimon/core/global_index/global_index_write_task.cpp
index 0288d1b9..82c86143 100644
--- a/src/paimon/core/global_index/global_index_write_task.cpp
+++ b/src/paimon/core/global_index/global_index_write_task.cpp
@@ -39,6 +39,7 @@
 #include "paimon/core/schema/table_schema.h"
 #include "paimon/core/table/sink/commit_message_impl.h"
 #include "paimon/core/table/source/data_split_impl.h"
+#include "paimon/core/utils/branch_manager.h"
 #include "paimon/core/utils/file_store_path_factory.h"
 #include "paimon/global_index/global_indexer.h"
 #include "paimon/global_index/global_indexer_factory.h"
@@ -57,7 +58,7 @@ Result<std::unique_ptr<GlobalIndexer>> 
CreateGlobalIndexer(const std::string& in
     return indexer;
 }
 
-Result<std::shared_ptr<GlobalIndexFileManager>> CreateGlobalIndexFileManager(
+Result<std::shared_ptr<FileStorePathFactory>> CreateFileStorePathFactory(
     const std::string& table_path, const std::shared_ptr<TableSchema>& 
table_schema,
     const CoreOptions& core_options, const std::shared_ptr<MemoryPool>& pool) {
     auto all_arrow_schema = 
DataField::ConvertDataFieldsToArrowSchema(table_schema->Fields());
@@ -65,18 +66,11 @@ Result<std::shared_ptr<GlobalIndexFileManager>> 
CreateGlobalIndexFileManager(
                            core_options.CreateExternalPaths());
     PAIMON_ASSIGN_OR_RAISE(std::optional<std::string> 
global_index_external_path,
                            core_options.CreateGlobalIndexExternalPath());
-    PAIMON_ASSIGN_OR_RAISE(
-        std::shared_ptr<FileStorePathFactory> path_factory,
-        FileStorePathFactory::Create(
-            table_path, all_arrow_schema, table_schema->PartitionKeys(),
-            core_options.GetPartitionDefaultName(), 
core_options.GetFileFormat()->Identifier(),
-            core_options.DataFilePrefix(), 
core_options.LegacyPartitionNameEnabled(),
-            external_paths, global_index_external_path, 
core_options.IndexFileInDataFileDir(),
-            pool));
-    std::shared_ptr<IndexPathFactory> index_path_factory =
-        path_factory->CreateGlobalIndexFileFactory();
-    return 
std::make_shared<GlobalIndexFileManager>(core_options.GetFileSystem(),
-                                                    index_path_factory);
+    return FileStorePathFactory::Create(
+        table_path, all_arrow_schema, table_schema->PartitionKeys(),
+        core_options.GetPartitionDefaultName(), 
core_options.GetFileFormat()->Identifier(),
+        core_options.DataFilePrefix(), 
core_options.LegacyPartitionNameEnabled(), external_paths,
+        global_index_external_path, core_options.IndexFileInDataFileDir(), 
pool);
 }
 
 Result<std::shared_ptr<GlobalIndexWriter>> CreateGlobalIndexWriter(
@@ -344,7 +338,7 @@ Result<std::shared_ptr<CommitMessage>> ToCommitMessage(
 Result<std::shared_ptr<CommitMessage>> GlobalIndexWriteTask::WriteIndex(
     const std::string& table_path, const std::string& field_name, const 
std::string& index_type,
     const std::shared_ptr<IndexedSplit>& indexed_split,
-    const std::map<std::string, std::string>& options,
+    const std::map<std::string, std::string>& options, const 
std::optional<std::string>& task_id,
     const std::shared_ptr<MemoryPool>& memory_pool,
     const std::shared_ptr<FileSystem>& file_system) {
     auto data_split = 
std::dynamic_pointer_cast<DataSplitImpl>(indexed_split->GetDataSplit());
@@ -387,10 +381,19 @@ Result<std::shared_ptr<CommitMessage>> 
GlobalIndexWriteTask::WriteIndex(
     std::vector<std::string> writer_field_names = 
BuildWriterFieldNames(field_name, extra_fields);
     std::vector<std::string> read_field_names = 
BuildReadFieldNames(field_name, extra_fields);
 
-    // create index file manager
+    // Checkpoint capability is optional; only plugins enabling checkpoints 
will use it.
     PAIMON_ASSIGN_OR_RAISE(
-        std::shared_ptr<GlobalIndexFileManager> index_file_manager,
-        CreateGlobalIndexFileManager(table_path, table_schema, core_options, 
pool));
+        std::shared_ptr<FileStorePathFactory> path_factory,
+        CreateFileStorePathFactory(table_path, table_schema, core_options, 
pool));
+    std::unique_ptr<IndexCheckpointPathFactory> checkpoint_path_factory;
+    if (task_id && !task_id->empty() && indexer->SupportsCheckpoint()) {
+        PAIMON_ASSIGN_OR_RAISE(checkpoint_path_factory,
+                               
path_factory->CreateGlobalIndexCheckpointPathFactory(
+                                   index_type, field_name, range, 
task_id.value()));
+    }
+    auto index_file_manager = std::make_shared<GlobalIndexFileManager>(
+        core_options.GetFileSystem(), 
path_factory->CreateGlobalIndexFileFactory(),
+        std::move(checkpoint_path_factory));
 
     // create batch reader
     PAIMON_ASSIGN_OR_RAISE(
diff --git a/src/paimon/core/index/index_checkpoint_path_factory.h 
b/src/paimon/core/index/index_checkpoint_path_factory.h
new file mode 100644
index 00000000..0c1b13e5
--- /dev/null
+++ b/src/paimon/core/index/index_checkpoint_path_factory.h
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <optional>
+#include <string>
+
+namespace paimon {
+
+/// Path factory for global index checkpoints scoped to one task.
+class IndexCheckpointPathFactory {
+ public:
+    virtual ~IndexCheckpointPathFactory() = default;
+
+    /// Creates the path for the specified checkpoint id, without storage I/O.
+    virtual std::string NewPath(int64_t checkpoint_id) const = 0;
+    virtual std::string ToPath(const std::string& file_name) const = 0;
+
+    /// Returns the directory containing the checkpoint files.
+    virtual const std::string& GetDirectoryPath() const = 0;
+
+    /// Returns the checkpoint id when the file name belongs to this factory.
+    virtual std::optional<int64_t> GetCheckpointId(const std::string& 
file_name) const = 0;
+};
+
+}  // namespace paimon
diff --git a/src/paimon/core/index/pksorted/pk_sorted_index_builder.cpp 
b/src/paimon/core/index/pksorted/pk_sorted_index_builder.cpp
index 765c1ea3..4d36aa1f 100644
--- a/src/paimon/core/index/pksorted/pk_sorted_index_builder.cpp
+++ b/src/paimon/core/index/pksorted/pk_sorted_index_builder.cpp
@@ -161,7 +161,8 @@ Result<std::shared_ptr<IndexFileMeta>> 
PkSortedIndexBuilder::Build(
     auto sorted_reader = std::make_unique<SortMergeReaderWithMinHeap>(
         std::move(readers), comparator, sequence_comparator,
         /*merge_function_wrapper=*/nullptr);
-    auto file_manager = std::make_shared<GlobalIndexFileManager>(fs_, 
index_path_factory_);
+    auto file_manager = std::make_shared<GlobalIndexFileManager>(
+        fs_, index_path_factory_, /*checkpoint_path_factory=*/nullptr);
     auto tracking_writer = 
std::make_shared<TrackingGlobalIndexFileWriter>(file_manager);
     Result<std::shared_ptr<IndexFileMeta>> result = 
PkSortedIndexFile::BuildFromSortedReader(
         field_, definition_.IndexType(), definition_.Options(), data_level, 
source_metas,
diff --git a/src/paimon/core/utils/file_store_path_factory.cpp 
b/src/paimon/core/utils/file_store_path_factory.cpp
index ef4aae42..ac15ad63 100644
--- a/src/paimon/core/utils/file_store_path_factory.cpp
+++ b/src/paimon/core/utils/file_store_path_factory.cpp
@@ -20,8 +20,11 @@
 
 #include <cassert>
 
+#include "fmt/format.h"
 #include "paimon/common/fs/external_path_provider.h"
+#include "paimon/common/utils/string_utils.h"
 #include "paimon/common/utils/uuid.h"
+#include "paimon/core/index/index_checkpoint_path_factory.h"
 #include "paimon/core/index/index_file_meta.h"
 #include "paimon/core/index/index_in_data_file_dir_path_factory.h"
 #include "paimon/core/io/data_file_path_factory.h"
@@ -31,6 +34,7 @@
 #include "paimon/macros.h"
 #include "paimon/memory/memory_segment.h"
 #include "paimon/status.h"
+#include "paimon/utils/range.h"
 
 namespace arrow {
 class Schema;
@@ -39,6 +43,31 @@ class Schema;
 namespace paimon {
 class MemoryPool;
 
+namespace {
+
+constexpr char kIndexCheckpointFileSuffix[] = ".index.ckpt";
+
+std::optional<int64_t> ParseCheckpointId(const std::string& file_name,
+                                         const std::string& file_name_prefix) {
+    if (!StringUtils::StartsWith(file_name, file_name_prefix) ||
+        !StringUtils::EndsWith(file_name, kIndexCheckpointFileSuffix)) {
+        return std::nullopt;
+    }
+    size_t suffix_pos =
+        file_name.size() - 
std::char_traits<char>::length(kIndexCheckpointFileSuffix);
+    if (suffix_pos <= file_name_prefix.size()) {
+        return std::nullopt;
+    }
+    std::optional<int64_t> id = StringUtils::StringToValue<int64_t>(
+        file_name.substr(file_name_prefix.size(), suffix_pos - 
file_name_prefix.size()));
+    if (!id || id.value() < 0) {
+        return std::nullopt;
+    }
+    return id;
+}
+
+}  // namespace
+
 FileStorePathFactory::FileStorePathFactory(
     const std::string& root, const std::string& format_identifier,
     const std::string& data_file_prefix, const std::string& uuid,
@@ -183,6 +212,50 @@ std::unique_ptr<IndexPathFactory> 
FileStorePathFactory::CreateGlobalIndexFileFac
     return std::make_unique<IndexPathFactoryImpl>(shared_from_this());
 }
 
+Result<std::unique_ptr<IndexCheckpointPathFactory>>
+FileStorePathFactory::CreateGlobalIndexCheckpointPathFactory(const 
std::string& index_type,
+                                                             const 
std::string& field_name,
+                                                             const Range& 
range,
+                                                             const 
std::string& task_id) {
+    class IndexCheckpointPathFactoryImpl : public IndexCheckpointPathFactory {
+     public:
+        IndexCheckpointPathFactoryImpl(const std::string& directory,
+                                       const std::string& file_name_prefix)
+            : directory_(directory), file_name_prefix_(file_name_prefix) {}
+
+        std::string NewPath(int64_t checkpoint_id) const override {
+            assert(checkpoint_id >= 0);
+            std::string file_name =
+                fmt::format("{}{}{}", file_name_prefix_, checkpoint_id, 
kIndexCheckpointFileSuffix);
+            return ToPath(file_name);
+        }
+
+        std::string ToPath(const std::string& file_name) const override {
+            return PathUtil::JoinPath(directory_, file_name);
+        }
+
+        const std::string& GetDirectoryPath() const override {
+            return directory_;
+        }
+
+        std::optional<int64_t> GetCheckpointId(const std::string& file_name) 
const override {
+            return ParseCheckpointId(file_name, file_name_prefix_);
+        }
+
+     private:
+        std::string directory_;
+        std::string file_name_prefix_;
+    };
+    PAIMON_RETURN_NOT_OK(PathUtil::CheckSinglePathComponent("checkpoint index 
type", index_type));
+    PAIMON_RETURN_NOT_OK(PathUtil::CheckSinglePathComponent("checkpoint 
field", field_name));
+    PAIMON_RETURN_NOT_OK(PathUtil::CheckSinglePathComponent("checkpoint task 
id", task_id));
+    std::string directory = PathUtil::JoinPath(IndexPath(root_), "checkpoint");
+    std::string file_name_prefix = fmt::format("{}-global-index-{}-{}-{}-{}-", 
index_type,
+                                               field_name, range.from, 
range.to, task_id);
+    return std::unique_ptr<IndexCheckpointPathFactory>(
+        std::make_unique<IndexCheckpointPathFactoryImpl>(directory, 
file_name_prefix));
+}
+
 Result<std::shared_ptr<DataFilePathFactory>> 
FileStorePathFactory::CreateDataFilePathFactory(
     const BinaryRow& partition, int32_t bucket) const {
     auto data_file_path_factory = std::make_shared<DataFilePathFactory>();
diff --git a/src/paimon/core/utils/file_store_path_factory.h 
b/src/paimon/core/utils/file_store_path_factory.h
index 1890f2bc..030b6b78 100644
--- a/src/paimon/core/utils/file_store_path_factory.h
+++ b/src/paimon/core/utils/file_store_path_factory.h
@@ -33,6 +33,7 @@
 #include "paimon/common/data/binary_row.h"
 #include "paimon/common/utils/binary_row_partition_computer.h"
 #include "paimon/common/utils/path_util.h"
+#include "paimon/core/index/index_checkpoint_path_factory.h"
 #include "paimon/core/index/index_path_factory.h"
 #include "paimon/memory/memory_pool.h"
 #include "paimon/result.h"
@@ -47,6 +48,7 @@ class DataFilePathFactory;
 class ExternalPathProvider;
 class PathFactory;
 class MemoryPool;
+struct Range;
 
 class FileStorePathFactory : public 
std::enable_shared_from_this<FileStorePathFactory> {
  public:
@@ -76,6 +78,9 @@ class FileStorePathFactory : public 
std::enable_shared_from_this<FileStorePathFa
     Result<std::unique_ptr<IndexPathFactory>> CreateIndexFileFactory(const 
BinaryRow& partition,
                                                                      int32_t 
bucket);
     std::unique_ptr<IndexPathFactory> CreateGlobalIndexFileFactory();
+    Result<std::unique_ptr<IndexCheckpointPathFactory>> 
CreateGlobalIndexCheckpointPathFactory(
+        const std::string& index_type, const std::string& field_name, const 
Range& range,
+        const std::string& task_id);
     Result<std::shared_ptr<DataFilePathFactory>> CreateDataFilePathFactory(
         const BinaryRow& partition, int32_t bucket) const;
     Result<BinaryRow> ToBinaryRow(const std::map<std::string, std::string>& 
partition) const;
diff --git a/src/paimon/core/utils/file_store_path_factory_test.cpp 
b/src/paimon/core/utils/file_store_path_factory_test.cpp
index c78db99d..bfb2b962 100644
--- a/src/paimon/core/utils/file_store_path_factory_test.cpp
+++ b/src/paimon/core/utils/file_store_path_factory_test.cpp
@@ -19,6 +19,7 @@
 #include "paimon/core/utils/file_store_path_factory.h"
 
 #include <atomic>
+#include <limits>
 #include <mutex>
 #include <optional>
 #include <thread>
@@ -28,14 +29,20 @@
 #include "gtest/gtest.h"
 #include "paimon/common/data/binary_row_writer.h"
 #include "paimon/common/data/data_define.h"
+#include "paimon/common/utils/path_util.h"
+#include "paimon/common/utils/string_utils.h"
 #include "paimon/core/core_options.h"
+#include "paimon/core/global_index/global_index_file_manager.h"
+#include "paimon/core/index/index_checkpoint_path_factory.h"
 #include "paimon/core/io/data_file_path_factory.h"
 #include "paimon/defs.h"
 #include "paimon/format/file_format.h"
+#include "paimon/fs/file_system.h"
 #include "paimon/memory/memory_pool.h"
 #include "paimon/status.h"
 #include "paimon/testing/utils/binary_row_generator.h"
 #include "paimon/testing/utils/testharness.h"
+#include "paimon/utils/range.h"
 
 namespace paimon::test {
 
@@ -582,4 +589,124 @@ TEST_F(FileStorePathFactoryTest, 
TestCreateIndexFileFactory) {
         ASSERT_EQ(index_path_factory->ToPath(index_file_meta), 
"/tmp/external-path/bitmap.index");
     }
 }
+
+TEST_F(FileStorePathFactoryTest, TestCreateGlobalIndexCheckpointPathFactory) {
+    auto dir = UniqueTestDirectory::Create();
+    ASSERT_TRUE(dir);
+    std::shared_ptr<FileStorePathFactory> file_store_path_factory = 
CreateFactory(dir->Str());
+    std::shared_ptr<FileSystem> file_system = dir->GetFileSystem();
+
+    std::string prefix = "lumina-global-index-vector-10-20-task-1-";
+    ASSERT_OK_AND_ASSIGN(std::unique_ptr<IndexCheckpointPathFactory> 
checkpoint_path_factory,
+                         
file_store_path_factory->CreateGlobalIndexCheckpointPathFactory(
+                             "lumina", "vector", Range(10, 20), "task-1"));
+    std::string checkpoint_dir = PathUtil::JoinPath(dir->Str(), 
"index/checkpoint");
+    ASSERT_EQ(checkpoint_path_factory->GetDirectoryPath(), checkpoint_dir);
+    std::string first_path = checkpoint_path_factory->NewPath(0);
+    ASSERT_EQ(first_path, PathUtil::JoinPath(checkpoint_dir, prefix + 
"0.index.ckpt"));
+    std::string second_path = checkpoint_path_factory->NewPath(1);
+    ASSERT_EQ(second_path, PathUtil::JoinPath(checkpoint_dir, prefix + 
"1.index.ckpt"));
+    ASSERT_EQ(checkpoint_path_factory->ToPath("checkpoint"),
+              PathUtil::JoinPath(checkpoint_dir, "checkpoint"));
+    ASSERT_EQ(checkpoint_path_factory->GetCheckpointId(prefix + 
"9.index.ckpt"), 9);
+    ASSERT_EQ(checkpoint_path_factory->GetCheckpointId(
+                  "lumina-global-index-other-10-20-task-1-9.index.ckpt"),
+              std::nullopt);
+    ASSERT_EQ(checkpoint_path_factory->GetCheckpointId(prefix + 
"invalid-10.index.ckpt"),
+              std::nullopt);
+    ASSERT_EQ(checkpoint_path_factory->GetCheckpointId(prefix + 
"-1.index.ckpt"), std::nullopt);
+    ASSERT_EQ(checkpoint_path_factory->GetCheckpointId(
+                  prefix + 
"00000000-0000-0000-0000-000000000000-9.index.ckpt"),
+              std::nullopt);
+    ASSERT_EQ(checkpoint_path_factory->GetCheckpointId(
+                  "lumina-global-index-7-vector-10_20-0000000000000000-"
+                  "00000000-0000-0000-0000-000000000000-9.index.ckpt"),
+              std::nullopt);
+    ASSERT_EQ(checkpoint_path_factory->GetCheckpointId(
+                  "lumina-global-index-field=vector-range=10_20-task=task-1-"
+                  "00000000-0000-0000-0000-000000000000-9.index.ckpt"),
+              std::nullopt);
+
+    ASSERT_OK_AND_ASSIGN(bool exists, file_system->Exists(checkpoint_dir));
+    ASSERT_FALSE(exists);
+}
+
+TEST_F(FileStorePathFactoryTest, TestCheckpointFileIdPath) {
+    auto dir = UniqueTestDirectory::Create();
+    ASSERT_TRUE(dir);
+    auto factory = CreateFactory(dir->Str());
+    for (int64_t file_id : {int64_t{9}, std::numeric_limits<int64_t>::max() - 
1,
+                            std::numeric_limits<int64_t>::max()}) {
+        ASSERT_OK_AND_ASSIGN(std::unique_ptr<IndexCheckpointPathFactory> 
checkpoint_factory,
+                             factory->CreateGlobalIndexCheckpointPathFactory(
+                                 "lumina", "vector", Range(10, 20), "task-1"));
+        std::string path = checkpoint_factory->NewPath(file_id);
+        
ASSERT_EQ(checkpoint_factory->GetCheckpointId(PathUtil::GetName(path)), 
file_id);
+        ASSERT_OK_AND_ASSIGN(bool exists,
+                             
dir->GetFileSystem()->Exists(checkpoint_factory->GetDirectoryPath()));
+        ASSERT_FALSE(exists);
+    }
+}
+
+TEST_F(FileStorePathFactoryTest, TestCheckpointTaskIsolation) {
+    auto dir = UniqueTestDirectory::Create();
+    ASSERT_TRUE(dir);
+    std::shared_ptr<FileSystem> fs = dir->GetFileSystem();
+    std::shared_ptr<FileStorePathFactory> factory = CreateFactory(dir->Str());
+    ASSERT_OK_AND_ASSIGN(std::unique_ptr<IndexCheckpointPathFactory> 
own_factory,
+                         
factory->CreateGlobalIndexCheckpointPathFactory("lumina", "vector",
+                                                                         
Range(10, 20), "task-1"));
+    ASSERT_OK_AND_ASSIGN(std::unique_ptr<IndexCheckpointPathFactory> 
other_factory,
+                         
factory->CreateGlobalIndexCheckpointPathFactory("lumina", "vector",
+                                                                         
Range(10, 20), "task-2"));
+    ASSERT_EQ(own_factory->GetDirectoryPath(), 
other_factory->GetDirectoryPath());
+    ASSERT_OK(fs->Mkdirs(own_factory->GetDirectoryPath()));
+    std::string own_path = own_factory->NewPath(0);
+    ASSERT_OK(fs->WriteFile(own_path, "checkpoint", /*overwrite=*/false));
+    std::string other_path = other_factory->NewPath(100);
+    ASSERT_OK(fs->WriteFile(other_path, "foreign", /*overwrite=*/false));
+    ASSERT_EQ(own_factory->GetCheckpointId(PathUtil::GetName(own_path)), 0);
+    ASSERT_EQ(own_factory->GetCheckpointId(PathUtil::GetName(other_path)), 
std::nullopt);
+
+    GlobalIndexFileManager manager(fs, factory->CreateGlobalIndexFileFactory(),
+                                   std::move(own_factory));
+    ASSERT_OK_AND_ASSIGN(bool exists, manager.CheckpointExists());
+    ASSERT_TRUE(exists);
+    ASSERT_OK_AND_ASSIGN(std::unique_ptr<InputStream> input, 
manager.OpenCheckpointInputStream());
+    char buffer[10];
+    ASSERT_OK_AND_ASSIGN(int64_t read, input->Read(buffer, sizeof(buffer)));
+    ASSERT_EQ(read, 10);
+    ASSERT_EQ(std::string(buffer, sizeof(buffer)), "checkpoint");
+    ASSERT_OK(input->Close());
+    ASSERT_OK_AND_ASSIGN(std::unique_ptr<OutputStream> output,
+                         manager.CreateCheckpointOutputStream());
+    ASSERT_OK_AND_ASSIGN(std::string uri, output->GetUri());
+    ASSERT_TRUE(StringUtils::EndsWith(uri, "-1.index.ckpt"));
+    ASSERT_OK(output->Close());
+    ASSERT_OK(manager.DeleteCheckpoint());
+    ASSERT_OK_AND_ASSIGN(exists, manager.CheckpointExists());
+    ASSERT_FALSE(exists);
+    ASSERT_NOK_WITH_MSG(manager.OpenCheckpointInputStream(), "checkpoint file 
does not exist");
+    ASSERT_OK(manager.DeleteCheckpoint());
+    ASSERT_OK_AND_ASSIGN(exists, fs->Exists(other_path));
+    ASSERT_TRUE(exists);
+}
+
+TEST_F(FileStorePathFactoryTest, TestCheckpointRejectsInvalidPathComponents) {
+    auto dir = UniqueTestDirectory::Create();
+    ASSERT_TRUE(dir);
+    auto factory = CreateFactory(dir->Str());
+    for (const std::string& invalid : std::vector<std::string>{"", ".", "..", 
" ", "a/b", "a\\b",
+                                                               "a\nb", 
std::string("a\0b", 3)}) {
+        ASSERT_NOK(factory->CreateGlobalIndexCheckpointPathFactory(invalid, 
"vector", Range(10, 20),
+                                                                   "task"));
+        ASSERT_NOK(factory->CreateGlobalIndexCheckpointPathFactory("lumina", 
invalid, Range(10, 20),
+                                                                   "task"));
+        ASSERT_NOK(factory->CreateGlobalIndexCheckpointPathFactory("lumina", 
"vector",
+                                                                   Range(10, 
20), invalid));
+    }
+    ASSERT_OK_AND_ASSIGN(bool exists, dir->GetFileSystem()->Exists(
+                                          PathUtil::JoinPath(dir->Str(), 
"index/checkpoint")));
+    ASSERT_FALSE(exists);
+}
 }  // namespace paimon::test
diff --git a/src/paimon/global_index/lucene/lucene_global_index_test.cpp 
b/src/paimon/global_index/lucene/lucene_global_index_test.cpp
index 1e30b366..a63e96e6 100644
--- a/src/paimon/global_index/lucene/lucene_global_index_test.cpp
+++ b/src/paimon/global_index/lucene/lucene_global_index_test.cpp
@@ -75,7 +75,8 @@ class LuceneGlobalIndexTest : public ::testing::Test,
                                                const std::string& tmp_dir) 
const {
         auto global_index = std::make_shared<LuceneGlobalIndex>(options);
         auto path_factory = std::make_shared<FakeIndexPathFactory>(index_root);
-        auto file_writer = std::make_shared<GlobalIndexFileManager>(fs_, 
path_factory);
+        auto file_writer = std::make_shared<GlobalIndexFileManager>(
+            fs_, path_factory, /*checkpoint_path_factory=*/nullptr);
 
         PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<GlobalIndexWriter> 
global_writer,
                                global_index->CreateWriter("f0", 
CreateArrowSchema(data_type).get(),
@@ -114,7 +115,8 @@ class LuceneGlobalIndexTest : public ::testing::Test,
         const std::map<std::string, std::string>& options, const 
GlobalIndexIOMeta& meta) const {
         auto global_index = std::make_shared<LuceneGlobalIndex>(options);
         auto path_factory = std::make_shared<FakeIndexPathFactory>(index_root);
-        auto file_reader = std::make_shared<GlobalIndexFileManager>(fs_, 
path_factory);
+        auto file_reader = std::make_shared<GlobalIndexFileManager>(
+            fs_, path_factory, /*checkpoint_path_factory=*/nullptr);
         return global_index->CreateReader(CreateArrowSchema(data_type).get(), 
file_reader, {meta},
                                           pool_);
     }
diff --git a/src/paimon/global_index/lumina/CMakeLists.txt 
b/src/paimon/global_index/lumina/CMakeLists.txt
index b0496df6..239c7702 100644
--- a/src/paimon/global_index/lumina/CMakeLists.txt
+++ b/src/paimon/global_index/lumina/CMakeLists.txt
@@ -15,8 +15,8 @@
 # limitations under the License.
 
 if(PAIMON_ENABLE_LUMINA)
-    set(PAIMON_LUMINA_INDEX lumina_global_index.cpp 
lumina_global_index_factory.cpp)
-
+    set(PAIMON_LUMINA_INDEX lumina_checkpoint_manager.cpp 
lumina_global_index.cpp
+                            lumina_global_index_factory.cpp)
     add_paimon_lib(paimon_lumina_index
                    SOURCES
                    ${PAIMON_LUMINA_INDEX}
diff --git a/src/paimon/global_index/lumina/lumina_checkpoint_manager.cpp 
b/src/paimon/global_index/lumina/lumina_checkpoint_manager.cpp
new file mode 100644
index 00000000..93187eda
--- /dev/null
+++ b/src/paimon/global_index/lumina/lumina_checkpoint_manager.cpp
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "paimon/global_index/lumina/lumina_checkpoint_manager.h"
+
+#include <utility>
+
+#include "paimon/global_index/lumina/lumina_file_reader.h"
+#include "paimon/global_index/lumina/lumina_file_writer.h"
+#include "paimon/global_index/lumina/lumina_utils.h"
+
+namespace paimon::lumina {
+
+std::unique_ptr<::lumina::io::FileWriter> 
LuminaCheckpointManager::CreateCkptFileWriter() {
+    Result<std::unique_ptr<OutputStream>> output = 
file_manager_->CreateCheckpointOutputStream();
+    if (!output.ok()) {
+        return nullptr;
+    }
+    std::shared_ptr<OutputStream> shared_output = std::move(output).value();
+    return std::make_unique<LuminaFileWriter>(shared_output);
+}
+
+::lumina::core::Result<bool> LuminaCheckpointManager::HasCkptFile() {
+    Result<bool> exists = file_manager_->CheckpointExists();
+    if (!exists.ok()) {
+        return 
::lumina::core::Result<bool>::Err(PaimonToLuminaStatus(exists.status()));
+    }
+    return ::lumina::core::Result<bool>::Ok(exists.value());
+}
+
+std::unique_ptr<::lumina::io::FileReader> 
LuminaCheckpointManager::GetCkptFileReader() {
+    Result<std::unique_ptr<InputStream>> input = 
file_manager_->OpenCheckpointInputStream();
+    if (!input.ok()) {
+        return nullptr;
+    }
+    std::shared_ptr<InputStream> shared_input = std::move(input).value();
+    return std::make_unique<LuminaFileReader>(shared_input);
+}
+
+}  // namespace paimon::lumina
diff --git a/src/paimon/global_index/lumina/lumina_checkpoint_manager.h 
b/src/paimon/global_index/lumina/lumina_checkpoint_manager.h
new file mode 100644
index 00000000..e52316e2
--- /dev/null
+++ b/src/paimon/global_index/lumina/lumina_checkpoint_manager.h
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "lumina/extensions/experimental/CkptManager.h"
+#include "paimon/global_index/io/global_index_checkpoint_file_manager.h"
+
+namespace paimon::lumina {
+
+/// Adapts Paimon's task-scoped global index checkpoint storage to Lumina.
+class LuminaCheckpointManager final : public 
::lumina::extensions::experimental::CkptManager {
+ public:
+    explicit LuminaCheckpointManager(
+        const std::shared_ptr<GlobalIndexCheckpointFileManager>& file_manager)
+        : file_manager_(file_manager) {}
+
+    std::unique_ptr<::lumina::io::FileWriter> CreateCkptFileWriter() override;
+
+    ::lumina::core::Result<bool> HasCkptFile() override;
+
+    std::unique_ptr<::lumina::io::FileReader> GetCkptFileReader() override;
+
+ private:
+    std::shared_ptr<GlobalIndexCheckpointFileManager> file_manager_;
+};
+
+}  // namespace paimon::lumina
diff --git a/src/paimon/global_index/lumina/lumina_global_index.cpp 
b/src/paimon/global_index/lumina/lumina_global_index.cpp
index 98585f21..06383e9f 100644
--- a/src/paimon/global_index/lumina/lumina_global_index.cpp
+++ b/src/paimon/global_index/lumina/lumina_global_index.cpp
@@ -21,25 +21,25 @@
 #include <cstring>
 #include <numeric>
 #include <type_traits>
+#include <unordered_map>
 #include <unordered_set>
 #include <utility>
 
 #include "arrow/c/bridge.h"
-#include "arrow/c/helpers.h"
+#include "glog/logging.h"
 #include "lumina/api/Dataset.h"
 #include "lumina/api/LuminaBuilder.h"
 #include "lumina/api/LuminaSearcher.h"
 #include "lumina/api/OptionsNormalize.h"
 #include "lumina/core/Constants.h"
-#include "lumina/core/Status.h"
-#include "lumina/core/Types.h"
 #include "lumina/extensions/experimental/BuildCombinedExtensionV0.h"
 #include "paimon/common/global_index/global_index_utils.h"
 #include "paimon/common/utils/checked_cast.h"
 #include "paimon/common/utils/options_utils.h"
 #include "paimon/common/utils/rapidjson_util.h"
-#include "paimon/common/utils/string_utils.h"
 #include "paimon/global_index/bitmap_scored_global_index_result.h"
+#include "paimon/global_index/io/global_index_checkpoint_file_manager.h"
+#include "paimon/global_index/lumina/lumina_checkpoint_manager.h"
 #include "paimon/global_index/lumina/lumina_file_reader.h"
 #include "paimon/global_index/lumina/lumina_file_writer.h"
 #include "paimon/global_index/lumina/lumina_utils.h"
@@ -360,6 +360,11 @@ Result<TagValues> LiteralsToTagValues(const 
std::vector<Literal>& literals) {
     }
 }
 
+bool IsCheckpointEnabled(const std::map<std::string, std::string>& 
lumina_options) {
+    return 
lumina_options.count(std::string(::lumina::core::kExtensionCkptThreshold)) != 0 
||
+           
lumina_options.count(std::string(::lumina::core::kExtensionCkptCount)) != 0;
+}
+
 }  // namespace
 
 Result<std::vector<TagDimensionData>> 
LuminaIndexWriter::ExtractTagDataForSegment(
@@ -582,10 +587,19 @@ Result<std::shared_ptr<GlobalIndexWriter>> 
LuminaGlobalIndex::CreateWriter(
         ::lumina::api::BuilderOptions builder_options,
         ::lumina::api::NormalizeBuilderOptions(std::unordered_map<std::string, 
std::string>(
             lumina_options.begin(), lumina_options.end())));
+    std::shared_ptr<GlobalIndexCheckpointFileManager> checkpoint_file_manager;
+    if (IsCheckpointEnabled(lumina_options)) {
+        checkpoint_file_manager =
+            
std::dynamic_pointer_cast<GlobalIndexCheckpointFileManager>(file_writer);
+        if (!checkpoint_file_manager || 
!checkpoint_file_manager->SupportsCheckpoint()) {
+            return Status::Invalid("Lumina checkpoint requires a 
checkpoint-capable file writer");
+        }
+    }
     auto lumina_pool = std::make_shared<LuminaMemoryPool>(pool);
     return std::make_shared<LuminaIndexWriter>(
         field_name, arrow_type, dimension, file_writer, 
std::move(builder_options),
-        ::lumina::api::IOOptions(), lumina_options, std::move(tag_fields), 
lumina_pool);
+        ::lumina::api::IOOptions(), lumina_options, std::move(tag_fields), 
checkpoint_file_manager,
+        lumina_pool);
 }
 
 Result<LuminaIndexReader::IndexInfo> LuminaIndexReader::GetIndexInfo(
@@ -804,12 +818,25 @@ class LuminaDatasetWithTag : public 
::lumina::extensions::experimental::DatasetW
     size_t cursor_ = 0;
 };
 
+struct LuminaBuildContext {
+    explicit LuminaBuildContext(::lumina::api::LuminaBuilder&& value) : 
builder(std::move(value)) {}
+
+    ::lumina::api::LuminaBuilder builder;
+    
std::unique_ptr<::lumina::extensions::experimental::BuildWithCheckpointExtension>
+        checkpoint_extension;
+    std::unique_ptr<::lumina::extensions::experimental::BuildWithTagExtension> 
tag_extension;
+    
std::unique_ptr<::lumina::extensions::experimental::BuildWithCkptAndTagExtension>
+        checkpoint_tag_extension;
+};
+
 LuminaIndexWriter::LuminaIndexWriter(
     const std::string& field_name, const std::shared_ptr<arrow::DataType>& 
arrow_type,
     uint32_t dimension, const std::shared_ptr<GlobalIndexFileWriter>& 
file_manager,
     ::lumina::api::BuilderOptions&& builder_options, 
::lumina::api::IOOptions&& io_options,
     const std::map<std::string, std::string>& lumina_options,
-    std::vector<LuminaTagField>&& tag_fields, const 
std::shared_ptr<LuminaMemoryPool>& pool)
+    std::vector<LuminaTagField>&& tag_fields,
+    const std::shared_ptr<GlobalIndexCheckpointFileManager>& 
checkpoint_file_manager,
+    const std::shared_ptr<LuminaMemoryPool>& pool)
     : pool_(pool),
       field_name_(field_name),
       arrow_type_(arrow_type),
@@ -818,7 +845,8 @@ LuminaIndexWriter::LuminaIndexWriter(
       builder_options_(std::move(builder_options)),
       io_options_(std::move(io_options)),
       lumina_options_(lumina_options),
-      tag_fields_(std::move(tag_fields)) {}
+      tag_fields_(std::move(tag_fields)),
+      checkpoint_file_manager_(checkpoint_file_manager) {}
 
 Status LuminaIndexWriter::AddBatch(::ArrowArray* arrow_array,
                                    std::vector<int64_t>&& relative_row_ids) {
@@ -846,7 +874,6 @@ Status LuminaIndexWriter::AddBatch(::ArrowArray* 
arrow_array,
     for (int64_t i = 0; i <= field_length; i++) {
         bool is_null = (i < field_length) && list_field_array->IsNull(i);
         bool is_end = (i == field_length);
-
         if (!is_null && !is_end && segment_start == -1) {
             segment_start = i;
         }
@@ -896,36 +923,89 @@ Result<std::vector<GlobalIndexIOMeta>> 
LuminaIndexWriter::Finish() {
     if (indexed_count_ == 0) {
         return std::vector<GlobalIndexIOMeta>();
     }
-    ::lumina::core::MemoryResourceConfig memory_resource(pool_.get());
-    PAIMON_ASSIGN_OR_RAISE_FROM_LUMINA(
-        ::lumina::api::LuminaBuilder builder,
-        ::lumina::api::LuminaBuilder::Create(builder_options_, 
memory_resource));
-    // pretrain
-    LuminaDataset dataset1(indexed_count_, dimension_, array_vec_, 
array_start_ids_);
-    PAIMON_RETURN_NOT_OK_FROM_LUMINA(builder.PretrainFrom(dataset1));
-
-    // insert data
-    if (tag_fields_.empty()) {
-        LuminaDataset dataset2(indexed_count_, dimension_, array_vec_, 
array_start_ids_);
-        std::vector<std::shared_ptr<arrow::FloatArray>>().swap(array_vec_);
-        PAIMON_RETURN_NOT_OK_FROM_LUMINA(builder.InsertFrom(dataset2));
-    } else {
-        ::lumina::extensions::experimental::BuildWithTagExtension 
tag_extension;
-        PAIMON_RETURN_NOT_OK_FROM_LUMINA(builder.Attach(tag_extension));
-        LuminaDatasetWithTag dataset2(indexed_count_, dimension_, array_vec_, 
array_start_ids_,
-                                      tag_data_vec_);
-        std::vector<std::shared_ptr<arrow::FloatArray>>().swap(array_vec_);
-        std::vector<std::vector<TagDimensionData>>().swap(tag_data_vec_);
-        
PAIMON_RETURN_NOT_OK_FROM_LUMINA(tag_extension.InsertFromWithTag(dataset2));
+
+    bool had_checkpoint = false;
+    if (checkpoint_file_manager_) {
+        PAIMON_ASSIGN_OR_RAISE(had_checkpoint, 
checkpoint_file_manager_->CheckpointExists());
     }
 
+    auto create_build_context = [&]() -> 
Result<std::unique_ptr<LuminaBuildContext>> {
+        ::lumina::core::MemoryResourceConfig memory_resource(pool_.get());
+        PAIMON_ASSIGN_OR_RAISE_FROM_LUMINA(
+            ::lumina::api::LuminaBuilder builder,
+            ::lumina::api::LuminaBuilder::Create(builder_options_, 
memory_resource));
+        auto context = 
std::make_unique<LuminaBuildContext>(std::move(builder));
+        if (checkpoint_file_manager_) {
+            auto checkpoint_manager =
+                
std::make_unique<LuminaCheckpointManager>(checkpoint_file_manager_);
+            auto attach_checkpoint = [&](auto* extension) -> Status {
+                
PAIMON_RETURN_NOT_OK_FROM_LUMINA(context->builder.Attach(*extension));
+                PAIMON_RETURN_NOT_OK_FROM_LUMINA(
+                    extension->LoadCkptManager(std::move(checkpoint_manager)));
+                return Status::OK();
+            };
+            if (tag_fields_.empty()) {
+                context->checkpoint_extension = std::make_unique<
+                    
::lumina::extensions::experimental::BuildWithCheckpointExtension>();
+                
PAIMON_RETURN_NOT_OK(attach_checkpoint(context->checkpoint_extension.get()));
+            } else {
+                context->checkpoint_tag_extension = std::make_unique<
+                    
::lumina::extensions::experimental::BuildWithCkptAndTagExtension>();
+                
PAIMON_RETURN_NOT_OK(attach_checkpoint(context->checkpoint_tag_extension.get()));
+            }
+        } else if (!tag_fields_.empty()) {
+            context->tag_extension =
+                
std::make_unique<::lumina::extensions::experimental::BuildWithTagExtension>();
+            
PAIMON_RETURN_NOT_OK_FROM_LUMINA(context->builder.Attach(*context->tag_extension));
+        }
+        return context;
+    };
+
+    auto build_index = [&]() -> Result<std::unique_ptr<LuminaBuildContext>> {
+        PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<LuminaBuildContext> context, 
create_build_context());
+        // pretrain
+        LuminaDataset pretrain_dataset(indexed_count_, dimension_, array_vec_, 
array_start_ids_);
+        
PAIMON_RETURN_NOT_OK_FROM_LUMINA(context->builder.PretrainFrom(pretrain_dataset));
+        // insert data
+        if (tag_fields_.empty()) {
+            LuminaDataset insert_dataset(indexed_count_, dimension_, 
array_vec_, array_start_ids_);
+            
PAIMON_RETURN_NOT_OK_FROM_LUMINA(context->builder.InsertFrom(insert_dataset));
+        } else {
+            LuminaDatasetWithTag insert_dataset(indexed_count_, dimension_, 
array_vec_,
+                                                array_start_ids_, 
tag_data_vec_);
+            if (context->checkpoint_tag_extension) {
+                PAIMON_RETURN_NOT_OK_FROM_LUMINA(
+                    
context->checkpoint_tag_extension->InsertFromWithTag(insert_dataset));
+            } else {
+                PAIMON_RETURN_NOT_OK_FROM_LUMINA(
+                    context->tag_extension->InsertFromWithTag(insert_dataset));
+            }
+        }
+        return context;
+    };
+
+    Result<std::unique_ptr<LuminaBuildContext>> build_result = build_index();
+    if (!build_result.ok() && had_checkpoint) {
+        LOG(WARNING) << "Failed to build Lumina index with checkpoint, discard 
it and rebuild "
+                        "from scratch: "
+                     << build_result.status().ToString();
+        PAIMON_RETURN_NOT_OK(checkpoint_file_manager_->DeleteCheckpoint());
+        build_result = build_index();
+    }
+    if (!build_result.ok()) {
+        return build_result.status();
+    }
+    std::unique_ptr<LuminaBuildContext> build_context = 
std::move(build_result).value();
+
     // dump index
     PAIMON_ASSIGN_OR_RAISE(std::string index_file_name,
                            
file_manager_->NewFileName(LuminaDefines::kIdentifier));
     PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<OutputStream> out,
                            file_manager_->NewOutputStream(index_file_name));
     auto file_writer = std::make_unique<LuminaFileWriter>(out);
-    PAIMON_RETURN_NOT_OK_FROM_LUMINA(builder.Dump(std::move(file_writer), 
io_options_));
+    PAIMON_RETURN_NOT_OK_FROM_LUMINA(
+        build_context->builder.Dump(std::move(file_writer), io_options_));
+
     // prepare GlobalIndexIOMeta
     PAIMON_ASSIGN_OR_RAISE(int64_t file_size, 
file_manager_->GetFileSize(index_file_name));
     std::string options_json;
@@ -933,12 +1013,18 @@ Result<std::vector<GlobalIndexIOMeta>> 
LuminaIndexWriter::Finish() {
     auto meta_bytes = std::make_shared<Bytes>(options_json, 
pool_->GetPaimonPool().get());
     GlobalIndexIOMeta meta(file_manager_->ToPath(index_file_name), file_size,
                            /*metadata=*/meta_bytes);
+    if (checkpoint_file_manager_) {
+        Status status = checkpoint_file_manager_->DeleteCheckpoint();
+        if (!status.ok()) {
+            LOG(WARNING) << "Failed to delete Lumina checkpoints after 
successful build: "
+                         << status.ToString();
+        }
+    }
     return std::vector<GlobalIndexIOMeta>({meta});
 }
 
 LuminaIndexReader::LuminaIndexReader(
-    const LuminaIndexReader::IndexInfo& index_info,
-    std::unique_ptr<::lumina::api::LuminaSearcher>&& searcher,
+    const IndexInfo& index_info, 
std::unique_ptr<::lumina::api::LuminaSearcher>&& searcher,
     std::unique_ptr<::lumina::extensions::SearchWithFilterExtension>&& 
searcher_with_filter,
     
std::unique_ptr<::lumina::extensions::experimental::SearchWithTagExtension>&& 
searcher_with_tag,
     const std::shared_ptr<LuminaMemoryPool>& pool)
diff --git a/src/paimon/global_index/lumina/lumina_global_index.h 
b/src/paimon/global_index/lumina/lumina_global_index.h
index c2c30475..b4787374 100644
--- a/src/paimon/global_index/lumina/lumina_global_index.h
+++ b/src/paimon/global_index/lumina/lumina_global_index.h
@@ -18,12 +18,11 @@
 
 #pragma once
 
+#include <cstdint>
 #include <map>
 #include <memory>
 #include <optional>
 #include <string>
-#include <unordered_map>
-#include <utility>
 #include <vector>
 
 #include "arrow/api.h"
@@ -33,8 +32,12 @@
 #include "lumina/extensions/experimental/DatasetWithTag.h"
 #include "lumina/extensions/experimental/SearchWithTagExtension.h"
 #include "lumina/extensions/experimental/TagFilter.h"
-#include "paimon/global_index/bitmap_global_index_result.h"
+#include "paimon/global_index/global_index_io_meta.h"
+#include "paimon/global_index/global_index_reader.h"
+#include "paimon/global_index/global_index_writer.h"
 #include "paimon/global_index/global_indexer.h"
+#include "paimon/global_index/io/global_index_checkpoint_file_manager.h"
+#include "paimon/global_index/io/global_index_file_writer.h"
 #include "paimon/global_index/lumina/lumina_memory_pool.h"
 #include "paimon/global_index/lumina/lumina_utils.h"
 
@@ -73,6 +76,8 @@ struct LuminaTagField {
 ///           lumina.diskann.build.thread_count:64
 ///           lumina.diskann.build.ef_construction:1024
 ///           lumina.diskann.build.neighbor_count:64
+///           lumina.extension.build.ckpt.threshold:10000
+///           lumina.extension.build.ckpt.count:3
 ///
 ///       - **Index Reader:**
 ///           No configuration required at load time — settings are stored in 
the index metadata,
@@ -87,8 +92,14 @@ class LuminaGlobalIndex : public GlobalIndexer {
     explicit LuminaGlobalIndex(const std::map<std::string, std::string>& 
options)
         : options_(options) {}
 
+    bool SupportsCheckpoint() const override {
+        return true;
+    }
+
     Result<std::optional<std::vector<std::string>>> GetExtraFieldNames() const 
override;
 
+    /// With checkpoints enabled, file_writer must implement 
GlobalIndexCheckpointFileManager and
+    /// return true from SupportsCheckpoint().
     Result<std::shared_ptr<GlobalIndexWriter>> CreateWriter(
         const std::string& field_name, ::ArrowSchema* arrow_schema,
         const std::shared_ptr<GlobalIndexFileWriter>& file_writer,
@@ -111,14 +122,14 @@ class LuminaGlobalIndex : public GlobalIndexer {
 
 class LuminaIndexWriter : public GlobalIndexWriter {
  public:
-    LuminaIndexWriter(const std::string& field_name,
-                      const std::shared_ptr<arrow::DataType>& arrow_type, 
uint32_t dimension,
-                      const std::shared_ptr<GlobalIndexFileWriter>& 
file_manager,
-                      ::lumina::api::BuilderOptions&& builder_options,
-                      ::lumina::api::IOOptions&& io_options,
-                      const std::map<std::string, std::string>& lumina_options,
-                      std::vector<LuminaTagField>&& tag_fields,
-                      const std::shared_ptr<LuminaMemoryPool>& pool);
+    LuminaIndexWriter(
+        const std::string& field_name, const std::shared_ptr<arrow::DataType>& 
arrow_type,
+        uint32_t dimension, const std::shared_ptr<GlobalIndexFileWriter>& 
file_manager,
+        ::lumina::api::BuilderOptions&& builder_options, 
::lumina::api::IOOptions&& io_options,
+        const std::map<std::string, std::string>& lumina_options,
+        std::vector<LuminaTagField>&& tag_fields,
+        const std::shared_ptr<GlobalIndexCheckpointFileManager>& 
checkpoint_file_manager,
+        const std::shared_ptr<LuminaMemoryPool>& pool);
 
     Status AddBatch(::ArrowArray* arrow_array, std::vector<int64_t>&& 
relative_row_ids) override;
 
@@ -141,6 +152,7 @@ class LuminaIndexWriter : public GlobalIndexWriter {
     ::lumina::api::IOOptions io_options_;
     std::map<std::string, std::string> lumina_options_;
     std::vector<LuminaTagField> tag_fields_;
+    std::shared_ptr<GlobalIndexCheckpointFileManager> checkpoint_file_manager_;
     std::vector<std::shared_ptr<arrow::FloatArray>> array_vec_;
     std::vector<int64_t> array_start_ids_;
     
std::vector<std::vector<::lumina::extensions::experimental::TagDimensionData>> 
tag_data_vec_;
@@ -249,7 +261,7 @@ class LuminaIndexReader : public GlobalIndexReader {
     static Result<::lumina::extensions::experimental::TagFilter> 
PredicateToTagFilter(
         const std::shared_ptr<Predicate>& predicate);
 
-    LuminaIndexReader::IndexInfo index_info_;
+    IndexInfo index_info_;
     std::shared_ptr<LuminaMemoryPool> pool_;
     std::unique_ptr<::lumina::api::LuminaSearcher> searcher_;
     std::unique_ptr<::lumina::extensions::SearchWithFilterExtension> 
searcher_with_filter_;
diff --git a/src/paimon/global_index/lumina/lumina_global_index_test.cpp 
b/src/paimon/global_index/lumina/lumina_global_index_test.cpp
index 2d54950a..0ad49a96 100644
--- a/src/paimon/global_index/lumina/lumina_global_index_test.cpp
+++ b/src/paimon/global_index/lumina/lumina_global_index_test.cpp
@@ -23,15 +23,23 @@
 #include "arrow/c/bridge.h"
 #include "arrow/ipc/api.h"
 #include "gtest/gtest.h"
+#include "lumina/api/Dataset.h"
+#include "lumina/api/LuminaBuilder.h"
+#include "lumina/core/Constants.h"
+#include "lumina/extensions/experimental/BuildCombinedExtensionV0.h"
 #include "paimon/common/utils/arrow/status_utils.h"
 #include "paimon/common/utils/date_time_utils.h"
 #include "paimon/common/utils/path_util.h"
 #include "paimon/common/utils/string_utils.h"
 #include "paimon/core/global_index/global_index_file_manager.h"
+#include "paimon/core/index/index_checkpoint_path_factory.h"
 #include "paimon/core/index/index_path_factory.h"
+#include "paimon/core/utils/file_store_path_factory.h"
 #include "paimon/fs/local/local_file_system.h"
 #include "paimon/global_index/bitmap_scored_global_index_result.h"
 #include "paimon/global_index/global_index_result.h"
+#include "paimon/global_index/lumina/lumina_checkpoint_manager.h"
+#include "paimon/global_index/lumina/lumina_memory_pool.h"
 #include "paimon/predicate/predicate_builder.h"
 #include "paimon/testing/utils/testharness.h"
 namespace paimon::lumina::test {
@@ -62,6 +70,63 @@ class LuminaGlobalIndexTest : public ::testing::Test {
         std::string index_path_;
     };
 
+    class CountingCheckpointFileManager : public GlobalIndexFileManager {
+     public:
+        using GlobalIndexFileManager::GlobalIndexFileManager;
+
+        Result<bool> CheckpointExists() const override {
+            check_checkpoint_count_++;
+            return GlobalIndexFileManager::CheckpointExists();
+        }
+
+        Result<std::unique_ptr<OutputStream>> CreateCheckpointOutputStream() 
const override {
+            create_checkpoint_count_++;
+            return GlobalIndexFileManager::CreateCheckpointOutputStream();
+        }
+
+        Result<std::unique_ptr<InputStream>> OpenCheckpointInputStream() const 
override {
+            open_checkpoint_count_++;
+            return GlobalIndexFileManager::OpenCheckpointInputStream();
+        }
+
+        mutable int32_t check_checkpoint_count_ = 0;
+        mutable int32_t create_checkpoint_count_ = 0;
+        mutable int32_t open_checkpoint_count_ = 0;
+    };
+
+    class TestLuminaDataset : public ::lumina::api::Dataset {
+     public:
+        TestLuminaDataset(uint32_t dimension, const std::vector<float>& 
vectors,
+                          const std::vector<::lumina::core::vector_id_t>& ids)
+            : dimension_(dimension), vectors_(vectors), ids_(ids) {}
+
+        uint32_t Dim() const noexcept override {
+            return dimension_;
+        }
+
+        uint64_t TotalSize() const noexcept override {
+            return ids_.size();
+        }
+
+        ::lumina::core::Result<uint64_t> GetNextBatch(
+            std::vector<float>& vector_buffer,
+            std::vector<::lumina::core::vector_id_t>& id_buffer) noexcept 
override {
+            if (consumed_) {
+                return ::lumina::core::Result<uint64_t>::Ok(0);
+            }
+            vector_buffer = vectors_;
+            id_buffer = ids_;
+            consumed_ = true;
+            return ::lumina::core::Result<uint64_t>::Ok(ids_.size());
+        }
+
+     private:
+        uint32_t dimension_;
+        std::vector<float> vectors_;
+        std::vector<::lumina::core::vector_id_t> ids_;
+        bool consumed_ = false;
+    };
+
     std::unique_ptr<::ArrowSchema> CreateArrowSchema(
         const std::shared_ptr<arrow::DataType>& data_type) const {
         auto c_schema = std::make_unique<::ArrowSchema>();
@@ -69,14 +134,47 @@ class LuminaGlobalIndexTest : public ::testing::Test {
         return c_schema;
     }
 
+    Result<std::shared_ptr<FileStorePathFactory>> CreateFileStorePathFactory(
+        const std::string& table_path) const {
+        return FileStorePathFactory::Create(
+            table_path, arrow::schema({}), /*partition_keys=*/{}, 
/*default_part_value=*/"",
+            /*identifier=*/"mock", /*data_file_prefix=*/"data-",
+            /*legacy_partition_name_enabled=*/true, /*external_paths=*/{},
+            /*global_index_external_path=*/std::nullopt,
+            /*index_file_in_data_file_dir=*/false, pool_);
+    }
+
+    Result<std::unique_ptr<IndexCheckpointPathFactory>> 
CreateCheckpointPathFactory(
+        const std::string& table_path, const std::string& index_type, const 
std::string& field_name,
+        const Range& range) const {
+        PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<FileStorePathFactory> 
file_store_path_factory,
+                               CreateFileStorePathFactory(table_path));
+        return file_store_path_factory->CreateGlobalIndexCheckpointPathFactory(
+            index_type, field_name, range, "task-1");
+    }
+
+    Result<std::shared_ptr<CountingCheckpointFileManager>> 
CreateCountingCheckpointFileManager(
+        const std::shared_ptr<FileStorePathFactory>& path_factory, const 
Range& range) const {
+        PAIMON_ASSIGN_OR_RAISE(
+            std::unique_ptr<IndexCheckpointPathFactory> 
checkpoint_path_factory,
+            path_factory->CreateGlobalIndexCheckpointPathFactory("lumina", 
"f0", range, "task-1"));
+        return std::make_shared<CountingCheckpointFileManager>(
+            fs_, path_factory->CreateGlobalIndexFileFactory(), 
std::move(checkpoint_path_factory));
+    }
+
     Result<GlobalIndexIOMeta> WriteGlobalIndex(const std::string& index_root,
                                                const 
std::shared_ptr<arrow::DataType>& data_type,
                                                const std::map<std::string, 
std::string>& options,
                                                const 
std::shared_ptr<arrow::Array>& array,
                                                const Range& expected_range) 
const {
         auto global_index = std::make_shared<LuminaGlobalIndex>(options);
-        auto path_factory = std::make_shared<FakeIndexPathFactory>(index_root);
-        auto file_writer = std::make_shared<GlobalIndexFileManager>(fs_, 
path_factory);
+        PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<FileStorePathFactory> 
path_factory,
+                               CreateFileStorePathFactory(index_root));
+        PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<IndexCheckpointPathFactory> 
checkpoint_path_factory,
+                               
path_factory->CreateGlobalIndexCheckpointPathFactory(
+                                   "lumina", "f0", expected_range, "task-1"));
+        auto file_writer = std::make_shared<GlobalIndexFileManager>(
+            fs_, path_factory->CreateGlobalIndexFileFactory(), 
std::move(checkpoint_path_factory));
 
         PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<GlobalIndexWriter> 
global_writer,
                                global_index->CreateWriter("f0", 
CreateArrowSchema(data_type).get(),
@@ -127,7 +225,8 @@ class LuminaGlobalIndexTest : public ::testing::Test {
         const std::map<std::string, std::string>& options, const 
GlobalIndexIOMeta& meta) const {
         auto global_index = std::make_shared<LuminaGlobalIndex>(options);
         auto path_factory = std::make_shared<FakeIndexPathFactory>(index_root);
-        auto file_reader = std::make_shared<GlobalIndexFileManager>(fs_, 
path_factory);
+        auto file_reader = std::make_shared<GlobalIndexFileManager>(
+            fs_, path_factory, /*checkpoint_path_factory=*/nullptr);
         return global_index->CreateReader(CreateArrowSchema(data_type).get(), 
file_reader, {meta},
                                           pool_);
     }
@@ -252,12 +351,344 @@ TEST_F(LuminaGlobalIndexTest, TestWithFilter) {
     }
 }
 
+TEST_F(LuminaGlobalIndexTest, TestCheckpointCapabilityOnlyRequiredWhenEnabled) 
{
+    ASSERT_TRUE(LuminaGlobalIndex(options_).SupportsCheckpoint());
+    auto dir = paimon::test::UniqueTestDirectory::Create();
+    ASSERT_TRUE(dir);
+    ASSERT_OK_AND_ASSIGN(std::shared_ptr<FileStorePathFactory> path_factory,
+                         CreateFileStorePathFactory(dir->Str()));
+    auto plain_manager =
+        std::make_shared<GlobalIndexFileManager>(fs_, 
path_factory->CreateGlobalIndexFileFactory(),
+                                                 
/*checkpoint_path_factory=*/nullptr);
+    ASSERT_OK_AND_ASSIGN(std::shared_ptr<CountingCheckpointFileManager> 
checkpoint_manager,
+                         CreateCountingCheckpointFileManager(path_factory, 
Range(0, 3)));
+    std::map<std::string, std::string> options = options_;
+    options["other.extension.build.ckpt.count"] = "1";
+    ASSERT_OK(LuminaGlobalIndex(options).CreateWriter("f0", 
CreateArrowSchema(data_type_).get(),
+                                                      plain_manager, pool_));
+    ASSERT_OK(LuminaGlobalIndex(options).CreateWriter("f0", 
CreateArrowSchema(data_type_).get(),
+                                                      checkpoint_manager, 
pool_));
+
+    for (const std::string& checkpoint_key :
+         std::vector<std::string>{"extension.build.ckpt.count", 
"extension.build.ckpt.threshold"}) {
+        auto enabled = options;
+        enabled["lumina." + checkpoint_key] = "1";
+        ASSERT_NOK_WITH_MSG(LuminaGlobalIndex(enabled).CreateWriter(
+                                "f0", CreateArrowSchema(data_type_).get(), 
plain_manager, pool_),
+                            "Lumina checkpoint requires a checkpoint-capable 
file writer");
+        ASSERT_NOK_WITH_MSG(LuminaGlobalIndex(enabled).CreateWriter(
+                                "f0", CreateArrowSchema(data_type_).get(), 
nullptr, pool_),
+                            "Lumina checkpoint requires a checkpoint-capable 
file writer");
+        ASSERT_OK(LuminaGlobalIndex(enabled).CreateWriter("f0", 
CreateArrowSchema(data_type_).get(),
+                                                          checkpoint_manager, 
pool_));
+    }
+    ASSERT_EQ(checkpoint_manager->check_checkpoint_count_, 0);
+    ASSERT_EQ(checkpoint_manager->create_checkpoint_count_, 0);
+}
+
+TEST_F(LuminaGlobalIndexTest, TestBuildWithCheckpoint) {
+    auto test_root_dir = paimon::test::UniqueTestDirectory::Create();
+    ASSERT_TRUE(test_root_dir);
+    std::string test_root = test_root_dir->Str();
+
+    std::map<std::string, std::string> checkpoint_options = options_;
+    checkpoint_options["lumina.extension.build.ckpt.threshold"] = "1";
+    checkpoint_options["lumina.extension.build.ckpt.count"] = "1";
+
+    auto global_index = 
std::make_shared<LuminaGlobalIndex>(checkpoint_options);
+    ASSERT_OK_AND_ASSIGN(std::shared_ptr<FileStorePathFactory> path_factory,
+                         CreateFileStorePathFactory(test_root));
+    ASSERT_OK_AND_ASSIGN(std::shared_ptr<CountingCheckpointFileManager> 
file_manager,
+                         CreateCountingCheckpointFileManager(path_factory, 
Range(0, 3)));
+    ASSERT_OK_AND_ASSIGN(
+        std::shared_ptr<GlobalIndexWriter> global_writer,
+        global_index->CreateWriter("f0", CreateArrowSchema(data_type_).get(), 
file_manager, pool_));
+
+    ArrowArray c_array;
+    ASSERT_TRUE(arrow::ExportArray(*array_, &c_array).ok());
+    ASSERT_OK(global_writer->AddBatch(&c_array, {0, 1, 2, 3}));
+    ASSERT_OK_AND_ASSIGN(std::vector<GlobalIndexIOMeta> result_metas, 
global_writer->Finish());
+    ASSERT_EQ(result_metas.size(), 1);
+    ASSERT_GT(file_manager->create_checkpoint_count_, 0);
+    ASSERT_OK_AND_ASSIGN(bool checkpoint_exists, 
file_manager->CheckpointExists());
+    ASSERT_FALSE(checkpoint_exists);
+
+    ASSERT_OK_AND_ASSIGN(
+        std::shared_ptr<GlobalIndexReader> reader,
+        CreateGlobalIndexReader(test_root, data_type_, checkpoint_options, 
result_metas[0]));
+    ASSERT_OK_AND_ASSIGN(std::shared_ptr<ScoredGlobalIndexResult> 
scored_result,
+                         
reader->VisitVectorSearch(std::make_shared<VectorSearch>(
+                             /*field_name=*/"f0", /*limit=*/4, query_, 
/*filter=*/nullptr,
+                             /*predicate=*/nullptr, 
/*distance_type=*/std::nullopt,
+                             /*options=*/checkpoint_options)));
+    CheckResult(scored_result, {3l, 1l, 2l, 0l}, {0.01f, 2.01f, 2.21f, 4.21f});
+}
+
+TEST_F(LuminaGlobalIndexTest, TestCheckpointFileManagement) {
+    auto test_root_dir = paimon::test::UniqueTestDirectory::Create();
+    ASSERT_TRUE(test_root_dir);
+    std::string test_root = test_root_dir->Str();
+    std::string prefix = "lumina-global-index-f0-10-20-task-1-";
+    ASSERT_OK_AND_ASSIGN(std::unique_ptr<IndexCheckpointPathFactory> 
checkpoint_path_factory,
+                         CreateCheckpointPathFactory(test_root, "lumina", 
"f0", Range(10, 20)));
+    ASSERT_OK_AND_ASSIGN(std::shared_ptr<FileStorePathFactory> path_factory,
+                         CreateFileStorePathFactory(test_root));
+    auto file_manager = std::make_shared<GlobalIndexFileManager>(
+        fs_, path_factory->CreateGlobalIndexFileFactory(), 
std::move(checkpoint_path_factory));
+
+    ASSERT_OK_AND_ASSIGN(bool checkpoint_exists, 
file_manager->CheckpointExists());
+    ASSERT_FALSE(checkpoint_exists);
+    ASSERT_OK_AND_ASSIGN(std::unique_ptr<OutputStream> first_output,
+                         file_manager->CreateCheckpointOutputStream());
+    ASSERT_OK_AND_ASSIGN(std::string first_path, first_output->GetUri());
+    ASSERT_TRUE(StringUtils::StartsWith(PathUtil::GetName(first_path), 
prefix));
+    ASSERT_TRUE(StringUtils::EndsWith(first_path, "-0.index.ckpt"));
+    ASSERT_OK_AND_ASSIGN(int64_t first_written, first_output->Write("old", 3));
+    ASSERT_EQ(first_written, 3);
+    ASSERT_OK(first_output->Close());
+
+    ASSERT_OK_AND_ASSIGN(std::unique_ptr<OutputStream> second_output,
+                         file_manager->CreateCheckpointOutputStream());
+    ASSERT_OK_AND_ASSIGN(std::string second_path, second_output->GetUri());
+    ASSERT_TRUE(StringUtils::StartsWith(PathUtil::GetName(second_path), 
prefix));
+    ASSERT_TRUE(StringUtils::EndsWith(second_path, "-1.index.ckpt"));
+    ASSERT_OK_AND_ASSIGN(int64_t second_written, 
second_output->Write("latest", 6));
+    ASSERT_EQ(second_written, 6);
+    ASSERT_OK(second_output->Close());
+
+    std::string checkpoint_dir = PathUtil::JoinPath(test_root, 
"index/checkpoint");
+    ASSERT_OK(fs_->WriteFile(PathUtil::JoinPath(checkpoint_dir, prefix + 
"9.index.ckpt"), "id-nine",
+                             /*overwrite=*/false));
+    ASSERT_OK(fs_->WriteFile(PathUtil::JoinPath(checkpoint_dir, prefix + 
"10.index.ckpt"), "id-ten",
+                             /*overwrite=*/false));
+    std::string unrelated_file =
+        PathUtil::JoinPath(checkpoint_dir, 
"lumina-global-index-other-10-20-task-2-99.index.ckpt");
+    ASSERT_OK(fs_->WriteFile(unrelated_file, "unrelated", 
/*overwrite=*/false));
+    std::string malformed_file =
+        PathUtil::JoinPath(checkpoint_dir, prefix + "invalid-99.index.ckpt");
+    ASSERT_OK(fs_->WriteFile(malformed_file, "malformed", 
/*overwrite=*/false));
+
+    ASSERT_OK_AND_ASSIGN(checkpoint_path_factory,
+                         CreateCheckpointPathFactory(test_root, "lumina", 
"f0", Range(10, 20)));
+    file_manager = std::make_shared<GlobalIndexFileManager>(
+        fs_, path_factory->CreateGlobalIndexFileFactory(), 
std::move(checkpoint_path_factory));
+
+    ASSERT_OK_AND_ASSIGN(std::unique_ptr<InputStream> latest_input,
+                         file_manager->OpenCheckpointInputStream());
+    char buffer[6];
+    ASSERT_OK_AND_ASSIGN(int64_t read_bytes, latest_input->Read(buffer, 
sizeof(buffer)));
+    ASSERT_EQ(read_bytes, 6);
+    ASSERT_EQ(std::string(buffer, sizeof(buffer)), "id-ten");
+    ASSERT_OK(latest_input->Close());
+    ASSERT_OK_AND_ASSIGN(std::unique_ptr<OutputStream> next_output,
+                         file_manager->CreateCheckpointOutputStream());
+    ASSERT_OK_AND_ASSIGN(std::string next_path, next_output->GetUri());
+    ASSERT_TRUE(StringUtils::EndsWith(next_path, "-11.index.ckpt"));
+    ASSERT_OK(next_output->Close());
+
+    ASSERT_OK(file_manager->DeleteCheckpoint());
+    ASSERT_OK_AND_ASSIGN(checkpoint_exists, file_manager->CheckpointExists());
+    ASSERT_FALSE(checkpoint_exists);
+    ASSERT_OK_AND_ASSIGN(bool unrelated_exists, fs_->Exists(unrelated_file));
+    ASSERT_TRUE(unrelated_exists);
+    ASSERT_OK_AND_ASSIGN(bool malformed_exists, fs_->Exists(malformed_file));
+    ASSERT_TRUE(malformed_exists);
+}
+
+TEST_F(LuminaGlobalIndexTest, TestResumeFromCheckpoint) {
+    auto test_root_dir = paimon::test::UniqueTestDirectory::Create();
+    ASSERT_TRUE(test_root_dir);
+    std::string test_root = test_root_dir->Str();
+    ASSERT_OK_AND_ASSIGN(std::shared_ptr<FileStorePathFactory> path_factory,
+                         CreateFileStorePathFactory(test_root));
+    ASSERT_OK_AND_ASSIGN(std::shared_ptr<CountingCheckpointFileManager> 
file_manager,
+                         CreateCountingCheckpointFileManager(path_factory, 
Range(0, 3)));
+
+    std::map<std::string, std::string> checkpoint_options = options_;
+    checkpoint_options["lumina.extension.build.ckpt.threshold"] = "1";
+    checkpoint_options["lumina.extension.build.ckpt.count"] = "1";
+    std::vector<float> vectors = {
+        0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,
+        1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f,
+    };
+    std::vector<::lumina::core::vector_id_t> ids = {0, 1, 2, 3};
+
+    {
+        ::lumina::api::BuilderOptions builder_options;
+        builder_options.Set(::lumina::core::kIndexType, 
::lumina::core::kIndexTypeBruteforce)
+            .Set(::lumina::core::kDimension, static_cast<int64_t>(4))
+            .Set(::lumina::core::kDistanceMetric, ::lumina::core::kDistanceL2)
+            .Set(::lumina::core::kEncodingType, 
::lumina::core::kEncodingRawf32)
+            .Set(::lumina::core::kExtensionCkptThreshold, 
static_cast<int64_t>(1))
+            .Set(::lumina::core::kExtensionCkptCount, static_cast<int64_t>(1));
+        LuminaMemoryPool lumina_pool(pool_);
+        ::lumina::core::MemoryResourceConfig memory_resource(&lumina_pool);
+        auto builder_result =
+            ::lumina::api::LuminaBuilder::Create(builder_options, 
memory_resource);
+        ASSERT_TRUE(builder_result.IsOk()) << 
builder_result.GetStatus().Message();
+        ::lumina::api::LuminaBuilder builder = 
std::move(builder_result).TakeValue();
+        ::lumina::extensions::experimental::BuildWithCheckpointExtension 
checkpoint_extension;
+        ASSERT_TRUE(builder.Attach(checkpoint_extension).IsOk());
+        ASSERT_TRUE(checkpoint_extension
+                        
.LoadCkptManager(std::make_unique<LuminaCheckpointManager>(file_manager))
+                        .IsOk());
+        TestLuminaDataset pretrain_dataset(/*dimension=*/4, vectors, ids);
+        ASSERT_TRUE(builder.PretrainFrom(pretrain_dataset).IsOk());
+        TestLuminaDataset insert_dataset(/*dimension=*/4, vectors, ids);
+        ASSERT_TRUE(builder.InsertFrom(insert_dataset).IsOk());
+    }
+    ASSERT_GT(file_manager->create_checkpoint_count_, 0);
+    ASSERT_OK_AND_ASSIGN(bool checkpoint_exists, 
file_manager->CheckpointExists());
+    ASSERT_TRUE(checkpoint_exists);
+
+    auto global_index = 
std::make_shared<LuminaGlobalIndex>(checkpoint_options);
+    ASSERT_OK_AND_ASSIGN(
+        std::shared_ptr<GlobalIndexWriter> global_writer,
+        global_index->CreateWriter("f0", CreateArrowSchema(data_type_).get(), 
file_manager, pool_));
+    ArrowArray c_array;
+    ASSERT_TRUE(arrow::ExportArray(*array_, &c_array).ok());
+    ASSERT_OK(global_writer->AddBatch(&c_array, {0, 1, 2, 3}));
+    ASSERT_OK_AND_ASSIGN(std::vector<GlobalIndexIOMeta> result_metas, 
global_writer->Finish());
+    ASSERT_EQ(result_metas.size(), 1);
+    ASSERT_GT(file_manager->open_checkpoint_count_, 0);
+    ASSERT_OK_AND_ASSIGN(checkpoint_exists, file_manager->CheckpointExists());
+    ASSERT_FALSE(checkpoint_exists);
+
+    ASSERT_OK_AND_ASSIGN(
+        std::shared_ptr<GlobalIndexReader> reader,
+        CreateGlobalIndexReader(test_root, data_type_, checkpoint_options, 
result_metas[0]));
+    ASSERT_OK_AND_ASSIGN(std::shared_ptr<ScoredGlobalIndexResult> 
scored_result,
+                         
reader->VisitVectorSearch(std::make_shared<VectorSearch>(
+                             /*field_name=*/"f0", /*limit=*/4, query_, 
/*filter=*/nullptr,
+                             /*predicate=*/nullptr, 
/*distance_type=*/std::nullopt,
+                             /*options=*/checkpoint_options)));
+    CheckResult(scored_result, {3l, 1l, 2l, 0l}, {0.01f, 2.01f, 2.21f, 4.21f});
+}
+
+TEST_F(LuminaGlobalIndexTest, TestDiscardInvalidCheckpointAndRebuild) {
+    auto test_root_dir = paimon::test::UniqueTestDirectory::Create();
+    ASSERT_TRUE(test_root_dir);
+    std::string test_root = test_root_dir->Str();
+    ASSERT_OK_AND_ASSIGN(std::shared_ptr<FileStorePathFactory> path_factory,
+                         CreateFileStorePathFactory(test_root));
+    ASSERT_OK_AND_ASSIGN(std::shared_ptr<CountingCheckpointFileManager> 
file_manager,
+                         CreateCountingCheckpointFileManager(path_factory, 
Range(0, 3)));
+
+    ASSERT_OK_AND_ASSIGN(std::unique_ptr<OutputStream> invalid_checkpoint,
+                         file_manager->CreateCheckpointOutputStream());
+    ASSERT_OK_AND_ASSIGN(int64_t written, invalid_checkpoint->Write("invalid 
checkpoint", 18));
+    ASSERT_EQ(written, 18);
+    ASSERT_OK(invalid_checkpoint->Close());
+
+    std::map<std::string, std::string> checkpoint_options = options_;
+    checkpoint_options["lumina.extension.build.ckpt.threshold"] = "1";
+    checkpoint_options["lumina.extension.build.ckpt.count"] = "1";
+    auto global_index = 
std::make_shared<LuminaGlobalIndex>(checkpoint_options);
+    ASSERT_OK_AND_ASSIGN(
+        std::shared_ptr<GlobalIndexWriter> global_writer,
+        global_index->CreateWriter("f0", CreateArrowSchema(data_type_).get(), 
file_manager, pool_));
+    ArrowArray c_array;
+    ASSERT_TRUE(arrow::ExportArray(*array_, &c_array).ok());
+    ASSERT_OK(global_writer->AddBatch(&c_array, {0, 1, 2, 3}));
+    ASSERT_OK_AND_ASSIGN(std::vector<GlobalIndexIOMeta> result_metas, 
global_writer->Finish());
+    ASSERT_EQ(result_metas.size(), 1);
+    ASSERT_GT(file_manager->open_checkpoint_count_, 0);
+    ASSERT_OK_AND_ASSIGN(bool checkpoint_exists, 
file_manager->CheckpointExists());
+    ASSERT_FALSE(checkpoint_exists);
+
+    ASSERT_OK_AND_ASSIGN(
+        std::shared_ptr<GlobalIndexReader> reader,
+        CreateGlobalIndexReader(test_root, data_type_, checkpoint_options, 
result_metas[0]));
+    ASSERT_OK_AND_ASSIGN(std::shared_ptr<ScoredGlobalIndexResult> 
scored_result,
+                         
reader->VisitVectorSearch(std::make_shared<VectorSearch>(
+                             /*field_name=*/"f0", /*limit=*/4, query_, 
/*filter=*/nullptr,
+                             /*predicate=*/nullptr, 
/*distance_type=*/std::nullopt,
+                             /*options=*/checkpoint_options)));
+    CheckResult(scored_result, {3l, 1l, 2l, 0l}, {0.01f, 2.01f, 2.21f, 4.21f});
+}
+
+TEST_F(LuminaGlobalIndexTest, TestBuildFailureDiscardsCheckpointAndRetries) {
+    class DeletionCountingCheckpointFileManager : public 
CountingCheckpointFileManager {
+     public:
+        using CountingCheckpointFileManager::CountingCheckpointFileManager;
+
+        Status DeleteCheckpoint() const override {
+            delete_count_++;
+            return CountingCheckpointFileManager::DeleteCheckpoint();
+        }
+
+        mutable int32_t delete_count_ = 0;
+    };
+
+    auto dir = paimon::test::UniqueTestDirectory::Create();
+    ASSERT_TRUE(dir);
+    ASSERT_OK_AND_ASSIGN(std::shared_ptr<FileStorePathFactory> path_factory,
+                         CreateFileStorePathFactory(dir->Str()));
+    ASSERT_OK_AND_ASSIGN(std::unique_ptr<IndexCheckpointPathFactory> 
checkpoint_path_factory,
+                         path_factory->CreateGlobalIndexCheckpointPathFactory(
+                             "lumina", "f0", Range(0, 3), "task-1"));
+    auto file_manager = 
std::make_shared<DeletionCountingCheckpointFileManager>(
+        fs_, path_factory->CreateGlobalIndexFileFactory(), 
std::move(checkpoint_path_factory));
+
+    ::lumina::api::BuilderOptions builder_options;
+    builder_options.Set(::lumina::core::kIndexType, 
::lumina::core::kIndexTypeBruteforce)
+        .Set(::lumina::core::kDimension, static_cast<int64_t>(4))
+        .Set(::lumina::core::kDistanceMetric, ::lumina::core::kDistanceL2)
+        .Set(::lumina::core::kEncodingType, ::lumina::core::kEncodingRawf32)
+        .Set(::lumina::core::kExtensionCkptThreshold, static_cast<int64_t>(1))
+        .Set(::lumina::core::kExtensionCkptCount, static_cast<int64_t>(1));
+    LuminaMemoryPool lumina_pool(pool_);
+    ::lumina::core::MemoryResourceConfig memory_resource(&lumina_pool);
+    auto builder_result = 
::lumina::api::LuminaBuilder::Create(builder_options, memory_resource);
+    ASSERT_TRUE(builder_result.IsOk()) << builder_result.GetStatus().Message();
+    {
+        ::lumina::api::LuminaBuilder builder = 
std::move(builder_result).TakeValue();
+        ::lumina::extensions::experimental::BuildWithCheckpointExtension 
checkpoint_extension;
+        ASSERT_TRUE(builder.Attach(checkpoint_extension).IsOk());
+        ASSERT_TRUE(checkpoint_extension
+                        
.LoadCkptManager(std::make_unique<LuminaCheckpointManager>(file_manager))
+                        .IsOk());
+        std::vector<float> vectors = {
+            0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,
+            1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f,
+        };
+        std::vector<::lumina::core::vector_id_t> ids = {0, 1, 2, 3};
+        TestLuminaDataset pretrain_dataset(/*dimension=*/4, vectors, ids);
+        ASSERT_TRUE(builder.PretrainFrom(pretrain_dataset).IsOk());
+        TestLuminaDataset insert_dataset(/*dimension=*/4, vectors, ids);
+        ASSERT_TRUE(builder.InsertFrom(insert_dataset).IsOk());
+    }
+    ASSERT_OK_AND_ASSIGN(bool checkpoint_exists, 
file_manager->CheckpointExists());
+    ASSERT_TRUE(checkpoint_exists);
+
+    std::map<std::string, std::string> checkpoint_options = options_;
+    checkpoint_options["lumina.extension.build.ckpt.threshold"] = "1";
+    checkpoint_options["lumina.extension.build.ckpt.count"] = "1";
+    ASSERT_OK_AND_ASSIGN(
+        std::shared_ptr<GlobalIndexWriter> writer,
+        LuminaGlobalIndex(checkpoint_options)
+            .CreateWriter("f0", CreateArrowSchema(data_type_).get(), 
file_manager, pool_));
+    // The two-row build fails after loading the checkpoint, and must be 
retried without it.
+    file_manager->check_checkpoint_count_ = 0;
+    ArrowArray c_array;
+    ASSERT_TRUE(arrow::ExportArray(*array_->Slice(0, 2), &c_array).ok());
+    ASSERT_OK(writer->AddBatch(&c_array, {0, 1}));
+    ASSERT_NOK(writer->Finish());
+    ASSERT_GT(file_manager->open_checkpoint_count_, 0);
+    ASSERT_EQ(file_manager->delete_count_, 1);
+    ASSERT_GE(file_manager->check_checkpoint_count_, 3);
+    ASSERT_OK_AND_ASSIGN(checkpoint_exists, file_manager->CheckpointExists());
+    ASSERT_FALSE(checkpoint_exists);
+}
+
 TEST_F(LuminaGlobalIndexTest, TestWriteAndReadWithTagFilter) {
     auto test_root_dir = paimon::test::UniqueTestDirectory::Create();
     ASSERT_TRUE(test_root_dir);
     std::string test_root = test_root_dir->Str();
 
     std::map<std::string, std::string> tag_options = options_;
+    tag_options["lumina.extension.build.ckpt.threshold"] = "1";
+    tag_options["lumina.extension.build.ckpt.count"] = "1";
     tag_options["lumina.extension.build.tag.tag_schema"] =
         R"({"key_name":"color","type":"enum","value_type":"string"})";
 
@@ -276,6 +707,9 @@ TEST_F(LuminaGlobalIndexTest, 
TestWriteAndReadWithTagFilter) {
     ASSERT_OK_AND_ASSIGN(
         GlobalIndexIOMeta meta,
         WriteGlobalIndex(test_root, tag_data_type, tag_options, tag_array, 
Range(0, 3)));
+    std::vector<BasicFileStatus> checkpoint_files;
+    ASSERT_OK(fs_->ListDir(PathUtil::JoinPath(test_root, "index/checkpoint"), 
&checkpoint_files));
+    ASSERT_TRUE(checkpoint_files.empty());
     ASSERT_OK_AND_ASSIGN(std::shared_ptr<GlobalIndexReader> reader,
                          CreateGlobalIndexReader(test_root, data_type_, 
tag_options, meta));
 
@@ -787,7 +1221,8 @@ TEST_F(LuminaGlobalIndexTest, TestInvalidInputs) {
         {
             auto global_index = std::make_shared<LuminaGlobalIndex>(options_);
             auto path_factory = 
std::make_shared<FakeIndexPathFactory>(index_root);
-            auto file_reader = std::make_shared<GlobalIndexFileManager>(fs_, 
path_factory);
+            auto file_reader = std::make_shared<GlobalIndexFileManager>(
+                fs_, path_factory, /*checkpoint_path_factory=*/nullptr);
 
             
ASSERT_NOK_WITH_MSG(global_index->CreateReader(CreateArrowSchema(data_type_).get(),
                                                            file_reader, {meta, 
meta}, pool_),
@@ -1005,7 +1440,8 @@ TEST_F(LuminaGlobalIndexTest, TestWriteWithAllNullRows) {
 
     auto global_index = std::make_shared<LuminaGlobalIndex>(options_);
     auto path_factory = std::make_shared<FakeIndexPathFactory>(test_root);
-    auto file_writer = std::make_shared<GlobalIndexFileManager>(fs_, 
path_factory);
+    auto file_writer = std::make_shared<GlobalIndexFileManager>(
+        fs_, path_factory, /*checkpoint_path_factory=*/nullptr);
 
     ASSERT_OK_AND_ASSIGN(
         std::shared_ptr<GlobalIndexWriter> global_writer,
@@ -1077,7 +1513,8 @@ TEST_F(LuminaGlobalIndexTest, 
TestWriteWithNullAcrossMultipleBatches) {
 
     auto global_index = std::make_shared<LuminaGlobalIndex>(options_);
     auto path_factory = std::make_shared<FakeIndexPathFactory>(test_root);
-    auto file_writer = std::make_shared<GlobalIndexFileManager>(fs_, 
path_factory);
+    auto file_writer = std::make_shared<GlobalIndexFileManager>(
+        fs_, path_factory, /*checkpoint_path_factory=*/nullptr);
 
     ASSERT_OK_AND_ASSIGN(
         std::shared_ptr<GlobalIndexWriter> global_writer,
diff --git a/src/paimon/global_index/tantivy/tantivy_equivalence_test.cpp 
b/src/paimon/global_index/tantivy/tantivy_equivalence_test.cpp
index 44f780ac..07f06200 100644
--- a/src/paimon/global_index/tantivy/tantivy_equivalence_test.cpp
+++ b/src/paimon/global_index/tantivy/tantivy_equivalence_test.cpp
@@ -117,7 +117,8 @@ class TantivyEquivalenceTest : public ::testing::Test {
                                const std::string& root) {
         EXPECT_OK_AND_ASSIGN(auto indexer, 
GlobalIndexerFactory::Get(factory_id, options));
         auto path_factory = std::make_shared<FakeIndexPathFactory>(root);
-        auto file_writer = std::make_shared<GlobalIndexFileManager>(fs_, 
path_factory);
+        auto file_writer = std::make_shared<GlobalIndexFileManager>(
+            fs_, path_factory, /*checkpoint_path_factory=*/nullptr);
         EXPECT_OK_AND_ASSIGN(
             auto writer,
             indexer->CreateWriter("f0", CreateArrowSchema(data_type).get(), 
file_writer, pool_));
@@ -139,7 +140,8 @@ class TantivyEquivalenceTest : public ::testing::Test {
                                                const std::string& root) {
         EXPECT_OK_AND_ASSIGN(auto indexer, 
GlobalIndexerFactory::Get(factory_id, options));
         auto path_factory = std::make_shared<FakeIndexPathFactory>(root);
-        auto file_reader = std::make_shared<GlobalIndexFileManager>(fs_, 
path_factory);
+        auto file_reader = std::make_shared<GlobalIndexFileManager>(
+            fs_, path_factory, /*checkpoint_path_factory=*/nullptr);
         EXPECT_OK_AND_ASSIGN(auto reader, 
indexer->CreateReader(CreateArrowSchema(data_type).get(),
                                                                 file_reader, 
{meta}, pool_));
         return reader;
diff --git a/src/paimon/global_index/tantivy/tantivy_filter_limit_test.cpp 
b/src/paimon/global_index/tantivy/tantivy_filter_limit_test.cpp
index 5de18683..19df5ac6 100644
--- a/src/paimon/global_index/tantivy/tantivy_filter_limit_test.cpp
+++ b/src/paimon/global_index/tantivy/tantivy_filter_limit_test.cpp
@@ -76,7 +76,8 @@ class TantivyFilterLimitTest : public ::testing::Test {
         std::string root = root_dir->Str();
         kept_dirs_.push_back(std::move(root_dir));
         auto path_factory = std::make_shared<FakeIndexPathFactory>(root);
-        auto fm = std::make_shared<GlobalIndexFileManager>(fs_, path_factory);
+        auto fm = std::make_shared<GlobalIndexFileManager>(fs_, path_factory,
+                                                           
/*checkpoint_path_factory=*/nullptr);
         auto data_type = arrow::struct_({arrow::field("f0", arrow::utf8())});
         EXPECT_OK_AND_ASSIGN(auto writer_res, TantivyGlobalIndexWriter::Create(
                                                   "f0", data_type, fm, 
options, GetDefaultPool()));
diff --git a/src/paimon/global_index/tantivy/tantivy_index_test.cpp 
b/src/paimon/global_index/tantivy/tantivy_index_test.cpp
index 199e347b..d901d77c 100644
--- a/src/paimon/global_index/tantivy/tantivy_index_test.cpp
+++ b/src/paimon/global_index/tantivy/tantivy_index_test.cpp
@@ -85,7 +85,8 @@ class TantivyGlobalIndexIntegrationTest : public 
::testing::Test {
                                                int64_t 
/*unused_expected_range_end*/) const {
         auto global_index = std::make_shared<TantivyGlobalIndex>(options);
         auto path_factory = std::make_shared<FakeIndexPathFactory>(root);
-        auto file_writer = std::make_shared<GlobalIndexFileManager>(fs_, 
path_factory);
+        auto file_writer = std::make_shared<GlobalIndexFileManager>(
+            fs_, path_factory, /*checkpoint_path_factory=*/nullptr);
         PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<GlobalIndexWriter> w,
                                global_index->CreateWriter("f0", 
CreateArrowSchema(data_type).get(),
                                                           file_writer, pool_));
@@ -111,7 +112,8 @@ class TantivyGlobalIndexIntegrationTest : public 
::testing::Test {
         const std::map<std::string, std::string>& options, const 
GlobalIndexIOMeta& meta) const {
         auto global_index = std::make_shared<TantivyGlobalIndex>(options);
         auto path_factory = std::make_shared<FakeIndexPathFactory>(root);
-        auto file_reader = std::make_shared<GlobalIndexFileManager>(fs_, 
path_factory);
+        auto file_reader = std::make_shared<GlobalIndexFileManager>(
+            fs_, path_factory, /*checkpoint_path_factory=*/nullptr);
         return global_index->CreateReader(CreateArrowSchema(data_type).get(), 
file_reader, {meta},
                                           pool_);
     }
diff --git a/src/paimon/global_index/tantivy/tantivy_java_compat_test.cpp 
b/src/paimon/global_index/tantivy/tantivy_java_compat_test.cpp
index b998402c..c3358482 100644
--- a/src/paimon/global_index/tantivy/tantivy_java_compat_test.cpp
+++ b/src/paimon/global_index/tantivy/tantivy_java_compat_test.cpp
@@ -96,7 +96,8 @@ class JavaCompatTest : public ::testing::Test {
         std::map<std::string, std::string> options;
         auto global_index = std::make_shared<TantivyGlobalIndex>(options);
         auto path_factory = std::make_shared<FixturePathFactory>(fixture_dir);
-        auto file_reader = std::make_shared<GlobalIndexFileManager>(fs_, 
path_factory);
+        auto file_reader = std::make_shared<GlobalIndexFileManager>(
+            fs_, path_factory, /*checkpoint_path_factory=*/nullptr);
 
         auto data_type = arrow::struct_({arrow::field("f0", arrow::utf8())});
         auto c_schema = std::make_unique<::ArrowSchema>();
@@ -417,7 +418,8 @@ TEST_F(JavaCompatTest, 
CppWriteDefaultTokenizerForJavaCrossRead) {
     auto reader_factory =
         std::make_shared<TantivyGlobalIndex>(std::map<std::string, 
std::string>{});
     auto reader_path_factory = std::make_shared<FixturePathFactory>(out_dir);
-    auto reader_file_mgr = std::make_shared<GlobalIndexFileManager>(fs_, 
reader_path_factory);
+    auto reader_file_mgr = std::make_shared<GlobalIndexFileManager>(
+        fs_, reader_path_factory, /*checkpoint_path_factory=*/nullptr);
 
     auto c_schema = std::make_unique<::ArrowSchema>();
     ASSERT_TRUE(arrow::ExportType(*data_type, c_schema.get()).ok());
diff --git a/src/paimon/global_index/tantivy/tantivy_lucene_coexist_test.cpp 
b/src/paimon/global_index/tantivy/tantivy_lucene_coexist_test.cpp
index 2b6fdeb5..e837a0de 100644
--- a/src/paimon/global_index/tantivy/tantivy_lucene_coexist_test.cpp
+++ b/src/paimon/global_index/tantivy/tantivy_lucene_coexist_test.cpp
@@ -101,7 +101,8 @@ class TantivyLuceneCoexistTest : public ::testing::Test {
             return Status::Invalid(fmt::format("factory returned null for {}", 
impl.factory_id));
         }
         auto path_factory = std::make_shared<FakeIndexPathFactory>(root);
-        auto file_writer = std::make_shared<GlobalIndexFileManager>(fs_, 
path_factory);
+        auto file_writer = std::make_shared<GlobalIndexFileManager>(
+            fs_, path_factory, /*checkpoint_path_factory=*/nullptr);
         PAIMON_ASSIGN_OR_RAISE(
             std::shared_ptr<GlobalIndexWriter> w,
             indexer->CreateWriter("f0", CreateArrowSchema(data_type).get(), 
file_writer, pool_));
@@ -127,7 +128,8 @@ class TantivyLuceneCoexistTest : public ::testing::Test {
         PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<GlobalIndexer> indexer,
                                GlobalIndexerFactory::Get(impl.factory_id, 
options));
         auto path_factory = std::make_shared<FakeIndexPathFactory>(root);
-        auto file_reader = std::make_shared<GlobalIndexFileManager>(fs_, 
path_factory);
+        auto file_reader = std::make_shared<GlobalIndexFileManager>(
+            fs_, path_factory, /*checkpoint_path_factory=*/nullptr);
         return indexer->CreateReader(CreateArrowSchema(data_type).get(), 
file_reader, {meta},
                                      pool_);
     }
diff --git a/src/paimon/global_index/tantivy/tantivy_streaming_test.cpp 
b/src/paimon/global_index/tantivy/tantivy_streaming_test.cpp
index 36ca7a3a..d5de3265 100644
--- a/src/paimon/global_index/tantivy/tantivy_streaming_test.cpp
+++ b/src/paimon/global_index/tantivy/tantivy_streaming_test.cpp
@@ -106,7 +106,8 @@ class StreamingTestFixture : public ::testing::Test {
         EXPECT_TRUE(arrow::ExportType(*data_type, c_schema.get()).ok());
         auto global_index = std::make_shared<TantivyGlobalIndex>(options);
         auto path_factory = std::make_shared<FakeIndexPathFactory>(root);
-        auto file_writer = std::make_shared<GlobalIndexFileManager>(fs_, 
path_factory);
+        auto file_writer = std::make_shared<GlobalIndexFileManager>(
+            fs_, path_factory, /*checkpoint_path_factory=*/nullptr);
         EXPECT_OK_AND_ASSIGN(auto w,
                              global_index->CreateWriter("f0", c_schema.get(), 
file_writer, pool_));
         ::ArrowArray c_array;
@@ -132,7 +133,8 @@ class StreamingTestFixture : public ::testing::Test {
         EXPECT_TRUE(arrow::ExportType(*data_type, c_schema.get()).ok());
         auto global_index = std::make_shared<TantivyGlobalIndex>(options);
         auto path_factory = std::make_shared<FakeIndexPathFactory>(root);
-        auto file_reader = std::make_shared<GlobalIndexFileManager>(fs_, 
path_factory);
+        auto file_reader = std::make_shared<GlobalIndexFileManager>(
+            fs_, path_factory, /*checkpoint_path_factory=*/nullptr);
         EXPECT_OK_AND_ASSIGN(
             auto reader, global_index->CreateReader(c_schema.get(), 
file_reader, {meta}, pool_));
         return reader;
diff --git a/src/paimon/global_index/tantivy/tantivy_writer_test.cpp 
b/src/paimon/global_index/tantivy/tantivy_writer_test.cpp
index 0d623b88..886f7f5c 100644
--- a/src/paimon/global_index/tantivy/tantivy_writer_test.cpp
+++ b/src/paimon/global_index/tantivy/tantivy_writer_test.cpp
@@ -132,7 +132,8 @@ class TantivyGlobalIndexWriterTest : public ::testing::Test 
{
         const std::map<std::string, std::string>& options,
         const std::shared_ptr<arrow::Array>& array) {
         auto path_factory = std::make_shared<FakeIndexPathFactory>(root);
-        auto file_writer = std::make_shared<GlobalIndexFileManager>(fs_, 
path_factory);
+        auto file_writer = std::make_shared<GlobalIndexFileManager>(
+            fs_, path_factory, /*checkpoint_path_factory=*/nullptr);
         PAIMON_ASSIGN_OR_RAISE(auto writer, TantivyGlobalIndexWriter::Create(
                                                 "f0", data_type, file_writer, 
options, pool_));
         ::ArrowArray c_array;
@@ -240,7 +241,8 @@ TEST_F(TantivyGlobalIndexWriterTest, 
RejectsHmmTokenizeMode) {
     auto root_dir = paimon::test::UniqueTestDirectory::Create();
     ASSERT_TRUE(root_dir);
     auto path_factory = 
std::make_shared<FakeIndexPathFactory>(root_dir->Str());
-    auto file_writer = std::make_shared<GlobalIndexFileManager>(fs_, 
path_factory);
+    auto file_writer = std::make_shared<GlobalIndexFileManager>(
+        fs_, path_factory, /*checkpoint_path_factory=*/nullptr);
     // hmm rejection only fires when the jieba tokenizer is actually 
constructed,
     // so this test must explicitly opt into jieba (default tokenizer skips
     // jieba construction entirely).
diff --git a/test/inte/global_index_test.cpp b/test/inte/global_index_test.cpp
index 355220c7..1a895e49 100644
--- a/test/inte/global_index_test.cpp
+++ b/test/inte/global_index_test.cpp
@@ -159,11 +159,12 @@ class GlobalIndexTest : public ::testing::Test, public 
::testing::WithParamInter
                       const std::string& index_field_name, const std::string& 
index_type,
                       const std::map<std::string, std::string>& options, const 
Range& range) {
         PAIMON_ASSIGN_OR_RAISE(auto split, ScanData(table_path, 
partition_filters));
-        PAIMON_ASSIGN_OR_RAISE(auto index_commit_msg, 
GlobalIndexWriteTask::WriteIndex(
-                                                          table_path, 
index_field_name, index_type,
-                                                          
std::make_shared<IndexedSplitImpl>(
-                                                              split, 
std::vector<Range>({range})),
-                                                          options, pool_, 
fs_));
+        PAIMON_ASSIGN_OR_RAISE(
+            auto index_commit_msg,
+            GlobalIndexWriteTask::WriteIndex(
+                table_path, index_field_name, index_type,
+                std::make_shared<IndexedSplitImpl>(split, 
std::vector<Range>({range})), options,
+                /*task_id=*/std::nullopt, pool_, fs_));
         return Commit(table_path, {index_commit_msg});
     }
 
@@ -292,7 +293,8 @@ TEST_P(GlobalIndexTest, TestWriteLuminaIndex) {
                                                     table_path, "f1", "lumina",
                                                     
std::make_shared<IndexedSplitImpl>(
                                                         split, 
std::vector<Range>({Range(0, 3)})),
-                                                    
/*options=*/lumina_options, pool_));
+                                                    /*options=*/lumina_options,
+                                                    /*task_id=*/std::nullopt, 
pool_));
     auto index_commit_msg_impl = 
std::dynamic_pointer_cast<CommitMessageImpl>(index_commit_msg);
     ASSERT_TRUE(index_commit_msg_impl);
 
@@ -313,6 +315,67 @@ TEST_P(GlobalIndexTest, TestWriteLuminaIndex) {
     ASSERT_TRUE(expected_commit_message->TEST_Equal(*index_commit_msg_impl));
 }
 
+TEST_P(GlobalIndexTest, TestWriteLuminaIndexWithCheckpoint) {
+    arrow::FieldVector fields = {arrow::field("f0", arrow::utf8()),
+                                 arrow::field("f1", 
arrow::list(arrow::float32()))};
+    auto schema = arrow::schema(fields);
+    std::map<std::string, std::string> lumina_options = {
+        {"lumina.index.dimension", "4"},
+        {"lumina.index.type", "bruteforce"},
+        {"lumina.distance.metric", "l2"},
+        {"lumina.encoding.type", "rawf32"},
+        {"lumina.extension.build.ckpt.count", "1"},
+        {"lumina.extension.build.ckpt.threshold", "1"},
+        {"lumina.search.parallel_number", "10"}};
+
+    std::map<std::string, std::string> options = {{Options::FILE_FORMAT, 
file_format_},
+                                                  {Options::FILE_SYSTEM, 
"local"},
+                                                  
{Options::ROW_TRACKING_ENABLED, "true"},
+                                                  
{Options::DATA_EVOLUTION_ENABLED, "true"}};
+
+    CreateTable(/*partition_keys=*/{}, schema, options);
+    std::string table_path = PathUtil::JoinPath(dir_->Str(), "foo.db/bar");
+
+    std::vector<std::string> write_cols = schema->field_names();
+    auto src_array = 
arrow::ipc::internal::json::ArrayFromJSON(arrow::struct_(fields), R"([
+        ["a", [0.0, 0.0, 0.0, 0.0]],
+        ["b", [0.0, 1.0, 0.0, 1.0]],
+        ["c", [1.0, 0.0, 1.0, 0.0]],
+        ["d", [1.0, 1.0, 1.0, 1.0]]
+
+    ])")
+                         .ValueOrDie();
+
+    ASSERT_OK_AND_ASSIGN(auto commit_msgs, WriteArray(table_path, write_cols, 
src_array));
+    ASSERT_OK(Commit(table_path, commit_msgs));
+
+    ASSERT_OK_AND_ASSIGN(auto split, ScanData(table_path, 
/*partition_filters=*/{}));
+    std::shared_ptr<FileSystem> checkpoint_fs = dir_->GetFileSystem();
+    std::string task_id = "task-1";
+    std::string checkpoint_dir = PathUtil::JoinPath(table_path, 
"index/checkpoint");
+    ASSERT_OK(checkpoint_fs->Mkdirs(checkpoint_dir));
+    auto checkpoint_path = [&](const std::string& task, int64_t id) {
+        return PathUtil::JoinPath(
+            checkpoint_dir, 
fmt::format("lumina-global-index-f1-0-3-{}-{}.index.ckpt", task, id));
+    };
+    std::string matching_checkpoint = checkpoint_path(task_id, 9);
+    ASSERT_OK(checkpoint_fs->WriteFile(matching_checkpoint, "invalid 
checkpoint",
+                                       /*overwrite=*/false));
+    std::string unrelated_checkpoint = checkpoint_path("another-task", 999);
+    ASSERT_OK(checkpoint_fs->WriteFile(unrelated_checkpoint, "another task",
+                                       /*overwrite=*/false));
+
+    ASSERT_OK(GlobalIndexWriteTask::WriteIndex(
+        table_path, "f1", "lumina",
+        std::make_shared<IndexedSplitImpl>(split, std::vector<Range>{Range(0, 
3)}), lumina_options,
+        task_id, pool_));
+    std::vector<BasicFileStatus> checkpoint_files;
+    ASSERT_OK(checkpoint_fs->ListDir(checkpoint_dir, &checkpoint_files));
+    ASSERT_EQ(checkpoint_files.size(), 1);
+    ASSERT_EQ(PathUtil::GetName(checkpoint_files[0].GetPath()),
+              PathUtil::GetName(unrelated_checkpoint));
+}
+
 TEST_P(GlobalIndexTest, TestWriteLuminaIndexWithMismatchedDimension) {
     arrow::FieldVector fields = {arrow::field("f0", arrow::utf8()),
                                  arrow::field("f1", 
arrow::list(arrow::float32()))};
@@ -438,11 +501,12 @@ TEST_P(GlobalIndexTest, TestWriteIndex) {
     ASSERT_OK(Commit(table_path, commit_msgs));
 
     ASSERT_OK_AND_ASSIGN(auto split, ScanData(table_path, 
/*partition_filters=*/{}));
-    ASSERT_OK_AND_ASSIGN(auto index_commit_msg, 
GlobalIndexWriteTask::WriteIndex(
-                                                    table_path, "f0", "bitmap",
-                                                    
std::make_shared<IndexedSplitImpl>(
-                                                        split, 
std::vector<Range>({Range(0, 7)})),
-                                                    /*options=*/{}, pool_));
+    ASSERT_OK_AND_ASSIGN(
+        auto index_commit_msg,
+        GlobalIndexWriteTask::WriteIndex(
+            table_path, "f0", "bitmap",
+            std::make_shared<IndexedSplitImpl>(split, 
std::vector<Range>({Range(0, 7)})),
+            /*options=*/{}, /*task_id=*/std::nullopt, pool_));
     auto index_commit_msg_impl = 
std::dynamic_pointer_cast<CommitMessageImpl>(index_commit_msg);
     ASSERT_TRUE(index_commit_msg_impl);
 
@@ -466,7 +530,7 @@ TEST_P(GlobalIndexTest, TestWriteIndex) {
             GlobalIndexWriteTask::WriteIndex(
                 table_path, "f0", "invalid",
                 std::make_shared<IndexedSplitImpl>(split, 
std::vector<Range>({Range(0, 7)})),
-                /*options=*/{}, pool_),
+                /*options=*/{}, /*task_id=*/std::nullopt, pool_),
             "Unknown index type invalid, may not registered");
     }
     {
@@ -475,7 +539,7 @@ TEST_P(GlobalIndexTest, TestWriteIndex) {
                                 table_path, "f0", "bitmap",
                                 std::make_shared<IndexedSplitImpl>(
                                     split, std::vector<Range>({Range(0, 6), 
Range(7, 7)})),
-                                /*options=*/{}, pool_),
+                                /*options=*/{}, /*task_id=*/std::nullopt, 
pool_),
                             "GlobalIndexWriteTask only supports a single 
contiguous range.");
     }
 }
@@ -517,7 +581,7 @@ TEST_P(GlobalIndexTest, TestWriteIndexWithPartition) {
                 GlobalIndexWriteTask::WriteIndex(
                     table_path, "f0", "bitmap",
                     std::make_shared<IndexedSplitImpl>(split, 
std::vector<Range>({expected_range})),
-                    /*options=*/{}, pool_));
+                    /*options=*/{}, /*task_id=*/std::nullopt, pool_));
             auto index_commit_msg_impl =
                 std::dynamic_pointer_cast<CommitMessageImpl>(index_commit_msg);
             ASSERT_TRUE(index_commit_msg_impl);
@@ -1273,7 +1337,7 @@ TEST_P(GlobalIndexTest, 
TestWriteAndQueryLuminaIndexWithTagNullAndEmptyValues) {
         GlobalIndexWriteTask::WriteIndex(
             table_path, "embedding", "lumina",
             std::make_shared<IndexedSplitImpl>(split, 
std::vector<Range>({Range(0, 4)})),
-            /*options=*/lumina_options, pool_, fs_));
+            /*options=*/lumina_options, /*task_id=*/std::nullopt, pool_, fs_));
 
     std::shared_ptr<CommitMessageImpl> index_commit_msg_impl =
         std::dynamic_pointer_cast<CommitMessageImpl>(index_commit_msg);

Reply via email to