Skip to content

Commit

Permalink
Add truncate to capabilities of AsyncFileHandle (#37870)
Browse files Browse the repository at this point in the history
The ability to truncate a file is a prerequisite for the next round of
file-based cache.

---------

Signed-off-by: Raven Black <[email protected]>
  • Loading branch information
ravenblackx authored Jan 7, 2025
1 parent b9cb0f0 commit 2674bd9
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,25 @@ class ActionWriteFile : public AsyncFileActionThreadPool<absl::StatusOr<size_t>>
const off_t offset_;
};

class ActionTruncateFile : public AsyncFileActionThreadPool<absl::Status> {
public:
ActionTruncateFile(AsyncFileHandle handle, size_t length,
absl::AnyInvocable<void(absl::Status)> on_complete)
: AsyncFileActionThreadPool<absl::Status>(handle, std::move(on_complete)), length_(length) {}

absl::Status executeImpl() override {
ASSERT(fileDescriptor() != -1);
Api::SysCallIntResult result = posix().ftruncate(fileDescriptor(), length_);
if (result.return_value_ == -1) {
return statusAfterFileError(result);
}
return absl::OkStatus();
}

private:
const size_t length_;
};

class ActionDuplicateFile : public AsyncFileActionThreadPool<absl::StatusOr<AsyncFileHandle>> {
public:
ActionDuplicateFile(AsyncFileHandle handle,
Expand Down Expand Up @@ -244,6 +263,13 @@ absl::StatusOr<CancelFunction> AsyncFileContextThreadPool::duplicate(
dispatcher, std::make_unique<ActionDuplicateFile>(handle(), std::move(on_complete)));
}

absl::StatusOr<CancelFunction>
AsyncFileContextThreadPool::truncate(Event::Dispatcher* dispatcher, size_t length,
absl::AnyInvocable<void(absl::Status)> on_complete) {
return checkFileAndEnqueue(
dispatcher, std::make_unique<ActionTruncateFile>(handle(), length, std::move(on_complete)));
}

absl::StatusOr<CancelFunction>
AsyncFileContextThreadPool::checkFileAndEnqueue(Event::Dispatcher* dispatcher,
std::unique_ptr<AsyncFileAction> action) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ class AsyncFileContextThreadPool final : public AsyncFileContextBase {
absl::StatusOr<CancelFunction>
duplicate(Event::Dispatcher* dispatcher,
absl::AnyInvocable<void(absl::StatusOr<AsyncFileHandle>)> on_complete) override;
absl::StatusOr<CancelFunction>
truncate(Event::Dispatcher* dispatcher, size_t length,
absl::AnyInvocable<void(absl::Status)> on_complete) override;

int& fileDescriptor() { return file_descriptor_; }

Expand Down
4 changes: 4 additions & 0 deletions source/extensions/common/async_files/async_file_handle.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ class AsyncFileContext : public std::enable_shared_from_this<AsyncFileContext> {
Event::Dispatcher* dispatcher,
absl::AnyInvocable<void(absl::StatusOr<std::shared_ptr<AsyncFileContext>>)> on_complete) PURE;

virtual absl::StatusOr<CancelFunction>
truncate(Event::Dispatcher* dispatcher, size_t length,
absl::AnyInvocable<void(absl::Status)> on_complete) PURE;

protected:
virtual ~AsyncFileContext() = default;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ using StatusHelpers::IsOkAndHolds;
using StatusHelpers::StatusIs;
using ::testing::_;
using ::testing::Eq;
using ::testing::Pointee;
using ::testing::Return;
using ::testing::StrictMock;

Expand Down Expand Up @@ -298,6 +299,29 @@ TEST_F(AsyncFileHandleTest, OpenExistingReadWriteCanReadAndWrite) {
close(handle);
}

TEST_F(AsyncFileHandleTest, TruncateReducesFileSize) {
AsyncFileHandle handle = createAnonymousFile();
absl::StatusOr<size_t> write_status;
Buffer::OwnedImpl buf("hello world");
EXPECT_OK(handle->write(dispatcher_.get(), buf, 0, [&](absl::StatusOr<size_t> result) {
write_status = std::move(result);
}));
resolveFileActions();
EXPECT_THAT(write_status, IsOkAndHolds(11U));
absl::Status truncate_status = absl::UnknownError("");
EXPECT_OK(handle->truncate(dispatcher_.get(), 5,
[&](absl::Status result) { truncate_status = std::move(result); }));
resolveFileActions();
EXPECT_OK(truncate_status);
absl::StatusOr<Buffer::InstancePtr> read_result;
EXPECT_OK(handle->read(dispatcher_.get(), 0, 11, [&](absl::StatusOr<Buffer::InstancePtr> result) {
read_result = std::move(result);
}));
resolveFileActions();
EXPECT_THAT(read_result, IsOkAndHolds(Pointee(BufferStringEqual("hello"))));
close(handle);
}

TEST_F(AsyncFileHandleTest, DuplicateCreatesIndependentHandle) {
auto handle = createAnonymousFile();
absl::StatusOr<AsyncFileHandle> duplicate_status;
Expand Down Expand Up @@ -360,6 +384,18 @@ TEST_F(AsyncFileHandleWithMockPosixTest, PartialWriteRetries) {
close(handle);
}

TEST_F(AsyncFileHandleWithMockPosixTest, TruncateReturnsErrorOnTruncatingToLargerThanFile) {
AsyncFileHandle handle = createAnonymousFile();
absl::Status truncate_status;
EXPECT_CALL(mock_posix_file_operations_, ftruncate(_, _))
.WillOnce(Return(Api::SysCallIntResult{-1, EBADF}));
EXPECT_OK(handle->truncate(dispatcher_.get(), 5,
[&](absl::Status result) { truncate_status = std::move(result); }));
resolveFileActions();
EXPECT_EQ(absl::StatusCode::kFailedPrecondition, truncate_status.code()) << truncate_status;
close(handle);
}

TEST_F(AsyncFileHandleWithMockPosixTest, CancellingDuplicateInProgressClosesTheFile) {
auto handle = createAnonymousFile();
std::promise<void> entering_dup, finishing_dup;
Expand Down
7 changes: 7 additions & 0 deletions test/extensions/common/async_files/mocks.cc
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ MockAsyncFileContext::MockAsyncFileContext(std::shared_ptr<MockAsyncFileManager>
std::unique_ptr<MockAsyncFileAction>(
new TypedMockAsyncFileAction(std::move(on_complete))));
});
ON_CALL(*this, truncate(_, _, _))
.WillByDefault([this](Event::Dispatcher* dispatcher, size_t,
absl::AnyInvocable<void(absl::Status)> on_complete) {
return manager_->enqueue(dispatcher,
std::unique_ptr<MockAsyncFileAction>(
new TypedMockAsyncFileAction(std::move(on_complete))));
});
};

MockAsyncFileManager::MockAsyncFileManager() {
Expand Down
3 changes: 3 additions & 0 deletions test/extensions/common/async_files/mocks.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ class MockAsyncFileContext : public Extensions::Common::AsyncFiles::AsyncFileCon
absl::StatusOr<CancelFunction>, duplicate,
(Event::Dispatcher * dispatcher,
absl::AnyInvocable<void(absl::StatusOr<std::shared_ptr<AsyncFileContext>>)> on_complete));
MOCK_METHOD(absl::StatusOr<CancelFunction>, truncate,
(Event::Dispatcher * dispatcher, size_t length,
absl::AnyInvocable<void(absl::Status)> on_complete));

private:
std::shared_ptr<MockAsyncFileManager> manager_;
Expand Down

0 comments on commit 2674bd9

Please sign in to comment.