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

yiguolei pushed a commit to branch branch-4.1
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-4.1 by this push:
     new 3b260b82f5d branch-4.1: [fix](cloud) bind the packed slice location 
lifetime to its writer #67347 (#67973)
3b260b82f5d is described below

commit 3b260b82f5d8659f70ef25a905697ae2e6d7c9f0
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Tue Sep 15 11:59:48 2026 +0800

    branch-4.1: [fix](cloud) bind the packed slice location lifetime to its 
writer #67347 (#67973)
    
    Cherry-picked from #67347
    
    Co-authored-by: Xin Liao <[email protected]>
---
 be/src/cloud/cloud_rowset_writer.cpp       |   5 +-
 be/src/io/fs/packed_file_manager.cpp       | 101 +++++----
 be/src/io/fs/packed_file_manager.h         |  77 ++++++-
 be/src/io/fs/packed_file_writer.cpp        |  20 +-
 be/src/io/fs/packed_file_writer.h          |   7 +-
 be/test/io/fs/packed_file_manager_test.cpp | 328 +++++++++++++++++++++++------
 6 files changed, 415 insertions(+), 123 deletions(-)

diff --git a/be/src/cloud/cloud_rowset_writer.cpp 
b/be/src/cloud/cloud_rowset_writer.cpp
index 47cc3865c1b..62f3a74657b 100644
--- a/be/src/cloud/cloud_rowset_writer.cpp
+++ b/be/src/cloud/cloud_rowset_writer.cpp
@@ -208,10 +208,11 @@ Status 
CloudRowsetWriter::_collect_packed_slice_location(io::FileWriter* file_wr
         return Status::OK();
     }
 
-    // Get packed slice location directly from PackedFileManager
+    // Ask the writer, which holds a reference to its own slice location. 
Looking it up by
+    // path in PackedFileManager would race with the retention based cleanup 
of the index.
     io::PackedSliceLocation index;
     RETURN_IF_ERROR(
-            
io::PackedFileManager::instance()->get_packed_slice_location(file_path, 
&index));
+            
static_cast<io::PackedFileWriter*>(file_writer)->get_packed_slice_location(&index));
     if (index.packed_file_path.empty()) {
         return Status::OK(); // File not in packed file, skip
     }
diff --git a/be/src/io/fs/packed_file_manager.cpp 
b/be/src/io/fs/packed_file_manager.cpp
index 76f0fb7b359..ccbc64f975f 100644
--- a/be/src/io/fs/packed_file_manager.cpp
+++ b/be/src/io/fs/packed_file_manager.cpp
@@ -337,7 +337,9 @@ Status PackedFileManager::ensure_file_system(const 
std::string& resource_id,
 }
 
 Status PackedFileManager::append_small_file(const std::string& path, const 
Slice& data,
-                                            const PackedAppendContext& info) {
+                                            const PackedAppendContext& info,
+                                            PackedSliceHandlePtr* handle) {
+    *handle = nullptr;
     // Check if file is too large to be merged
     if (data.get_size() > config::small_file_threshold_bytes) {
         return Status::OK(); // Skip merging for large files
@@ -387,7 +389,9 @@ Status PackedFileManager::append_small_file(const 
std::string& path, const Slice
     location.resource_id = info.resource_id;
     location.txn_id = info.txn_id;
 
-    active_state->slice_locations[path] = location;
+    auto slice_handle = 
std::make_shared<PackedSliceHandle>(std::move(location));
+    active_state->slice_locations[path] = slice_handle;
+    active_state->appended_slices.push_back(slice_handle);
     active_state->current_offset += data.get_size();
     active_state->total_size += data.get_size();
 
@@ -409,9 +413,10 @@ Status PackedFileManager::append_small_file(const 
std::string& path, const Slice
     // Update global index
     {
         std::lock_guard<std::mutex> global_lock(_global_index_mutex);
-        _global_slice_locations[path] = location;
+        _global_slice_locations[path] = slice_handle;
     }
 
+    *handle = std::move(slice_handle);
     return Status::OK();
 }
 
@@ -431,17 +436,20 @@ Status 
PackedFileManager::wait_for_packed_file_upload(PackedFileContext* packed_
     return Status::OK();
 }
 
-Status PackedFileManager::wait_upload_done(const std::string& path) {
-    std::string packed_file_path;
-    {
-        std::lock_guard<std::mutex> global_lock(_global_index_mutex);
-        auto it = _global_slice_locations.find(path);
-        if (it == _global_slice_locations.end()) {
-            return Status::InternalError("File not found in global index: " + 
path);
-        }
-        packed_file_path = it->second.packed_file_path;
+Status PackedFileManager::wait_upload_done(const PackedSliceHandlePtr& handle) 
{
+    if (handle == nullptr) {
+        return Status::InternalError("Missing packed slice handle");
     }
 
+    // The upload already finished, so there is no need to look up the 
PackedFileContext,
+    // which is only kept for a limited time after the upload.
+    auto upload_state = handle->upload_state();
+    if (upload_state == PackedSliceUploadState::UPLOADED) {
+        return Status::OK();
+    }
+    const bool upload_failed = upload_state == PackedSliceUploadState::FAILED;
+    const std::string& packed_file_path = handle->packed_file_path();
+
     // Find the packed file in uploaded files first - if already uploaded, no 
need to wait
     std::shared_ptr<PackedFileContext> managed_packed_file;
     std::shared_ptr<PackedFileContext> failed_packed_file;
@@ -498,8 +506,12 @@ Status PackedFileManager::wait_upload_done(const 
std::string& path) {
     }
 
     if (!packed_file_ptr) {
+        if (upload_failed) {
+            // The context carrying the original error has already been 
recycled
+            return Status::InternalError("Packed file upload failed: " + 
packed_file_path);
+        }
         // Packed file not found in any location, this is unexpected
-        return Status::InternalError("Packed file not found for path: " + 
path);
+        return Status::InternalError("Packed file not found: " + 
packed_file_path);
     }
 
     Status wait_status = wait_for_packed_file_upload(packed_file_ptr);
@@ -515,10 +527,19 @@ Status PackedFileManager::get_packed_slice_location(const 
std::string& path,
         return Status::NotFound("File not found in global packed index: {}", 
path);
     }
 
-    *location = it->second;
+    *location = it->second->location();
     return Status::OK();
 }
 
+void PackedFileManager::mark_slices_upload_result(const PackedFileContext& 
packed_file,
+                                                  PackedSliceUploadState 
state) {
+    // Not slice_locations: a path written twice into this packed file is only 
in there once,
+    // and the writer of the shadowed slice is waiting for the result too
+    for (const auto& handle : packed_file.appended_slices) {
+        handle->set_upload_result(state, packed_file.total_size);
+    }
+}
+
 void PackedFileManager::start_background_manager() {
     if (_background_thread) {
         return; // Already started
@@ -733,18 +754,11 @@ void PackedFileManager::process_uploading_packed_files() {
                 slices_stream << "; ";
             }
             first_slice = false;
-            slices_stream << small_file_path << "(txn=" << index.txn_id
-                          << ", offset=" << index.offset << ", size=" << 
index.size << ")";
-
-            // Update packed_file_size in global index
-            {
-                std::lock_guard<std::mutex> global_lock(_global_index_mutex);
-                auto it = _global_slice_locations.find(small_file_path);
-                if (it != _global_slice_locations.end()) {
-                    it->second.packed_file_size = packed_file->total_size;
-                }
-            }
+            auto location = index->location();
+            slices_stream << small_file_path << "(txn=" << location.txn_id
+                          << ", offset=" << location.offset << ", size=" << 
location.size << ")";
         }
+        mark_slices_upload_result(*packed_file, 
PackedSliceUploadState::UPLOADED);
         LOG(INFO) << "Packed file " << packed_file->packed_file_path
                   << " uploaded; slices=" << 
packed_file->slice_locations.size()
                   << ", total_bytes=" << packed_file->total_size << ", 
slice_detail=["
@@ -770,6 +784,7 @@ void PackedFileManager::process_uploading_packed_files() {
                               const Status& status) {
         LOG(WARNING) << "Failed to upload packed file: " << 
packed_file->packed_file_path
                      << ", error: " << status.to_string();
+        mark_slices_upload_result(*packed_file, 
PackedSliceUploadState::FAILED);
         {
             std::lock_guard<std::mutex> upload_lock(packed_file->upload_mutex);
             packed_file->state = PackedFileState::FAILED;
@@ -797,19 +812,20 @@ void PackedFileManager::process_uploading_packed_files() {
         packed_file_info.set_resource_id(packed_file->resource_id);
 
         for (const auto& [small_file_path, index] : 
packed_file->slice_locations) {
+            auto location = index->location();
             auto* small_file = packed_file_info.add_slices();
             small_file->set_path(small_file_path);
-            small_file->set_offset(index.offset);
-            small_file->set_size(index.size);
+            small_file->set_offset(location.offset);
+            small_file->set_size(location.size);
             small_file->set_deleted(false);
-            if (index.tablet_id != 0) {
-                small_file->set_tablet_id(index.tablet_id);
+            if (location.tablet_id != 0) {
+                small_file->set_tablet_id(location.tablet_id);
             }
-            if (!index.rowset_id.empty()) {
-                small_file->set_rowset_id(index.rowset_id);
+            if (!location.rowset_id.empty()) {
+                small_file->set_rowset_id(location.rowset_id);
             }
-            if (index.txn_id != 0) {
-                small_file->set_txn_id(index.txn_id);
+            if (location.txn_id != 0) {
+                small_file->set_txn_id(location.txn_id);
             }
         }
 
@@ -842,8 +858,9 @@ void PackedFileManager::process_uploading_packed_files() {
                     oss << ", ";
                 }
                 first = false;
-                oss << "[" << small_file_path << ", offset=" << index.offset
-                    << ", size=" << index.size << "]";
+                auto location = index->location();
+                oss << "[" << small_file_path << ", offset=" << location.offset
+                    << ", size=" << location.size << "]";
             }
             VLOG_DEBUG << oss.str();
         } else {
@@ -940,9 +957,17 @@ void PackedFileManager::cleanup_expired_data() {
         std::lock_guard<std::mutex> global_lock(_global_index_mutex);
         auto it = _global_slice_locations.begin();
         while (it != _global_slice_locations.end()) {
-            const auto& index = it->second;
-            if (index.create_time > 0 &&
-                current_time - index.create_time > 
config::uploaded_file_retention_seconds) {
+            // A writer or a packed file context still holds this slice, so 
whoever holds it
+            // may still read the entry back. Age says nothing about that: a 
load can run for
+            // much longer than the retention time. Only entries the index 
alone holds are
+            // stale, and the reference is dropped for us when the last owner 
goes away.
+            if (it->second.use_count() > 1) {
+                ++it;
+                continue;
+            }
+            const auto create_time = it->second->create_time();
+            if (create_time > 0 &&
+                current_time - create_time > 
config::uploaded_file_retention_seconds) {
                 it = _global_slice_locations.erase(it);
             } else {
                 ++it;
diff --git a/be/src/io/fs/packed_file_manager.h 
b/be/src/io/fs/packed_file_manager.h
index 13b5a39bfc0..ab79d5bc7e4 100644
--- a/be/src/io/fs/packed_file_manager.h
+++ b/be/src/io/fs/packed_file_manager.h
@@ -53,6 +53,53 @@ struct PackedSliceLocation {
     int64_t packed_file_size = -1; // Total size of the packed file, -1 means 
not set
 };
 
+// Upload state of the packed file a slice belongs to
+enum class PackedSliceUploadState : uint8_t {
+    PENDING = 0,
+    UPLOADED,
+    FAILED,
+};
+
+// A slice of a packed file, shared by PackedFileManager and the 
PackedFileWriter that
+// produced it. The writer holds its handle for as long as it lives, so it can 
wait for the
+// upload and read the location back at the end of the load, whatever the 
manager recycled
+// from its by-path index in the meantime.
+class PackedSliceHandle {
+public:
+    explicit PackedSliceHandle(PackedSliceLocation location) : 
_location(std::move(location)) {}
+
+    const std::string& packed_file_path() const { return 
_location.packed_file_path; }
+
+    int64_t create_time() const { return _location.create_time; }
+
+    PackedSliceUploadState upload_state() const {
+        return _upload_state.load(std::memory_order_acquire);
+    }
+
+    // Called once the packed file this slice belongs to reaches a terminal 
state
+    void set_upload_result(PackedSliceUploadState state, int64_t 
packed_file_size) {
+        if (state == PackedSliceUploadState::UPLOADED) {
+            _packed_file_size.store(packed_file_size, 
std::memory_order_relaxed);
+        }
+        _upload_state.store(state, std::memory_order_release);
+    }
+
+    PackedSliceLocation location() const {
+        PackedSliceLocation location = _location;
+        if (upload_state() == PackedSliceUploadState::UPLOADED) {
+            location.packed_file_size = 
_packed_file_size.load(std::memory_order_relaxed);
+        }
+        return location;
+    }
+
+private:
+    const PackedSliceLocation _location; // Immutable once the slice has been 
appended
+    std::atomic<int64_t> _packed_file_size {-1};
+    std::atomic<PackedSliceUploadState> _upload_state 
{PackedSliceUploadState::PENDING};
+};
+
+using PackedSliceHandlePtr = std::shared_ptr<PackedSliceHandle>;
+
 struct PackedAppendContext {
     std::string resource_id;
     int64_t tablet_id = 0;
@@ -72,14 +119,17 @@ public:
     // Initialize manager state; file system will be resolved lazily
     Status init();
 
-    // Write a small file to the current packed file
+    // Write a small file to the current packed file. On success `handle` 
receives a handle
+    // to the new slice, or nullptr if `data` was too large to be packed.
     Status append_small_file(const std::string& path, const Slice& data,
-                             const PackedAppendContext& info);
+                             const PackedAppendContext& info, 
PackedSliceHandlePtr* handle);
 
-    // Block until the small file's packed file is uploaded to S3
-    Status wait_upload_done(const std::string& path);
+    // Block until the packed file holding `handle` is uploaded to S3
+    Status wait_upload_done(const PackedSliceHandlePtr& handle);
 
-    // Get packed file index information for a small file
+    // Look a slice location up by small file path, for readers that have no 
handle to the
+    // slice. The entry lives as long as anything else holds the slice, so 
this only fails
+    // for a file whose writer and packed file context are both long gone.
     Status get_packed_slice_location(const std::string& path, 
PackedSliceLocation* location);
 
     // Start the background management thread
@@ -120,6 +170,10 @@ private:
     // Clean up expired data
     void cleanup_expired_data();
 
+    // Record the terminal upload state of `packed_file` on the slices it 
contains
+    void mark_slices_upload_result(const PackedFileContext& packed_file,
+                                   PackedSliceUploadState state);
+
     // Internal structure to track packed file state
     enum class PackedFileState {
         INIT,            // Initial state, no files written yet
@@ -133,7 +187,11 @@ private:
     struct PackedFileContext {
         std::string packed_file_path;
         std::unique_ptr<FileWriter> writer;
-        std::unordered_map<std::string, PackedSliceLocation> slice_locations;
+        std::unordered_map<std::string, PackedSliceHandlePtr> slice_locations;
+        // Every slice appended to this packed file. `slice_locations` is 
keyed by path, so
+        // writing one path twice into the same packed file only leaves the 
last handle
+        // there, while the upload result still has to reach both.
+        std::vector<PackedSliceHandlePtr> appended_slices;
         int64_t current_offset = 0;
         int64_t total_size = 0;
         int64_t create_time;
@@ -179,8 +237,11 @@ private:
     std::unordered_map<std::string, std::shared_ptr<PackedFileContext>> 
_uploaded_packed_files;
     std::mutex _packed_files_mutex;
 
-    // Global index mapping small file path to packed file index
-    std::unordered_map<std::string, PackedSliceLocation> 
_global_slice_locations;
+    // Global index mapping small file path to packed file index, for readers 
that have no
+    // handle to the slice, such as PackedFileSystem::open_file_impl() reading 
a segment back
+    // before its rowset meta exists. An entry is only recycled once nothing 
else holds the
+    // slice, so it outlives every writer and packed file context that could 
still read it.
+    std::unordered_map<std::string, PackedSliceHandlePtr> 
_global_slice_locations;
     std::mutex _global_index_mutex;
 
 #ifdef BE_TEST
diff --git a/be/src/io/fs/packed_file_writer.cpp 
b/be/src/io/fs/packed_file_writer.cpp
index 45f49e0e03f..7d97f0acfcd 100644
--- a/be/src/io/fs/packed_file_writer.cpp
+++ b/be/src/io/fs/packed_file_writer.cpp
@@ -176,8 +176,8 @@ Status PackedFileWriter::_close_sync() {
 Status PackedFileWriter::_wait_packed_upload() {
     DCHECK(!_is_direct_write);
     // Only wait if we have data that was sent to packed manager
-    if (_bytes_appended > 0 && _packed_file_manager != nullptr) {
-        return _packed_file_manager->wait_upload_done(_file_path);
+    if (_bytes_appended > 0 && _packed_file_manager != nullptr && 
_packed_slice_handle != nullptr) {
+        return _packed_file_manager->wait_upload_done(_packed_slice_handle);
     }
     return Status::OK();
 }
@@ -217,7 +217,17 @@ Status PackedFileWriter::_send_to_packed_manager() {
     }
 
     Slice data_slice(_buffer.data(), _buffer.size());
-    RETURN_IF_ERROR(_packed_file_manager->append_small_file(_file_path, 
data_slice, _append_info));
+    RETURN_IF_ERROR(_packed_file_manager->append_small_file(_file_path, 
data_slice, _append_info,
+                                                            
&_packed_slice_handle));
+    if (_packed_slice_handle == nullptr) {
+        // append_small_file() skips data larger than 
`small_file_threshold_bytes` without
+        // writing it anywhere. appendv() switches to direct write before the 
buffer can
+        // reach that size, unless the threshold was lowered in between. 
Report the failure
+        // instead of closing successfully on a file that does not exist.
+        return Status::InternalError(
+                "Packed file did not accept the buffered data for {}, size={}, 
threshold={}",
+                _file_path, data_slice.get_size(), 
config::small_file_threshold_bytes);
+    }
     _release_buffer();
     return Status::OK();
 }
@@ -225,11 +235,11 @@ Status PackedFileWriter::_send_to_packed_manager() {
 Status PackedFileWriter::get_packed_slice_location(PackedSliceLocation* 
location) const {
     DCHECK(_state == State::CLOSED)
             << " file_path: " << _file_path << " bytes_appended: " << 
_bytes_appended;
-    if (_is_direct_write) {
+    if (_is_direct_write || _packed_slice_handle == nullptr) {
         *location = PackedSliceLocation {};
         return Status::OK();
     }
-    
RETURN_IF_ERROR(_packed_file_manager->get_packed_slice_location(_file_path, 
location));
+    *location = _packed_slice_handle->location();
     LOG(INFO) << "get_packed_slice_location: " << _file_path
               << " packed_path: " << location->packed_file_path << " " << 
location->offset << " "
               << location->size;
diff --git a/be/src/io/fs/packed_file_writer.h 
b/be/src/io/fs/packed_file_writer.h
index d2c06621138..a53d412e667 100644
--- a/be/src/io/fs/packed_file_writer.h
+++ b/be/src/io/fs/packed_file_writer.h
@@ -89,9 +89,10 @@ private:
     std::string _buffer;
     size_t _bytes_appended = 0;
     State _state = State::OPENED;
-    bool _is_direct_write = false;                      // Whether to use 
direct write mode
-    PackedFileManager* _packed_file_manager = nullptr;  // Packed file manager 
instance
-    mutable PackedSliceLocation _packed_slice_location; // Packed slice info 
(mutable for lazy init)
+    bool _is_direct_write = false;                     // Whether to use 
direct write mode
+    PackedFileManager* _packed_file_manager = nullptr; // Packed file manager 
instance
+    // Handle to this file's slice, shared with PackedFileManager
+    PackedSliceHandlePtr _packed_slice_handle;
     PackedAppendContext _append_info;
     std::optional<std::chrono::steady_clock::time_point> 
_first_append_timestamp;
     bool _close_latency_recorded = false;
diff --git a/be/test/io/fs/packed_file_manager_test.cpp 
b/be/test/io/fs/packed_file_manager_test.cpp
index 93548f8c955..c481be20c1a 100644
--- a/be/test/io/fs/packed_file_manager_test.cpp
+++ b/be/test/io/fs/packed_file_manager_test.cpp
@@ -264,6 +264,14 @@ protected:
                 _old_enable_file_cache_write_from_s3_file_writer;
     }
 
+    // Most tests do not care about the handle they get back
+    Status append_small_file(const std::string& path, const Slice& data,
+                             const PackedAppendContext& info,
+                             PackedSliceHandlePtr* handle = nullptr) {
+        PackedSliceHandlePtr unused;
+        return manager->append_small_file(path, data, info, handle != nullptr 
? handle : &unused);
+    }
+
     PackedAppendContext default_append_info() const {
         PackedAppendContext info;
         info.resource_id = _resource_id;
@@ -308,19 +316,19 @@ TEST_F(PackedFileManagerTest, AppendSmallFileSuccess) {
     Slice slice(payload);
 
     auto info = default_append_info();
-    Status st = manager->append_small_file("s/path", slice, info);
+    Status st = append_small_file("s/path", slice, info);
     EXPECT_TRUE(st.ok());
     EXPECT_EQ(writer->append_calls(), 1);
     EXPECT_EQ(writer->bytes_appended(), payload.size());
 
     auto it = manager->global_slice_locations_for_test().find("s/path");
     ASSERT_NE(it, manager->global_slice_locations_for_test().end());
-    EXPECT_EQ(it->second.size, payload.size());
-    EXPECT_EQ(it->second.offset, 0);
-    EXPECT_EQ(it->second.tablet_id, info.tablet_id);
-    EXPECT_EQ(it->second.rowset_id, info.rowset_id);
-    EXPECT_EQ(it->second.resource_id, info.resource_id);
-    EXPECT_EQ(it->second.txn_id, info.txn_id);
+    EXPECT_EQ(it->second->location().size, payload.size());
+    EXPECT_EQ(it->second->location().offset, 0);
+    EXPECT_EQ(it->second->location().tablet_id, info.tablet_id);
+    EXPECT_EQ(it->second->location().rowset_id, info.rowset_id);
+    EXPECT_EQ(it->second->location().resource_id, info.resource_id);
+    EXPECT_EQ(it->second->location().txn_id, info.txn_id);
 }
 
 TEST_F(PackedFileManagerTest, DisableFileCacheWriteFromS3FileWriter) {
@@ -334,7 +342,7 @@ TEST_F(PackedFileManagerTest, 
DisableFileCacheWriteFromS3FileWriter) {
 
     auto info = default_append_info();
     ASSERT_TRUE(info.write_file_cache);
-    EXPECT_TRUE(manager->append_small_file("s/no_cache", slice, info).ok());
+    EXPECT_TRUE(append_small_file("s/no_cache", slice, info).ok());
     EXPECT_EQ(writer->append_calls(), 1);
     EXPECT_EQ(writer->bytes_appended(), payload.size());
 }
@@ -345,7 +353,7 @@ TEST_F(PackedFileManagerTest, AppendFailsWithoutTxnId) {
     auto info = default_append_info();
     info.txn_id = 0;
 
-    Status st = manager->append_small_file("missing_txn", slice, info);
+    Status st = append_small_file("missing_txn", slice, info);
     EXPECT_EQ(st.code(), doris::ErrorCode::INVALID_ARGUMENT);
     EXPECT_EQ(manager->global_slice_locations_for_test().count("missing_txn"), 
0);
 }
@@ -358,7 +366,7 @@ TEST_F(PackedFileManagerTest, AppendLargeFileSkipped) {
     std::string payload = "0123456789";
     Slice slice(payload);
     auto info = default_append_info();
-    Status st = manager->append_small_file("large", slice, info);
+    Status st = append_small_file("large", slice, info);
     EXPECT_TRUE(st.ok());
     EXPECT_EQ(writer->append_calls(), 0);
     EXPECT_EQ(manager->global_slice_locations_for_test().count("large"), 0);
@@ -372,7 +380,7 @@ TEST_F(PackedFileManagerTest, AppendWriterFailure) {
     std::string payload = "data";
     Slice slice(payload);
     auto info = default_append_info();
-    Status st = manager->append_small_file("broken", slice, info);
+    Status st = append_small_file("broken", slice, info);
     EXPECT_FALSE(st.ok());
     EXPECT_EQ(manager->global_slice_locations_for_test().count("broken"), 0);
 }
@@ -385,10 +393,10 @@ TEST_F(PackedFileManagerTest, 
AppendTriggersRotationWhenThresholdReached) {
     Slice slice2(payload2);
     auto info = default_append_info();
 
-    EXPECT_TRUE(manager->append_small_file("file1", slice1, info).ok());
+    EXPECT_TRUE(append_small_file("file1", slice1, info).ok());
 
     size_t create_before = file_system->created_paths().size();
-    EXPECT_TRUE(manager->append_small_file("file2", slice2, info).ok());
+    EXPECT_TRUE(append_small_file("file2", slice2, info).ok());
     EXPECT_EQ(file_system->created_paths().size(), create_before + 1);
     EXPECT_EQ(manager->uploading_packed_files_for_test().size(), 1);
 }
@@ -401,11 +409,11 @@ TEST_F(PackedFileManagerTest, 
AppendTriggersRotationWhenFileCountThresholdReache
     Slice slice(payload);
     auto info = default_append_info();
 
-    EXPECT_TRUE(manager->append_small_file("file1", slice, info).ok());
+    EXPECT_TRUE(append_small_file("file1", slice, info).ok());
     EXPECT_TRUE(manager->uploading_packed_files_for_test().empty());
 
     size_t created_before = file_system->created_paths().size();
-    EXPECT_TRUE(manager->append_small_file("file2", slice, info).ok());
+    EXPECT_TRUE(append_small_file("file2", slice, info).ok());
     EXPECT_EQ(manager->uploading_packed_files_for_test().size(), 1);
     EXPECT_EQ(file_system->created_paths().size(), created_before + 1);
 
@@ -442,7 +450,7 @@ TEST_F(PackedFileManagerTest, 
GetMergeFileIndexReturnsStoredValue) {
     std::string payload = "abc";
     Slice slice(payload);
     auto info = default_append_info();
-    EXPECT_TRUE(manager->append_small_file("stored", slice, info).ok());
+    EXPECT_TRUE(append_small_file("stored", slice, info).ok());
     PackedSliceLocation index;
     EXPECT_TRUE(manager->get_packed_slice_location("stored", &index).ok());
     EXPECT_EQ(index.size, payload.size());
@@ -456,7 +464,9 @@ TEST_F(PackedFileManagerTest, 
WaitWriteDoneReturnsOkWhenAlreadyUploaded) {
     std::string payload = "abc";
     Slice slice(payload);
     auto info = default_append_info();
-    ASSERT_TRUE(manager->append_small_file("path", slice, info).ok());
+    PackedSliceHandlePtr handle;
+    ASSERT_TRUE(append_small_file("path", slice, info, &handle).ok());
+    ASSERT_NE(handle, nullptr);
     
ASSERT_TRUE(manager->mark_current_packed_file_for_upload(_resource_id).ok());
     ASSERT_EQ(manager->uploading_packed_files_for_test().size(), 1);
 
@@ -470,14 +480,15 @@ TEST_F(PackedFileManagerTest, 
WaitWriteDoneReturnsOkWhenAlreadyUploaded) {
     manager->uploading_packed_files_for_test().clear();
     uploading->upload_cv.notify_all();
 
-    EXPECT_TRUE(manager->wait_upload_done("path").ok());
+    EXPECT_TRUE(manager->wait_upload_done(handle).ok());
 }
 
 TEST_F(PackedFileManagerTest, WaitWriteDoneReturnsErrorWhenFailed) {
     std::string payload = "abc";
     Slice slice(payload);
     auto info = default_append_info();
-    ASSERT_TRUE(manager->append_small_file("bad", slice, info).ok());
+    PackedSliceHandlePtr handle;
+    ASSERT_TRUE(append_small_file("bad", slice, info, &handle).ok());
     
ASSERT_TRUE(manager->mark_current_packed_file_for_upload(_resource_id).ok());
 
     auto uploading = 
manager->uploading_packed_files_for_test().begin()->second;
@@ -491,15 +502,13 @@ TEST_F(PackedFileManagerTest, 
WaitWriteDoneReturnsErrorWhenFailed) {
     manager->uploading_packed_files_for_test().clear();
     uploading->upload_cv.notify_all();
 
-    auto status = manager->wait_upload_done("bad");
+    auto status = manager->wait_upload_done(handle);
     EXPECT_FALSE(status.ok());
     EXPECT_NE(status.to_string().find("meta failed"), std::string::npos);
 }
 
-TEST_F(PackedFileManagerTest, WaitWriteDoneReturnsErrorWhenPathMissing) {
-    auto indices = manager->global_slice_locations_for_test().find("ghost");
-    ASSERT_EQ(indices, manager->global_slice_locations_for_test().end());
-    auto status = manager->wait_upload_done("ghost");
+TEST_F(PackedFileManagerTest, WaitWriteDoneReturnsErrorWhenLocationMissing) {
+    auto status = manager->wait_upload_done(nullptr);
     EXPECT_FALSE(status.ok());
 }
 
@@ -507,7 +516,8 @@ TEST_F(PackedFileManagerTest, 
WaitWriteDoneBlocksUntilUploadCompletes) {
     std::string payload = "abc";
     Slice slice(payload);
     auto info = default_append_info();
-    ASSERT_TRUE(manager->append_small_file("waiting", slice, info).ok());
+    PackedSliceHandlePtr handle;
+    ASSERT_TRUE(append_small_file("waiting", slice, info, &handle).ok());
 
     auto current_state = 
manager->current_packed_files_for_test()[_resource_id].get();
     ASSERT_NE(current_state, nullptr);
@@ -520,7 +530,7 @@ TEST_F(PackedFileManagerTest, 
WaitWriteDoneBlocksUntilUploadCompletes) {
     std::future<void> ready_future = ready.get_future();
     std::thread waiter([&] {
         ready.set_value();
-        auto status = manager->wait_upload_done("waiting");
+        auto status = manager->wait_upload_done(handle);
         EXPECT_TRUE(status.ok());
     });
 
@@ -560,7 +570,7 @@ TEST_F(PackedFileManagerTest, 
ProcessUploadingFilesSetsFailedWhenMetaUpdateFails
     std::string payload = "abc";
     Slice slice(payload);
     auto info = default_append_info();
-    ASSERT_TRUE(manager->append_small_file("meta_fail", slice, info).ok());
+    ASSERT_TRUE(append_small_file("meta_fail", slice, info).ok());
     
ASSERT_TRUE(manager->mark_current_packed_file_for_upload(_resource_id).ok());
     ASSERT_EQ(manager->uploading_packed_files_for_test().size(), 1);
 
@@ -577,7 +587,7 @@ TEST_F(PackedFileManagerTest, 
ProcessUploadingFilesCompletesAsyncUpload) {
     std::string payload = "abc";
     Slice slice(payload);
     auto info = default_append_info();
-    ASSERT_TRUE(manager->append_small_file("async_success", slice, info).ok());
+    ASSERT_TRUE(append_small_file("async_success", slice, info).ok());
     
ASSERT_TRUE(manager->mark_current_packed_file_for_upload(_resource_id).ok());
     ASSERT_EQ(manager->uploading_packed_files_for_test().size(), 1);
 
@@ -603,7 +613,7 @@ TEST_F(PackedFileManagerTest, 
ProcessUploadingFilesSetsFailedWhenAsyncCloseFails
     std::string payload = "abc";
     Slice slice(payload);
     auto info = default_append_info();
-    ASSERT_TRUE(manager->append_small_file("async_fail", slice, info).ok());
+    ASSERT_TRUE(append_small_file("async_fail", slice, info).ok());
     
ASSERT_TRUE(manager->mark_current_packed_file_for_upload(_resource_id).ok());
     ASSERT_EQ(manager->uploading_packed_files_for_test().size(), 1);
 
@@ -630,7 +640,7 @@ TEST_F(PackedFileManagerTest, 
ProcessUploadingFilesPollsAsyncCloseWithoutBlockin
     std::string payload = "abc";
     Slice slice(payload);
     auto info = default_append_info();
-    ASSERT_TRUE(manager->append_small_file("async_poll_fail", slice, 
info).ok());
+    ASSERT_TRUE(append_small_file("async_poll_fail", slice, info).ok());
     
ASSERT_TRUE(manager->mark_current_packed_file_for_upload(_resource_id).ok());
     ASSERT_EQ(manager->uploading_packed_files_for_test().size(), 1);
 
@@ -664,7 +674,7 @@ TEST_F(PackedFileManagerTest, 
AppendPackedFileInfoToFileTail) {
     std::string payload = "abc";
     Slice slice(payload);
     auto info = default_append_info();
-    ASSERT_TRUE(manager->append_small_file("trailer_path", slice, info).ok());
+    ASSERT_TRUE(append_small_file("trailer_path", slice, info).ok());
     
ASSERT_TRUE(manager->mark_current_packed_file_for_upload(_resource_id).ok());
     ASSERT_EQ(manager->uploading_packed_files_for_test().size(), 1);
 
@@ -716,6 +726,190 @@ TEST_F(PackedFileManagerTest, 
CleanupExpiredDataRemovesOldEntries) {
     EXPECT_TRUE(manager->uploaded_packed_files_for_test().empty());
 }
 
+TEST_F(PackedFileManagerTest, CleanupKeepsIndexEntryWhileSliceIsStillHeld) {
+    config::uploaded_file_retention_seconds = -1;
+    std::string payload = "abc";
+    Slice slice(payload);
+    auto info = default_append_info();
+    PackedSliceHandlePtr handle;
+    ASSERT_TRUE(append_small_file("long_load", slice, info, &handle).ok());
+    ASSERT_NE(handle, nullptr);
+
+    // Destroy the packed file context, so `handle` is the only holder besides 
the index. It
+    // stands in for the writer that produced the slice: a load can run for 
far longer than
+    // the retention time, and the location must still be there when it reads 
the file back.
+    manager->current_packed_files_for_test().clear();
+
+    manager->cleanup_expired_data();
+    EXPECT_EQ(manager->global_slice_locations_for_test().count("long_load"), 
1);
+
+    PackedSliceLocation location;
+    EXPECT_TRUE(manager->get_packed_slice_location("long_load", 
&location).ok());
+    EXPECT_EQ(location.size, payload.size());
+}
+
+TEST_F(PackedFileManagerTest, 
CleanupRemovesIndexEntryOnceNothingHoldsTheSlice) {
+    config::uploaded_file_retention_seconds = -1;
+    std::string payload = "abc";
+    Slice slice(payload);
+    auto info = default_append_info();
+    ASSERT_TRUE(append_small_file("short_load", slice, info).ok());
+    // Destroy the packed file context, so the index holds the only remaining 
reference, the
+    // way it does once the writer is gone and the packed file has been 
recycled
+    manager->current_packed_files_for_test().clear();
+
+    manager->cleanup_expired_data();
+    EXPECT_EQ(manager->global_slice_locations_for_test().count("short_load"), 
0);
+}
+
+TEST_F(PackedFileManagerTest, WaitUploadDoneSurvivesRecycledContext) {
+    std::string payload = "abc";
+    Slice slice(payload);
+    auto info = default_append_info();
+    PackedSliceHandlePtr handle;
+    ASSERT_TRUE(append_small_file("long_load", slice, info, &handle).ok());
+    
ASSERT_TRUE(manager->mark_current_packed_file_for_upload(_resource_id).ok());
+    ASSERT_EQ(manager->uploading_packed_files_for_test().size(), 1);
+
+    auto uploading = 
manager->uploading_packed_files_for_test().begin()->second;
+    auto* writer = dynamic_cast<MockFileWriter*>(uploading->writer.get());
+    ASSERT_NE(writer, nullptr);
+    uploading->state = PackedFileManager::PackedFileState::UPLOADING;
+    ASSERT_TRUE(writer->close(true).ok());
+    writer->complete_async_close();
+    manager->process_uploading_packed_files();
+    ASSERT_EQ(manager->uploaded_packed_files_for_test().size(), 1);
+    EXPECT_EQ(handle->upload_state(), PackedSliceUploadState::UPLOADED);
+    EXPECT_EQ(handle->location().packed_file_size, uploading->total_size);
+
+    // A load that runs far longer than the retention time: the packed file 
context is
+    // recycled while the writer that produced the slice is still open. A 
negative retention
+    // expires everything, including entries created in this very second.
+    config::uploaded_file_retention_seconds = -1;
+    manager->uploaded_packed_files_for_test().begin()->second->upload_time = 1;
+    uploading.reset(); // the test's own reference, so recycling really drops 
the context
+    manager->cleanup_expired_data();
+    ASSERT_TRUE(manager->uploaded_packed_files_for_test().empty());
+    // `handle` stands in for the still open writer, so the index entry is not 
stale
+    EXPECT_EQ(manager->global_slice_locations_for_test().count("long_load"), 
1);
+
+    EXPECT_TRUE(manager->wait_upload_done(handle).ok());
+}
+
+// The handle carries the upload result on its own, so it does not need the 
index either
+TEST_F(PackedFileManagerTest, WaitUploadDoneSurvivesRemovedIndexEntry) {
+    std::string payload = "abc";
+    Slice slice(payload);
+    auto info = default_append_info();
+    PackedSliceHandlePtr handle;
+    ASSERT_TRUE(append_small_file("long_load", slice, info, &handle).ok());
+    
ASSERT_TRUE(manager->mark_current_packed_file_for_upload(_resource_id).ok());
+
+    auto uploading = 
manager->uploading_packed_files_for_test().begin()->second;
+    auto* writer = dynamic_cast<MockFileWriter*>(uploading->writer.get());
+    ASSERT_NE(writer, nullptr);
+    uploading->state = PackedFileManager::PackedFileState::UPLOADING;
+    ASSERT_TRUE(writer->close(true).ok());
+    writer->complete_async_close();
+    manager->process_uploading_packed_files();
+    ASSERT_EQ(handle->upload_state(), PackedSliceUploadState::UPLOADED);
+
+    uploading.reset();
+    manager->global_slice_locations_for_test().clear();
+    manager->uploaded_packed_files_for_test().clear();
+
+    EXPECT_TRUE(manager->wait_upload_done(handle).ok());
+    EXPECT_EQ(handle->location().size, payload.size());
+}
+
+TEST_F(PackedFileManagerTest, 
WaitUploadDoneReportsFailureAfterContextRecycled) {
+    std::string payload = "abc";
+    Slice slice(payload);
+    auto info = default_append_info();
+    PackedSliceHandlePtr handle;
+    ASSERT_TRUE(append_small_file("long_load_fail", slice, info, 
&handle).ok());
+    
ASSERT_TRUE(manager->mark_current_packed_file_for_upload(_resource_id).ok());
+
+    auto uploading = 
manager->uploading_packed_files_for_test().begin()->second;
+    auto* writer = dynamic_cast<MockFileWriter*>(uploading->writer.get());
+    ASSERT_NE(writer, nullptr);
+    uploading->state = PackedFileManager::PackedFileState::UPLOADING;
+    writer->set_close_status(Status::IOError("async close fail"));
+    ASSERT_TRUE(writer->close(true).ok());
+    writer->complete_async_close();
+    manager->process_uploading_packed_files();
+    ASSERT_EQ(manager->uploaded_packed_files_for_test().size(), 1);
+    EXPECT_EQ(handle->upload_state(), PackedSliceUploadState::FAILED);
+
+    config::uploaded_file_retention_seconds = 0;
+    manager->uploaded_packed_files_for_test().begin()->second->upload_time = 1;
+    manager->cleanup_expired_data();
+    ASSERT_TRUE(manager->uploaded_packed_files_for_test().empty());
+
+    auto status = manager->wait_upload_done(handle);
+    EXPECT_FALSE(status.ok());
+    EXPECT_NE(status.to_string().find("Packed file upload failed"), 
std::string::npos);
+}
+
+// Same path twice with no rotation in between: both slices live in one packed 
file, and
+// slice_locations only keeps the last of them
+TEST_F(PackedFileManagerTest, 
RewritingTheSamePathWithinOnePackedFileNotifiesBothSlices) {
+    std::string payload = "abc";
+    Slice slice(payload);
+    auto info = default_append_info();
+    PackedSliceHandlePtr shadowed_handle;
+    PackedSliceHandlePtr fresh_handle;
+    ASSERT_TRUE(append_small_file("reused", slice, info, 
&shadowed_handle).ok());
+    ASSERT_TRUE(append_small_file("reused", slice, info, &fresh_handle).ok());
+    ASSERT_NE(shadowed_handle, fresh_handle);
+
+    auto* state = manager->current_packed_files_for_test()[_resource_id].get();
+    ASSERT_NE(state, nullptr);
+    EXPECT_EQ(state->slice_locations.size(), 1);
+    EXPECT_EQ(state->appended_slices.size(), 2);
+
+    
ASSERT_TRUE(manager->mark_current_packed_file_for_upload(_resource_id).ok());
+    auto uploading = 
manager->uploading_packed_files_for_test().begin()->second;
+    auto* writer = dynamic_cast<MockFileWriter*>(uploading->writer.get());
+    ASSERT_NE(writer, nullptr);
+    uploading->state = PackedFileManager::PackedFileState::UPLOADING;
+    ASSERT_TRUE(writer->close(true).ok());
+    writer->complete_async_close();
+    manager->process_uploading_packed_files();
+
+    // The shadowed slice has a writer waiting on it too
+    EXPECT_EQ(shadowed_handle->upload_state(), 
PackedSliceUploadState::UPLOADED);
+    EXPECT_EQ(fresh_handle->upload_state(), PackedSliceUploadState::UPLOADED);
+    EXPECT_EQ(shadowed_handle->location().packed_file_size, 
uploading->total_size);
+    EXPECT_TRUE(manager->wait_upload_done(shadowed_handle).ok());
+}
+
+TEST_F(PackedFileManagerTest, 
RewritingTheSamePathKeepsBothLocationsIndependent) {
+    std::string payload = "abc";
+    Slice slice(payload);
+    auto info = default_append_info();
+    PackedSliceHandlePtr stale_handle;
+    ASSERT_TRUE(append_small_file("reused", slice, info, &stale_handle).ok());
+    
ASSERT_TRUE(manager->mark_current_packed_file_for_upload(_resource_id).ok());
+    auto stale = manager->uploading_packed_files_for_test().begin()->second;
+
+    // The same path is written again, e.g. a retried publish rewriting the 
same segment
+    PackedSliceHandlePtr fresh_handle;
+    ASSERT_TRUE(append_small_file("reused", slice, info, &fresh_handle).ok());
+    ASSERT_NE(stale_handle, fresh_handle);
+
+    auto* writer = dynamic_cast<MockFileWriter*>(stale->writer.get());
+    ASSERT_NE(writer, nullptr);
+    stale->state = PackedFileManager::PackedFileState::UPLOADING;
+    ASSERT_TRUE(writer->close(true).ok());
+    writer->complete_async_close();
+    manager->process_uploading_packed_files();
+
+    // Finishing the older packed file says nothing about the newer write
+    EXPECT_EQ(stale_handle->upload_state(), PackedSliceUploadState::UPLOADED);
+    EXPECT_EQ(fresh_handle->upload_state(), PackedSliceUploadState::PENDING);
+}
+
 TEST_F(PackedFileManagerTest, MergeFileBvarMetricsUpdated) {
     manager->reset_packed_file_bvars_for_test();
     auto state = std::make_shared<PackedFileManager::PackedFileContext>();
@@ -724,8 +918,8 @@ TEST_F(PackedFileManagerTest, MergeFileBvarMetricsUpdated) {
     idx1.size = 100;
     PackedSliceLocation idx2;
     idx2.size = 200;
-    state->slice_locations["a"] = idx1;
-    state->slice_locations["b"] = idx2;
+    state->slice_locations["a"] = std::make_shared<PackedSliceHandle>(idx1);
+    state->slice_locations["b"] = std::make_shared<PackedSliceHandle>(idx2);
 
     manager->record_packed_file_metrics_for_test(state.get());
 
@@ -763,15 +957,15 @@ TEST_F(PackedFileManagerTest, 
MultipleSmallFilesTriggerMergeFile) {
         std::string payload(file_size, 'a' + (i % 26)); // 50 bytes per file
         Slice slice(payload);
 
-        Status st = manager->append_small_file(path, slice, info);
+        Status st = append_small_file(path, slice, info);
         EXPECT_TRUE(st.ok()) << "Failed to append file " << i << ": " << 
st.msg();
 
         // Verify file is in global index map
         auto it = manager->global_slice_locations_for_test().find(path);
         ASSERT_NE(it, manager->global_slice_locations_for_test().end());
-        EXPECT_EQ(it->second.size, file_size);
-        EXPECT_EQ(it->second.tablet_id, info.tablet_id);
-        EXPECT_EQ(it->second.rowset_id, info.rowset_id);
+        EXPECT_EQ(it->second->location().size, file_size);
+        EXPECT_EQ(it->second->location().tablet_id, info.tablet_id);
+        EXPECT_EQ(it->second->location().rowset_id, info.rowset_id);
     }
 
     // Verify all files are in global index map
@@ -784,9 +978,9 @@ TEST_F(PackedFileManagerTest, 
MultipleSmallFilesTriggerMergeFile) {
         auto it = manager->global_slice_locations_for_test().find(path);
         if (it != manager->global_slice_locations_for_test().end()) {
             total_files_in_index++;
-            total_size_in_index += it->second.size;
-            EXPECT_LT(it->second.size, config::small_file_threshold_bytes)
-                    << "File " << path << " size " << it->second.size
+            total_size_in_index += it->second->location().size;
+            EXPECT_LT(it->second->location().size, 
config::small_file_threshold_bytes)
+                    << "File " << path << " size " << 
it->second->location().size
                     << " should be less than threshold " << 
config::small_file_threshold_bytes;
         }
     }
@@ -819,7 +1013,7 @@ TEST_F(PackedFileManagerTest, 
MultipleSmallFilesTriggerMergeFile) {
     Slice slice_trigger(payload_trigger);
 
     size_t create_before = file_system->created_paths().size();
-    Status st = manager->append_small_file(path_trigger, slice_trigger, info);
+    Status st = append_small_file(path_trigger, slice_trigger, info);
     EXPECT_TRUE(st.ok());
 
     // Should create new merge file when threshold is reached
@@ -853,13 +1047,13 @@ TEST_F(PackedFileManagerTest, 
FilesNearThresholdBoundaryMergeFile) {
         std::string payload1(99, 'x'); // 99 bytes, just below threshold
         Slice slice1(payload1);
 
-        Status st = manager->append_small_file(path1, slice1, info);
+        Status st = append_small_file(path1, slice1, info);
         EXPECT_TRUE(st.ok());
 
         auto it = manager->global_slice_locations_for_test().find(path1);
         ASSERT_NE(it, manager->global_slice_locations_for_test().end());
-        EXPECT_EQ(it->second.size, 99);
-        EXPECT_LT(it->second.size, config::small_file_threshold_bytes);
+        EXPECT_EQ(it->second->location().size, 99);
+        EXPECT_LT(it->second->location().size, 
config::small_file_threshold_bytes);
     }
 
     // Files exactly at threshold (should be merged, uses > not >=, so 100 <= 
100 means merged)
@@ -868,14 +1062,14 @@ TEST_F(PackedFileManagerTest, 
FilesNearThresholdBoundaryMergeFile) {
         std::string payload2(100, 'y'); // 100 bytes, exactly at threshold
         Slice slice2(payload2);
 
-        Status st = manager->append_small_file(path2, slice2, info);
+        Status st = append_small_file(path2, slice2, info);
         EXPECT_TRUE(st.ok());
 
         // File at threshold should be merged (check uses >, so 100 is not > 
100, so it's merged)
         auto it = manager->global_slice_locations_for_test().find(path2);
         ASSERT_NE(it, manager->global_slice_locations_for_test().end());
-        EXPECT_EQ(it->second.size, 100);
-        EXPECT_LE(it->second.size, config::small_file_threshold_bytes);
+        EXPECT_EQ(it->second->location().size, 100);
+        EXPECT_LE(it->second->location().size, 
config::small_file_threshold_bytes);
     }
 
     // Files at threshold + 1 (should NOT be merged)
@@ -885,7 +1079,7 @@ TEST_F(PackedFileManagerTest, 
FilesNearThresholdBoundaryMergeFile) {
         Slice slice3(payload3);
 
         size_t append_calls_before = writer->append_calls();
-        Status st = manager->append_small_file(path3, slice3, info);
+        Status st = append_small_file(path3, slice3, info);
         EXPECT_TRUE(st.ok());
 
         // File above threshold should not be merged
@@ -904,14 +1098,14 @@ TEST_F(PackedFileManagerTest, 
FilesNearThresholdBoundaryMergeFile) {
         std::string payload(file_size, 'a' + (i % 26));
         Slice slice(payload);
 
-        Status st = manager->append_small_file(path, slice, info);
+        Status st = append_small_file(path, slice, info);
         EXPECT_TRUE(st.ok()) << "Failed to append file " << i << ": " << 
st.msg();
 
         // Verify file is merged
         auto it = manager->global_slice_locations_for_test().find(path);
         ASSERT_NE(it, manager->global_slice_locations_for_test().end());
-        EXPECT_EQ(it->second.size, file_size);
-        EXPECT_LT(it->second.size, config::small_file_threshold_bytes);
+        EXPECT_EQ(it->second->location().size, file_size);
+        EXPECT_LT(it->second->location().size, 
config::small_file_threshold_bytes);
     }
 
     // Verify merge file state
@@ -938,15 +1132,15 @@ TEST_F(PackedFileManagerTest, 
FilesNearThresholdBoundaryMergeFile) {
 
     // Verify all merged files are at or below threshold (uses >, so == 
threshold is merged)
     for (const auto& [path, index] : current_state->slice_locations) {
-        EXPECT_LE(index.size, config::small_file_threshold_bytes)
-                << "File " << path << " size " << index.size
+        EXPECT_LE(index->location().size, config::small_file_threshold_bytes)
+                << "File " << path << " size " << index->location().size
                 << " should be less than or equal to threshold "
                 << config::small_file_threshold_bytes;
     }
     for (const auto& [path, state] : 
manager->uploading_packed_files_for_test()) {
         for (const auto& [file_path, index] : state->slice_locations) {
-            EXPECT_LE(index.size, config::small_file_threshold_bytes)
-                    << "File " << file_path << " size " << index.size
+            EXPECT_LE(index->location().size, 
config::small_file_threshold_bytes)
+                    << "File " << file_path << " size " << 
index->location().size
                     << " should be less than or equal to threshold "
                     << config::small_file_threshold_bytes;
         }
@@ -991,7 +1185,7 @@ TEST_F(PackedFileManagerTest, TimeoutTriggersDirectUpload) 
{
     std::string payload = "small_file_data";
     Slice slice(payload);
     auto info = default_append_info();
-    ASSERT_TRUE(manager->append_small_file("small_file_1", slice, info).ok());
+    ASSERT_TRUE(append_small_file("small_file_1", slice, info).ok());
 
     // Get the merge file path before timeout
     PackedSliceLocation index_before;
@@ -1085,14 +1279,14 @@ TEST_F(PackedFileManagerTest, 
ModifyThresholdDuringContinuousImport) {
         std::string payload(file_size, 'a' + (i % 26));
         Slice slice(payload);
 
-        Status st = manager->append_small_file(path, slice, info);
+        Status st = append_small_file(path, slice, info);
         EXPECT_TRUE(st.ok()) << "Failed to append file " << i << ": " << 
st.msg();
 
         // Verify file is in global index map
         auto it = manager->global_slice_locations_for_test().find(path);
         ASSERT_NE(it, manager->global_slice_locations_for_test().end());
-        EXPECT_EQ(it->second.size, file_size);
-        EXPECT_LT(it->second.size, config::small_file_threshold_bytes);
+        EXPECT_EQ(it->second->location().size, file_size);
+        EXPECT_LT(it->second->location().size, 
config::small_file_threshold_bytes);
     }
 
     // Phase 2: Modify threshold to 200 bytes (larger than before)
@@ -1106,14 +1300,14 @@ TEST_F(PackedFileManagerTest, 
ModifyThresholdDuringContinuousImport) {
         std::string payload(file_size, 'x' + (i % 26));
         Slice slice(payload);
 
-        Status st = manager->append_small_file(path, slice, info);
+        Status st = append_small_file(path, slice, info);
         EXPECT_TRUE(st.ok()) << "Failed to append file phase2_" << i << ": " 
<< st.msg();
 
         // Verify file is in global index map
         auto it = manager->global_slice_locations_for_test().find(path);
         ASSERT_NE(it, manager->global_slice_locations_for_test().end());
-        EXPECT_EQ(it->second.size, file_size);
-        EXPECT_LT(it->second.size, config::small_file_threshold_bytes);
+        EXPECT_EQ(it->second->location().size, file_size);
+        EXPECT_LT(it->second->location().size, 
config::small_file_threshold_bytes);
     }
 
     // Phase 4: Modify threshold to 30 bytes (smaller than file size)
@@ -1126,7 +1320,7 @@ TEST_F(PackedFileManagerTest, 
ModifyThresholdDuringContinuousImport) {
         std::string payload(file_size, 'z');
         Slice slice(payload);
 
-        Status st = manager->append_small_file(path, slice, info);
+        Status st = append_small_file(path, slice, info);
         EXPECT_TRUE(st.ok());
 
         // Verify file is NOT in global index map (should be skipped)
@@ -1162,7 +1356,7 @@ TEST_F(PackedFileManagerTest, 
ModifyThresholdDuringContinuousImport) {
         EXPECT_NE(it, manager->global_slice_locations_for_test().end())
                 << "Phase 1 file " << i << " should be in global index map";
         if (it != manager->global_slice_locations_for_test().end()) {
-            EXPECT_EQ(it->second.size, file_size);
+            EXPECT_EQ(it->second->location().size, file_size);
         }
     }
 
@@ -1172,7 +1366,7 @@ TEST_F(PackedFileManagerTest, 
ModifyThresholdDuringContinuousImport) {
         EXPECT_NE(it, manager->global_slice_locations_for_test().end())
                 << "Phase 2 file " << i << " should be in global index map";
         if (it != manager->global_slice_locations_for_test().end()) {
-            EXPECT_EQ(it->second.size, file_size);
+            EXPECT_EQ(it->second->location().size, file_size);
         }
     }
 
@@ -1189,7 +1383,7 @@ TEST_F(PackedFileManagerTest, 
ModifyThresholdDuringContinuousImport) {
         Slice slice_trigger(payload_trigger);
 
         size_t create_before = file_system->created_paths().size();
-        Status st = manager->append_small_file(path_trigger, slice_trigger, 
info);
+        Status st = append_small_file(path_trigger, slice_trigger, info);
         EXPECT_TRUE(st.ok());
 
         // Should create new merge file when threshold is reached (if not 
already)
@@ -1200,7 +1394,7 @@ TEST_F(PackedFileManagerTest, 
ModifyThresholdDuringContinuousImport) {
         EXPECT_NE(trigger_it, manager->global_slice_locations_for_test().end())
                 << "Trigger file should be in global index map after threshold 
change";
         if (trigger_it != manager->global_slice_locations_for_test().end()) {
-            EXPECT_EQ(trigger_it->second.size, trigger_file_size);
+            EXPECT_EQ(trigger_it->second->location().size, trigger_file_size);
         }
     }
 }


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

Reply via email to