This is an automated email from the ASF dual-hosted git repository.
gavinchou pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new a7c0a7843a0 [fix](cloud) bind the packed slice location lifetime to
its writer (#67347)
a7c0a7843a0 is described below
commit a7c0a7843a0a363b9f298c4a35d5914061ad0784
Author: Xin Liao <[email protected]>
AuthorDate: Tue Sep 15 01:06:44 2026 +0800
[fix](cloud) bind the packed slice location lifetime to its writer (#67347)
Problem Summary:
A small file is handed to `PackedFileManager` when the segment is
flushed, but its slice
location is read again only when the rowset is closed —
`wait_upload_done()` from
`finish_close()`, and `get_packed_slice_location()` when the rowset meta
is built. The gap
between the two is the duration of the whole load, not the lifetime of
the packed file.
Both reads went through `_global_slice_locations`, which
`cleanup_expired_data()` recycles
purely on the age of the small file (`uploaded_file_retention_seconds`,
1800s by default).
Any load running longer than that lost the mapping and failed with:
```
[INTERNAL_ERROR]File not found in global index:
data/<tablet_id>/<segment>_0.idx
```
The packed file itself had been uploaded successfully — only the
in-memory mapping was gone.
We hit this in production with a broker load that ran for 38 minutes.
Two related points:
`_uploaded_packed_files` is recycled on the same config, so a waiter
could also fail with
`Packed file not found for path: ...`; and this is not specific to
inverted index files,
since segments smaller than `small_file_threshold_bytes` take the same
async-close /
late-wait path.
### What is changed and how it works?
A slice location should live as long as whoever may still read it, not
until a wall clock
deadline.
1. `append_small_file()` returns the location as a `std::shared_ptr` and
`PackedFileWriter`
keeps it for as long as it lives. `wait_upload_done()` and the writer's
`get_packed_slice_location()` take that reference, and
`CloudRowsetWriter` asks the writer
instead of the manager, so neither can fail because the index was
recycled. The terminal
upload state (UPLOADED / FAILED) is recorded on the slices, so a waiter
does not need the
`PackedFileContext` either.
2. The index itself gets the same property.
`PackedFileSystem::open_file_impl()` falls back
to `_global_slice_locations` for a file whose location has not reached
the rowset meta
yet, which is every read during the load — `SegmentIndexFileCacheLoader`
does exactly that
when it preloads a segment's index and footer ranges into file cache
right after close.
Since a location is now a `shared_ptr`, the cleanup can tell whether an
entry is still
needed: entries some writer or packed file context still holds are
skipped, and the age
check applies only to those the index alone holds. The last owner going
away drops the
reference, so an entry outlives everything that could still read it.
3. `PackedFileContext::slice_locations` is keyed by the small file path,
so one path written
twice into the same packed file left only the second handle in there and
the first never
reached a terminal state. Every appended handle is now kept alongside
that map and
notified from it. No caller does this today —
`CloudTablet::create_transient_rowset_writer()` sets `allow_packed_file
= false`, so the
MOW publish retry that writes one segment path twice never produces
packed files — but
the interface allowed it and failed quietly.
Behavior note: an index entry is no longer guaranteed to be dropped
after
`uploaded_file_retention_seconds`. It now stays for as long as a writer
or a packed file
context holds the slice, so a load producing N packed small files keeps
N entries resident
until it finishes. That is the point of the fix — those entries were
being dropped while
still in use — but it does change how much the map holds during a long
load.
Behavior for short loads is unchanged.
### Release note
Fix `File not found in global index` failures for loads that run longer
than
`uploaded_file_retention_seconds` when packed file is enabled.
---
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 ca39096c9cf..9753ca3dd99 100644
--- a/be/src/cloud/cloud_rowset_writer.cpp
+++ b/be/src/cloud/cloud_rowset_writer.cpp
@@ -223,10 +223,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 9245387379a..9cc76eb803e 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;
@@ -73,14 +120,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
@@ -121,6 +171,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
@@ -134,7 +188,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;
@@ -180,8 +238,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]