Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions include/paimon/realtime/arrow_realtime_store_factory.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,7 @@ namespace paimon {
class PAIMON_EXPORT ArrowRealtimeStoreFactory : public RealtimeStoreFactory {
public:
/// Creates an Arrow-backed store for one partition and bucket.
Result<std::shared_ptr<RealtimeStore>> Create(
std::unique_ptr<::ArrowSchema> write_schema, StatisticsMode statistics_mode,
const std::map<std::string, std::string>& options,
const std::shared_ptr<MemoryPool>& memory_pool) override;
Result<std::shared_ptr<RealtimeStore>> Create(RealtimeStoreCreateRequest&& request) override;
};

} // namespace paimon
70 changes: 48 additions & 22 deletions include/paimon/realtime/realtime_store.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <utility>
#include <vector>

#include "arrow/c/abi.h"
#include "paimon/reader/batch_reader.h"
#include "paimon/realtime/offset_range.h"
#include "paimon/record_batch.h"
Expand All @@ -41,10 +42,33 @@ namespace paimon {
class MemoryPool;
class Predicate;

/// A table record batch and its framework-assigned contiguous offset range.
enum class PAIMON_EXPORT RealtimeStoreMode {
APPEND_ONLY,
PRIMARY_KEY,
};

/// Parameters used by a `RealtimeStoreFactory` to create a store.
struct PAIMON_EXPORT RealtimeStoreCreateRequest {
/// Schema whose ownership is transferred to the factory. Append mode receives the complete
/// table write schema. Primary-key mode receives the realtime primary-key transport schema:
/// [_VALUE_KIND, _SEQUENCE_NUMBER, _REALTIME_OFFSET, table write fields].
std::unique_ptr<::ArrowSchema> write_schema;
/// Table options available to the store implementation.
std::map<std::string, std::string> options;
/// Memory pool for allocations retained by the store.
std::shared_ptr<MemoryPool> memory_pool;
/// Table mode implemented by the store.
RealtimeStoreMode mode = RealtimeStoreMode::APPEND_ONLY;
/// Statistics collected by append-only stores.
StatisticsMode statistics_mode = StatisticsMode::NONE;
};

/// A record batch and its framework-assigned contiguous offset range.
///
/// The batch contains only table write fields. Row `i` is associated with
/// `offset_range.begin + i`; the offset is progress metadata and is not a table field.
/// Append-mode batches contain table write fields, and row `i` has offset
/// `offset_range.begin + i`. Primary-key batches use the realtime primary-key transport schema,
/// are sorted by full primary key then sequence number, and retain the original offset in
/// `_REALTIME_OFFSET`.
struct PAIMON_EXPORT RealtimeWriteBatch {
/// Input batch whose ownership is transferred to `RealtimeStore::Write`.
std::unique_ptr<RecordBatch> batch;
Expand Down Expand Up @@ -79,7 +103,11 @@ class PAIMON_EXPORT RealtimeReadView {

/// Parameters used by a `RealtimeStore` to create readers for a query.
struct PAIMON_EXPORT RealtimeQueryContext {
/// Requested output fields before the mandatory leading `_VALUE_KIND` field is added.
/// Append mode receives the requested output fields before the mandatory leading
/// `_VALUE_KIND` field is added. Primary-key mode receives the requested realtime primary-key
/// transport schema.
/// This schema is borrowed and remains valid only during `CreateQueryReaders`; plugins must
/// import or copy it synchronously.
::ArrowSchema* read_schema;
/// Predicate using field indexes from `read_schema`.
std::shared_ptr<Predicate> predicate;
Expand Down Expand Up @@ -116,9 +144,10 @@ class PAIMON_EXPORT RealtimeStore {

/// Creates readers that expose all rows in a sealed segment for Paimon file writing.
///
/// Concatenating the returned readers must produce every sealed row exactly once and in write
/// order. Each output batch contains `_VALUE_KIND` followed by all fields from the factory's
/// `write_schema`.
/// The returned readers collectively expose every sealed row exactly once. Append-mode readers
/// preserve write order and contain `_VALUE_KIND` followed by table write fields. Primary-key
/// readers use the realtime primary-key transport schema; each reader's complete stream is
/// sorted by full primary key then sequence number.
virtual Result<std::vector<std::unique_ptr<BatchReader>>> CreateCommitReaders(
const std::shared_ptr<RealtimeSegmentHandle>& segment) = 0;

Expand All @@ -128,13 +157,16 @@ class PAIMON_EXPORT RealtimeStore {
/// also provide a consistent snapshot when a write or seal is in progress.
virtual Result<std::shared_ptr<RealtimeReadView>> AcquireReadView() = 0;

/// Creates readers over rows in `view` whose offsets are greater than or equal to
/// `offset_begin`.
/// Creates readers over rows in `view`. Append mode returns rows whose offsets are greater than
/// or equal to `offset_begin`; primary-key mode ignores `offset_begin`.
///
/// Each output batch contains `_VALUE_KIND` first, followed by the fields requested by
/// `context.read_schema` except a duplicate `_VALUE_KIND`. Concatenating all returned readers
/// must produce every matching row once. Paimon retains `view` for the lifetime of the
/// resulting framework reader.
/// Append-mode batches contain `_VALUE_KIND` followed by the requested fields except a
/// duplicate `_VALUE_KIND`, and collectively expose every matching row exactly once.
/// Primary-key batches use the requested realtime primary-key transport schema, including
/// nested field-ID alignment, and may contain multiple mutations per key; each reader's
/// complete stream is sorted by full primary key then sequence number, and the readers
/// collectively expose every raw mutation exactly once. Paimon retains `view` for the lifetime
/// of the resulting framework reader.
virtual Result<std::vector<std::unique_ptr<BatchReader>>> CreateQueryReaders(
const std::shared_ptr<RealtimeReadView>& view, int64_t offset_begin,
const RealtimeQueryContext& context) = 0;
Expand All @@ -158,15 +190,9 @@ class PAIMON_EXPORT RealtimeStoreFactory {
virtual ~RealtimeStoreFactory() = default;

/// Creates a store configured with the supplied schema, statistics, options, and memory pool.
/// @param write_schema Complete table write schema whose ownership is transferred to the
/// factory. The factory may consume it or retain it in the created store.
/// @param statistics_mode Framework-parsed statistics collection mode.
/// @param options Effective table options available to the store.
/// @param memory_pool Memory pool provided by the write context.
virtual Result<std::shared_ptr<RealtimeStore>> Create(
std::unique_ptr<::ArrowSchema> write_schema, StatisticsMode statistics_mode,
const std::map<std::string, std::string>& options,
const std::shared_ptr<MemoryPool>& memory_pool) = 0;
/// Creates a store for the requested table mode.
/// The factory consumes `request`, including ownership of `request.write_schema`.
virtual Result<std::shared_ptr<RealtimeStore>> Create(RealtimeStoreCreateRequest&& request) = 0;
};

} // namespace paimon
2 changes: 2 additions & 0 deletions include/paimon/utils/special_field_ids.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ class SpecialFieldIds {

/// Special field ID reserved for index score. Value: CPP_FIELD_ID_END - 1
inline static constexpr int32_t INDEX_SCORE = CPP_FIELD_ID_END - 1;
/// Special field ID reserved for realtime offset. Value: CPP_FIELD_ID_END - 2
inline static constexpr int32_t REALTIME_OFFSET = CPP_FIELD_ID_END - 2;

/// Lowest field ID reserved for system fields; IDs at or above it are excluded from the
/// highest field ID of a schema. Value: INT32_MAX / 2
Expand Down
10 changes: 9 additions & 1 deletion src/paimon/common/table/special_fields.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <cstdint>
#include <limits>
#include <string>
#include <utility>

#include "arrow/type_fwd.h"
#include "paimon/common/types/data_field.h"
Expand Down Expand Up @@ -66,13 +67,20 @@ struct SpecialFields {
return data_field;
}

static const DataField& RealtimeOffset() {
static const DataField data_field =
DataField(SpecialFieldIds::REALTIME_OFFSET,
arrow::field("_REALTIME_OFFSET", arrow::int64(), false));
return data_field;
}

static bool IsSystemField(const std::string& field_name) {
if (StringUtils::StartsWith(field_name, KEY_FIELD_PREFIX)) {
return true;
}
return field_name == SequenceNumber().Name() || field_name == ValueKind().Name() ||
field_name == RowKind().Name() || field_name == RowId().Name() ||
field_name == IndexScore().Name();
field_name == IndexScore().Name() || field_name == RealtimeOffset().Name();
}

// TODO(xinyu.lxy): add a func to complete row-tracking fields
Expand Down
8 changes: 8 additions & 0 deletions src/paimon/common/table/special_fields_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ TEST(SpecialFieldsTest, TestIndexScore) {
ASSERT_EQ(SpecialFields::IndexScore().Type()->id(), arrow::Type::FLOAT);
}

TEST(SpecialFieldsTest, TestRealtimeOffset) {
ASSERT_EQ(SpecialFields::RealtimeOffset().Id(), SpecialFieldIds::REALTIME_OFFSET);
ASSERT_EQ(SpecialFields::RealtimeOffset().Name(), "_REALTIME_OFFSET");
ASSERT_EQ(SpecialFields::RealtimeOffset().Type()->id(), arrow::Type::INT64);
ASSERT_FALSE(SpecialFields::RealtimeOffset().Nullable());
}

TEST(SpecialFieldsTest, TestKeyValueSpecialFieldCount) {
ASSERT_EQ(SpecialFields::KEY_VALUE_SPECIAL_FIELD_COUNT, 2);
}
Expand All @@ -66,6 +73,7 @@ TEST(SpecialFieldsTest, TestIsSystemField) {
ASSERT_TRUE(SpecialFields::IsSystemField("rowkind"));
ASSERT_TRUE(SpecialFields::IsSystemField("_ROW_ID"));
ASSERT_TRUE(SpecialFields::IsSystemField("_INDEX_SCORE"));
ASSERT_TRUE(SpecialFields::IsSystemField("_REALTIME_OFFSET"));
ASSERT_TRUE(SpecialFields::IsSystemField("_KEY_0"));
}

Expand Down
27 changes: 18 additions & 9 deletions src/paimon/core/realtime/arrow_realtime_store_factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,29 @@
namespace paimon {

Result<std::shared_ptr<RealtimeStore>> ArrowRealtimeStoreFactory::Create(
std::unique_ptr<ArrowSchema> write_schema, StatisticsMode statistics_mode,
const std::map<std::string, std::string>&, const std::shared_ptr<MemoryPool>& memory_pool) {
if (!write_schema || !write_schema->release) {
RealtimeStoreCreateRequest&& request) {
if (!request.write_schema || !request.write_schema->release) {
return Status::Invalid("real-time store write schema is null");
}
ScopeGuard schema_guard([schema = write_schema.get()]() { ArrowSchemaRelease(schema); });
if (!memory_pool) {
ScopeGuard schema_guard(
[schema = request.write_schema.get()]() { ArrowSchemaRelease(schema); });
if (!request.memory_pool) {
return Status::Invalid("real-time store memory pool is null");
}
PAIMON_ASSIGN_OR_RAISE_FROM_ARROW(std::shared_ptr<arrow::Schema> imported_schema,
arrow::ImportSchema(write_schema.get()));
std::shared_ptr<arrow::MemoryPool> arrow_pool = GetArrowPool(memory_pool);
return std::make_shared<ArrowRealtimeStore>(imported_schema, statistics_mode, memory_pool,
arrow_pool);
arrow::ImportSchema(request.write_schema.get()));
switch (request.mode) {
case RealtimeStoreMode::APPEND_ONLY: {
std::shared_ptr<arrow::MemoryPool> arrow_pool = GetArrowPool(request.memory_pool);
return std::make_shared<ArrowRealtimeStore>(imported_schema, request.statistics_mode,
request.memory_pool, arrow_pool);
}
case RealtimeStoreMode::PRIMARY_KEY: {
return Status::NotImplemented(
"primary-key real-time store support is not installed");
}
}
return Status::Invalid("invalid real-time store mode: ", static_cast<int32_t>(request.mode));
}

} // namespace paimon
5 changes: 4 additions & 1 deletion src/paimon/core/realtime/arrow_realtime_store_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,11 @@ TEST_F(ArrowRealtimeStoreTest, TestCommitReaderPreservesSlicedBatch) {
TEST_F(ArrowRealtimeStoreTest, TestFullStatisticsPrunesNonMatchingBatch) {
ArrowRealtimeStoreFactory factory;
std::unique_ptr<ArrowSchema> write_schema = MakeReadSchema(schema_);
RealtimeStoreCreateRequest request{std::move(write_schema),
/*options=*/{}, pool_, RealtimeStoreMode::APPEND_ONLY,
StatisticsMode::FULL};
ASSERT_OK_AND_ASSIGN(std::shared_ptr<RealtimeStore> realtime_store,
factory.Create(std::move(write_schema), StatisticsMode::FULL, {}, pool_));
factory.Create(std::move(request)));
std::shared_ptr<ArrowRealtimeStore> store =
std::dynamic_pointer_cast<ArrowRealtimeStore>(realtime_store);
ASSERT_NE(nullptr, store);
Expand Down
9 changes: 5 additions & 4 deletions src/paimon/core/realtime/realtime_append_only_writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,11 @@ Result<std::shared_ptr<RealtimeAppendOnlyWriter>> RealtimeAppendOnlyWriter::Crea
}
PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<RealtimeContextImpl> realtime_context_impl,
RealtimeContextImpl::Cast(realtime_context));
PAIMON_ASSIGN_OR_RAISE(
RealtimeStoreState store_state,
realtime_context_impl->GetOrCreateRealtimeStore(partition, bucket, std::move(write_schema),
statistics_mode, options, memory_pool));
RealtimeStoreCreateRequest request{std::move(write_schema), options, memory_pool,
RealtimeStoreMode::APPEND_ONLY, statistics_mode};
PAIMON_ASSIGN_OR_RAISE(RealtimeStoreState store_state,
realtime_context_impl->GetOrCreateRealtimeStore(
std::move(request), RealtimePartitionBucket(partition, bucket)));
return std::shared_ptr<RealtimeAppendOnlyWriter>(new RealtimeAppendOnlyWriter(
store_state.store, file_writer, input_schema, store_state.initial_offset, memory_pool));
}
Expand Down
Loading
Loading