Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[kv] Support version merge engine #277

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

sunxiaojian
Copy link

@sunxiaojian sunxiaojian commented Dec 27, 2024

Purpose

Support version merge engine

Linked issue: close #213

Tests

com.alibaba.fluss.connector.flink.sink.FlinkTableSinkITCase#testVersionMergeEngineWithTypeBigint
com.alibaba.fluss.connector.flink.sink.FlinkTableSinkITCase#testVersionMergeEngineWithTypeTimestamp
com.alibaba.fluss.client.table.FlussTableITCase#testMergeEngineWithVersion

API and Format

No

Documentation

No

@CLAassistant
Copy link

CLAassistant commented Dec 27, 2024

CLA assistant check
All committers have signed the CLA.

@sunxiaojian sunxiaojian force-pushed the support-merge-engine-version branch 2 times, most recently from 944bdb3 to 7f115bf Compare December 27, 2024 11:12
@sunxiaojian
Copy link
Author

@wuchong CLA has been sent, PTAL

@wuchong
Copy link
Member

wuchong commented Dec 29, 2024

Thanks for the contribution @sunxiaojian ! Will take a look. cc @luoyuxia as well.

@wuchong
Copy link
Member

wuchong commented Dec 29, 2024

@sunxiaojian sunxiaojian force-pushed the support-merge-engine-version branch 2 times, most recently from b1363d6 to 5b67c20 Compare December 30, 2024 02:12
@sunxiaojian
Copy link
Author

Copy link
Collaborator

@luoyuxia luoyuxia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sunxiaojian Thanks for the pr... I left some comments... PTAL

@sunxiaojian
Copy link
Author

@sunxiaojian Thanks for the pr... I left some comments... PTAL

@luoyuxia Thanks for the review. I will handle it as soon as possible

@sunxiaojian sunxiaojian force-pushed the support-merge-engine-version branch 12 times, most recently from 023351d to ef2eada Compare January 6, 2025 09:13
@sunxiaojian
Copy link
Author

@luoyuxia PTAL again , thanks

Copy link
Collaborator

@luoyuxia luoyuxia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sunxiaojian Thanks for update... Left some comments..

@sunxiaojian sunxiaojian force-pushed the support-merge-engine-version branch 2 times, most recently from 40b7987 to d037237 Compare January 8, 2025 02:57
@sunxiaojian
Copy link
Author

@luoyuxia PTAL again, thanks.

@sunxiaojian sunxiaojian force-pushed the support-merge-engine-version branch from d037237 to fce5768 Compare January 8, 2025 03:09
Copy link
Collaborator

@luoyuxia luoyuxia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sunxiaojian Thanks for updating again... I left some comments. PTAL. Should be look good to me in next iterations..

@@ -281,7 +281,7 @@ public boolean isDataLakeEnabled() {
}

public @Nullable MergeEngine getMergeEngine() {
return configuration().get(ConfigOptions.TABLE_MERGE_ENGINE);
return MergeEngine.create(properties);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean in here to check the data type of version column of version merge engine..

@@ -372,6 +380,23 @@ public LogAppendInfo putAsLeader(
});
}

private RowMergeEngine getRowMergeEngine(Schema schema) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I append a commit about RowMergeEngine in my own branch luoyuxia@67f7605
There're two changes:

  1. If no merge engine, I set the RowMergeEngine to null instead of creating a new RowMergeEngine.. Since using a RowMergeEngine for tablet without setting merge engine look strange to me.
  2. if it's first row merge engine, don't to deserize the old value

You can have a look..Hope it can help

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@luoyuxia About 1 ,Can refer to Paimon's default merge engine Deduplication (https://paimon.apache.org/docs/master/primary-key-table/merge-engine/overview/), which is similar to ours. I do not recommend returning null. By default, it can be understood as the last row merge engine. @wuchong What do you think of it?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TBH, althogth we haven't consider the default behavior of kv as Deduplication merge engine in our doc like Paimon and code design, I'm not too objection to condier the default behaviorv as another special merge engine in here since now it's only for internal use.

But I think consider the default behavior as a specical merge engine may make the code logic complex..May be I'm wong.

For example, in update case, default behavior will need to consider partial update/delete, but the first_row, version merge engine don't need, so you will need to introduce method ignoreDelete and ignorePartialUpdate in RowMergeEngine. Logically, RowMergeEngine should only care how how to mere two row, the logical of ignoreDelete and ignorePartialUpdate should be considered by caller..

Also, If we decide to consider the default as Deduplication , I'd like to suggest use a new class naed DeduplicateRowMergeEngine or KeepLastRowMergeEngine or any thing else instead of using anonymous class

@sunxiaojian sunxiaojian force-pushed the support-merge-engine-version branch 4 times, most recently from 5a9c7b7 to ef304c0 Compare January 9, 2025 16:43
Copy link
Author

@sunxiaojian sunxiaojian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@luoyuxia Thanks for review ... I left some comments. PTAL.

@@ -372,6 +380,23 @@ public LogAppendInfo putAsLeader(
});
}

private RowMergeEngine getRowMergeEngine(Schema schema) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@luoyuxia About 1 ,Can refer to Paimon's default merge engine Deduplication (https://paimon.apache.org/docs/master/primary-key-table/merge-engine/overview/), which is similar to ours. I do not recommend returning null. By default, it can be understood as the last row merge engine. @wuchong What do you think of it?

Copy link
Collaborator

@luoyuxia luoyuxia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sunxiaojian Thanks again.. Left minor comments..

@@ -372,6 +380,23 @@ public LogAppendInfo putAsLeader(
});
}

private RowMergeEngine getRowMergeEngine(Schema schema) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TBH, althogth we haven't consider the default behavior of kv as Deduplication merge engine in our doc like Paimon and code design, I'm not too objection to condier the default behaviorv as another special merge engine in here since now it's only for internal use.

But I think consider the default behavior as a specical merge engine may make the code logic complex..May be I'm wong.

For example, in update case, default behavior will need to consider partial update/delete, but the first_row, version merge engine don't need, so you will need to introduce method ignoreDelete and ignorePartialUpdate in RowMergeEngine. Logically, RowMergeEngine should only care how how to mere two row, the logical of ignoreDelete and ignorePartialUpdate should be considered by caller..

Also, If we decide to consider the default as Deduplication , I'd like to suggest use a new class naed DeduplicateRowMergeEngine or KeepLastRowMergeEngine or any thing else instead of using anonymous class

@sunxiaojian sunxiaojian force-pushed the support-merge-engine-version branch 3 times, most recently from a03e5de to bf696d9 Compare January 10, 2025 06:51
@sunxiaojian sunxiaojian force-pushed the support-merge-engine-version branch from 1895fe3 to ed96ec7 Compare January 10, 2025 06:53
Copy link
Collaborator

@luoyuxia luoyuxia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sunxiaojian Thanks.. I have no further comment exception for the argument in here about the abstract of RowMergeEngine in KvTablet. Btw, next time, please append a new commit instead of squash all into a commit so that reviewer can track what changes since last review instead of go through all files..
@wuchong Could you please have a final check?

@@ -600,6 +603,80 @@ void testFirstRowMergeEngine(@TempDir File tempLogDir, @TempDir File tmpKvDir)
checkEqual(actualLogRecords, expectedLogs);
}

@Test
void testVersionRowMergeEngine(@TempDir File tempLogDir, @TempDir File tmpKvDir)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't forget my comments in here.

@sunxiaojian
Copy link
Author

sunxiaojian commented Jan 11, 2025

@sunxiaojian Thanks.. I have no further comment exception for the argument in here about the abstract of RowMergeEngine in KvTablet. Btw, next time, please append a new commit instead of squash all into a commit so that reviewer can track what changes since last review instead of go through all files.. @wuchong Could you please have a final check?

@luoyuxia Fixed all, sorry, Because there was a conflict with the main branch, I accidentally merged everything. I will pay attention next time. Thanks again.

@wuchong
Copy link
Member

wuchong commented Jan 15, 2025

Thanks for the awesome work @sunxiaojian @luoyuxia ! I will go through the code tomorrow to have a final check!

@swuferhong
Copy link
Collaborator

Hi, @sunxiaojian For some comments that have already been resolved and are no longer in dispute, can you click "resolve conversion" on your side? There are too many comments, and I don't know which ones have been resolved finally.

@sunxiaojian
Copy link
Author

Hi, @sunxiaojian For some comments that have already been resolved and are no longer in dispute, can you click "resolve conversion" on your side? There are too many comments, and I don't know which ones have been resolved finally.

@swuferhong Fixed, PTAL again, thanks.

Copy link
Member

@wuchong wuchong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sunxiaojian thanks for the contribution. This PR is already in a good shape. The comments are mainly around row merge engine. If you don't mind, I can help to refine this PR and merge it.

@@ -826,6 +826,111 @@ void testUnsupportedStmtOnFirstRowMergeEngine() {
tablePath);
}

@Test
void testUnsupportedStmtOnVersionRowMergeEngine() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
void testUnsupportedStmtOnVersionRowMergeEngine() {
void testUnsupportedStmtOnVersionMergeEngine() {

"create table log_sink (a int not null primary key not enforced, b string, ts bigint)");

JobClient insertJobClient =
tEnv.executeSql("insert into log_sink select * from merge_engine_with_version")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why insert into another pk table? If we want to subscribe the changelogs of merge_engine_with_version, we can directly iterate tEnv.executeSql("select * from merge_engine_with_version").collect() in streaming mode.


// insert again, update id=3
tEnv.executeSql(
"insert into merge_engine_with_version (a, b, ts) VALUES (3, 'v33', 1001), (4, 'v44', 1000)")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a test data 2, v22, 1000 that has the same version to verify it should also be updated.

+ "(1, 'v1', TIMESTAMP '2024-12-27 12:00:00.123'), "
+ "(2, 'v2', TIMESTAMP '2024-12-27 12:00:00.123'), "
+ "(1, 'v11', TIMESTAMP '2024-12-27 11:59:59.123'), "
+ "(3, 'v3', TIMESTAMP '2024-12-27 12:00:00.123');")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a test data that has the same timestamp to verify the later one should replace the previous one.

Suggested change
+ "(3, 'v3', TIMESTAMP '2024-12-27 12:00:00.123');")
+ "(3, 'v33', TIMESTAMP '2024-12-27 12:00:00.123');")
+ "(3, 'v3', TIMESTAMP '2024-12-27 12:00:00.123');")

Comment on lines +42 to +48
public static final Set<String> VERSION_MERGE_ENGINE_SUPPORTED_DATA_TYPES =
ImmutableSet.of(
BigIntType.class.getName(),
IntType.class.getName(),
TimestampType.class.getName(),
TimeType.class.getName(),
LocalZonedTimestampType.class.getName());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use DataTypeRoot instead.

@@ -267,12 +271,27 @@ public LogAppendInfo putAsLeader(
KvRecordReadContext.createReadContext(kvFormat, fieldTypes);
ValueDecoder valueDecoder =
new ValueDecoder(readContext.getRowDecoder(schemaId));

RowMergeEngine rowMergeEngine = getRowMergeEngine(schema);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a heavy operation, the row merger should be initialized when kv tablet is setup. And a dynamic PartialUpdateRowMerger will be used if the put is a partial update.

int appendedRecordCount = 0;
for (KvRecord kvRecord : kvRecords.records(readContext)) {
byte[] keyBytes = BytesUtils.toArray(kvRecord.getKey());
KvPreWriteBuffer.Key key = KvPreWriteBuffer.Key.of(keyBytes);
if (kvRecord.getRow() == null) {
// currently, all supported merge engine will ignore delete row.
if (rowMergeEngine.ignoreDelete()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We still need the nullable MergeEngineType in the KvTablet to skip deleting if we introduce RowMerger without ignoreDelete().

if (dataType instanceof TimestampType) {
return (left, right) ->
((TimestampNtz) left).getMillisecond()
> ((TimestampNtz) right).getMillisecond();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should use TimestampNtz#compareTo to consider nano seconds. Add tests for TIMESTAMP(9).

}
if (dataType instanceof LocalZonedTimestampType) {
return (left, right) ->
((TimestampLtz) left).toEpochMicros() > ((TimestampLtz) right).toEpochMicros();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should use TimestampLtz#compareTo to consider nano seconds. Add tests for TIMESTAMP_LTZ(9).

}

interface ValueComparator {
boolean isGreaterThan(Object left, Object right);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should return int or change to isGreaterOrEqualThan to handle equal case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Feature] Introduce version merge engine for primary key table
5 participants