-
Notifications
You must be signed in to change notification settings - Fork 222
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
base: main
Are you sure you want to change the base?
[kv] Support version merge engine #277
Conversation
944bdb3
to
7f115bf
Compare
@wuchong CLA has been sent, PTAL |
Thanks for the contribution @sunxiaojian ! Will take a look. cc @luoyuxia as well. |
Btw, the checkstyle is failed: https://github.com/alibaba/fluss/actions/runs/12515363835/job/34959455034?pr=277 |
b1363d6
to
5b67c20
Compare
fixed |
There was a problem hiding this 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
fluss-common/src/main/java/com/alibaba/fluss/config/MergeEngine.java
Outdated
Show resolved
Hide resolved
...-server/src/main/java/com/alibaba/fluss/server/kv/mergeengine/VersionMergeEngineWrapper.java
Outdated
Show resolved
Hide resolved
fluss-server/src/main/java/com/alibaba/fluss/server/kv/mergeengine/MergeEngineWrapper.java
Outdated
Show resolved
Hide resolved
...-server/src/main/java/com/alibaba/fluss/server/kv/mergeengine/VersionMergeEngineWrapper.java
Outdated
Show resolved
Hide resolved
fluss-server/src/test/java/com/alibaba/fluss/server/kv/KvTabletTest.java
Outdated
Show resolved
Hide resolved
...uss-connector-flink/src/main/java/com/alibaba/fluss/connector/flink/sink/FlinkTableSink.java
Outdated
Show resolved
Hide resolved
@luoyuxia Thanks for the review. I will handle it as soon as possible |
023351d
to
ef2eada
Compare
@luoyuxia PTAL again , thanks |
There was a problem hiding this 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..
fluss-common/src/main/java/com/alibaba/fluss/metadata/MergeEngine.java
Outdated
Show resolved
Hide resolved
fluss-common/src/main/java/com/alibaba/fluss/metadata/MergeEngine.java
Outdated
Show resolved
Hide resolved
fluss-common/src/main/java/com/alibaba/fluss/metadata/MergeEngine.java
Outdated
Show resolved
Hide resolved
fluss-common/src/main/java/com/alibaba/fluss/metadata/MergeEngine.java
Outdated
Show resolved
Hide resolved
fluss-client/src/test/java/com/alibaba/fluss/client/table/FlussTableITCase.java
Show resolved
Hide resolved
...nnector-flink/src/test/java/com/alibaba/fluss/connector/flink/sink/FlinkTableSinkITCase.java
Outdated
Show resolved
Hide resolved
fluss-server/src/main/java/com/alibaba/fluss/server/kv/mergeengine/MergeEngineWrapper.java
Outdated
Show resolved
Hide resolved
fluss-server/src/main/java/com/alibaba/fluss/server/kv/mergeengine/VersionRowMergeEngine.java
Outdated
Show resolved
Hide resolved
fluss-server/src/main/java/com/alibaba/fluss/server/kv/mergeengine/VersionRowMergeEngine.java
Outdated
Show resolved
Hide resolved
40b7987
to
d037237
Compare
@luoyuxia PTAL again, thanks. |
d037237
to
fce5768
Compare
There was a problem hiding this 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..
fluss-common/src/main/java/com/alibaba/fluss/metadata/MergeEngine.java
Outdated
Show resolved
Hide resolved
fluss-server/src/main/java/com/alibaba/fluss/server/kv/KvTablet.java
Outdated
Show resolved
Hide resolved
fluss-server/src/main/java/com/alibaba/fluss/server/kv/KvTablet.java
Outdated
Show resolved
Hide resolved
fluss-server/src/main/java/com/alibaba/fluss/server/kv/mergeengine/VersionRowMergeEngine.java
Outdated
Show resolved
Hide resolved
@@ -281,7 +281,7 @@ public boolean isDataLakeEnabled() { | |||
} | |||
|
|||
public @Nullable MergeEngine getMergeEngine() { | |||
return configuration().get(ConfigOptions.TABLE_MERGE_ENGINE); | |||
return MergeEngine.create(properties); |
There was a problem hiding this comment.
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..
fluss-server/src/main/java/com/alibaba/fluss/server/kv/mergeengine/RowMergeEngine.java
Outdated
Show resolved
Hide resolved
fluss-server/src/main/java/com/alibaba/fluss/server/kv/mergeengine/FirstRowMergeEngine.java
Outdated
Show resolved
Hide resolved
fluss-server/src/main/java/com/alibaba/fluss/server/kv/KvTablet.java
Outdated
Show resolved
Hide resolved
@@ -372,6 +380,23 @@ public LogAppendInfo putAsLeader( | |||
}); | |||
} | |||
|
|||
private RowMergeEngine getRowMergeEngine(Schema schema) { |
There was a problem hiding this comment.
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:
- 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. - if it's first row merge engine, don't to deserize the old value
You can have a look..Hope it can help
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
5a9c7b7
to
ef304c0
Compare
There was a problem hiding this 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) { |
There was a problem hiding this comment.
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?
fluss-common/src/main/java/com/alibaba/fluss/metadata/TableDescriptor.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this 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) { |
There was a problem hiding this comment.
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
a03e5de
to
bf696d9
Compare
1895fe3
to
ed96ec7
Compare
There was a problem hiding this 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?
fluss-server/src/main/java/com/alibaba/fluss/server/coordinator/CoordinatorService.java
Outdated
Show resolved
Hide resolved
@@ -600,6 +603,80 @@ void testFirstRowMergeEngine(@TempDir File tempLogDir, @TempDir File tmpKvDir) | |||
checkEqual(actualLogRecords, expectedLogs); | |||
} | |||
|
|||
@Test | |||
void testVersionRowMergeEngine(@TempDir File tempLogDir, @TempDir File tmpKvDir) |
There was a problem hiding this comment.
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.
@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. |
Thanks for the awesome work @sunxiaojian @luoyuxia ! I will go through the code tomorrow to have a final check! |
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. |
There was a problem hiding this 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() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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") |
There was a problem hiding this comment.
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)") |
There was a problem hiding this comment.
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');") |
There was a problem hiding this comment.
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.
+ "(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');") |
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()); |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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()) { |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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.
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