Skip to content

Commit 30dcf83

Browse files
authored
feat: add metrics storage-read-bytes for parquet and fix clean inte test (#439)
1 parent 34b8b92 commit 30dcf83

19 files changed

Lines changed: 134 additions & 111 deletions

src/paimon/common/utils/arrow/arrow_input_stream_adapter.cpp

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,12 @@ arrow::Status ValidateArrowIoRange(int64_t value, const char* name) {
4343
} // namespace
4444

4545
ArrowInputStreamAdapter::ArrowInputStreamAdapter(
46-
const std::shared_ptr<paimon::InputStream>& input_stream,
47-
const std::shared_ptr<arrow::MemoryPool>& pool, int64_t file_size)
48-
: input_stream_(input_stream), pool_(pool), file_size_(file_size) {
46+
const std::shared_ptr<paimon::InputStream>& input_stream, int64_t file_size,
47+
const std::shared_ptr<arrow::MemoryPool>& pool)
48+
: input_stream_(input_stream),
49+
pool_(pool),
50+
file_size_(file_size),
51+
storage_read_bytes_(std::make_shared<std::atomic<uint64_t>>(0)) {
4952
assert(file_size >= 0);
5053
}
5154

@@ -63,6 +66,9 @@ arrow::Result<int64_t> ArrowInputStreamAdapter::Read(int64_t nbytes, void* out)
6366
if (!read_bytes.ok()) {
6467
return ToArrowStatus(read_bytes.status());
6568
}
69+
if (storage_read_bytes_) {
70+
storage_read_bytes_->fetch_add(static_cast<uint64_t>(read_bytes.value()));
71+
}
6672
return read_bytes.value();
6773
}
6874

@@ -84,6 +90,9 @@ arrow::Result<int64_t> ArrowInputStreamAdapter::ReadAt(int64_t position, int64_t
8490
if (!read_bytes.ok()) {
8591
return ToArrowStatus(read_bytes.status());
8692
}
93+
if (storage_read_bytes_) {
94+
storage_read_bytes_->fetch_add(static_cast<uint64_t>(read_bytes.value()));
95+
}
8796
return read_bytes.value();
8897
}
8998

@@ -119,14 +128,19 @@ arrow::Future<std::shared_ptr<arrow::Buffer>> ArrowInputStreamAdapter::ReadAsync
119128
return fut;
120129
}
121130
std::shared_ptr<arrow::Buffer> buffer = std::move(buffer_result).ValueUnsafe();
122-
input_stream_->ReadAsync(reinterpret_cast<char*>(buffer->mutable_data()), nbytes, position,
123-
[fut, buffer](Status callback_status) mutable {
124-
if (callback_status.ok()) {
125-
fut.MarkFinished(std::move(buffer));
126-
} else {
127-
fut.MarkFinished(ToArrowStatus(callback_status));
128-
}
129-
});
131+
std::shared_ptr<std::atomic<uint64_t>> storage_read_bytes = storage_read_bytes_;
132+
input_stream_->ReadAsync(
133+
reinterpret_cast<char*>(buffer->mutable_data()), nbytes, position,
134+
[fut, buffer, storage_read_bytes, nbytes](Status callback_status) mutable {
135+
if (callback_status.ok()) {
136+
if (storage_read_bytes) {
137+
storage_read_bytes->fetch_add(static_cast<uint64_t>(nbytes));
138+
}
139+
fut.MarkFinished(std::move(buffer));
140+
} else {
141+
fut.MarkFinished(ToArrowStatus(callback_status));
142+
}
143+
});
130144
return fut;
131145
}
132146

src/paimon/common/utils/arrow/arrow_input_stream_adapter.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#pragma once
1818

19+
#include <atomic>
1920
#include <cstdint>
2021
#include <memory>
2122

@@ -30,7 +31,7 @@ class InputStream;
3031
class PAIMON_EXPORT ArrowInputStreamAdapter : public arrow::io::RandomAccessFile {
3132
public:
3233
ArrowInputStreamAdapter(const std::shared_ptr<paimon::InputStream>& input_stream,
33-
const std::shared_ptr<arrow::MemoryPool>& pool, int64_t file_size);
34+
int64_t file_size, const std::shared_ptr<arrow::MemoryPool>& pool);
3435
~ArrowInputStreamAdapter() override;
3536

3637
// NOTE: In paimon file system definition, position + nbytes should not exceed file_size_.
@@ -49,12 +50,21 @@ class PAIMON_EXPORT ArrowInputStreamAdapter : public arrow::io::RandomAccessFile
4950
}
5051
bool closed() const override;
5152

53+
// Accumulated bytes read from the underlying stream (storageReadBytes). The counter is owned
54+
// by this adapter and initialized to 0; callers may retain the returned shared_ptr to read the
55+
// value after the adapter is closed or destroyed.
56+
const std::shared_ptr<std::atomic<uint64_t>>& StorageReadBytes() const {
57+
return storage_read_bytes_;
58+
}
59+
5260
private:
5361
arrow::Status DoClose();
5462

5563
std::shared_ptr<paimon::InputStream> input_stream_;
5664
std::shared_ptr<arrow::MemoryPool> pool_;
5765
int64_t file_size_;
66+
// Accumulates the number of bytes read from the underlying stream (storageReadBytes).
67+
std::shared_ptr<std::atomic<uint64_t>> storage_read_bytes_;
5868
bool closed_ = false;
5969
};
6070

src/paimon/common/utils/arrow/arrow_stream_adapter_test.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ TEST(ArrowStreamAdapterTest, TestInputAndOutputStream) {
5959
ASSERT_OK_AND_ASSIGN(std::shared_ptr<InputStream> in, file_system->Open(file_name));
6060
ASSERT_OK_AND_ASSIGN(int64_t length, in->Length());
6161
auto in_stream =
62-
std::make_unique<ArrowInputStreamAdapter>(in, GetArrowPool(GetDefaultPool()), length);
62+
std::make_unique<ArrowInputStreamAdapter>(in, length, GetArrowPool(GetDefaultPool()));
6363
ASSERT_EQ(in_stream->GetSize().ValueOrDie(), static_cast<int64_t>(data.length()));
6464
ASSERT_EQ(in_stream->Tell().ValueOrDie(), 0);
6565
ASSERT_FALSE(in_stream->closed());

src/paimon/core/mergetree/spill_reader.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ Status SpillReader::Open(const FileIOChannel::ID& channel_id) {
5454
PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<FileStatus> file_status, fs_->GetFileStatus(file_path));
5555
int64_t file_len = file_status->GetLen();
5656
arrow_input_stream_adapter_ =
57-
std::make_shared<ArrowInputStreamAdapter>(in_stream_, arrow_pool_, file_len);
57+
std::make_shared<ArrowInputStreamAdapter>(in_stream_, file_len, arrow_pool_);
5858
auto ipc_read_options = arrow::ipc::IpcReadOptions::Defaults();
5959
ipc_read_options.memory_pool = arrow_pool_.get();
6060
ipc_read_options.use_threads = use_threads_;

src/paimon/core/operation/file_store_commit_impl.cpp

Lines changed: 15 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -892,7 +892,6 @@ Result<int32_t> FileStoreCommitImpl::TryCommit(
892892
Snapshot::CommitKind commit_kind, bool detect_conflicts) {
893893
int32_t retry_count = 0;
894894
int64_t start_millis = DateTimeUtils::GetCurrentUTCTimeUs() / 1000;
895-
std::optional<int64_t> retry_start_snapshot_id;
896895
while (true) {
897896
PAIMON_ASSIGN_OR_RAISE(std::optional<Snapshot> latest_snapshot,
898897
snapshot_manager_->LatestSnapshot());
@@ -902,13 +901,10 @@ Result<int32_t> FileStoreCommitImpl::TryCommit(
902901
bool commit_success,
903902
TryCommitOnce(commit_changes->delta_files, commit_changes->changelog_files,
904903
commit_changes->index_entries, identifier, watermark, properties,
905-
commit_kind, latest_snapshot, detect_conflicts, retry_start_snapshot_id));
904+
commit_kind, latest_snapshot, detect_conflicts));
906905
if (commit_success) {
907906
break;
908907
}
909-
retry_start_snapshot_id = latest_snapshot
910-
? std::optional<int64_t>(latest_snapshot.value().Id() + 1)
911-
: std::optional<int64_t>(Snapshot::FIRST_SNAPSHOT_ID);
912908
int64_t current_millis = DateTimeUtils::GetCurrentUTCTimeUs() / 1000;
913909
if (current_millis - start_millis > options_.GetCommitTimeout() ||
914910
retry_count >= options_.GetCommitMaxRetries()) {
@@ -923,26 +919,6 @@ Result<int32_t> FileStoreCommitImpl::TryCommit(
923919
return retry_count + 1;
924920
}
925921

926-
Result<bool> FileStoreCommitImpl::CheckCommitted(const std::optional<Snapshot>& latest_snapshot,
927-
std::optional<int64_t> retry_start_snapshot_id,
928-
int64_t identifier,
929-
const Snapshot::CommitKind& commit_kind) const {
930-
if (!latest_snapshot || !retry_start_snapshot_id ||
931-
retry_start_snapshot_id.value() > latest_snapshot.value().Id()) {
932-
return false;
933-
}
934-
935-
for (int64_t snapshot_id = retry_start_snapshot_id.value();
936-
snapshot_id <= latest_snapshot.value().Id(); ++snapshot_id) {
937-
PAIMON_ASSIGN_OR_RAISE(Snapshot snapshot, snapshot_manager_->LoadSnapshot(snapshot_id));
938-
if (snapshot.CommitUser() == commit_user_ && snapshot.CommitIdentifier() == identifier &&
939-
snapshot.GetCommitKind() == commit_kind) {
940-
return true;
941-
}
942-
}
943-
return false;
944-
}
945-
946922
Status FileStoreCommitImpl::CheckSameBucketFromSnapshot(
947923
const std::vector<ManifestEntry>& delta_entries,
948924
const std::optional<Snapshot>& latest_snapshot) const {
@@ -1015,13 +991,7 @@ Result<bool> FileStoreCommitImpl::TryCommitOnce(
1015991
const std::vector<IndexManifestEntry>& index_entries, int64_t identifier,
1016992
std::optional<int64_t> watermark, const std::map<std::string, std::string>& properties,
1017993
Snapshot::CommitKind commit_kind, const std::optional<Snapshot>& latest_snapshot,
1018-
bool detect_conflicts, std::optional<int64_t> retry_start_snapshot_id) {
1019-
PAIMON_ASSIGN_OR_RAISE(bool committed, CheckCommitted(latest_snapshot, retry_start_snapshot_id,
1020-
identifier, commit_kind));
1021-
if (committed) {
1022-
return true;
1023-
}
1024-
994+
bool detect_conflicts) {
1025995
std::vector<ManifestEntry> delta_files = delta_entries;
1026996
int64_t start_millis = DateTimeUtils::GetCurrentUTCTimeUs() / 1000;
1027997

@@ -1250,11 +1220,21 @@ Result<bool> FileStoreCommitImpl::TryCommitOnce(
12501220

12511221
Result<bool> commit_result = CommitSnapshotImpl(new_snapshot, delta_statistics);
12521222
if (!commit_result.ok()) {
1253-
// commit exception is uncertain; retry after checking whether this commit already exists.
1254-
PAIMON_LOG_WARN(logger_, "Retry commit for exception. %s",
1223+
// commit exception, not sure about the situation and should not clean up the files.
1224+
PAIMON_LOG_WARN(logger_,
1225+
"You need to call FilterAndCommit to retry commit for exception. %s",
12551226
commit_result.status().ToString().c_str());
1227+
1228+
// To prevent the case where an atomic write times out but actually succeeds,
1229+
// retrying the commit could lead to the snapshot file being committed multiple times.
1230+
// Therefore, retries should be handled by the upper layer,
1231+
// which should call FilterAndCommit to avoid duplicate commits.
1232+
// Therefore, we should not trigger cleanup here,
1233+
// as it may delete meta files from a snapshot that was just written by ourselves,
1234+
// leading to an incomplete or corrupted snapshot.
12561235
guard.Release();
1257-
return false;
1236+
return Status::Invalid("You need to call FilterAndCommit to retry commit for exception. ",
1237+
commit_result.status().ToString());
12581238
}
12591239
bool commit_success = commit_result.value();
12601240
if (commit_success) {

src/paimon/core/operation/file_store_commit_impl.h

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,7 @@ class FileStoreCommitImpl : public FileStoreCommit {
196196
const std::map<std::string, std::string>& properties,
197197
Snapshot::CommitKind commit_kind,
198198
const std::optional<Snapshot>& latest_snapshot,
199-
bool detect_conflicts,
200-
std::optional<int64_t> retry_start_snapshot_id);
199+
bool detect_conflicts);
201200

202201
Result<bool> CommitSnapshotImpl(const Snapshot& new_snapshot,
203202
const std::vector<PartitionEntry>& delta_statistics);
@@ -211,10 +210,6 @@ class FileStoreCommitImpl : public FileStoreCommit {
211210
const std::optional<std::string>& old_index_manifest,
212211
const std::optional<std::string>& new_index_manifest);
213212

214-
Result<bool> CheckCommitted(const std::optional<Snapshot>& latest_snapshot,
215-
std::optional<int64_t> retry_start_snapshot_id, int64_t identifier,
216-
const Snapshot::CommitKind& commit_kind) const;
217-
218213
Status CheckSameBucketFromSnapshot(const std::vector<ManifestEntry>& delta_entries,
219214
const std::optional<Snapshot>& latest_snapshot) const;
220215

src/paimon/core/operation/file_store_commit_impl_test.cpp

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -276,8 +276,8 @@ class FileStoreCommitImplTest : public testing::Test {
276276
/*creation_time=*/Timestamp(0, 0),
277277
/*delete_row_count=*/std::nullopt,
278278
/*embedded_index=*/nullptr, FileSource::Append(),
279-
/*external_path=*/std::nullopt,
280-
/*value_stats_cols=*/std::nullopt, /*first_row_id=*/std::nullopt,
279+
/*value_stats_cols=*/std::nullopt,
280+
/*external_path=*/std::nullopt, /*first_row_id=*/std::nullopt,
281281
/*write_cols=*/std::nullopt);
282282
}
283283

@@ -292,8 +292,8 @@ class FileStoreCommitImplTest : public testing::Test {
292292
/*creation_time=*/Timestamp(0, 0),
293293
/*delete_row_count=*/std::nullopt,
294294
/*embedded_index=*/nullptr, FileSource::Append(),
295-
/*external_path=*/std::nullopt,
296-
/*value_stats_cols=*/std::nullopt, /*first_row_id=*/std::nullopt,
295+
/*value_stats_cols=*/std::nullopt,
296+
/*external_path=*/std::nullopt, /*first_row_id=*/std::nullopt,
297297
/*write_cols=*/std::nullopt);
298298
}
299299

@@ -638,12 +638,7 @@ TEST_F(FileStoreCommitImplTest, TestCommitWithAtomicWriteSnapshotTimeoutAndActua
638638
"/orc/append_09.db/append_09/commit_messages/commit_messages-01",
639639
/*version=*/3);
640640
ASSERT_GT(msgs.size(), 0);
641-
ASSERT_OK(commit->Commit(msgs, /*commit_identifier=*/1));
642-
std::shared_ptr<Metrics> metrics = commit->GetCommitMetrics();
643-
ASSERT_TRUE(metrics);
644-
ASSERT_OK_AND_ASSIGN(uint64_t counter,
645-
metrics->GetCounter(CommitMetrics::LAST_COMMIT_ATTEMPTS));
646-
ASSERT_EQ(2u, counter);
641+
ASSERT_NOK(commit->Commit(msgs, /*commit_identifier=*/1));
647642
ASSERT_OK_AND_ASSIGN(
648643
bool exist, file_system_->Exists(PathUtil::JoinPath(table_path, "snapshot/snapshot-6")));
649644
ASSERT_TRUE(exist);
@@ -656,6 +651,8 @@ TEST_F(FileStoreCommitImplTest, TestCommitWithAtomicWriteSnapshotTimeoutAndActua
656651
.Finish());
657652

658653
ASSERT_OK_AND_ASSIGN(auto commit_2, FileStoreCommit::Create(std::move(commit_context_2)));
654+
ASSERT_OK_AND_ASSIGN(int32_t num_committed, commit_2->FilterAndCommit({{1, msgs}}));
655+
ASSERT_EQ(0, num_committed);
659656
std::string new_snapshot_7 = PathUtil::JoinPath(table_path, "snapshot/snapshot-7");
660657
EXPECT_CALL(*mock_fs, AtomicStore(testing::StrEq(new_snapshot_7), testing::_))
661658
.WillOnce(testing::Invoke([&](const std::string& path, const std::string& content) {

src/paimon/format/parquet/column_index_filter_test.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ class ColumnIndexFilterTest : public ::testing::Test {
252252
// Open as raw ParquetFileReader
253253
ASSERT_OK_AND_ASSIGN(std::shared_ptr<InputStream> in, fs_->Open(file_name_));
254254
ASSERT_OK_AND_ASSIGN(int64_t length, in->Length());
255-
auto in_stream = std::make_shared<ArrowInputStreamAdapter>(in, arrow_pool_, length);
255+
auto in_stream = std::make_shared<ArrowInputStreamAdapter>(in, length, arrow_pool_);
256256
parquet_reader_ = ::parquet::ParquetFileReader::Open(in_stream);
257257
ASSERT_TRUE(parquet_reader_);
258258

src/paimon/format/parquet/file_reader_wrapper_test.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ class FileReaderWrapperTest : public ::testing::Test {
119119
const std::string& file_path, int64_t wrapper_batch_size = 0) {
120120
PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<InputStream> in, fs_->Open(file_path));
121121
PAIMON_ASSIGN_OR_RAISE(int64_t file_length, in->Length());
122-
auto input_stream = std::make_unique<ArrowInputStreamAdapter>(in, arrow_pool_, file_length);
122+
auto input_stream = std::make_unique<ArrowInputStreamAdapter>(in, file_length, arrow_pool_);
123123
::parquet::arrow::FileReaderBuilder file_reader_builder;
124124
::parquet::ReaderProperties reader_properties;
125125
reader_properties.enable_buffered_stream();

0 commit comments

Comments
 (0)