You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hudi.apache.org by "danny0405 (via GitHub)" <gi...@apache.org> on 2023/12/06 10:58:32 UTC
[PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
danny0405 opened a new pull request, #10255:
URL: https://github.com/apache/hudi/pull/10255
…cremental queries
### Change Logs
A new auziliary component for analyzng the incremental query context.
### Impact
none
### Risk level (write none, low medium or high below)
none
### Documentation Update
_Describe any necessary documentation update if there is any new feature, config, or user-facing change_
- _The config description must be updated if new configs are added or the default value of the configs are changed_
- _Any new feature or user-facing change requires updating the Hudi website. Please create a Jira ticket, attach the
ticket number here and follow the [instruction](https://hudi.apache.org/contribute/developer-setup#website) to make
changes to the website._
### Contributor's checklist
- [ ] Read through [contributor's guide](https://hudi.apache.org/contribute/how-to-contribute)
- [ ] Change Logs and Impact were stated clearly
- [ ] Adequate tests were added if applicable
- [ ] CI passed
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "linliu-code (via GitHub)" <gi...@apache.org>.
linliu-code commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1427422770
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,429 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed before or on 'endTime';
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed in the given time range;
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * </table>
+ *
+ * <p> A range type is required for analyzing the query so that the query range boundary inclusiveness have clear semantics.
+ *
+ * <p>IMPORTANT: the reader may optionally choose to fall back to reading the latest snapshot if there are files missing from decoding the commit metadata.
+ */
+public class IncrementalQueryAnalyzer {
Review Comment:
the name of the class is a bit broader than what it does. To me, it is more like TimeLineAnalyzer something.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "danny0405 (via GitHub)" <gi...@apache.org>.
danny0405 commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1427514359
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,429 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
Review Comment:
> instant == instant candidates
yeah.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "linliu-code (via GitHub)" <gi...@apache.org>.
linliu-code commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1428428388
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,429 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed before or on 'endTime';
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed in the given time range;
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * </table>
+ *
+ * <p> A range type is required for analyzing the query so that the query range boundary inclusiveness have clear semantics.
+ *
+ * <p>IMPORTANT: the reader may optionally choose to fall back to reading the latest snapshot if there are files missing from decoding the commit metadata.
+ */
+public class IncrementalQueryAnalyzer {
+ public static final String START_COMMIT_EARLIEST = "earliest";
+
+ private final HoodieTableMetaClient metaClient;
+ private final Option<String> startTime;
+ private final Option<String> endTime;
+ private final InstantRange.RangeType rangeType;
+ private final boolean skipCompaction;
+ private final boolean skipClustering;
+ private final int limit;
+
+ private IncrementalQueryAnalyzer(
+ HoodieTableMetaClient metaClient,
+ String startTime,
+ String endTime,
Review Comment:
@danny0405 , is it possible that the entime is older than the last instant timestamp?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "hudi-bot (via GitHub)" <gi...@apache.org>.
hudi-bot commented on PR #10255:
URL: https://github.com/apache/hudi/pull/10255#issuecomment-1932157094
<!--
Meta data
{
"version" : 1,
"metaDataEntries" : [ {
"hash" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21338",
"triggerID" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"triggerType" : "PUSH"
}, {
"hash" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21364",
"triggerID" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"triggerType" : "PUSH"
}, {
"hash" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21395",
"triggerID" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"triggerType" : "PUSH"
}, {
"hash" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21488",
"triggerID" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"triggerType" : "PUSH"
}, {
"hash" : "b39e93881c130fd8832e5c260740f8f59bb8033e",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21577",
"triggerID" : "b39e93881c130fd8832e5c260740f8f59bb8033e",
"triggerType" : "PUSH"
}, {
"hash" : "d2c7c5cd379c4c8ba4297fd5fa1e989f73e58b5d",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21854",
"triggerID" : "d2c7c5cd379c4c8ba4297fd5fa1e989f73e58b5d",
"triggerType" : "PUSH"
}, {
"hash" : "975fbb9de10066ac67a875f3484fe9f5922e197d",
"status" : "FAILURE",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=22346",
"triggerID" : "975fbb9de10066ac67a875f3484fe9f5922e197d",
"triggerType" : "PUSH"
}, {
"hash" : "a2cb2403ded21b17b991304af14eb57897b4100d",
"status" : "UNKNOWN",
"url" : "TBD",
"triggerID" : "a2cb2403ded21b17b991304af14eb57897b4100d",
"triggerType" : "PUSH"
} ]
}-->
## CI report:
* 975fbb9de10066ac67a875f3484fe9f5922e197d Azure: [FAILURE](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=22346)
* a2cb2403ded21b17b991304af14eb57897b4100d UNKNOWN
<details>
<summary>Bot commands</summary>
@hudi-bot supports the following commands:
- `@hudi-bot run azure` re-run the last Azure build
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "hudi-bot (via GitHub)" <gi...@apache.org>.
hudi-bot commented on PR #10255:
URL: https://github.com/apache/hudi/pull/10255#issuecomment-1932172445
<!--
Meta data
{
"version" : 1,
"metaDataEntries" : [ {
"hash" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21338",
"triggerID" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"triggerType" : "PUSH"
}, {
"hash" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21364",
"triggerID" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"triggerType" : "PUSH"
}, {
"hash" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21395",
"triggerID" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"triggerType" : "PUSH"
}, {
"hash" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21488",
"triggerID" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"triggerType" : "PUSH"
}, {
"hash" : "b39e93881c130fd8832e5c260740f8f59bb8033e",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21577",
"triggerID" : "b39e93881c130fd8832e5c260740f8f59bb8033e",
"triggerType" : "PUSH"
}, {
"hash" : "d2c7c5cd379c4c8ba4297fd5fa1e989f73e58b5d",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21854",
"triggerID" : "d2c7c5cd379c4c8ba4297fd5fa1e989f73e58b5d",
"triggerType" : "PUSH"
}, {
"hash" : "975fbb9de10066ac67a875f3484fe9f5922e197d",
"status" : "FAILURE",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=22346",
"triggerID" : "975fbb9de10066ac67a875f3484fe9f5922e197d",
"triggerType" : "PUSH"
}, {
"hash" : "a2cb2403ded21b17b991304af14eb57897b4100d",
"status" : "PENDING",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=22357",
"triggerID" : "a2cb2403ded21b17b991304af14eb57897b4100d",
"triggerType" : "PUSH"
} ]
}-->
## CI report:
* 975fbb9de10066ac67a875f3484fe9f5922e197d Azure: [FAILURE](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=22346)
* a2cb2403ded21b17b991304af14eb57897b4100d Azure: [PENDING](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=22357)
<details>
<summary>Bot commands</summary>
@hudi-bot supports the following commands:
- `@hudi-bot run azure` re-run the last Azure build
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "hudi-bot (via GitHub)" <gi...@apache.org>.
hudi-bot commented on PR #10255:
URL: https://github.com/apache/hudi/pull/10255#issuecomment-1939256594
<!--
Meta data
{
"version" : 1,
"metaDataEntries" : [ {
"hash" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21338",
"triggerID" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"triggerType" : "PUSH"
}, {
"hash" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21364",
"triggerID" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"triggerType" : "PUSH"
}, {
"hash" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21395",
"triggerID" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"triggerType" : "PUSH"
}, {
"hash" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21488",
"triggerID" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"triggerType" : "PUSH"
}, {
"hash" : "b39e93881c130fd8832e5c260740f8f59bb8033e",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21577",
"triggerID" : "b39e93881c130fd8832e5c260740f8f59bb8033e",
"triggerType" : "PUSH"
}, {
"hash" : "d2c7c5cd379c4c8ba4297fd5fa1e989f73e58b5d",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21854",
"triggerID" : "d2c7c5cd379c4c8ba4297fd5fa1e989f73e58b5d",
"triggerType" : "PUSH"
}, {
"hash" : "975fbb9de10066ac67a875f3484fe9f5922e197d",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=22346",
"triggerID" : "975fbb9de10066ac67a875f3484fe9f5922e197d",
"triggerType" : "PUSH"
}, {
"hash" : "a2cb2403ded21b17b991304af14eb57897b4100d",
"status" : "FAILURE",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=22357",
"triggerID" : "a2cb2403ded21b17b991304af14eb57897b4100d",
"triggerType" : "PUSH"
}, {
"hash" : "3153fe03616237ff7814c53923fec2a2504b6d01",
"status" : "PENDING",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=22420",
"triggerID" : "3153fe03616237ff7814c53923fec2a2504b6d01",
"triggerType" : "PUSH"
} ]
}-->
## CI report:
* a2cb2403ded21b17b991304af14eb57897b4100d Azure: [FAILURE](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=22357)
* 3153fe03616237ff7814c53923fec2a2504b6d01 Azure: [PENDING](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=22420)
<details>
<summary>Bot commands</summary>
@hudi-bot supports the following commands:
- `@hudi-bot run azure` re-run the last Azure build
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "hudi-bot (via GitHub)" <gi...@apache.org>.
hudi-bot commented on PR #10255:
URL: https://github.com/apache/hudi/pull/10255#issuecomment-1931014215
<!--
Meta data
{
"version" : 1,
"metaDataEntries" : [ {
"hash" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21338",
"triggerID" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"triggerType" : "PUSH"
}, {
"hash" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21364",
"triggerID" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"triggerType" : "PUSH"
}, {
"hash" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21395",
"triggerID" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"triggerType" : "PUSH"
}, {
"hash" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21488",
"triggerID" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"triggerType" : "PUSH"
}, {
"hash" : "b39e93881c130fd8832e5c260740f8f59bb8033e",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21577",
"triggerID" : "b39e93881c130fd8832e5c260740f8f59bb8033e",
"triggerType" : "PUSH"
}, {
"hash" : "d2c7c5cd379c4c8ba4297fd5fa1e989f73e58b5d",
"status" : "CANCELED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21854",
"triggerID" : "d2c7c5cd379c4c8ba4297fd5fa1e989f73e58b5d",
"triggerType" : "PUSH"
}, {
"hash" : "975fbb9de10066ac67a875f3484fe9f5922e197d",
"status" : "UNKNOWN",
"url" : "TBD",
"triggerID" : "975fbb9de10066ac67a875f3484fe9f5922e197d",
"triggerType" : "PUSH"
} ]
}-->
## CI report:
* d2c7c5cd379c4c8ba4297fd5fa1e989f73e58b5d Azure: [CANCELED](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21854)
* 975fbb9de10066ac67a875f3484fe9f5922e197d UNKNOWN
<details>
<summary>Bot commands</summary>
@hudi-bot supports the following commands:
- `@hudi-bot run azure` re-run the last Azure build
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "linliu-code (via GitHub)" <gi...@apache.org>.
linliu-code commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1427384608
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,429 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed before or on 'endTime';
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed in the given time range;
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * </table>
+ *
+ * <p> A range type is required for analyzing the query so that the query range boundary inclusiveness have clear semantics.
+ *
+ * <p>IMPORTANT: the reader may optionally choose to fall back to reading the latest snapshot if there are files missing from decoding the commit metadata.
+ */
+public class IncrementalQueryAnalyzer {
+ public static final String START_COMMIT_EARLIEST = "earliest";
+
+ private final HoodieTableMetaClient metaClient;
+ private final Option<String> startTime;
+ private final Option<String> endTime;
+ private final InstantRange.RangeType rangeType;
+ private final boolean skipCompaction;
+ private final boolean skipClustering;
+ private final int limit;
+
+ private IncrementalQueryAnalyzer(
+ HoodieTableMetaClient metaClient,
+ String startTime,
+ String endTime,
+ InstantRange.RangeType rangeType,
+ boolean skipCompaction,
+ boolean skipClustering,
+ int limit) {
+ this.metaClient = metaClient;
+ this.startTime = Option.ofNullable(startTime);
+ this.endTime = Option.ofNullable(endTime);
+ this.rangeType = rangeType;
+ this.skipCompaction = skipCompaction;
+ this.skipClustering = skipClustering;
+ this.limit = limit;
+ }
+
+ /**
+ * Returns a builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Analyzes the incremental query context with given completion time range.
+ *
+ * @return An incremental query context including the instant time range info.
+ */
+ public QueryContext analyze() {
+ try (CompletionTimeQueryView completionTimeQueryView = new CompletionTimeQueryView(this.metaClient)) {
+ if (completionTimeQueryView.isEmptyTable()) {
+ // no dataset committed in the table
+ return QueryContext.EMPTY;
+ }
+ HoodieTimeline readTimeline = getReadTimeline(this.metaClient);
Review Comment:
Question: Do we have "writeTimeline"?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "linliu-code (via GitHub)" <gi...@apache.org>.
linliu-code commented on PR #10255:
URL: https://github.com/apache/hudi/pull/10255#issuecomment-1858435541
Overall it looks solid to me. Only concern is that this code is very critical and complex, do we have enough tests to ensure the correctness?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "hudi-bot (via GitHub)" <gi...@apache.org>.
hudi-bot commented on PR #10255:
URL: https://github.com/apache/hudi/pull/10255#issuecomment-1842668247
<!--
Meta data
{
"version" : 1,
"metaDataEntries" : [ {
"hash" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"status" : "UNKNOWN",
"url" : "TBD",
"triggerID" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"triggerType" : "PUSH"
} ]
}-->
## CI report:
* 07b8dd347e0157ed848cddd4d5bb3b229d42db5a UNKNOWN
<details>
<summary>Bot commands</summary>
@hudi-bot supports the following commands:
- `@hudi-bot run azure` re-run the last Azure build
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "danny0405 (via GitHub)" <gi...@apache.org>.
danny0405 commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1425028862
##########
hudi-common/src/main/java/org/apache/hudi/common/table/log/InstantRange.java:
##########
@@ -60,8 +63,8 @@ public String getEndInstant() {
@Override
public String toString() {
return "InstantRange{"
- + "startInstant='" + startInstant == null ? "null" : startInstant + '\''
- + ", endInstant='" + endInstant == null ? "null" : endInstant + '\''
+ + "startInstant='" + (startInstant == null ? "null" : startInstant) + '\''
+ + ", endInstant='" + (endInstant == null ? "null" : endInstant) + '\''
+ ", rangeType='" + this.getClass().getSimpleName() + '\''
Review Comment:
yeah
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "hudi-bot (via GitHub)" <gi...@apache.org>.
hudi-bot commented on PR #10255:
URL: https://github.com/apache/hudi/pull/10255#issuecomment-1853664347
<!--
Meta data
{
"version" : 1,
"metaDataEntries" : [ {
"hash" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21338",
"triggerID" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"triggerType" : "PUSH"
}, {
"hash" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21364",
"triggerID" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"triggerType" : "PUSH"
}, {
"hash" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"status" : "FAILURE",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21395",
"triggerID" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"triggerType" : "PUSH"
}, {
"hash" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"status" : "UNKNOWN",
"url" : "TBD",
"triggerID" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"triggerType" : "PUSH"
} ]
}-->
## CI report:
* 3787083e09a0bf7d754d095b5826657efd4f468e Azure: [FAILURE](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21395)
* 3e12be6f351ee6da145de331af53c27c985b7d25 UNKNOWN
<details>
<summary>Bot commands</summary>
@hudi-bot supports the following commands:
- `@hudi-bot run azure` re-run the last Azure build
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "danny0405 (via GitHub)" <gi...@apache.org>.
danny0405 commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1442460176
##########
hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/source/IncrementalInputSplits.java:
##########
@@ -135,44 +127,37 @@ public static Builder builder() {
public Result inputSplits(
HoodieTableMetaClient metaClient,
boolean cdcEnabled) {
- HoodieTimeline commitTimeline = getReadTimeline(metaClient);
- if (commitTimeline.empty()) {
- LOG.warn("No splits found for the table under path " + path);
+
+ IncrementalQueryAnalyzer analyzer = IncrementalQueryAnalyzer.builder()
+ .metaClient(metaClient)
+ .startTime(this.conf.getString(FlinkOptions.READ_START_COMMIT))
+ .endTime(this.conf.getString(FlinkOptions.READ_END_COMMIT))
Review Comment:
In streaming query, the user may declares nothing. And similar with Kafka, it consumes from the latest commit.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "linliu-code (via GitHub)" <gi...@apache.org>.
linliu-code commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1427379166
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,429 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
Review Comment:
Question: What is the criterion to qualify for being active? Do we use a time threshold?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "linliu-code (via GitHub)" <gi...@apache.org>.
linliu-code commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1428422995
##########
hudi-common/src/main/java/org/apache/hudi/common/table/timeline/CompletionTimeQueryView.java:
##########
@@ -175,42 +187,111 @@ public Option<String> getCompletionTime(String startTime) {
*
* <p>By default, assumes there is at most 1 day time of duration for an instant to accelerate the queries.
*
- * @param startCompletionTime The start completion time.
- * @param endCompletionTime The end completion time.
+ * @param readTimeline The read timeline.
+ * @param rangeStart The query range start completion time.
+ * @param rangeEnd The query range end completion time.
+ * @param rangeType The range type.
*
- * @return The instant time set.
+ * @return The sorted instant time list.
*/
- public Set<String> getStartTimeSet(String startCompletionTime, String endCompletionTime) {
+ public List<String> getStartTimes(
+ HoodieTimeline readTimeline,
+ Option<String> rangeStart,
+ Option<String> rangeEnd,
+ InstantRange.RangeType rangeType) {
// assumes any instant/transaction lasts at most 1 day to optimize the query efficiency.
- return getStartTimeSet(startCompletionTime, endCompletionTime, s -> HoodieInstantTimeGenerator.instantTimeMinusMillis(s, MILLI_SECONDS_IN_ONE_DAY));
+ return getStartTimes(readTimeline, rangeStart, rangeEnd, rangeType, s -> HoodieInstantTimeGenerator.instantTimeMinusMillis(s, MILLI_SECONDS_IN_ONE_DAY));
}
/**
* Queries the instant start time with given completion time range.
*
- * @param startCompletionTime The start completion time.
- * @param endCompletionTime The end completion time.
- * @param earliestStartTimeFunc The function to generate the earliest start time boundary
- * with the minimum completion time {@code startCompletionTime}.
+ * @param rangeStart The query range start completion time.
+ * @param rangeEnd The query range end completion time.
+ * @param earliestInstantTimeFunc The function to generate the earliest start time boundary
+ * with the minimum completion time.
*
- * @return The instant time set.
+ * @return The sorted instant time list.
*/
- public Set<String> getStartTimeSet(String startCompletionTime, String endCompletionTime, Function<String, String> earliestStartTimeFunc) {
- String startInstant = earliestStartTimeFunc.apply(startCompletionTime);
+ @VisibleForTesting
+ public List<String> getStartTimes(
+ String rangeStart,
+ String rangeEnd,
+ Function<String, String> earliestInstantTimeFunc) {
+ return getStartTimes(metaClient.getCommitsTimeline().filterCompletedInstants(), Option.ofNullable(rangeStart), Option.ofNullable(rangeEnd),
+ InstantRange.RangeType.CLOSE_CLOSE, earliestInstantTimeFunc);
+ }
+
+ /**
+ * Queries the instant start time with given completion time range.
+ *
+ * @param readTimeline The read timeline.
+ * @param rangeStart The query range start completion time.
+ * @param rangeEnd The query range end completion time.
+ * @param rangeType The range type.
+ * @param earliestInstantTimeFunc The function to generate the earliest start time boundary
+ * with the minimum completion time.
+ *
+ * @return The sorted instant time list.
+ */
+ public List<String> getStartTimes(
Review Comment:
Is it possible for us to return the completion time directly?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "danny0405 (via GitHub)" <gi...@apache.org>.
danny0405 commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1424874743
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,425 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
Review Comment:
We always assume `_` as the latest available commit, which is more matching to the streaming semantics, when both start and end time are `_`, just return the latest commit.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "vinothchandar (via GitHub)" <gi...@apache.org>.
vinothchandar commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1480671223
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,428 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTime;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed after or on 'startTime';
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed in the given time range;
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * </table>
+ *
+ * <p> A range type is required for analyzing the query so that the query range boundary inclusiveness have clear semantics.
+ *
+ * <p>IMPORTANT: the reader may optionally choose to fall back to reading the latest snapshot if there are files missing from decoding the commit metadata.
+ */
+public class IncrementalQueryAnalyzer {
+ public static final String START_COMMIT_EARLIEST = "earliest";
+
+ private final HoodieTableMetaClient metaClient;
+ private final Option<String> startTime;
+ private final Option<String> endTime;
+ private final InstantRange.RangeType rangeType;
+ private final boolean skipCompaction;
+ private final boolean skipClustering;
+ private final int limit;
+
+ private IncrementalQueryAnalyzer(
+ HoodieTableMetaClient metaClient,
+ String startTime,
+ String endTime,
+ InstantRange.RangeType rangeType,
+ boolean skipCompaction,
+ boolean skipClustering,
+ int limit) {
+ this.metaClient = metaClient;
+ this.startTime = Option.ofNullable(startTime);
+ this.endTime = Option.ofNullable(endTime);
+ this.rangeType = rangeType;
+ this.skipCompaction = skipCompaction;
+ this.skipClustering = skipClustering;
+ this.limit = limit;
+ }
+
+ /**
+ * Returns a builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Analyzes the incremental query context with given completion time range.
+ *
+ * @return An incremental query context including the instant time range info.
+ */
+ public QueryContext analyze() {
+ try (CompletionTimeQueryView completionTimeQueryView = new CompletionTimeQueryView(this.metaClient)) {
+ if (completionTimeQueryView.isEmptyTable()) {
+ // no dataset committed in the table
+ return QueryContext.EMPTY;
+ }
+ HoodieTimeline readTimeline = getReadTimeline(this.metaClient);
+ List<String> instantTimeList = completionTimeQueryView.getStartTimes(readTimeline, startTime, endTime, rangeType);
+ if (instantTimeList.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ // get hoodie instants
+ Pair<List<String>, List<String>> splitInstantTime = splitInstantByActiveness(instantTimeList, completionTimeQueryView);
+ Set<String> instantTimeSet = new HashSet<>(instantTimeList);
+ List<String> archivedInstantTime = splitInstantTime.getKey();
+ List<String> activeInstantTime = splitInstantTime.getValue();
+ List<HoodieInstant> archivedInstants = new ArrayList<>();
+ List<HoodieInstant> activeInstants = new ArrayList<>();
+ HoodieTimeline archivedReadTimeline = null;
+ if (!activeInstantTime.isEmpty()) {
+ activeInstants = readTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ if (limit > 0 && limit < activeInstants.size()) {
+ // streaming read speed limit, limits the maximum number of commits allowed to read for each run
+ activeInstants = activeInstants.subList(0, limit);
+ }
+ }
+ if (!archivedInstantTime.isEmpty()) {
+ archivedReadTimeline = getArchivedReadTimeline(metaClient, archivedInstantTime.get(0));
+ archivedInstants = archivedReadTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ }
+ List<String> instants = Stream.concat(archivedInstants.stream(), activeInstants.stream()).map(HoodieInstant::getTimestamp).collect(Collectors.toList());
+ if (instants.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ if (startTime.isEmpty() && endTime.isPresent()) {
Review Comment:
nts: need to understand this better
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,428 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTime;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed after or on 'startTime';
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed in the given time range;
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * </table>
+ *
+ * <p> A range type is required for analyzing the query so that the query range boundary inclusiveness have clear semantics.
+ *
+ * <p>IMPORTANT: the reader may optionally choose to fall back to reading the latest snapshot if there are files missing from decoding the commit metadata.
+ */
+public class IncrementalQueryAnalyzer {
+ public static final String START_COMMIT_EARLIEST = "earliest";
+
+ private final HoodieTableMetaClient metaClient;
+ private final Option<String> startTime;
+ private final Option<String> endTime;
+ private final InstantRange.RangeType rangeType;
+ private final boolean skipCompaction;
+ private final boolean skipClustering;
+ private final int limit;
+
+ private IncrementalQueryAnalyzer(
+ HoodieTableMetaClient metaClient,
+ String startTime,
+ String endTime,
+ InstantRange.RangeType rangeType,
+ boolean skipCompaction,
+ boolean skipClustering,
+ int limit) {
+ this.metaClient = metaClient;
+ this.startTime = Option.ofNullable(startTime);
+ this.endTime = Option.ofNullable(endTime);
+ this.rangeType = rangeType;
+ this.skipCompaction = skipCompaction;
+ this.skipClustering = skipClustering;
+ this.limit = limit;
+ }
+
+ /**
+ * Returns a builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Analyzes the incremental query context with given completion time range.
+ *
+ * @return An incremental query context including the instant time range info.
+ */
+ public QueryContext analyze() {
+ try (CompletionTimeQueryView completionTimeQueryView = new CompletionTimeQueryView(this.metaClient)) {
+ if (completionTimeQueryView.isEmptyTable()) {
+ // no dataset committed in the table
+ return QueryContext.EMPTY;
+ }
+ HoodieTimeline readTimeline = getReadTimeline(this.metaClient);
+ List<String> instantTimeList = completionTimeQueryView.getStartTimes(readTimeline, startTime, endTime, rangeType);
+ if (instantTimeList.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ // get hoodie instants
+ Pair<List<String>, List<String>> splitInstantTime = splitInstantByActiveness(instantTimeList, completionTimeQueryView);
+ Set<String> instantTimeSet = new HashSet<>(instantTimeList);
+ List<String> archivedInstantTime = splitInstantTime.getKey();
+ List<String> activeInstantTime = splitInstantTime.getValue();
+ List<HoodieInstant> archivedInstants = new ArrayList<>();
+ List<HoodieInstant> activeInstants = new ArrayList<>();
+ HoodieTimeline archivedReadTimeline = null;
+ if (!activeInstantTime.isEmpty()) {
+ activeInstants = readTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ if (limit > 0 && limit < activeInstants.size()) {
+ // streaming read speed limit, limits the maximum number of commits allowed to read for each run
+ activeInstants = activeInstants.subList(0, limit);
+ }
+ }
+ if (!archivedInstantTime.isEmpty()) {
+ archivedReadTimeline = getArchivedReadTimeline(metaClient, archivedInstantTime.get(0));
+ archivedInstants = archivedReadTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ }
+ List<String> instants = Stream.concat(archivedInstants.stream(), activeInstants.stream()).map(HoodieInstant::getTimestamp).collect(Collectors.toList());
+ if (instants.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ if (startTime.isEmpty() && endTime.isPresent()) {
+ instants = Collections.singletonList(instants.get(instants.size() - 1));
+ }
+ String lastInstant = instants.get(instants.size() - 1);
+ // keep the same semantics with streaming read, default start from the latest commit
+ String startInstant = START_COMMIT_EARLIEST.equalsIgnoreCase(startTime.orElse(null)) ? null : startTime.isEmpty() ? lastInstant : instants.get(0);
Review Comment:
nts: some of these `null` sentinels can be eliminated also.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "hudi-bot (via GitHub)" <gi...@apache.org>.
hudi-bot commented on PR #10255:
URL: https://github.com/apache/hudi/pull/10255#issuecomment-1846566776
<!--
Meta data
{
"version" : 1,
"metaDataEntries" : [ {
"hash" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21338",
"triggerID" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"triggerType" : "PUSH"
}, {
"hash" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"status" : "SUCCESS",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21364",
"triggerID" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"triggerType" : "PUSH"
}, {
"hash" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"status" : "UNKNOWN",
"url" : "TBD",
"triggerID" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"triggerType" : "PUSH"
} ]
}-->
## CI report:
* bde657c2c091c062c342b0600c3269bbc15ebaaa Azure: [SUCCESS](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21364)
* 3787083e09a0bf7d754d095b5826657efd4f468e UNKNOWN
<details>
<summary>Bot commands</summary>
@hudi-bot supports the following commands:
- `@hudi-bot run azure` re-run the last Azure build
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "hudi-bot (via GitHub)" <gi...@apache.org>.
hudi-bot commented on PR #10255:
URL: https://github.com/apache/hudi/pull/10255#issuecomment-1845772369
<!--
Meta data
{
"version" : 1,
"metaDataEntries" : [ {
"hash" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21338",
"triggerID" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"triggerType" : "PUSH"
}, {
"hash" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"status" : "SUCCESS",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21364",
"triggerID" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"triggerType" : "PUSH"
} ]
}-->
## CI report:
* bde657c2c091c062c342b0600c3269bbc15ebaaa Azure: [SUCCESS](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21364)
<details>
<summary>Bot commands</summary>
@hudi-bot supports the following commands:
- `@hudi-bot run azure` re-run the last Azure build
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "danny0405 (via GitHub)" <gi...@apache.org>.
danny0405 commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1424806427
##########
hudi-common/src/main/java/org/apache/hudi/common/table/timeline/CompletionTimeQueryView.java:
##########
@@ -175,42 +190,109 @@ public Option<String> getCompletionTime(String startTime) {
*
* <p>By default, assumes there is at most 1 day time of duration for an instant to accelerate the queries.
*
- * @param startCompletionTime The start completion time.
- * @param endCompletionTime The end completion time.
+ * @param readTimeline The read timeline.
+ * @param startTime The start completion time.
+ * @param endTime The end completion time.
+ * @param rangeType The range type.
*
- * @return The instant time set.
+ * @return The sorted instant time list.
*/
- public Set<String> getStartTimeSet(String startCompletionTime, String endCompletionTime) {
+ public List<String> getStartTime(
+ HoodieTimeline readTimeline,
+ @Nullable String startTime,
+ @Nullable String endTime,
+ InstantRange.RangeType rangeType) {
// assumes any instant/transaction lasts at most 1 day to optimize the query efficiency.
- return getStartTimeSet(startCompletionTime, endCompletionTime, s -> HoodieInstantTimeGenerator.instantTimeMinusMillis(s, MILLI_SECONDS_IN_ONE_DAY));
+ return getStartTime(readTimeline, startTime, endTime, rangeType, s -> HoodieInstantTimeGenerator.instantTimeMinusMillis(s, MILLI_SECONDS_IN_ONE_DAY));
+ }
+
+ /**
+ * Queries the instant start time with given completion time range.
+ *
+ * @param startTime The start completion time.
+ * @param endTime The end completion time.
+ * @param earliestInstantTimeFunc The function to generate the earliest start time boundary
+ * with the minimum completion time.
+ *
+ * @return The sorted instant time list.
+ */
+ @VisibleForTesting
+ public List<String> getStartTime(
Review Comment:
Currently we are using start instant time, i'm okay if we want to switch to 'begin', have no preference for naming.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "linliu-code (via GitHub)" <gi...@apache.org>.
linliu-code commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1427389589
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,429 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
Review Comment:
Nvm, i have figured it out from the followding code.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "danny0405 (via GitHub)" <gi...@apache.org>.
danny0405 commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1427515924
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,429 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed before or on 'endTime';
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed in the given time range;
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * </table>
+ *
+ * <p> A range type is required for analyzing the query so that the query range boundary inclusiveness have clear semantics.
+ *
+ * <p>IMPORTANT: the reader may optionally choose to fall back to reading the latest snapshot if there are files missing from decoding the commit metadata.
+ */
+public class IncrementalQueryAnalyzer {
+ public static final String START_COMMIT_EARLIEST = "earliest";
+
+ private final HoodieTableMetaClient metaClient;
+ private final Option<String> startTime;
+ private final Option<String> endTime;
+ private final InstantRange.RangeType rangeType;
+ private final boolean skipCompaction;
+ private final boolean skipClustering;
+ private final int limit;
+
+ private IncrementalQueryAnalyzer(
+ HoodieTableMetaClient metaClient,
+ String startTime,
+ String endTime,
+ InstantRange.RangeType rangeType,
+ boolean skipCompaction,
+ boolean skipClustering,
+ int limit) {
+ this.metaClient = metaClient;
+ this.startTime = Option.ofNullable(startTime);
+ this.endTime = Option.ofNullable(endTime);
+ this.rangeType = rangeType;
+ this.skipCompaction = skipCompaction;
+ this.skipClustering = skipClustering;
+ this.limit = limit;
+ }
+
+ /**
+ * Returns a builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Analyzes the incremental query context with given completion time range.
+ *
+ * @return An incremental query context including the instant time range info.
+ */
+ public QueryContext analyze() {
+ try (CompletionTimeQueryView completionTimeQueryView = new CompletionTimeQueryView(this.metaClient)) {
+ if (completionTimeQueryView.isEmptyTable()) {
+ // no dataset committed in the table
+ return QueryContext.EMPTY;
+ }
+ HoodieTimeline readTimeline = getReadTimeline(this.metaClient);
Review Comment:
No, the `readTimeline` is used for compaction and clustering filtering just in the reading use case.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "danny0405 (via GitHub)" <gi...@apache.org>.
danny0405 commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1428786517
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,429 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed before or on 'endTime';
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed in the given time range;
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * </table>
+ *
+ * <p> A range type is required for analyzing the query so that the query range boundary inclusiveness have clear semantics.
+ *
+ * <p>IMPORTANT: the reader may optionally choose to fall back to reading the latest snapshot if there are files missing from decoding the commit metadata.
+ */
+public class IncrementalQueryAnalyzer {
Review Comment:
Kind of, we can rename to better one.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "danny0405 (via GitHub)" <gi...@apache.org>.
danny0405 commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1428787998
##########
hudi-common/src/main/java/org/apache/hudi/common/table/timeline/CompletionTimeQueryView.java:
##########
@@ -175,42 +187,111 @@ public Option<String> getCompletionTime(String startTime) {
*
* <p>By default, assumes there is at most 1 day time of duration for an instant to accelerate the queries.
*
- * @param startCompletionTime The start completion time.
- * @param endCompletionTime The end completion time.
+ * @param readTimeline The read timeline.
+ * @param rangeStart The query range start completion time.
+ * @param rangeEnd The query range end completion time.
+ * @param rangeType The range type.
*
- * @return The instant time set.
+ * @return The sorted instant time list.
*/
- public Set<String> getStartTimeSet(String startCompletionTime, String endCompletionTime) {
+ public List<String> getStartTimes(
+ HoodieTimeline readTimeline,
+ Option<String> rangeStart,
+ Option<String> rangeEnd,
+ InstantRange.RangeType rangeType) {
// assumes any instant/transaction lasts at most 1 day to optimize the query efficiency.
- return getStartTimeSet(startCompletionTime, endCompletionTime, s -> HoodieInstantTimeGenerator.instantTimeMinusMillis(s, MILLI_SECONDS_IN_ONE_DAY));
+ return getStartTimes(readTimeline, rangeStart, rangeEnd, rangeType, s -> HoodieInstantTimeGenerator.instantTimeMinusMillis(s, MILLI_SECONDS_IN_ONE_DAY));
}
/**
* Queries the instant start time with given completion time range.
*
- * @param startCompletionTime The start completion time.
- * @param endCompletionTime The end completion time.
- * @param earliestStartTimeFunc The function to generate the earliest start time boundary
- * with the minimum completion time {@code startCompletionTime}.
+ * @param rangeStart The query range start completion time.
+ * @param rangeEnd The query range end completion time.
+ * @param earliestInstantTimeFunc The function to generate the earliest start time boundary
+ * with the minimum completion time.
*
- * @return The instant time set.
+ * @return The sorted instant time list.
*/
- public Set<String> getStartTimeSet(String startCompletionTime, String endCompletionTime, Function<String, String> earliestStartTimeFunc) {
- String startInstant = earliestStartTimeFunc.apply(startCompletionTime);
+ @VisibleForTesting
+ public List<String> getStartTimes(
+ String rangeStart,
+ String rangeEnd,
+ Function<String, String> earliestInstantTimeFunc) {
+ return getStartTimes(metaClient.getCommitsTimeline().filterCompletedInstants(), Option.ofNullable(rangeStart), Option.ofNullable(rangeEnd),
+ InstantRange.RangeType.CLOSE_CLOSE, earliestInstantTimeFunc);
+ }
+
+ /**
+ * Queries the instant start time with given completion time range.
+ *
+ * @param readTimeline The read timeline.
+ * @param rangeStart The query range start completion time.
+ * @param rangeEnd The query range end completion time.
+ * @param rangeType The range type.
+ * @param earliestInstantTimeFunc The function to generate the earliest start time boundary
+ * with the minimum completion time.
+ *
+ * @return The sorted instant time list.
+ */
+ public List<String> getStartTimes(
Review Comment:
The completion time is user specified, the main purpose of this clazz is transfering the ocmpletion time range into instant time range.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "danny0405 (via GitHub)" <gi...@apache.org>.
danny0405 commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1442459532
##########
hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/source/TestIncrementalInputSplits.java:
##########
@@ -72,67 +76,77 @@
public class TestIncrementalInputSplits extends HoodieCommonTestHarness {
@BeforeEach
- void init() throws IOException {
+ void init() {
initPath();
- initMetaClient();
}
@Test
- void testFilterInstantsWithRange() {
- HoodieActiveTimeline timeline = new HoodieActiveTimeline(metaClient, true);
+ void testFilterInstantsWithRange() throws IOException {
Configuration conf = TestConfigurations.getDefaultConf(basePath);
conf.set(FlinkOptions.READ_STREAMING_SKIP_CLUSTERING, true);
- IncrementalInputSplits iis = IncrementalInputSplits.builder()
- .conf(conf)
- .path(new Path(basePath))
- .rowType(TestConfigurations.ROW_TYPE)
- .skipClustering(conf.getBoolean(FlinkOptions.READ_STREAMING_SKIP_CLUSTERING))
- .build();
+ conf.set(FlinkOptions.TABLE_TYPE, FlinkOptions.TABLE_TYPE_MERGE_ON_READ);
+ metaClient = HoodieTestUtils.init(basePath, HoodieTableType.MERGE_ON_READ);
+ HoodieActiveTimeline timeline = metaClient.getActiveTimeline();
HoodieInstant commit1 = new HoodieInstant(HoodieInstant.State.COMPLETED, HoodieTimeline.DELTA_COMMIT_ACTION, "1");
HoodieInstant commit2 = new HoodieInstant(HoodieInstant.State.COMPLETED, HoodieTimeline.DELTA_COMMIT_ACTION, "2");
HoodieInstant commit3 = new HoodieInstant(HoodieInstant.State.COMPLETED, HoodieTimeline.DELTA_COMMIT_ACTION, "3");
timeline.createCompleteInstant(commit1);
timeline.createCompleteInstant(commit2);
timeline.createCompleteInstant(commit3);
- timeline = timeline.reload();
+ timeline = metaClient.reloadActiveTimeline();
+
+ Map<String, String> completionTimeMap = timeline.filterCompletedInstants().getInstantsAsStream()
+ .collect(Collectors.toMap(HoodieInstant::getTimestamp, HoodieInstant::getCompletionTime));
+ IncrementalQueryAnalyzer analyzer1 = IncrementalQueryAnalyzer.builder()
+ .metaClient(metaClient)
+ .rangeType(InstantRange.RangeType.OPEN_CLOSE)
+ .startTime(completionTimeMap.get("1"))
+ .skipClustering(true)
+ .build();
// previous read iteration read till instant time "1", next read iteration should return ["2", "3"]
- List<HoodieInstant> instantRange2 = iis.filterInstantsWithRange(timeline, "1");
- assertEquals(2, instantRange2.size());
- assertIterableEquals(Arrays.asList(commit2, commit3), instantRange2);
+ List<HoodieInstant> activeInstants1 = analyzer1.analyze().getActiveInstants();
+ assertEquals(2, activeInstants1.size());
+ assertIterableEquals(Arrays.asList(commit2, commit3), activeInstants1);
// simulate first iteration cycle with read from the LATEST commit
- List<HoodieInstant> instantRange1 = iis.filterInstantsWithRange(timeline, null);
- assertEquals(1, instantRange1.size());
- assertIterableEquals(Collections.singletonList(commit3), instantRange1);
+ IncrementalQueryAnalyzer analyzer2 = IncrementalQueryAnalyzer.builder()
+ .metaClient(metaClient)
+ .rangeType(InstantRange.RangeType.CLOSE_CLOSE)
+ .skipClustering(true)
+ .build();
+ List<HoodieInstant> activeInstants2 = analyzer2.analyze().getActiveInstants();
+ assertEquals(1, activeInstants2.size());
+ assertIterableEquals(Collections.singletonList(commit3), activeInstants2);
// specifying a start and end commit
- conf.set(FlinkOptions.READ_START_COMMIT, "1");
- conf.set(FlinkOptions.READ_END_COMMIT, "3");
- List<HoodieInstant> instantRange3 = iis.filterInstantsWithRange(timeline, null);
- assertEquals(3, instantRange3.size());
- assertIterableEquals(Arrays.asList(commit1, commit2, commit3), instantRange3);
+ IncrementalQueryAnalyzer analyzer3 = IncrementalQueryAnalyzer.builder()
Review Comment:
Should already been tested.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "hudi-bot (via GitHub)" <gi...@apache.org>.
hudi-bot commented on PR #10255:
URL: https://github.com/apache/hudi/pull/10255#issuecomment-1931021650
<!--
Meta data
{
"version" : 1,
"metaDataEntries" : [ {
"hash" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21338",
"triggerID" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"triggerType" : "PUSH"
}, {
"hash" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21364",
"triggerID" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"triggerType" : "PUSH"
}, {
"hash" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21395",
"triggerID" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"triggerType" : "PUSH"
}, {
"hash" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21488",
"triggerID" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"triggerType" : "PUSH"
}, {
"hash" : "b39e93881c130fd8832e5c260740f8f59bb8033e",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21577",
"triggerID" : "b39e93881c130fd8832e5c260740f8f59bb8033e",
"triggerType" : "PUSH"
}, {
"hash" : "d2c7c5cd379c4c8ba4297fd5fa1e989f73e58b5d",
"status" : "CANCELED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21854",
"triggerID" : "d2c7c5cd379c4c8ba4297fd5fa1e989f73e58b5d",
"triggerType" : "PUSH"
}, {
"hash" : "975fbb9de10066ac67a875f3484fe9f5922e197d",
"status" : "PENDING",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=22346",
"triggerID" : "975fbb9de10066ac67a875f3484fe9f5922e197d",
"triggerType" : "PUSH"
} ]
}-->
## CI report:
* d2c7c5cd379c4c8ba4297fd5fa1e989f73e58b5d Azure: [CANCELED](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21854)
* 975fbb9de10066ac67a875f3484fe9f5922e197d Azure: [PENDING](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=22346)
<details>
<summary>Bot commands</summary>
@hudi-bot supports the following commands:
- `@hudi-bot run azure` re-run the last Azure build
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "danny0405 (via GitHub)" <gi...@apache.org>.
danny0405 commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1424806038
##########
hudi-common/src/main/java/org/apache/hudi/common/table/timeline/CompletionTimeQueryView.java:
##########
@@ -175,42 +190,109 @@ public Option<String> getCompletionTime(String startTime) {
*
* <p>By default, assumes there is at most 1 day time of duration for an instant to accelerate the queries.
*
- * @param startCompletionTime The start completion time.
- * @param endCompletionTime The end completion time.
+ * @param readTimeline The read timeline.
Review Comment:
The timeline can be optionally filtered with `skip_compaction` and `skip_clustering` flag. For example, when you wanna filter the clustering, if the replace commits is not filtered, the fs view would try to mask out the normal commits which make the commit invisible to reader.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "danny0405 (via GitHub)" <gi...@apache.org>.
danny0405 commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1424873530
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,425 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
Review Comment:
+1, but for follow-up JIRA issue.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "hudi-bot (via GitHub)" <gi...@apache.org>.
hudi-bot commented on PR #10255:
URL: https://github.com/apache/hudi/pull/10255#issuecomment-1862081603
<!--
Meta data
{
"version" : 1,
"metaDataEntries" : [ {
"hash" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21338",
"triggerID" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"triggerType" : "PUSH"
}, {
"hash" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21364",
"triggerID" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"triggerType" : "PUSH"
}, {
"hash" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21395",
"triggerID" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"triggerType" : "PUSH"
}, {
"hash" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"status" : "SUCCESS",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21488",
"triggerID" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"triggerType" : "PUSH"
}, {
"hash" : "b39e93881c130fd8832e5c260740f8f59bb8033e",
"status" : "UNKNOWN",
"url" : "TBD",
"triggerID" : "b39e93881c130fd8832e5c260740f8f59bb8033e",
"triggerType" : "PUSH"
} ]
}-->
## CI report:
* 3e12be6f351ee6da145de331af53c27c985b7d25 Azure: [SUCCESS](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21488)
* b39e93881c130fd8832e5c260740f8f59bb8033e UNKNOWN
<details>
<summary>Bot commands</summary>
@hudi-bot supports the following commands:
- `@hudi-bot run azure` re-run the last Azure build
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "vinothchandar (via GitHub)" <gi...@apache.org>.
vinothchandar commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1470522333
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,428 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
Review Comment:
what do we exactly mean by this ? `_hoodie_commit_time` is begin time and not completion time, right? Just for my understanding
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,428 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTime;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed after or on 'startTime';
Review Comment:
Ideally, dont like to leak things like archived instants into this definition.
##########
hudi-common/src/main/java/org/apache/hudi/common/table/log/InstantRange.java:
##########
@@ -166,6 +169,28 @@ public boolean isInRange(String instant) {
}
}
+ /**
+ * Composition of multiple instant ranges in disjunctive form.
+ */
+ private static class CompositionRange extends InstantRange {
+ List<InstantRange> instantRanges;
+
+ public CompositionRange(List<InstantRange> instantRanges) {
+ super(null, null);
Review Comment:
Should this be an `InstantRange` given it has null ranges?
##########
hudi-common/src/main/java/org/apache/hudi/common/table/log/InstantRange.java:
##########
@@ -22,9 +22,12 @@
import org.apache.hudi.common.util.ValidationUtils;
import java.io.Serializable;
+import java.util.Arrays;
import java.util.Collections;
+import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.stream.Collectors;
/**
* A instant commits range used for incremental reader filtering.
Review Comment:
lets do the package changes in this PR itself?
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,428 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTime;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed after or on 'startTime';
Review Comment:
Thats more implementation detail IMO.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "hudi-bot (via GitHub)" <gi...@apache.org>.
hudi-bot commented on PR #10255:
URL: https://github.com/apache/hudi/pull/10255#issuecomment-1932526613
<!--
Meta data
{
"version" : 1,
"metaDataEntries" : [ {
"hash" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21338",
"triggerID" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"triggerType" : "PUSH"
}, {
"hash" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21364",
"triggerID" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"triggerType" : "PUSH"
}, {
"hash" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21395",
"triggerID" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"triggerType" : "PUSH"
}, {
"hash" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21488",
"triggerID" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"triggerType" : "PUSH"
}, {
"hash" : "b39e93881c130fd8832e5c260740f8f59bb8033e",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21577",
"triggerID" : "b39e93881c130fd8832e5c260740f8f59bb8033e",
"triggerType" : "PUSH"
}, {
"hash" : "d2c7c5cd379c4c8ba4297fd5fa1e989f73e58b5d",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21854",
"triggerID" : "d2c7c5cd379c4c8ba4297fd5fa1e989f73e58b5d",
"triggerType" : "PUSH"
}, {
"hash" : "975fbb9de10066ac67a875f3484fe9f5922e197d",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=22346",
"triggerID" : "975fbb9de10066ac67a875f3484fe9f5922e197d",
"triggerType" : "PUSH"
}, {
"hash" : "a2cb2403ded21b17b991304af14eb57897b4100d",
"status" : "FAILURE",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=22357",
"triggerID" : "a2cb2403ded21b17b991304af14eb57897b4100d",
"triggerType" : "PUSH"
} ]
}-->
## CI report:
* a2cb2403ded21b17b991304af14eb57897b4100d Azure: [FAILURE](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=22357)
<details>
<summary>Bot commands</summary>
@hudi-bot supports the following commands:
- `@hudi-bot run azure` re-run the last Azure build
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "danny0405 (via GitHub)" <gi...@apache.org>.
danny0405 commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1428787379
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,429 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed before or on 'endTime';
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed in the given time range;
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * </table>
+ *
+ * <p> A range type is required for analyzing the query so that the query range boundary inclusiveness have clear semantics.
+ *
+ * <p>IMPORTANT: the reader may optionally choose to fall back to reading the latest snapshot if there are files missing from decoding the commit metadata.
+ */
+public class IncrementalQueryAnalyzer {
+ public static final String START_COMMIT_EARLIEST = "earliest";
+
+ private final HoodieTableMetaClient metaClient;
+ private final Option<String> startTime;
+ private final Option<String> endTime;
+ private final InstantRange.RangeType rangeType;
+ private final boolean skipCompaction;
+ private final boolean skipClustering;
+ private final int limit;
+
+ private IncrementalQueryAnalyzer(
+ HoodieTableMetaClient metaClient,
+ String startTime,
+ String endTime,
+ InstantRange.RangeType rangeType,
+ boolean skipCompaction,
+ boolean skipClustering,
+ int limit) {
+ this.metaClient = metaClient;
+ this.startTime = Option.ofNullable(startTime);
+ this.endTime = Option.ofNullable(endTime);
+ this.rangeType = rangeType;
+ this.skipCompaction = skipCompaction;
+ this.skipClustering = skipClustering;
+ this.limit = limit;
+ }
+
+ /**
+ * Returns a builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Analyzes the incremental query context with given completion time range.
+ *
+ * @return An incremental query context including the instant time range info.
+ */
+ public QueryContext analyze() {
+ try (CompletionTimeQueryView completionTimeQueryView = new CompletionTimeQueryView(this.metaClient)) {
+ if (completionTimeQueryView.isEmptyTable()) {
+ // no dataset committed in the table
+ return QueryContext.EMPTY;
+ }
+ HoodieTimeline readTimeline = getReadTimeline(this.metaClient);
+ List<String> instantTimeList = completionTimeQueryView.getStartTimes(readTimeline, startTime, endTime, rangeType);
+ if (instantTimeList.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ // get hoodie instants
+ Pair<List<String>, List<String>> splitInstantTime = splitInstantByActiveness(instantTimeList, completionTimeQueryView);
+ Set<String> instantTimeSet = new HashSet<>(instantTimeList);
+ List<String> archivedInstantTime = splitInstantTime.getKey();
+ List<String> activeInstantTime = splitInstantTime.getValue();
+ List<HoodieInstant> archivedInstants = new ArrayList<>();
+ List<HoodieInstant> activeInstants = new ArrayList<>();
+ HoodieTimeline archivedReadTimeline = null;
+ if (!activeInstantTime.isEmpty()) {
+ activeInstants = readTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ if (limit > 0 && limit < activeInstants.size()) {
+ // streaming read speed limit, limits the maximum number of commits allowed to read for each run
+ activeInstants = activeInstants.subList(0, limit);
Review Comment:
The incremental query can return N number of commits in one bunch or just the limited first M ones. M <= N.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "danny0405 (via GitHub)" <gi...@apache.org>.
danny0405 commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1428787494
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,429 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed before or on 'endTime';
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed in the given time range;
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * </table>
+ *
+ * <p> A range type is required for analyzing the query so that the query range boundary inclusiveness have clear semantics.
+ *
+ * <p>IMPORTANT: the reader may optionally choose to fall back to reading the latest snapshot if there are files missing from decoding the commit metadata.
+ */
+public class IncrementalQueryAnalyzer {
+ public static final String START_COMMIT_EARLIEST = "earliest";
+
+ private final HoodieTableMetaClient metaClient;
+ private final Option<String> startTime;
+ private final Option<String> endTime;
+ private final InstantRange.RangeType rangeType;
+ private final boolean skipCompaction;
+ private final boolean skipClustering;
+ private final int limit;
+
+ private IncrementalQueryAnalyzer(
+ HoodieTableMetaClient metaClient,
+ String startTime,
+ String endTime,
+ InstantRange.RangeType rangeType,
+ boolean skipCompaction,
+ boolean skipClustering,
+ int limit) {
+ this.metaClient = metaClient;
+ this.startTime = Option.ofNullable(startTime);
+ this.endTime = Option.ofNullable(endTime);
+ this.rangeType = rangeType;
+ this.skipCompaction = skipCompaction;
+ this.skipClustering = skipClustering;
+ this.limit = limit;
+ }
+
+ /**
+ * Returns a builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Analyzes the incremental query context with given completion time range.
+ *
+ * @return An incremental query context including the instant time range info.
+ */
+ public QueryContext analyze() {
+ try (CompletionTimeQueryView completionTimeQueryView = new CompletionTimeQueryView(this.metaClient)) {
+ if (completionTimeQueryView.isEmptyTable()) {
+ // no dataset committed in the table
+ return QueryContext.EMPTY;
+ }
+ HoodieTimeline readTimeline = getReadTimeline(this.metaClient);
+ List<String> instantTimeList = completionTimeQueryView.getStartTimes(readTimeline, startTime, endTime, rangeType);
+ if (instantTimeList.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ // get hoodie instants
+ Pair<List<String>, List<String>> splitInstantTime = splitInstantByActiveness(instantTimeList, completionTimeQueryView);
+ Set<String> instantTimeSet = new HashSet<>(instantTimeList);
+ List<String> archivedInstantTime = splitInstantTime.getKey();
+ List<String> activeInstantTime = splitInstantTime.getValue();
+ List<HoodieInstant> archivedInstants = new ArrayList<>();
+ List<HoodieInstant> activeInstants = new ArrayList<>();
+ HoodieTimeline archivedReadTimeline = null;
+ if (!activeInstantTime.isEmpty()) {
+ activeInstants = readTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ if (limit > 0 && limit < activeInstants.size()) {
+ // streaming read speed limit, limits the maximum number of commits allowed to read for each run
+ activeInstants = activeInstants.subList(0, limit);
+ }
+ }
+ if (!archivedInstantTime.isEmpty()) {
Review Comment:
Just not implement yet.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "danny0405 (via GitHub)" <gi...@apache.org>.
danny0405 commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1424872867
##########
hudi-common/src/main/java/org/apache/hudi/common/table/timeline/CompletionTimeQueryView.java:
##########
@@ -175,42 +190,109 @@ public Option<String> getCompletionTime(String startTime) {
*
* <p>By default, assumes there is at most 1 day time of duration for an instant to accelerate the queries.
*
- * @param startCompletionTime The start completion time.
- * @param endCompletionTime The end completion time.
+ * @param readTimeline The read timeline.
+ * @param startTime The start completion time.
+ * @param endTime The end completion time.
+ * @param rangeType The range type.
*
- * @return The instant time set.
+ * @return The sorted instant time list.
*/
- public Set<String> getStartTimeSet(String startCompletionTime, String endCompletionTime) {
+ public List<String> getStartTime(
+ HoodieTimeline readTimeline,
+ @Nullable String startTime,
+ @Nullable String endTime,
+ InstantRange.RangeType rangeType) {
// assumes any instant/transaction lasts at most 1 day to optimize the query efficiency.
- return getStartTimeSet(startCompletionTime, endCompletionTime, s -> HoodieInstantTimeGenerator.instantTimeMinusMillis(s, MILLI_SECONDS_IN_ONE_DAY));
+ return getStartTime(readTimeline, startTime, endTime, rangeType, s -> HoodieInstantTimeGenerator.instantTimeMinusMillis(s, MILLI_SECONDS_IN_ONE_DAY));
+ }
+
+ /**
+ * Queries the instant start time with given completion time range.
+ *
+ * @param startTime The start completion time.
+ * @param endTime The end completion time.
+ * @param earliestInstantTimeFunc The function to generate the earliest start time boundary
+ * with the minimum completion time.
+ *
+ * @return The sorted instant time list.
+ */
+ @VisibleForTesting
+ public List<String> getStartTime(
+ @Nullable String startTime,
+ @Nullable String endTime,
+ Function<String, String> earliestInstantTimeFunc) {
+ return getStartTime(metaClient.getCommitsTimeline().filterCompletedInstants(), startTime, endTime, InstantRange.RangeType.CLOSE_CLOSE, earliestInstantTimeFunc);
}
/**
* Queries the instant start time with given completion time range.
*
- * @param startCompletionTime The start completion time.
- * @param endCompletionTime The end completion time.
- * @param earliestStartTimeFunc The function to generate the earliest start time boundary
- * with the minimum completion time {@code startCompletionTime}.
+ * @param readTimeline The read timeline.
+ * @param startTime The start completion time.
+ * @param endTime The end completion time.
+ * @param rangeType The range type.
+ * @param earliestInstantTimeFunc The function to generate the earliest start time boundary
+ * with the minimum completion time.
*
- * @return The instant time set.
+ * @return The sorted instant time list.
*/
- public Set<String> getStartTimeSet(String startCompletionTime, String endCompletionTime, Function<String, String> earliestStartTimeFunc) {
- String startInstant = earliestStartTimeFunc.apply(startCompletionTime);
+ public List<String> getStartTime(
+ HoodieTimeline readTimeline,
+ @Nullable String startTime,
+ @Nullable String endTime,
+ InstantRange.RangeType rangeType,
+ Function<String, String> earliestInstantTimeFunc) {
+ final boolean startFromEarliest = START_COMMIT_EARLIEST.equalsIgnoreCase(startTime);
+ String earliestInstantToLoad = null;
+ if (startTime != null && !startFromEarliest) {
+ earliestInstantToLoad = earliestInstantTimeFunc.apply(startTime);
+ } else if (endTime != null) {
+ earliestInstantToLoad = earliestInstantTimeFunc.apply(endTime);
+ }
+
+ // ensure the earliest instant boundary be loaded.
+ if (earliestInstantToLoad != null && HoodieTimeline.compareTimestamps(this.cursorInstant, GREATER_THAN, earliestInstantToLoad)) {
+ loadCompletionTimeIncrementally(earliestInstantToLoad);
+ }
+
+ if (startTime == null && endTime != null) {
+ // returns the last instant that finished at or before the given completion time 'endTime'.
+ String maxInstantTime = readTimeline.getInstantsAsStream()
+ .filter(instant -> instant.isCompleted() && HoodieTimeline.compareTimestamps(instant.getCompletionTime(), LESSER_THAN_OR_EQUALS, endTime))
+ .max(Comparator.comparing(HoodieInstant::getCompletionTime)).map(HoodieInstant::getTimestamp).orElse(null);
+ if (maxInstantTime != null) {
+ return Collections.singletonList(maxInstantTime);
+ }
+ // fallback to archived timeline
+ return this.startToCompletionInstantTimeMap.entrySet().stream()
+ .filter(entry -> HoodieTimeline.compareTimestamps(entry.getValue(), LESSER_THAN_OR_EQUALS, endTime))
+ .map(Map.Entry::getKey).collect(Collectors.toList());
+ }
+
+ if (startFromEarliest) {
+ // expedience for snapshot read: ['earliest', _) to avoid loading unnecessary instants.
+ startTime = null;
Review Comment:
For flink inc read, we have such option for user to consume from the earliest. Here I want to reuse some code path for `startTime == null && endTime == null` so put it here.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "linliu-code (via GitHub)" <gi...@apache.org>.
linliu-code commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1427374230
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,429 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
Review Comment:
instant == instant candidates?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "linliu-code (via GitHub)" <gi...@apache.org>.
linliu-code commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1428354941
##########
hudi-common/src/main/java/org/apache/hudi/common/table/log/InstantRange.java:
##########
@@ -166,6 +169,28 @@ public boolean isInRange(String instant) {
}
}
+ /**
+ * Composition of multiple instant ranges in disjunctive form.
Review Comment:
Here by "disjuntive", do you mean the relation between these range is "OR", or do you mean these ranges are not consecutive? Meanwhile, in this function, we don't need to know their relation at all.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "danny0405 (via GitHub)" <gi...@apache.org>.
danny0405 commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1427516351
##########
hudi-common/src/main/java/org/apache/hudi/common/table/timeline/CompletionTimeQueryView.java:
##########
@@ -175,42 +187,111 @@ public Option<String> getCompletionTime(String startTime) {
*
* <p>By default, assumes there is at most 1 day time of duration for an instant to accelerate the queries.
*
- * @param startCompletionTime The start completion time.
- * @param endCompletionTime The end completion time.
+ * @param readTimeline The read timeline.
+ * @param rangeStart The query range start completion time.
+ * @param rangeEnd The query range end completion time.
+ * @param rangeType The range type.
*
- * @return The instant time set.
+ * @return The sorted instant time list.
*/
- public Set<String> getStartTimeSet(String startCompletionTime, String endCompletionTime) {
+ public List<String> getStartTimes(
+ HoodieTimeline readTimeline,
+ Option<String> rangeStart,
+ Option<String> rangeEnd,
+ InstantRange.RangeType rangeType) {
// assumes any instant/transaction lasts at most 1 day to optimize the query efficiency.
Review Comment:
That would be rare case, we can make it configurable if needed, but 1 day is generally enough for most of the use cases.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "danny0405 (via GitHub)" <gi...@apache.org>.
danny0405 commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1428788123
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,429 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed before or on 'endTime';
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed in the given time range;
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * </table>
+ *
+ * <p> A range type is required for analyzing the query so that the query range boundary inclusiveness have clear semantics.
+ *
+ * <p>IMPORTANT: the reader may optionally choose to fall back to reading the latest snapshot if there are files missing from decoding the commit metadata.
+ */
+public class IncrementalQueryAnalyzer {
+ public static final String START_COMMIT_EARLIEST = "earliest";
+
+ private final HoodieTableMetaClient metaClient;
+ private final Option<String> startTime;
+ private final Option<String> endTime;
+ private final InstantRange.RangeType rangeType;
+ private final boolean skipCompaction;
+ private final boolean skipClustering;
+ private final int limit;
+
+ private IncrementalQueryAnalyzer(
+ HoodieTableMetaClient metaClient,
+ String startTime,
+ String endTime,
Review Comment:
It could be, and that would induce empty dataset.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "danny0405 (via GitHub)" <gi...@apache.org>.
danny0405 commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1442458339
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,428 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed before or on 'endTime';
Review Comment:
Yeah, the doc can be improved.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "hudi-bot (via GitHub)" <gi...@apache.org>.
hudi-bot commented on PR #10255:
URL: https://github.com/apache/hudi/pull/10255#issuecomment-1862221288
<!--
Meta data
{
"version" : 1,
"metaDataEntries" : [ {
"hash" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21338",
"triggerID" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"triggerType" : "PUSH"
}, {
"hash" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21364",
"triggerID" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"triggerType" : "PUSH"
}, {
"hash" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21395",
"triggerID" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"triggerType" : "PUSH"
}, {
"hash" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21488",
"triggerID" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"triggerType" : "PUSH"
}, {
"hash" : "b39e93881c130fd8832e5c260740f8f59bb8033e",
"status" : "SUCCESS",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21577",
"triggerID" : "b39e93881c130fd8832e5c260740f8f59bb8033e",
"triggerType" : "PUSH"
} ]
}-->
## CI report:
* b39e93881c130fd8832e5c260740f8f59bb8033e Azure: [SUCCESS](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21577)
<details>
<summary>Bot commands</summary>
@hudi-bot supports the following commands:
- `@hudi-bot run azure` re-run the last Azure build
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "hudi-bot (via GitHub)" <gi...@apache.org>.
hudi-bot commented on PR #10255:
URL: https://github.com/apache/hudi/pull/10255#issuecomment-1880018357
<!--
Meta data
{
"version" : 1,
"metaDataEntries" : [ {
"hash" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21338",
"triggerID" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"triggerType" : "PUSH"
}, {
"hash" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21364",
"triggerID" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"triggerType" : "PUSH"
}, {
"hash" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21395",
"triggerID" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"triggerType" : "PUSH"
}, {
"hash" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21488",
"triggerID" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"triggerType" : "PUSH"
}, {
"hash" : "b39e93881c130fd8832e5c260740f8f59bb8033e",
"status" : "SUCCESS",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21577",
"triggerID" : "b39e93881c130fd8832e5c260740f8f59bb8033e",
"triggerType" : "PUSH"
}, {
"hash" : "d2c7c5cd379c4c8ba4297fd5fa1e989f73e58b5d",
"status" : "PENDING",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21854",
"triggerID" : "d2c7c5cd379c4c8ba4297fd5fa1e989f73e58b5d",
"triggerType" : "PUSH"
} ]
}-->
## CI report:
* b39e93881c130fd8832e5c260740f8f59bb8033e Azure: [SUCCESS](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21577)
* d2c7c5cd379c4c8ba4297fd5fa1e989f73e58b5d Azure: [PENDING](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21854)
<details>
<summary>Bot commands</summary>
@hudi-bot supports the following commands:
- `@hudi-bot run azure` re-run the last Azure build
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "danny0405 (via GitHub)" <gi...@apache.org>.
danny0405 commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1428787116
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,429 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed before or on 'endTime';
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed in the given time range;
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * </table>
+ *
+ * <p> A range type is required for analyzing the query so that the query range boundary inclusiveness have clear semantics.
+ *
+ * <p>IMPORTANT: the reader may optionally choose to fall back to reading the latest snapshot if there are files missing from decoding the commit metadata.
+ */
+public class IncrementalQueryAnalyzer {
+ public static final String START_COMMIT_EARLIEST = "earliest";
+
+ private final HoodieTableMetaClient metaClient;
+ private final Option<String> startTime;
+ private final Option<String> endTime;
+ private final InstantRange.RangeType rangeType;
+ private final boolean skipCompaction;
+ private final boolean skipClustering;
+ private final int limit;
+
+ private IncrementalQueryAnalyzer(
+ HoodieTableMetaClient metaClient,
+ String startTime,
+ String endTime,
+ InstantRange.RangeType rangeType,
+ boolean skipCompaction,
+ boolean skipClustering,
+ int limit) {
+ this.metaClient = metaClient;
+ this.startTime = Option.ofNullable(startTime);
+ this.endTime = Option.ofNullable(endTime);
+ this.rangeType = rangeType;
+ this.skipCompaction = skipCompaction;
+ this.skipClustering = skipClustering;
+ this.limit = limit;
+ }
+
+ /**
+ * Returns a builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Analyzes the incremental query context with given completion time range.
+ *
+ * @return An incremental query context including the instant time range info.
+ */
+ public QueryContext analyze() {
+ try (CompletionTimeQueryView completionTimeQueryView = new CompletionTimeQueryView(this.metaClient)) {
+ if (completionTimeQueryView.isEmptyTable()) {
+ // no dataset committed in the table
+ return QueryContext.EMPTY;
+ }
+ HoodieTimeline readTimeline = getReadTimeline(this.metaClient);
+ List<String> instantTimeList = completionTimeQueryView.getStartTimes(readTimeline, startTime, endTime, rangeType);
+ if (instantTimeList.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ // get hoodie instants
+ Pair<List<String>, List<String>> splitInstantTime = splitInstantByActiveness(instantTimeList, completionTimeQueryView);
+ Set<String> instantTimeSet = new HashSet<>(instantTimeList);
+ List<String> archivedInstantTime = splitInstantTime.getKey();
+ List<String> activeInstantTime = splitInstantTime.getValue();
+ List<HoodieInstant> archivedInstants = new ArrayList<>();
+ List<HoodieInstant> activeInstants = new ArrayList<>();
+ HoodieTimeline archivedReadTimeline = null;
+ if (!activeInstantTime.isEmpty()) {
+ activeInstants = readTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ if (limit > 0 && limit < activeInstants.size()) {
+ // streaming read speed limit, limits the maximum number of commits allowed to read for each run
+ activeInstants = activeInstants.subList(0, limit);
+ }
+ }
+ if (!archivedInstantTime.isEmpty()) {
+ archivedReadTimeline = getArchivedReadTimeline(metaClient, archivedInstantTime.get(0));
+ archivedInstants = archivedReadTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ }
+ List<String> instants = Stream.concat(archivedInstants.stream(), activeInstants.stream()).map(HoodieInstant::getTimestamp).collect(Collectors.toList());
+ if (instants.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ if (startTime.isEmpty() && endTime.isPresent()) {
+ instants = Collections.singletonList(instants.get(instants.size() - 1));
+ }
+ String lastInstant = instants.get(instants.size() - 1);
+ // keep the same semantics with streaming read, default start from the latest commit
+ String startInstant = START_COMMIT_EARLIEST.equalsIgnoreCase(startTime.orElse(null)) ? null : startTime.isEmpty() ? lastInstant : instants.get(0);
+ String endInstant = endTime.isEmpty() ? null : lastInstant;
+ return QueryContext.create(startInstant, endInstant, instants, archivedInstants, activeInstants, readTimeline, archivedReadTimeline);
+ }
+ }
Review Comment:
I have no very good idea now, one way is to introduce a enum for each kind of the 6 cases, and handle them separately. But the logic itself is complicated, need to think through how to reuse the logic there.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "linliu-code (via GitHub)" <gi...@apache.org>.
linliu-code commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1428363554
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,429 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed before or on 'endTime';
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed in the given time range;
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * </table>
+ *
+ * <p> A range type is required for analyzing the query so that the query range boundary inclusiveness have clear semantics.
+ *
+ * <p>IMPORTANT: the reader may optionally choose to fall back to reading the latest snapshot if there are files missing from decoding the commit metadata.
+ */
+public class IncrementalQueryAnalyzer {
+ public static final String START_COMMIT_EARLIEST = "earliest";
+
+ private final HoodieTableMetaClient metaClient;
+ private final Option<String> startTime;
+ private final Option<String> endTime;
+ private final InstantRange.RangeType rangeType;
+ private final boolean skipCompaction;
+ private final boolean skipClustering;
+ private final int limit;
+
+ private IncrementalQueryAnalyzer(
+ HoodieTableMetaClient metaClient,
+ String startTime,
+ String endTime,
+ InstantRange.RangeType rangeType,
+ boolean skipCompaction,
+ boolean skipClustering,
+ int limit) {
+ this.metaClient = metaClient;
+ this.startTime = Option.ofNullable(startTime);
+ this.endTime = Option.ofNullable(endTime);
+ this.rangeType = rangeType;
+ this.skipCompaction = skipCompaction;
+ this.skipClustering = skipClustering;
+ this.limit = limit;
+ }
+
+ /**
+ * Returns a builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Analyzes the incremental query context with given completion time range.
+ *
+ * @return An incremental query context including the instant time range info.
+ */
+ public QueryContext analyze() {
+ try (CompletionTimeQueryView completionTimeQueryView = new CompletionTimeQueryView(this.metaClient)) {
+ if (completionTimeQueryView.isEmptyTable()) {
+ // no dataset committed in the table
+ return QueryContext.EMPTY;
+ }
+ HoodieTimeline readTimeline = getReadTimeline(this.metaClient);
Review Comment:
Initially a bit confusing tho.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "linliu-code (via GitHub)" <gi...@apache.org>.
linliu-code commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1428418974
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,429 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed before or on 'endTime';
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed in the given time range;
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * </table>
+ *
+ * <p> A range type is required for analyzing the query so that the query range boundary inclusiveness have clear semantics.
+ *
+ * <p>IMPORTANT: the reader may optionally choose to fall back to reading the latest snapshot if there are files missing from decoding the commit metadata.
+ */
+public class IncrementalQueryAnalyzer {
+ public static final String START_COMMIT_EARLIEST = "earliest";
+
+ private final HoodieTableMetaClient metaClient;
+ private final Option<String> startTime;
+ private final Option<String> endTime;
+ private final InstantRange.RangeType rangeType;
+ private final boolean skipCompaction;
+ private final boolean skipClustering;
+ private final int limit;
+
+ private IncrementalQueryAnalyzer(
+ HoodieTableMetaClient metaClient,
+ String startTime,
+ String endTime,
+ InstantRange.RangeType rangeType,
+ boolean skipCompaction,
+ boolean skipClustering,
+ int limit) {
+ this.metaClient = metaClient;
+ this.startTime = Option.ofNullable(startTime);
+ this.endTime = Option.ofNullable(endTime);
+ this.rangeType = rangeType;
+ this.skipCompaction = skipCompaction;
+ this.skipClustering = skipClustering;
+ this.limit = limit;
+ }
+
+ /**
+ * Returns a builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Analyzes the incremental query context with given completion time range.
+ *
+ * @return An incremental query context including the instant time range info.
+ */
+ public QueryContext analyze() {
+ try (CompletionTimeQueryView completionTimeQueryView = new CompletionTimeQueryView(this.metaClient)) {
+ if (completionTimeQueryView.isEmptyTable()) {
+ // no dataset committed in the table
+ return QueryContext.EMPTY;
+ }
+ HoodieTimeline readTimeline = getReadTimeline(this.metaClient);
+ List<String> instantTimeList = completionTimeQueryView.getStartTimes(readTimeline, startTime, endTime, rangeType);
+ if (instantTimeList.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ // get hoodie instants
+ Pair<List<String>, List<String>> splitInstantTime = splitInstantByActiveness(instantTimeList, completionTimeQueryView);
Review Comment:
What is the key difference between active and archived instants? Can we use a unified api for them?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "codope (via GitHub)" <gi...@apache.org>.
codope commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1439208778
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,428 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed before or on 'endTime';
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed in the given time range;
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * </table>
+ *
+ * <p> A range type is required for analyzing the query so that the query range boundary inclusiveness have clear semantics.
+ *
+ * <p>IMPORTANT: the reader may optionally choose to fall back to reading the latest snapshot if there are files missing from decoding the commit metadata.
+ */
+public class IncrementalQueryAnalyzer {
+ public static final String START_COMMIT_EARLIEST = "earliest";
+
+ private final HoodieTableMetaClient metaClient;
+ private final Option<String> startTime;
+ private final Option<String> endTime;
+ private final InstantRange.RangeType rangeType;
+ private final boolean skipCompaction;
+ private final boolean skipClustering;
+ private final int limit;
+
+ private IncrementalQueryAnalyzer(
+ HoodieTableMetaClient metaClient,
+ String startTime,
+ String endTime,
+ InstantRange.RangeType rangeType,
+ boolean skipCompaction,
+ boolean skipClustering,
+ int limit) {
+ this.metaClient = metaClient;
+ this.startTime = Option.ofNullable(startTime);
+ this.endTime = Option.ofNullable(endTime);
+ this.rangeType = rangeType;
+ this.skipCompaction = skipCompaction;
+ this.skipClustering = skipClustering;
+ this.limit = limit;
+ }
+
+ /**
+ * Returns a builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Analyzes the incremental query context with given completion time range.
+ *
+ * @return An incremental query context including the instant time range info.
+ */
+ public QueryContext analyze() {
+ try (CompletionTimeQueryView completionTimeQueryView = new CompletionTimeQueryView(this.metaClient)) {
+ if (completionTimeQueryView.isEmptyTable()) {
+ // no dataset committed in the table
+ return QueryContext.EMPTY;
+ }
+ HoodieTimeline readTimeline = getReadTimeline(this.metaClient);
+ List<String> instantTimeList = completionTimeQueryView.getStartTimes(readTimeline, startTime, endTime, rangeType);
+ if (instantTimeList.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ // get hoodie instants
+ Pair<List<String>, List<String>> splitInstantTime = splitInstantByActiveness(instantTimeList, completionTimeQueryView);
+ Set<String> instantTimeSet = new HashSet<>(instantTimeList);
+ List<String> archivedInstantTime = splitInstantTime.getKey();
+ List<String> activeInstantTime = splitInstantTime.getValue();
+ List<HoodieInstant> archivedInstants = new ArrayList<>();
+ List<HoodieInstant> activeInstants = new ArrayList<>();
+ HoodieTimeline archivedReadTimeline = null;
+ if (!activeInstantTime.isEmpty()) {
+ activeInstants = readTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ if (limit > 0 && limit < activeInstants.size()) {
+ // streaming read speed limit, limits the maximum number of commits allowed to read for each run
+ activeInstants = activeInstants.subList(0, limit);
+ }
+ }
+ if (!archivedInstantTime.isEmpty()) {
+ archivedReadTimeline = getArchivedReadTimeline(metaClient, archivedInstantTime.get(0));
+ archivedInstants = archivedReadTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ }
+ List<String> instants = Stream.concat(archivedInstants.stream(), activeInstants.stream()).map(HoodieInstant::getTimestamp).collect(Collectors.toList());
+ if (instants.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ if (startTime.isEmpty() && endTime.isPresent()) {
+ instants = Collections.singletonList(instants.get(instants.size() - 1));
+ }
+ String lastInstant = instants.get(instants.size() - 1);
+ // keep the same semantics with streaming read, default start from the latest commit
+ String startInstant = START_COMMIT_EARLIEST.equalsIgnoreCase(startTime.orElse(null)) ? null : startTime.isEmpty() ? lastInstant : instants.get(0);
+ String endInstant = endTime.isEmpty() ? null : lastInstant;
+ return QueryContext.create(startInstant, endInstant, instants, archivedInstants, activeInstants, readTimeline, archivedReadTimeline);
+ }
+ }
+
+ /**
+ * Splits the given instant time list into a pair of archived instant list and active instant list.
+ */
+ private static Pair<List<String>, List<String>> splitInstantByActiveness(List<String> instantTimeList, CompletionTimeQueryView completionTimeQueryView) {
Review Comment:
`Pair` implements `Map.Entry`. I think for this use case, a simple POJO would suffice. Wdyt?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "vinothchandar (via GitHub)" <gi...@apache.org>.
vinothchandar commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1486534637
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,428 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTime;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed after or on 'startTime';
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed in the given time range;
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * </table>
+ *
+ * <p> A range type is required for analyzing the query so that the query range boundary inclusiveness have clear semantics.
+ *
+ * <p>IMPORTANT: the reader may optionally choose to fall back to reading the latest snapshot if there are files missing from decoding the commit metadata.
+ */
+public class IncrementalQueryAnalyzer {
+ public static final String START_COMMIT_EARLIEST = "earliest";
+
+ private final HoodieTableMetaClient metaClient;
+ private final Option<String> startTime;
+ private final Option<String> endTime;
+ private final InstantRange.RangeType rangeType;
+ private final boolean skipCompaction;
+ private final boolean skipClustering;
+ private final int limit;
+
+ private IncrementalQueryAnalyzer(
+ HoodieTableMetaClient metaClient,
+ String startTime,
+ String endTime,
+ InstantRange.RangeType rangeType,
+ boolean skipCompaction,
+ boolean skipClustering,
+ int limit) {
+ this.metaClient = metaClient;
+ this.startTime = Option.ofNullable(startTime);
+ this.endTime = Option.ofNullable(endTime);
+ this.rangeType = rangeType;
+ this.skipCompaction = skipCompaction;
+ this.skipClustering = skipClustering;
+ this.limit = limit;
+ }
+
+ /**
+ * Returns a builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Analyzes the incremental query context with given completion time range.
+ *
+ * @return An incremental query context including the instant time range info.
+ */
+ public QueryContext analyze() {
+ try (CompletionTimeQueryView completionTimeQueryView = new CompletionTimeQueryView(this.metaClient)) {
+ if (completionTimeQueryView.isEmptyTable()) {
+ // no dataset committed in the table
+ return QueryContext.EMPTY;
+ }
+ HoodieTimeline readTimeline = getReadTimeline(this.metaClient);
+ List<String> instantTimeList = completionTimeQueryView.getStartTimes(readTimeline, startTime, endTime, rangeType);
+ if (instantTimeList.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ // get hoodie instants
+ Pair<List<String>, List<String>> splitInstantTime = splitInstantByActiveness(instantTimeList, completionTimeQueryView);
+ Set<String> instantTimeSet = new HashSet<>(instantTimeList);
+ List<String> archivedInstantTime = splitInstantTime.getKey();
+ List<String> activeInstantTime = splitInstantTime.getValue();
+ List<HoodieInstant> archivedInstants = new ArrayList<>();
+ List<HoodieInstant> activeInstants = new ArrayList<>();
+ HoodieTimeline archivedReadTimeline = null;
+ if (!activeInstantTime.isEmpty()) {
+ activeInstants = readTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ if (limit > 0 && limit < activeInstants.size()) {
+ // streaming read speed limit, limits the maximum number of commits allowed to read for each run
+ activeInstants = activeInstants.subList(0, limit);
+ }
+ }
+ if (!archivedInstantTime.isEmpty()) {
+ archivedReadTimeline = getArchivedReadTimeline(metaClient, archivedInstantTime.get(0));
+ archivedInstants = archivedReadTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ }
+ List<String> instants = Stream.concat(archivedInstants.stream(), activeInstants.stream()).map(HoodieInstant::getTimestamp).collect(Collectors.toList());
+ if (instants.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ if (startTime.isEmpty() && endTime.isPresent()) {
+ instants = Collections.singletonList(instants.get(instants.size() - 1));
+ }
+ String lastInstant = instants.get(instants.size() - 1);
+ // keep the same semantics with streaming read, default start from the latest commit
+ String startInstant = START_COMMIT_EARLIEST.equalsIgnoreCase(startTime.orElse(null)) ? null : startTime.isEmpty() ? lastInstant : instants.get(0);
Review Comment:
I think some of these semantics can be improved. Will take it up as follow on.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "hudi-bot (via GitHub)" <gi...@apache.org>.
hudi-bot commented on PR #10255:
URL: https://github.com/apache/hudi/pull/10255#issuecomment-1931073583
<!--
Meta data
{
"version" : 1,
"metaDataEntries" : [ {
"hash" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21338",
"triggerID" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"triggerType" : "PUSH"
}, {
"hash" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21364",
"triggerID" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"triggerType" : "PUSH"
}, {
"hash" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21395",
"triggerID" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"triggerType" : "PUSH"
}, {
"hash" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21488",
"triggerID" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"triggerType" : "PUSH"
}, {
"hash" : "b39e93881c130fd8832e5c260740f8f59bb8033e",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21577",
"triggerID" : "b39e93881c130fd8832e5c260740f8f59bb8033e",
"triggerType" : "PUSH"
}, {
"hash" : "d2c7c5cd379c4c8ba4297fd5fa1e989f73e58b5d",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21854",
"triggerID" : "d2c7c5cd379c4c8ba4297fd5fa1e989f73e58b5d",
"triggerType" : "PUSH"
}, {
"hash" : "975fbb9de10066ac67a875f3484fe9f5922e197d",
"status" : "FAILURE",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=22346",
"triggerID" : "975fbb9de10066ac67a875f3484fe9f5922e197d",
"triggerType" : "PUSH"
} ]
}-->
## CI report:
* 975fbb9de10066ac67a875f3484fe9f5922e197d Azure: [FAILURE](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=22346)
<details>
<summary>Bot commands</summary>
@hudi-bot supports the following commands:
- `@hudi-bot run azure` re-run the last Azure build
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "hudi-bot (via GitHub)" <gi...@apache.org>.
hudi-bot commented on PR #10255:
URL: https://github.com/apache/hudi/pull/10255#issuecomment-1939541288
<!--
Meta data
{
"version" : 1,
"metaDataEntries" : [ {
"hash" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21338",
"triggerID" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"triggerType" : "PUSH"
}, {
"hash" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21364",
"triggerID" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"triggerType" : "PUSH"
}, {
"hash" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21395",
"triggerID" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"triggerType" : "PUSH"
}, {
"hash" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21488",
"triggerID" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"triggerType" : "PUSH"
}, {
"hash" : "b39e93881c130fd8832e5c260740f8f59bb8033e",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21577",
"triggerID" : "b39e93881c130fd8832e5c260740f8f59bb8033e",
"triggerType" : "PUSH"
}, {
"hash" : "d2c7c5cd379c4c8ba4297fd5fa1e989f73e58b5d",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21854",
"triggerID" : "d2c7c5cd379c4c8ba4297fd5fa1e989f73e58b5d",
"triggerType" : "PUSH"
}, {
"hash" : "975fbb9de10066ac67a875f3484fe9f5922e197d",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=22346",
"triggerID" : "975fbb9de10066ac67a875f3484fe9f5922e197d",
"triggerType" : "PUSH"
}, {
"hash" : "a2cb2403ded21b17b991304af14eb57897b4100d",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=22357",
"triggerID" : "a2cb2403ded21b17b991304af14eb57897b4100d",
"triggerType" : "PUSH"
}, {
"hash" : "3153fe03616237ff7814c53923fec2a2504b6d01",
"status" : "SUCCESS",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=22420",
"triggerID" : "3153fe03616237ff7814c53923fec2a2504b6d01",
"triggerType" : "PUSH"
} ]
}-->
## CI report:
* 3153fe03616237ff7814c53923fec2a2504b6d01 Azure: [SUCCESS](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=22420)
<details>
<summary>Bot commands</summary>
@hudi-bot supports the following commands:
- `@hudi-bot run azure` re-run the last Azure build
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "vinothchandar (via GitHub)" <gi...@apache.org>.
vinothchandar commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1420410913
##########
hudi-common/src/main/java/org/apache/hudi/common/table/log/InstantRange.java:
##########
@@ -22,9 +22,12 @@
import org.apache.hudi.common.util.ValidationUtils;
import java.io.Serializable;
+import java.util.Arrays;
import java.util.Collections;
+import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.stream.Collectors;
/**
* A instant commits range used for incremental reader filtering.
Review Comment:
Rename this class to base instant range? Also should this class should be in a different package? Like "timeline"?
##########
hudi-common/src/main/java/org/apache/hudi/common/table/timeline/HoodieInstantTimeGenerator.java:
##########
@@ -108,6 +108,9 @@ public static String instantTimePlusMillis(String timestamp, long milliseconds)
public static String instantTimeMinusMillis(String timestamp, long milliseconds) {
try {
String timestampInMillis = fixInstantTimeCompatibility(timestamp);
+ if (timestampInMillis.length() < MILLIS_INSTANT_TIMESTAMP_FORMAT_LENGTH) {
Review Comment:
Can you help me understand whats going on here.
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,425 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
Review Comment:
should we even allow this? seems like invalid input to Incremental query. Can we simplify to just .
````
[startTime, endTime]
[startTime, _] (we assume _ is latest time)
[_, endTime] (we assume _ is earliest time)
```
##########
hudi-common/src/main/java/org/apache/hudi/common/table/timeline/CompletionTimeQueryView.java:
##########
@@ -175,42 +190,109 @@ public Option<String> getCompletionTime(String startTime) {
*
* <p>By default, assumes there is at most 1 day time of duration for an instant to accelerate the queries.
*
- * @param startCompletionTime The start completion time.
- * @param endCompletionTime The end completion time.
+ * @param readTimeline The read timeline.
+ * @param startTime The start completion time.
+ * @param endTime The end completion time.
+ * @param rangeType The range type.
*
- * @return The instant time set.
+ * @return The sorted instant time list.
*/
- public Set<String> getStartTimeSet(String startCompletionTime, String endCompletionTime) {
+ public List<String> getStartTime(
+ HoodieTimeline readTimeline,
+ @Nullable String startTime,
+ @Nullable String endTime,
+ InstantRange.RangeType rangeType) {
// assumes any instant/transaction lasts at most 1 day to optimize the query efficiency.
- return getStartTimeSet(startCompletionTime, endCompletionTime, s -> HoodieInstantTimeGenerator.instantTimeMinusMillis(s, MILLI_SECONDS_IN_ONE_DAY));
+ return getStartTime(readTimeline, startTime, endTime, rangeType, s -> HoodieInstantTimeGenerator.instantTimeMinusMillis(s, MILLI_SECONDS_IN_ONE_DAY));
+ }
+
+ /**
+ * Queries the instant start time with given completion time range.
+ *
+ * @param startTime The start completion time.
+ * @param endTime The end completion time.
+ * @param earliestInstantTimeFunc The function to generate the earliest start time boundary
+ * with the minimum completion time.
+ *
+ * @return The sorted instant time list.
+ */
+ @VisibleForTesting
+ public List<String> getStartTime(
+ @Nullable String startTime,
+ @Nullable String endTime,
+ Function<String, String> earliestInstantTimeFunc) {
+ return getStartTime(metaClient.getCommitsTimeline().filterCompletedInstants(), startTime, endTime, InstantRange.RangeType.CLOSE_CLOSE, earliestInstantTimeFunc);
}
/**
* Queries the instant start time with given completion time range.
*
- * @param startCompletionTime The start completion time.
- * @param endCompletionTime The end completion time.
- * @param earliestStartTimeFunc The function to generate the earliest start time boundary
- * with the minimum completion time {@code startCompletionTime}.
+ * @param readTimeline The read timeline.
+ * @param startTime The start completion time.
+ * @param endTime The end completion time.
+ * @param rangeType The range type.
+ * @param earliestInstantTimeFunc The function to generate the earliest start time boundary
+ * with the minimum completion time.
*
- * @return The instant time set.
+ * @return The sorted instant time list.
*/
- public Set<String> getStartTimeSet(String startCompletionTime, String endCompletionTime, Function<String, String> earliestStartTimeFunc) {
- String startInstant = earliestStartTimeFunc.apply(startCompletionTime);
+ public List<String> getStartTime(
+ HoodieTimeline readTimeline,
+ @Nullable String startTime,
+ @Nullable String endTime,
+ InstantRange.RangeType rangeType,
+ Function<String, String> earliestInstantTimeFunc) {
+ final boolean startFromEarliest = START_COMMIT_EARLIEST.equalsIgnoreCase(startTime);
+ String earliestInstantToLoad = null;
+ if (startTime != null && !startFromEarliest) {
+ earliestInstantToLoad = earliestInstantTimeFunc.apply(startTime);
+ } else if (endTime != null) {
+ earliestInstantToLoad = earliestInstantTimeFunc.apply(endTime);
+ }
+
+ // ensure the earliest instant boundary be loaded.
+ if (earliestInstantToLoad != null && HoodieTimeline.compareTimestamps(this.cursorInstant, GREATER_THAN, earliestInstantToLoad)) {
+ loadCompletionTimeIncrementally(earliestInstantToLoad);
+ }
+
+ if (startTime == null && endTime != null) {
+ // returns the last instant that finished at or before the given completion time 'endTime'.
+ String maxInstantTime = readTimeline.getInstantsAsStream()
+ .filter(instant -> instant.isCompleted() && HoodieTimeline.compareTimestamps(instant.getCompletionTime(), LESSER_THAN_OR_EQUALS, endTime))
+ .max(Comparator.comparing(HoodieInstant::getCompletionTime)).map(HoodieInstant::getTimestamp).orElse(null);
+ if (maxInstantTime != null) {
+ return Collections.singletonList(maxInstantTime);
+ }
+ // fallback to archived timeline
+ return this.startToCompletionInstantTimeMap.entrySet().stream()
+ .filter(entry -> HoodieTimeline.compareTimestamps(entry.getValue(), LESSER_THAN_OR_EQUALS, endTime))
+ .map(Map.Entry::getKey).collect(Collectors.toList());
+ }
+
+ if (startFromEarliest) {
+ // expedience for snapshot read: ['earliest', _) to avoid loading unnecessary instants.
+ startTime = null;
+ }
+
+ if (startTime == null && endTime == null) {
+ return readTimeline.filterCompletedInstants().lastInstant().map(instant -> Collections.singletonList(instant.getTimestamp())).orElse(Collections.emptyList());
Review Comment:
this is basically reading the latest snapshot? add a comment?
##########
hudi-common/src/main/java/org/apache/hudi/common/table/timeline/CompletionTimeQueryView.java:
##########
@@ -175,42 +190,109 @@ public Option<String> getCompletionTime(String startTime) {
*
* <p>By default, assumes there is at most 1 day time of duration for an instant to accelerate the queries.
*
- * @param startCompletionTime The start completion time.
- * @param endCompletionTime The end completion time.
+ * @param readTimeline The read timeline.
+ * @param startTime The start completion time.
+ * @param endTime The end completion time.
+ * @param rangeType The range type.
*
- * @return The instant time set.
+ * @return The sorted instant time list.
*/
- public Set<String> getStartTimeSet(String startCompletionTime, String endCompletionTime) {
+ public List<String> getStartTime(
+ HoodieTimeline readTimeline,
+ @Nullable String startTime,
+ @Nullable String endTime,
+ InstantRange.RangeType rangeType) {
// assumes any instant/transaction lasts at most 1 day to optimize the query efficiency.
- return getStartTimeSet(startCompletionTime, endCompletionTime, s -> HoodieInstantTimeGenerator.instantTimeMinusMillis(s, MILLI_SECONDS_IN_ONE_DAY));
+ return getStartTime(readTimeline, startTime, endTime, rangeType, s -> HoodieInstantTimeGenerator.instantTimeMinusMillis(s, MILLI_SECONDS_IN_ONE_DAY));
+ }
+
+ /**
+ * Queries the instant start time with given completion time range.
+ *
+ * @param startTime The start completion time.
+ * @param endTime The end completion time.
+ * @param earliestInstantTimeFunc The function to generate the earliest start time boundary
+ * with the minimum completion time.
+ *
+ * @return The sorted instant time list.
+ */
+ @VisibleForTesting
+ public List<String> getStartTime(
Review Comment:
should we standardize to one things - begin instant time vs start time. I prefer "begin". What does the rest of the code use?
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,425 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed before or on 'endTime';
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed in the given time range;
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * </table>
+ *
+ * <p> A range type is required for analyzing the query so that the query range boundary inclusiveness have clear semantics.
+ *
+ * <p>IMPORTANT: the reader may optionally choose to fall back to reading the latest snapshot if there are files missing from decoding the commit metadata.
+ */
+public class IncrementalQueryAnalyzer {
+ public static final String START_COMMIT_EARLIEST = "earliest";
+
+ private final HoodieTableMetaClient metaClient;
+ private final String startTime;
+ private final String endTime;
+ private final InstantRange.RangeType rangeType;
+ private final boolean skipCompaction;
+ private final boolean skipClustering;
+ private final int limit;
+
+ private IncrementalQueryAnalyzer(
+ HoodieTableMetaClient metaClient,
+ String startTime,
+ String endTime,
+ InstantRange.RangeType rangeType,
+ boolean skipCompaction,
+ boolean skipClustering,
+ int limit) {
+ this.metaClient = metaClient;
+ this.startTime = startTime;
+ this.endTime = endTime;
+ this.rangeType = rangeType;
+ this.skipCompaction = skipCompaction;
+ this.skipClustering = skipClustering;
+ this.limit = limit;
+ }
+
+ /**
+ * Returns a builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Analyzes the incremental query context with given completion time range.
+ *
+ * @return An incremental query context including the instant time range info.
+ */
+ public QueryContext analyze() {
+ try (CompletionTimeQueryView completionTimeQueryView = new CompletionTimeQueryView(this.metaClient)) {
+ if (completionTimeQueryView.isEmptyTable()) {
+ // no dataset committed in the table
+ return QueryContext.EMPTY;
+ }
+ HoodieTimeline readTimeline = getReadTimeline(this.metaClient);
+ List<String> instantTimeList = completionTimeQueryView.getStartTime(readTimeline, startTime, endTime, rangeType);
+ if (instantTimeList.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ // get hoodie instants
+ Pair<List<String>, List<String>> splitInstantTime = splitInstantByActiveness(instantTimeList, completionTimeQueryView);
+ Set<String> instantTimeSet = new HashSet<>(instantTimeList);
+ List<String> archivedInstantTime = splitInstantTime.getKey();
+ List<String> activeInstantTime = splitInstantTime.getValue();
+ List<HoodieInstant> archivedInstants = new ArrayList<>();
+ List<HoodieInstant> activeInstants = new ArrayList<>();
+ HoodieTimeline archivedReadTimeline = null;
+ if (!activeInstantTime.isEmpty()) {
+ activeInstants = readTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ if (limit > 0 && limit < activeInstants.size()) {
+ // streaming read speed limit, limits the maximum number of commits allowed to read for each run
+ activeInstants = activeInstants.subList(0, limit);
+ }
+ }
+ if (!archivedInstantTime.isEmpty()) {
+ archivedReadTimeline = getArchivedReadTimeline(metaClient, archivedInstantTime.get(0));
+ archivedInstants = archivedReadTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ }
+ List<String> instants = Stream.concat(archivedInstants.stream(), activeInstants.stream()).map(HoodieInstant::getTimestamp).collect(Collectors.toList());
+ if (instants.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ if (startTime == null && endTime != null) {
+ instants = Collections.singletonList(instants.get(instants.size() - 1));
+ }
+ String lastInstant = instants.get(instants.size() - 1);
+ // keep the same semantics with streaming read, default start from the latest commit
+ String startInstant = START_COMMIT_EARLIEST.equalsIgnoreCase(startTime) ? null : startTime == null ? lastInstant : instants.get(0);
+ String endInstant = endTime == null ? null : lastInstant;
+ return QueryContext.create(startInstant, endInstant, instants, archivedInstants, activeInstants, readTimeline, archivedReadTimeline);
+ }
+ }
+
+ /**
+ * Splits the given instant time list into a pair of archived instant list and active instant list.
+ */
+ private static Pair<List<String>, List<String>> splitInstantByActiveness(List<String> instantTimeList, CompletionTimeQueryView completionTimeQueryView) {
+ int firstActiveIdx = IntStream.range(0, instantTimeList.size()).filter(i -> !completionTimeQueryView.isArchived(instantTimeList.get(i))).findFirst().orElse(-1);
+ if (firstActiveIdx == -1) {
+ return Pair.of(instantTimeList, Collections.emptyList());
+ } else if (firstActiveIdx == 0) {
+ return Pair.of(Collections.emptyList(), instantTimeList);
+ } else {
+ return Pair.of(instantTimeList.subList(0, firstActiveIdx), instantTimeList.subList(firstActiveIdx, instantTimeList.size()));
+ }
+ }
+
+ private HoodieTimeline getReadTimeline(HoodieTableMetaClient metaClient) {
+ HoodieTimeline timeline = metaClient.getCommitsAndCompactionTimeline().filterCompletedAndCompactionInstants();
+ return filterInstantsAsPerUserConfigs(metaClient, timeline, this.skipCompaction, this.skipClustering);
+ }
+
+ private HoodieTimeline getArchivedReadTimeline(HoodieTableMetaClient metaClient, String startInstant) {
+ HoodieArchivedTimeline archivedTimeline = metaClient.getArchivedTimeline(startInstant, false);
+ HoodieTimeline archivedCompleteTimeline = archivedTimeline.getCommitsTimeline().filterCompletedInstants();
+ return filterInstantsAsPerUserConfigs(metaClient, archivedCompleteTimeline, this.skipCompaction, this.skipClustering);
+ }
+
+ /**
+ * Filters out the unnecessary instants as per user specified configs.
+ *
+ * @param timeline The timeline.
+ *
+ * @return the filtered timeline
+ */
+ @VisibleForTesting
+ public static HoodieTimeline filterInstantsAsPerUserConfigs(HoodieTableMetaClient metaClient, HoodieTimeline timeline, boolean skipCompaction, boolean skipClustering) {
+ final HoodieTimeline oriTimeline = timeline;
+ if (metaClient.getTableType() == HoodieTableType.MERGE_ON_READ & skipCompaction) {
+ // the compaction commit uses 'commit' as action which is tricky
+ timeline = timeline.filter(instant -> !instant.getAction().equals(HoodieTimeline.COMMIT_ACTION));
+ }
+ if (skipClustering) {
+ timeline = timeline.filter(instant -> !ClusteringUtils.isClusteringInstant(instant, oriTimeline));
+ }
+ return timeline;
+ }
+
+ // -------------------------------------------------------------------------
+ // Inner Class
+ // -------------------------------------------------------------------------
+ /**
+ * Builder for {@link IncrementalQueryAnalyzer}.
+ */
+ public static class Builder {
+ /**
+ * Start completion time.
+ */
+ private String startTime;
+ /**
+ * End completion time.
+ */
+ private String endTime;
+ private InstantRange.RangeType rangeType;
+ private HoodieTableMetaClient metaClient;
+ private boolean skipCompaction = false;
+ private boolean skipClustering = false;
+ /**
+ * Maximum number of instants to read per run.
+ */
+ private int limit = -1;
+
+ public Builder() {
+ }
+
+ public Builder startTime(String startTime) {
+ this.startTime = startTime;
+ return this;
+ }
+
+ public Builder endTime(String endTime) {
+ this.endTime = endTime;
+ return this;
+ }
+
+ public Builder rangeType(InstantRange.RangeType rangeType) {
+ this.rangeType = rangeType;
+ return this;
+ }
+
+ public Builder metaClient(HoodieTableMetaClient metaClient) {
+ this.metaClient = metaClient;
+ return this;
+ }
+
+ public Builder skipCompaction(boolean skipCompaction) {
Review Comment:
From a user standpoint, all of this should be transparent right.
##########
hudi-common/src/main/java/org/apache/hudi/common/table/timeline/CompletionTimeQueryView.java:
##########
@@ -175,42 +190,109 @@ public Option<String> getCompletionTime(String startTime) {
*
* <p>By default, assumes there is at most 1 day time of duration for an instant to accelerate the queries.
*
- * @param startCompletionTime The start completion time.
- * @param endCompletionTime The end completion time.
+ * @param readTimeline The read timeline.
+ * @param startTime The start completion time.
+ * @param endTime The end completion time.
+ * @param rangeType The range type.
*
- * @return The instant time set.
+ * @return The sorted instant time list.
*/
- public Set<String> getStartTimeSet(String startCompletionTime, String endCompletionTime) {
+ public List<String> getStartTime(
+ HoodieTimeline readTimeline,
+ @Nullable String startTime,
+ @Nullable String endTime,
+ InstantRange.RangeType rangeType) {
// assumes any instant/transaction lasts at most 1 day to optimize the query efficiency.
- return getStartTimeSet(startCompletionTime, endCompletionTime, s -> HoodieInstantTimeGenerator.instantTimeMinusMillis(s, MILLI_SECONDS_IN_ONE_DAY));
+ return getStartTime(readTimeline, startTime, endTime, rangeType, s -> HoodieInstantTimeGenerator.instantTimeMinusMillis(s, MILLI_SECONDS_IN_ONE_DAY));
+ }
+
+ /**
+ * Queries the instant start time with given completion time range.
+ *
+ * @param startTime The start completion time.
+ * @param endTime The end completion time.
+ * @param earliestInstantTimeFunc The function to generate the earliest start time boundary
+ * with the minimum completion time.
+ *
+ * @return The sorted instant time list.
+ */
+ @VisibleForTesting
+ public List<String> getStartTime(
Review Comment:
rename: getStartTimes
##########
hudi-common/src/main/java/org/apache/hudi/common/table/timeline/CompletionTimeQueryView.java:
##########
@@ -175,42 +190,109 @@ public Option<String> getCompletionTime(String startTime) {
*
* <p>By default, assumes there is at most 1 day time of duration for an instant to accelerate the queries.
*
- * @param startCompletionTime The start completion time.
- * @param endCompletionTime The end completion time.
+ * @param readTimeline The read timeline.
+ * @param startTime The start completion time.
+ * @param endTime The end completion time.
+ * @param rangeType The range type.
*
- * @return The instant time set.
+ * @return The sorted instant time list.
*/
- public Set<String> getStartTimeSet(String startCompletionTime, String endCompletionTime) {
+ public List<String> getStartTime(
+ HoodieTimeline readTimeline,
+ @Nullable String startTime,
+ @Nullable String endTime,
+ InstantRange.RangeType rangeType) {
// assumes any instant/transaction lasts at most 1 day to optimize the query efficiency.
- return getStartTimeSet(startCompletionTime, endCompletionTime, s -> HoodieInstantTimeGenerator.instantTimeMinusMillis(s, MILLI_SECONDS_IN_ONE_DAY));
+ return getStartTime(readTimeline, startTime, endTime, rangeType, s -> HoodieInstantTimeGenerator.instantTimeMinusMillis(s, MILLI_SECONDS_IN_ONE_DAY));
+ }
+
+ /**
+ * Queries the instant start time with given completion time range.
+ *
+ * @param startTime The start completion time.
+ * @param endTime The end completion time.
+ * @param earliestInstantTimeFunc The function to generate the earliest start time boundary
+ * with the minimum completion time.
+ *
+ * @return The sorted instant time list.
+ */
+ @VisibleForTesting
+ public List<String> getStartTime(
+ @Nullable String startTime,
+ @Nullable String endTime,
+ Function<String, String> earliestInstantTimeFunc) {
+ return getStartTime(metaClient.getCommitsTimeline().filterCompletedInstants(), startTime, endTime, InstantRange.RangeType.CLOSE_CLOSE, earliestInstantTimeFunc);
}
/**
* Queries the instant start time with given completion time range.
*
- * @param startCompletionTime The start completion time.
- * @param endCompletionTime The end completion time.
- * @param earliestStartTimeFunc The function to generate the earliest start time boundary
- * with the minimum completion time {@code startCompletionTime}.
+ * @param readTimeline The read timeline.
+ * @param startTime The start completion time.
+ * @param endTime The end completion time.
+ * @param rangeType The range type.
+ * @param earliestInstantTimeFunc The function to generate the earliest start time boundary
+ * with the minimum completion time.
*
- * @return The instant time set.
+ * @return The sorted instant time list.
*/
- public Set<String> getStartTimeSet(String startCompletionTime, String endCompletionTime, Function<String, String> earliestStartTimeFunc) {
- String startInstant = earliestStartTimeFunc.apply(startCompletionTime);
+ public List<String> getStartTime(
+ HoodieTimeline readTimeline,
+ @Nullable String startTime,
+ @Nullable String endTime,
+ InstantRange.RangeType rangeType,
+ Function<String, String> earliestInstantTimeFunc) {
+ final boolean startFromEarliest = START_COMMIT_EARLIEST.equalsIgnoreCase(startTime);
+ String earliestInstantToLoad = null;
+ if (startTime != null && !startFromEarliest) {
+ earliestInstantToLoad = earliestInstantTimeFunc.apply(startTime);
+ } else if (endTime != null) {
+ earliestInstantToLoad = earliestInstantTimeFunc.apply(endTime);
+ }
+
+ // ensure the earliest instant boundary be loaded.
+ if (earliestInstantToLoad != null && HoodieTimeline.compareTimestamps(this.cursorInstant, GREATER_THAN, earliestInstantToLoad)) {
+ loadCompletionTimeIncrementally(earliestInstantToLoad);
+ }
+
+ if (startTime == null && endTime != null) {
Review Comment:
lets please use Option and not `null` to do these.
##########
hudi-common/src/main/java/org/apache/hudi/common/table/timeline/CompletionTimeQueryView.java:
##########
@@ -175,42 +190,109 @@ public Option<String> getCompletionTime(String startTime) {
*
* <p>By default, assumes there is at most 1 day time of duration for an instant to accelerate the queries.
*
- * @param startCompletionTime The start completion time.
- * @param endCompletionTime The end completion time.
+ * @param readTimeline The read timeline.
+ * @param startTime The start completion time.
+ * @param endTime The end completion time.
+ * @param rangeType The range type.
*
- * @return The instant time set.
+ * @return The sorted instant time list.
*/
- public Set<String> getStartTimeSet(String startCompletionTime, String endCompletionTime) {
+ public List<String> getStartTime(
+ HoodieTimeline readTimeline,
+ @Nullable String startTime,
+ @Nullable String endTime,
+ InstantRange.RangeType rangeType) {
// assumes any instant/transaction lasts at most 1 day to optimize the query efficiency.
- return getStartTimeSet(startCompletionTime, endCompletionTime, s -> HoodieInstantTimeGenerator.instantTimeMinusMillis(s, MILLI_SECONDS_IN_ONE_DAY));
+ return getStartTime(readTimeline, startTime, endTime, rangeType, s -> HoodieInstantTimeGenerator.instantTimeMinusMillis(s, MILLI_SECONDS_IN_ONE_DAY));
+ }
+
+ /**
+ * Queries the instant start time with given completion time range.
+ *
+ * @param startTime The start completion time.
+ * @param endTime The end completion time.
+ * @param earliestInstantTimeFunc The function to generate the earliest start time boundary
+ * with the minimum completion time.
+ *
+ * @return The sorted instant time list.
+ */
+ @VisibleForTesting
+ public List<String> getStartTime(
+ @Nullable String startTime,
+ @Nullable String endTime,
+ Function<String, String> earliestInstantTimeFunc) {
+ return getStartTime(metaClient.getCommitsTimeline().filterCompletedInstants(), startTime, endTime, InstantRange.RangeType.CLOSE_CLOSE, earliestInstantTimeFunc);
}
/**
* Queries the instant start time with given completion time range.
*
- * @param startCompletionTime The start completion time.
- * @param endCompletionTime The end completion time.
- * @param earliestStartTimeFunc The function to generate the earliest start time boundary
- * with the minimum completion time {@code startCompletionTime}.
+ * @param readTimeline The read timeline.
+ * @param startTime The start completion time.
+ * @param endTime The end completion time.
+ * @param rangeType The range type.
+ * @param earliestInstantTimeFunc The function to generate the earliest start time boundary
+ * with the minimum completion time.
*
- * @return The instant time set.
+ * @return The sorted instant time list.
*/
- public Set<String> getStartTimeSet(String startCompletionTime, String endCompletionTime, Function<String, String> earliestStartTimeFunc) {
- String startInstant = earliestStartTimeFunc.apply(startCompletionTime);
+ public List<String> getStartTime(
+ HoodieTimeline readTimeline,
+ @Nullable String startTime,
+ @Nullable String endTime,
+ InstantRange.RangeType rangeType,
+ Function<String, String> earliestInstantTimeFunc) {
+ final boolean startFromEarliest = START_COMMIT_EARLIEST.equalsIgnoreCase(startTime);
+ String earliestInstantToLoad = null;
+ if (startTime != null && !startFromEarliest) {
+ earliestInstantToLoad = earliestInstantTimeFunc.apply(startTime);
+ } else if (endTime != null) {
+ earliestInstantToLoad = earliestInstantTimeFunc.apply(endTime);
+ }
+
+ // ensure the earliest instant boundary be loaded.
+ if (earliestInstantToLoad != null && HoodieTimeline.compareTimestamps(this.cursorInstant, GREATER_THAN, earliestInstantToLoad)) {
+ loadCompletionTimeIncrementally(earliestInstantToLoad);
+ }
+
+ if (startTime == null && endTime != null) {
+ // returns the last instant that finished at or before the given completion time 'endTime'.
+ String maxInstantTime = readTimeline.getInstantsAsStream()
+ .filter(instant -> instant.isCompleted() && HoodieTimeline.compareTimestamps(instant.getCompletionTime(), LESSER_THAN_OR_EQUALS, endTime))
+ .max(Comparator.comparing(HoodieInstant::getCompletionTime)).map(HoodieInstant::getTimestamp).orElse(null);
+ if (maxInstantTime != null) {
+ return Collections.singletonList(maxInstantTime);
+ }
+ // fallback to archived timeline
+ return this.startToCompletionInstantTimeMap.entrySet().stream()
Review Comment:
why don't we return the max from this code path?
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,425 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
Review Comment:
I feel it'd be good to write code against a single global timeline abstraction instead of this archived/active divide.
##########
hudi-common/src/main/java/org/apache/hudi/common/table/log/InstantRange.java:
##########
@@ -60,8 +63,8 @@ public String getEndInstant() {
@Override
public String toString() {
return "InstantRange{"
- + "startInstant='" + startInstant == null ? "null" : startInstant + '\''
- + ", endInstant='" + endInstant == null ? "null" : endInstant + '\''
+ + "startInstant='" + (startInstant == null ? "null" : startInstant) + '\''
+ + ", endInstant='" + (endInstant == null ? "null" : endInstant) + '\''
+ ", rangeType='" + this.getClass().getSimpleName() + '\''
Review Comment:
Could we use the range type enum name here? Cleaner than printing class name? '
##########
hudi-common/src/main/java/org/apache/hudi/common/table/timeline/CompletionTimeQueryView.java:
##########
@@ -175,42 +190,109 @@ public Option<String> getCompletionTime(String startTime) {
*
* <p>By default, assumes there is at most 1 day time of duration for an instant to accelerate the queries.
*
- * @param startCompletionTime The start completion time.
- * @param endCompletionTime The end completion time.
+ * @param readTimeline The read timeline.
Review Comment:
whats special about the timeline being passed in?
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,425 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed before or on 'endTime';
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed in the given time range;
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * </table>
+ *
+ * <p> A range type is required for analyzing the query so that the query range boundary inclusiveness have clear semantics.
+ *
+ * <p>IMPORTANT: the reader may optionally choose to fall back to reading the latest snapshot if there are files missing from decoding the commit metadata.
+ */
+public class IncrementalQueryAnalyzer {
+ public static final String START_COMMIT_EARLIEST = "earliest";
+
+ private final HoodieTableMetaClient metaClient;
+ private final String startTime;
+ private final String endTime;
+ private final InstantRange.RangeType rangeType;
+ private final boolean skipCompaction;
+ private final boolean skipClustering;
+ private final int limit;
+
+ private IncrementalQueryAnalyzer(
+ HoodieTableMetaClient metaClient,
+ String startTime,
+ String endTime,
+ InstantRange.RangeType rangeType,
+ boolean skipCompaction,
+ boolean skipClustering,
+ int limit) {
+ this.metaClient = metaClient;
+ this.startTime = startTime;
+ this.endTime = endTime;
+ this.rangeType = rangeType;
+ this.skipCompaction = skipCompaction;
+ this.skipClustering = skipClustering;
+ this.limit = limit;
+ }
+
+ /**
+ * Returns a builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Analyzes the incremental query context with given completion time range.
+ *
+ * @return An incremental query context including the instant time range info.
+ */
+ public QueryContext analyze() {
Review Comment:
yet to review this.
##########
hudi-common/src/main/java/org/apache/hudi/common/table/timeline/CompletionTimeQueryView.java:
##########
@@ -175,42 +190,109 @@ public Option<String> getCompletionTime(String startTime) {
*
* <p>By default, assumes there is at most 1 day time of duration for an instant to accelerate the queries.
*
- * @param startCompletionTime The start completion time.
- * @param endCompletionTime The end completion time.
+ * @param readTimeline The read timeline.
+ * @param startTime The start completion time.
+ * @param endTime The end completion time.
+ * @param rangeType The range type.
*
- * @return The instant time set.
+ * @return The sorted instant time list.
*/
- public Set<String> getStartTimeSet(String startCompletionTime, String endCompletionTime) {
+ public List<String> getStartTime(
+ HoodieTimeline readTimeline,
+ @Nullable String startTime,
+ @Nullable String endTime,
+ InstantRange.RangeType rangeType) {
// assumes any instant/transaction lasts at most 1 day to optimize the query efficiency.
- return getStartTimeSet(startCompletionTime, endCompletionTime, s -> HoodieInstantTimeGenerator.instantTimeMinusMillis(s, MILLI_SECONDS_IN_ONE_DAY));
+ return getStartTime(readTimeline, startTime, endTime, rangeType, s -> HoodieInstantTimeGenerator.instantTimeMinusMillis(s, MILLI_SECONDS_IN_ONE_DAY));
+ }
+
+ /**
+ * Queries the instant start time with given completion time range.
+ *
+ * @param startTime The start completion time.
+ * @param endTime The end completion time.
+ * @param earliestInstantTimeFunc The function to generate the earliest start time boundary
+ * with the minimum completion time.
+ *
+ * @return The sorted instant time list.
+ */
+ @VisibleForTesting
+ public List<String> getStartTime(
+ @Nullable String startTime,
+ @Nullable String endTime,
+ Function<String, String> earliestInstantTimeFunc) {
+ return getStartTime(metaClient.getCommitsTimeline().filterCompletedInstants(), startTime, endTime, InstantRange.RangeType.CLOSE_CLOSE, earliestInstantTimeFunc);
}
/**
* Queries the instant start time with given completion time range.
*
- * @param startCompletionTime The start completion time.
- * @param endCompletionTime The end completion time.
- * @param earliestStartTimeFunc The function to generate the earliest start time boundary
- * with the minimum completion time {@code startCompletionTime}.
+ * @param readTimeline The read timeline.
+ * @param startTime The start completion time.
+ * @param endTime The end completion time.
+ * @param rangeType The range type.
+ * @param earliestInstantTimeFunc The function to generate the earliest start time boundary
+ * with the minimum completion time.
*
- * @return The instant time set.
+ * @return The sorted instant time list.
*/
- public Set<String> getStartTimeSet(String startCompletionTime, String endCompletionTime, Function<String, String> earliestStartTimeFunc) {
- String startInstant = earliestStartTimeFunc.apply(startCompletionTime);
+ public List<String> getStartTime(
+ HoodieTimeline readTimeline,
+ @Nullable String startTime,
+ @Nullable String endTime,
+ InstantRange.RangeType rangeType,
+ Function<String, String> earliestInstantTimeFunc) {
+ final boolean startFromEarliest = START_COMMIT_EARLIEST.equalsIgnoreCase(startTime);
+ String earliestInstantToLoad = null;
+ if (startTime != null && !startFromEarliest) {
+ earliestInstantToLoad = earliestInstantTimeFunc.apply(startTime);
+ } else if (endTime != null) {
+ earliestInstantToLoad = earliestInstantTimeFunc.apply(endTime);
+ }
+
+ // ensure the earliest instant boundary be loaded.
+ if (earliestInstantToLoad != null && HoodieTimeline.compareTimestamps(this.cursorInstant, GREATER_THAN, earliestInstantToLoad)) {
+ loadCompletionTimeIncrementally(earliestInstantToLoad);
+ }
+
+ if (startTime == null && endTime != null) {
+ // returns the last instant that finished at or before the given completion time 'endTime'.
+ String maxInstantTime = readTimeline.getInstantsAsStream()
+ .filter(instant -> instant.isCompleted() && HoodieTimeline.compareTimestamps(instant.getCompletionTime(), LESSER_THAN_OR_EQUALS, endTime))
+ .max(Comparator.comparing(HoodieInstant::getCompletionTime)).map(HoodieInstant::getTimestamp).orElse(null);
+ if (maxInstantTime != null) {
+ return Collections.singletonList(maxInstantTime);
+ }
+ // fallback to archived timeline
+ return this.startToCompletionInstantTimeMap.entrySet().stream()
+ .filter(entry -> HoodieTimeline.compareTimestamps(entry.getValue(), LESSER_THAN_OR_EQUALS, endTime))
+ .map(Map.Entry::getKey).collect(Collectors.toList());
+ }
+
+ if (startFromEarliest) {
+ // expedience for snapshot read: ['earliest', _) to avoid loading unnecessary instants.
+ startTime = null;
Review Comment:
should this happen before.
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,425 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed before or on 'endTime';
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed in the given time range;
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * </table>
+ *
+ * <p> A range type is required for analyzing the query so that the query range boundary inclusiveness have clear semantics.
+ *
+ * <p>IMPORTANT: the reader may optionally choose to fall back to reading the latest snapshot if there are files missing from decoding the commit metadata.
+ */
+public class IncrementalQueryAnalyzer {
+ public static final String START_COMMIT_EARLIEST = "earliest";
+
+ private final HoodieTableMetaClient metaClient;
+ private final String startTime;
+ private final String endTime;
+ private final InstantRange.RangeType rangeType;
+ private final boolean skipCompaction;
+ private final boolean skipClustering;
+ private final int limit;
+
+ private IncrementalQueryAnalyzer(
+ HoodieTableMetaClient metaClient,
+ String startTime,
+ String endTime,
+ InstantRange.RangeType rangeType,
+ boolean skipCompaction,
+ boolean skipClustering,
+ int limit) {
+ this.metaClient = metaClient;
+ this.startTime = startTime;
+ this.endTime = endTime;
+ this.rangeType = rangeType;
+ this.skipCompaction = skipCompaction;
+ this.skipClustering = skipClustering;
+ this.limit = limit;
+ }
+
+ /**
+ * Returns a builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Analyzes the incremental query context with given completion time range.
+ *
+ * @return An incremental query context including the instant time range info.
+ */
+ public QueryContext analyze() {
+ try (CompletionTimeQueryView completionTimeQueryView = new CompletionTimeQueryView(this.metaClient)) {
+ if (completionTimeQueryView.isEmptyTable()) {
+ // no dataset committed in the table
+ return QueryContext.EMPTY;
+ }
+ HoodieTimeline readTimeline = getReadTimeline(this.metaClient);
+ List<String> instantTimeList = completionTimeQueryView.getStartTime(readTimeline, startTime, endTime, rangeType);
+ if (instantTimeList.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ // get hoodie instants
+ Pair<List<String>, List<String>> splitInstantTime = splitInstantByActiveness(instantTimeList, completionTimeQueryView);
+ Set<String> instantTimeSet = new HashSet<>(instantTimeList);
+ List<String> archivedInstantTime = splitInstantTime.getKey();
+ List<String> activeInstantTime = splitInstantTime.getValue();
+ List<HoodieInstant> archivedInstants = new ArrayList<>();
+ List<HoodieInstant> activeInstants = new ArrayList<>();
+ HoodieTimeline archivedReadTimeline = null;
+ if (!activeInstantTime.isEmpty()) {
+ activeInstants = readTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ if (limit > 0 && limit < activeInstants.size()) {
+ // streaming read speed limit, limits the maximum number of commits allowed to read for each run
+ activeInstants = activeInstants.subList(0, limit);
+ }
+ }
+ if (!archivedInstantTime.isEmpty()) {
+ archivedReadTimeline = getArchivedReadTimeline(metaClient, archivedInstantTime.get(0));
+ archivedInstants = archivedReadTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ }
+ List<String> instants = Stream.concat(archivedInstants.stream(), activeInstants.stream()).map(HoodieInstant::getTimestamp).collect(Collectors.toList());
+ if (instants.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ if (startTime == null && endTime != null) {
+ instants = Collections.singletonList(instants.get(instants.size() - 1));
+ }
+ String lastInstant = instants.get(instants.size() - 1);
+ // keep the same semantics with streaming read, default start from the latest commit
+ String startInstant = START_COMMIT_EARLIEST.equalsIgnoreCase(startTime) ? null : startTime == null ? lastInstant : instants.get(0);
+ String endInstant = endTime == null ? null : lastInstant;
+ return QueryContext.create(startInstant, endInstant, instants, archivedInstants, activeInstants, readTimeline, archivedReadTimeline);
+ }
+ }
+
+ /**
+ * Splits the given instant time list into a pair of archived instant list and active instant list.
+ */
+ private static Pair<List<String>, List<String>> splitInstantByActiveness(List<String> instantTimeList, CompletionTimeQueryView completionTimeQueryView) {
+ int firstActiveIdx = IntStream.range(0, instantTimeList.size()).filter(i -> !completionTimeQueryView.isArchived(instantTimeList.get(i))).findFirst().orElse(-1);
+ if (firstActiveIdx == -1) {
+ return Pair.of(instantTimeList, Collections.emptyList());
+ } else if (firstActiveIdx == 0) {
+ return Pair.of(Collections.emptyList(), instantTimeList);
+ } else {
+ return Pair.of(instantTimeList.subList(0, firstActiveIdx), instantTimeList.subList(firstActiveIdx, instantTimeList.size()));
+ }
+ }
+
+ private HoodieTimeline getReadTimeline(HoodieTableMetaClient metaClient) {
+ HoodieTimeline timeline = metaClient.getCommitsAndCompactionTimeline().filterCompletedAndCompactionInstants();
+ return filterInstantsAsPerUserConfigs(metaClient, timeline, this.skipCompaction, this.skipClustering);
+ }
+
+ private HoodieTimeline getArchivedReadTimeline(HoodieTableMetaClient metaClient, String startInstant) {
+ HoodieArchivedTimeline archivedTimeline = metaClient.getArchivedTimeline(startInstant, false);
+ HoodieTimeline archivedCompleteTimeline = archivedTimeline.getCommitsTimeline().filterCompletedInstants();
+ return filterInstantsAsPerUserConfigs(metaClient, archivedCompleteTimeline, this.skipCompaction, this.skipClustering);
+ }
+
+ /**
+ * Filters out the unnecessary instants as per user specified configs.
+ *
+ * @param timeline The timeline.
+ *
+ * @return the filtered timeline
+ */
+ @VisibleForTesting
+ public static HoodieTimeline filterInstantsAsPerUserConfigs(HoodieTableMetaClient metaClient, HoodieTimeline timeline, boolean skipCompaction, boolean skipClustering) {
+ final HoodieTimeline oriTimeline = timeline;
+ if (metaClient.getTableType() == HoodieTableType.MERGE_ON_READ & skipCompaction) {
+ // the compaction commit uses 'commit' as action which is tricky
+ timeline = timeline.filter(instant -> !instant.getAction().equals(HoodieTimeline.COMMIT_ACTION));
+ }
+ if (skipClustering) {
+ timeline = timeline.filter(instant -> !ClusteringUtils.isClusteringInstant(instant, oriTimeline));
+ }
+ return timeline;
+ }
+
+ // -------------------------------------------------------------------------
+ // Inner Class
+ // -------------------------------------------------------------------------
+ /**
+ * Builder for {@link IncrementalQueryAnalyzer}.
+ */
+ public static class Builder {
+ /**
+ * Start completion time.
+ */
+ private String startTime;
+ /**
+ * End completion time.
+ */
+ private String endTime;
+ private InstantRange.RangeType rangeType;
+ private HoodieTableMetaClient metaClient;
+ private boolean skipCompaction = false;
+ private boolean skipClustering = false;
+ /**
+ * Maximum number of instants to read per run.
+ */
+ private int limit = -1;
+
+ public Builder() {
+ }
+
+ public Builder startTime(String startTime) {
+ this.startTime = startTime;
+ return this;
+ }
+
+ public Builder endTime(String endTime) {
+ this.endTime = endTime;
+ return this;
+ }
+
+ public Builder rangeType(InstantRange.RangeType rangeType) {
+ this.rangeType = rangeType;
+ return this;
+ }
+
+ public Builder metaClient(HoodieTableMetaClient metaClient) {
+ this.metaClient = metaClient;
+ return this;
+ }
+
+ public Builder skipCompaction(boolean skipCompaction) {
Review Comment:
why are these needed? is it for table services and other internal processes?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "hudi-bot (via GitHub)" <gi...@apache.org>.
hudi-bot commented on PR #10255:
URL: https://github.com/apache/hudi/pull/10255#issuecomment-1880057543
<!--
Meta data
{
"version" : 1,
"metaDataEntries" : [ {
"hash" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21338",
"triggerID" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"triggerType" : "PUSH"
}, {
"hash" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21364",
"triggerID" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"triggerType" : "PUSH"
}, {
"hash" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21395",
"triggerID" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"triggerType" : "PUSH"
}, {
"hash" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21488",
"triggerID" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"triggerType" : "PUSH"
}, {
"hash" : "b39e93881c130fd8832e5c260740f8f59bb8033e",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21577",
"triggerID" : "b39e93881c130fd8832e5c260740f8f59bb8033e",
"triggerType" : "PUSH"
}, {
"hash" : "d2c7c5cd379c4c8ba4297fd5fa1e989f73e58b5d",
"status" : "CANCELED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21854",
"triggerID" : "d2c7c5cd379c4c8ba4297fd5fa1e989f73e58b5d",
"triggerType" : "PUSH"
} ]
}-->
## CI report:
* d2c7c5cd379c4c8ba4297fd5fa1e989f73e58b5d Azure: [CANCELED](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21854)
<details>
<summary>Bot commands</summary>
@hudi-bot supports the following commands:
- `@hudi-bot run azure` re-run the last Azure build
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "codope (via GitHub)" <gi...@apache.org>.
codope commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1439195459
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,428 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
Review Comment:
What is `earliest` here? Is it the earliest instant in the active, or is it including archived? Also, from the doc it is not clear why the instant filtering predicate is empty?
##########
hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/source/TestIncrementalInputSplits.java:
##########
@@ -72,67 +76,77 @@
public class TestIncrementalInputSplits extends HoodieCommonTestHarness {
@BeforeEach
- void init() throws IOException {
+ void init() {
initPath();
- initMetaClient();
}
@Test
- void testFilterInstantsWithRange() {
- HoodieActiveTimeline timeline = new HoodieActiveTimeline(metaClient, true);
+ void testFilterInstantsWithRange() throws IOException {
Configuration conf = TestConfigurations.getDefaultConf(basePath);
conf.set(FlinkOptions.READ_STREAMING_SKIP_CLUSTERING, true);
- IncrementalInputSplits iis = IncrementalInputSplits.builder()
- .conf(conf)
- .path(new Path(basePath))
- .rowType(TestConfigurations.ROW_TYPE)
- .skipClustering(conf.getBoolean(FlinkOptions.READ_STREAMING_SKIP_CLUSTERING))
- .build();
+ conf.set(FlinkOptions.TABLE_TYPE, FlinkOptions.TABLE_TYPE_MERGE_ON_READ);
+ metaClient = HoodieTestUtils.init(basePath, HoodieTableType.MERGE_ON_READ);
+ HoodieActiveTimeline timeline = metaClient.getActiveTimeline();
HoodieInstant commit1 = new HoodieInstant(HoodieInstant.State.COMPLETED, HoodieTimeline.DELTA_COMMIT_ACTION, "1");
HoodieInstant commit2 = new HoodieInstant(HoodieInstant.State.COMPLETED, HoodieTimeline.DELTA_COMMIT_ACTION, "2");
HoodieInstant commit3 = new HoodieInstant(HoodieInstant.State.COMPLETED, HoodieTimeline.DELTA_COMMIT_ACTION, "3");
timeline.createCompleteInstant(commit1);
timeline.createCompleteInstant(commit2);
timeline.createCompleteInstant(commit3);
- timeline = timeline.reload();
+ timeline = metaClient.reloadActiveTimeline();
+
+ Map<String, String> completionTimeMap = timeline.filterCompletedInstants().getInstantsAsStream()
+ .collect(Collectors.toMap(HoodieInstant::getTimestamp, HoodieInstant::getCompletionTime));
+ IncrementalQueryAnalyzer analyzer1 = IncrementalQueryAnalyzer.builder()
+ .metaClient(metaClient)
+ .rangeType(InstantRange.RangeType.OPEN_CLOSE)
+ .startTime(completionTimeMap.get("1"))
+ .skipClustering(true)
+ .build();
// previous read iteration read till instant time "1", next read iteration should return ["2", "3"]
- List<HoodieInstant> instantRange2 = iis.filterInstantsWithRange(timeline, "1");
- assertEquals(2, instantRange2.size());
- assertIterableEquals(Arrays.asList(commit2, commit3), instantRange2);
+ List<HoodieInstant> activeInstants1 = analyzer1.analyze().getActiveInstants();
+ assertEquals(2, activeInstants1.size());
+ assertIterableEquals(Arrays.asList(commit2, commit3), activeInstants1);
// simulate first iteration cycle with read from the LATEST commit
- List<HoodieInstant> instantRange1 = iis.filterInstantsWithRange(timeline, null);
- assertEquals(1, instantRange1.size());
- assertIterableEquals(Collections.singletonList(commit3), instantRange1);
+ IncrementalQueryAnalyzer analyzer2 = IncrementalQueryAnalyzer.builder()
+ .metaClient(metaClient)
+ .rangeType(InstantRange.RangeType.CLOSE_CLOSE)
+ .skipClustering(true)
+ .build();
+ List<HoodieInstant> activeInstants2 = analyzer2.analyze().getActiveInstants();
+ assertEquals(1, activeInstants2.size());
+ assertIterableEquals(Collections.singletonList(commit3), activeInstants2);
// specifying a start and end commit
- conf.set(FlinkOptions.READ_START_COMMIT, "1");
- conf.set(FlinkOptions.READ_END_COMMIT, "3");
- List<HoodieInstant> instantRange3 = iis.filterInstantsWithRange(timeline, null);
- assertEquals(3, instantRange3.size());
- assertIterableEquals(Arrays.asList(commit1, commit2, commit3), instantRange3);
+ IncrementalQueryAnalyzer analyzer3 = IncrementalQueryAnalyzer.builder()
Review Comment:
let's also test for null/emoty start and end time.
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,428 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed before or on 'endTime';
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed in the given time range;
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * </table>
+ *
+ * <p> A range type is required for analyzing the query so that the query range boundary inclusiveness have clear semantics.
+ *
+ * <p>IMPORTANT: the reader may optionally choose to fall back to reading the latest snapshot if there are files missing from decoding the commit metadata.
+ */
+public class IncrementalQueryAnalyzer {
+ public static final String START_COMMIT_EARLIEST = "earliest";
+
+ private final HoodieTableMetaClient metaClient;
+ private final Option<String> startTime;
+ private final Option<String> endTime;
+ private final InstantRange.RangeType rangeType;
+ private final boolean skipCompaction;
+ private final boolean skipClustering;
+ private final int limit;
+
+ private IncrementalQueryAnalyzer(
+ HoodieTableMetaClient metaClient,
+ String startTime,
+ String endTime,
+ InstantRange.RangeType rangeType,
+ boolean skipCompaction,
+ boolean skipClustering,
+ int limit) {
+ this.metaClient = metaClient;
+ this.startTime = Option.ofNullable(startTime);
+ this.endTime = Option.ofNullable(endTime);
+ this.rangeType = rangeType;
+ this.skipCompaction = skipCompaction;
+ this.skipClustering = skipClustering;
+ this.limit = limit;
+ }
+
+ /**
+ * Returns a builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Analyzes the incremental query context with given completion time range.
+ *
+ * @return An incremental query context including the instant time range info.
+ */
+ public QueryContext analyze() {
+ try (CompletionTimeQueryView completionTimeQueryView = new CompletionTimeQueryView(this.metaClient)) {
+ if (completionTimeQueryView.isEmptyTable()) {
+ // no dataset committed in the table
+ return QueryContext.EMPTY;
+ }
+ HoodieTimeline readTimeline = getReadTimeline(this.metaClient);
+ List<String> instantTimeList = completionTimeQueryView.getStartTimes(readTimeline, startTime, endTime, rangeType);
+ if (instantTimeList.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ // get hoodie instants
+ Pair<List<String>, List<String>> splitInstantTime = splitInstantByActiveness(instantTimeList, completionTimeQueryView);
+ Set<String> instantTimeSet = new HashSet<>(instantTimeList);
+ List<String> archivedInstantTime = splitInstantTime.getKey();
+ List<String> activeInstantTime = splitInstantTime.getValue();
+ List<HoodieInstant> archivedInstants = new ArrayList<>();
+ List<HoodieInstant> activeInstants = new ArrayList<>();
+ HoodieTimeline archivedReadTimeline = null;
+ if (!activeInstantTime.isEmpty()) {
+ activeInstants = readTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ if (limit > 0 && limit < activeInstants.size()) {
+ // streaming read speed limit, limits the maximum number of commits allowed to read for each run
+ activeInstants = activeInstants.subList(0, limit);
+ }
+ }
+ if (!archivedInstantTime.isEmpty()) {
+ archivedReadTimeline = getArchivedReadTimeline(metaClient, archivedInstantTime.get(0));
+ archivedInstants = archivedReadTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ }
+ List<String> instants = Stream.concat(archivedInstants.stream(), activeInstants.stream()).map(HoodieInstant::getTimestamp).collect(Collectors.toList());
+ if (instants.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ if (startTime.isEmpty() && endTime.isPresent()) {
+ instants = Collections.singletonList(instants.get(instants.size() - 1));
+ }
+ String lastInstant = instants.get(instants.size() - 1);
+ // keep the same semantics with streaming read, default start from the latest commit
+ String startInstant = START_COMMIT_EARLIEST.equalsIgnoreCase(startTime.orElse(null)) ? null : startTime.isEmpty() ? lastInstant : instants.get(0);
+ String endInstant = endTime.isEmpty() ? null : lastInstant;
+ return QueryContext.create(startInstant, endInstant, instants, archivedInstants, activeInstants, readTimeline, archivedReadTimeline);
+ }
+ }
+
+ /**
+ * Splits the given instant time list into a pair of archived instant list and active instant list.
+ */
+ private static Pair<List<String>, List<String>> splitInstantByActiveness(List<String> instantTimeList, CompletionTimeQueryView completionTimeQueryView) {
+ int firstActiveIdx = IntStream.range(0, instantTimeList.size()).filter(i -> !completionTimeQueryView.isArchived(instantTimeList.get(i))).findFirst().orElse(-1);
+ if (firstActiveIdx == -1) {
+ return Pair.of(instantTimeList, Collections.emptyList());
+ } else if (firstActiveIdx == 0) {
+ return Pair.of(Collections.emptyList(), instantTimeList);
+ } else {
+ return Pair.of(instantTimeList.subList(0, firstActiveIdx), instantTimeList.subList(firstActiveIdx, instantTimeList.size()));
+ }
+ }
+
+ private HoodieTimeline getReadTimeline(HoodieTableMetaClient metaClient) {
+ HoodieTimeline timeline = metaClient.getCommitsAndCompactionTimeline().filterCompletedAndCompactionInstants();
+ return filterInstantsAsPerUserConfigs(metaClient, timeline, this.skipCompaction, this.skipClustering);
+ }
+
+ private HoodieTimeline getArchivedReadTimeline(HoodieTableMetaClient metaClient, String startInstant) {
+ HoodieArchivedTimeline archivedTimeline = metaClient.getArchivedTimeline(startInstant, false);
+ HoodieTimeline archivedCompleteTimeline = archivedTimeline.getCommitsTimeline().filterCompletedInstants();
+ return filterInstantsAsPerUserConfigs(metaClient, archivedCompleteTimeline, this.skipCompaction, this.skipClustering);
+ }
+
+ /**
+ * Filters out the unnecessary instants as per user specified configs.
+ *
+ * @param timeline The timeline.
+ *
+ * @return the filtered timeline
+ */
+ @VisibleForTesting
+ public static HoodieTimeline filterInstantsAsPerUserConfigs(HoodieTableMetaClient metaClient, HoodieTimeline timeline, boolean skipCompaction, boolean skipClustering) {
+ final HoodieTimeline oriTimeline = timeline;
+ if (metaClient.getTableType() == HoodieTableType.MERGE_ON_READ & skipCompaction) {
+ // the compaction commit uses 'commit' as action which is tricky
+ timeline = timeline.filter(instant -> !instant.getAction().equals(HoodieTimeline.COMMIT_ACTION));
+ }
+ if (skipClustering) {
+ timeline = timeline.filter(instant -> !ClusteringUtils.isClusteringInstant(instant, oriTimeline));
+ }
+ return timeline;
+ }
+
+ // -------------------------------------------------------------------------
+ // Inner Class
+ // -------------------------------------------------------------------------
+ /**
+ * Builder for {@link IncrementalQueryAnalyzer}.
+ */
+ public static class Builder {
+ /**
+ * Start completion time.
+ */
+ private String startTime;
+ /**
+ * End completion time.
+ */
+ private String endTime;
+ private InstantRange.RangeType rangeType;
+ private HoodieTableMetaClient metaClient;
+ private boolean skipCompaction = false;
+ private boolean skipClustering = false;
+ /**
+ * Maximum number of instants to read per run.
+ */
+ private int limit = -1;
+
+ public Builder() {
+ }
+
+ public Builder startTime(String startTime) {
+ this.startTime = startTime;
+ return this;
+ }
+
+ public Builder endTime(String endTime) {
+ this.endTime = endTime;
+ return this;
+ }
+
+ public Builder rangeType(InstantRange.RangeType rangeType) {
+ this.rangeType = rangeType;
+ return this;
+ }
+
+ public Builder metaClient(HoodieTableMetaClient metaClient) {
+ this.metaClient = metaClient;
+ return this;
+ }
+
+ public Builder skipCompaction(boolean skipCompaction) {
+ this.skipCompaction = skipCompaction;
+ return this;
+ }
+
+ public Builder skipClustering(boolean skipClustering) {
+ this.skipClustering = skipClustering;
+ return this;
+ }
+
+ public Builder limit(int limit) {
+ this.limit = limit;
+ return this;
+ }
+
+ public IncrementalQueryAnalyzer build() {
+ return new IncrementalQueryAnalyzer(Objects.requireNonNull(this.metaClient), this.startTime, this.endTime,
+ Objects.requireNonNull(this.rangeType), this.skipCompaction, this.skipClustering, this.limit);
+ }
+ }
+
+ /**
+ * Represents the analyzed query context.
+ */
+ public static class QueryContext {
+ public static final QueryContext EMPTY = new QueryContext(null, null, Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), null, null);
+
+ /**
+ * An empty option indicates consumption from the earliest instant.
+ */
+ private final Option<String> startInstant;
+ /**
+ * An empty option indicates consumption to the latest instant.
+ */
+ private final Option<String> endInstant;
+ private final List<HoodieInstant> archivedInstants;
+ private final List<HoodieInstant> activeInstants;
+ /**
+ * The active timeline to read filtered by given configurations.
+ */
+ private final HoodieTimeline readTimeline;
+ /**
+ * The archived timeline to read filtered by given configurations.
+ */
+ private final HoodieTimeline archivedReadTimeline;
Review Comment:
`activeTimelineFiltered` and `archivedTimelineFiltered` sounds more intuitive.
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,425 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
Review Comment:
+1. Please file a followup and link it here.
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,428 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed before or on 'endTime';
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed in the given time range;
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * </table>
+ *
+ * <p> A range type is required for analyzing the query so that the query range boundary inclusiveness have clear semantics.
+ *
+ * <p>IMPORTANT: the reader may optionally choose to fall back to reading the latest snapshot if there are files missing from decoding the commit metadata.
+ */
+public class IncrementalQueryAnalyzer {
+ public static final String START_COMMIT_EARLIEST = "earliest";
+
+ private final HoodieTableMetaClient metaClient;
+ private final Option<String> startTime;
+ private final Option<String> endTime;
+ private final InstantRange.RangeType rangeType;
+ private final boolean skipCompaction;
+ private final boolean skipClustering;
+ private final int limit;
+
+ private IncrementalQueryAnalyzer(
+ HoodieTableMetaClient metaClient,
+ String startTime,
+ String endTime,
+ InstantRange.RangeType rangeType,
+ boolean skipCompaction,
+ boolean skipClustering,
+ int limit) {
+ this.metaClient = metaClient;
+ this.startTime = Option.ofNullable(startTime);
+ this.endTime = Option.ofNullable(endTime);
+ this.rangeType = rangeType;
+ this.skipCompaction = skipCompaction;
+ this.skipClustering = skipClustering;
+ this.limit = limit;
+ }
+
+ /**
+ * Returns a builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Analyzes the incremental query context with given completion time range.
+ *
+ * @return An incremental query context including the instant time range info.
+ */
+ public QueryContext analyze() {
+ try (CompletionTimeQueryView completionTimeQueryView = new CompletionTimeQueryView(this.metaClient)) {
+ if (completionTimeQueryView.isEmptyTable()) {
+ // no dataset committed in the table
+ return QueryContext.EMPTY;
+ }
+ HoodieTimeline readTimeline = getReadTimeline(this.metaClient);
+ List<String> instantTimeList = completionTimeQueryView.getStartTimes(readTimeline, startTime, endTime, rangeType);
+ if (instantTimeList.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ // get hoodie instants
+ Pair<List<String>, List<String>> splitInstantTime = splitInstantByActiveness(instantTimeList, completionTimeQueryView);
+ Set<String> instantTimeSet = new HashSet<>(instantTimeList);
+ List<String> archivedInstantTime = splitInstantTime.getKey();
+ List<String> activeInstantTime = splitInstantTime.getValue();
+ List<HoodieInstant> archivedInstants = new ArrayList<>();
+ List<HoodieInstant> activeInstants = new ArrayList<>();
+ HoodieTimeline archivedReadTimeline = null;
+ if (!activeInstantTime.isEmpty()) {
+ activeInstants = readTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ if (limit > 0 && limit < activeInstants.size()) {
+ // streaming read speed limit, limits the maximum number of commits allowed to read for each run
+ activeInstants = activeInstants.subList(0, limit);
+ }
+ }
+ if (!archivedInstantTime.isEmpty()) {
+ archivedReadTimeline = getArchivedReadTimeline(metaClient, archivedInstantTime.get(0));
+ archivedInstants = archivedReadTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ }
+ List<String> instants = Stream.concat(archivedInstants.stream(), activeInstants.stream()).map(HoodieInstant::getTimestamp).collect(Collectors.toList());
+ if (instants.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ if (startTime.isEmpty() && endTime.isPresent()) {
+ instants = Collections.singletonList(instants.get(instants.size() - 1));
+ }
+ String lastInstant = instants.get(instants.size() - 1);
+ // keep the same semantics with streaming read, default start from the latest commit
+ String startInstant = START_COMMIT_EARLIEST.equalsIgnoreCase(startTime.orElse(null)) ? null : startTime.isEmpty() ? lastInstant : instants.get(0);
+ String endInstant = endTime.isEmpty() ? null : lastInstant;
+ return QueryContext.create(startInstant, endInstant, instants, archivedInstants, activeInstants, readTimeline, archivedReadTimeline);
+ }
+ }
+
+ /**
+ * Splits the given instant time list into a pair of archived instant list and active instant list.
+ */
+ private static Pair<List<String>, List<String>> splitInstantByActiveness(List<String> instantTimeList, CompletionTimeQueryView completionTimeQueryView) {
Review Comment:
`Pair` implements `Map`. I think for this use case, a simple POJO would suffice. Wdyt?
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,425 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
Review Comment:
When can it happen that both startTime and endTime are null/empty?
Also, since this is a range, it looks slightly confusing as a user. I mean if startTime is null/empty then should we not treat the earliest instant as the start of the range? Just like if endTime is null/emoty then we pick latest instant as the end of the range.
##########
hudi-common/src/main/java/org/apache/hudi/common/table/timeline/CompletionTimeQueryView.java:
##########
@@ -175,42 +190,109 @@ public Option<String> getCompletionTime(String startTime) {
*
* <p>By default, assumes there is at most 1 day time of duration for an instant to accelerate the queries.
*
- * @param startCompletionTime The start completion time.
- * @param endCompletionTime The end completion time.
+ * @param readTimeline The read timeline.
Review Comment:
let's rename to `activeTimelineFiltered`? `readTimeline` introduces ambiguity such as there is a writeTimeline or not.
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,428 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed before or on 'endTime';
Review Comment:
Not following this doc. I think setA should be a collection of all instants the completed on or after `startTime` right? In `[startTime, _]` endTime is not defined so it's basically the latest instant.
##########
hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/util/StreamerUtil.java:
##########
@@ -516,7 +517,7 @@ public static boolean fileExists(FileSystem fs, Path path) {
public static boolean isWriteCommit(HoodieTableType tableType, HoodieInstant instant, HoodieTimeline timeline) {
return tableType == HoodieTableType.MERGE_ON_READ
? !instant.getAction().equals(HoodieTimeline.COMMIT_ACTION) // not a compaction
- : !ClusteringUtil.isClusteringInstant(instant, timeline); // not a clustering
+ : !ClusteringUtils.isClusteringInstant(instant, timeline); // not a clustering
Review Comment:
let's merge the methods of `ClusteringUtil` in hudi-flink to `ClusteringUtils` in hudi-common. Alternatively, rename `ClusteringUtil` to `FlinkClusteringUtil`
##########
hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/source/IncrementalInputSplits.java:
##########
@@ -135,44 +127,37 @@ public static Builder builder() {
public Result inputSplits(
HoodieTableMetaClient metaClient,
boolean cdcEnabled) {
- HoodieTimeline commitTimeline = getReadTimeline(metaClient);
- if (commitTimeline.empty()) {
- LOG.warn("No splits found for the table under path " + path);
+
+ IncrementalQueryAnalyzer analyzer = IncrementalQueryAnalyzer.builder()
+ .metaClient(metaClient)
+ .startTime(this.conf.getString(FlinkOptions.READ_START_COMMIT))
+ .endTime(this.conf.getString(FlinkOptions.READ_END_COMMIT))
Review Comment:
When is it possible that both READ_START_COMMIT and READ_END_COMMIT are null/empty?
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,428 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed before or on 'endTime';
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed in the given time range;
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * </table>
+ *
+ * <p> A range type is required for analyzing the query so that the query range boundary inclusiveness have clear semantics.
+ *
+ * <p>IMPORTANT: the reader may optionally choose to fall back to reading the latest snapshot if there are files missing from decoding the commit metadata.
+ */
+public class IncrementalQueryAnalyzer {
+ public static final String START_COMMIT_EARLIEST = "earliest";
+
+ private final HoodieTableMetaClient metaClient;
+ private final Option<String> startTime;
+ private final Option<String> endTime;
+ private final InstantRange.RangeType rangeType;
+ private final boolean skipCompaction;
+ private final boolean skipClustering;
+ private final int limit;
+
+ private IncrementalQueryAnalyzer(
+ HoodieTableMetaClient metaClient,
+ String startTime,
+ String endTime,
+ InstantRange.RangeType rangeType,
+ boolean skipCompaction,
+ boolean skipClustering,
+ int limit) {
+ this.metaClient = metaClient;
+ this.startTime = Option.ofNullable(startTime);
+ this.endTime = Option.ofNullable(endTime);
+ this.rangeType = rangeType;
+ this.skipCompaction = skipCompaction;
+ this.skipClustering = skipClustering;
+ this.limit = limit;
+ }
+
+ /**
+ * Returns a builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Analyzes the incremental query context with given completion time range.
+ *
+ * @return An incremental query context including the instant time range info.
+ */
+ public QueryContext analyze() {
+ try (CompletionTimeQueryView completionTimeQueryView = new CompletionTimeQueryView(this.metaClient)) {
+ if (completionTimeQueryView.isEmptyTable()) {
+ // no dataset committed in the table
+ return QueryContext.EMPTY;
+ }
+ HoodieTimeline readTimeline = getReadTimeline(this.metaClient);
+ List<String> instantTimeList = completionTimeQueryView.getStartTimes(readTimeline, startTime, endTime, rangeType);
+ if (instantTimeList.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ // get hoodie instants
+ Pair<List<String>, List<String>> splitInstantTime = splitInstantByActiveness(instantTimeList, completionTimeQueryView);
+ Set<String> instantTimeSet = new HashSet<>(instantTimeList);
+ List<String> archivedInstantTime = splitInstantTime.getKey();
+ List<String> activeInstantTime = splitInstantTime.getValue();
+ List<HoodieInstant> archivedInstants = new ArrayList<>();
+ List<HoodieInstant> activeInstants = new ArrayList<>();
+ HoodieTimeline archivedReadTimeline = null;
+ if (!activeInstantTime.isEmpty()) {
+ activeInstants = readTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ if (limit > 0 && limit < activeInstants.size()) {
+ // streaming read speed limit, limits the maximum number of commits allowed to read for each run
+ activeInstants = activeInstants.subList(0, limit);
+ }
+ }
+ if (!archivedInstantTime.isEmpty()) {
+ archivedReadTimeline = getArchivedReadTimeline(metaClient, archivedInstantTime.get(0));
+ archivedInstants = archivedReadTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ }
+ List<String> instants = Stream.concat(archivedInstants.stream(), activeInstants.stream()).map(HoodieInstant::getTimestamp).collect(Collectors.toList());
+ if (instants.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ if (startTime.isEmpty() && endTime.isPresent()) {
Review Comment:
Where do we handle both startTime and endTime being empty?
##########
hudi-common/src/main/java/org/apache/hudi/common/table/timeline/CompletionTimeQueryView.java:
##########
@@ -175,42 +187,111 @@ public Option<String> getCompletionTime(String startTime) {
*
* <p>By default, assumes there is at most 1 day time of duration for an instant to accelerate the queries.
*
- * @param startCompletionTime The start completion time.
- * @param endCompletionTime The end completion time.
+ * @param readTimeline The read timeline.
+ * @param rangeStart The query range start completion time.
+ * @param rangeEnd The query range end completion time.
+ * @param rangeType The range type.
*
- * @return The instant time set.
+ * @return The sorted instant time list.
*/
- public Set<String> getStartTimeSet(String startCompletionTime, String endCompletionTime) {
+ public List<String> getStartTimes(
+ HoodieTimeline readTimeline,
+ Option<String> rangeStart,
+ Option<String> rangeEnd,
+ InstantRange.RangeType rangeType) {
// assumes any instant/transaction lasts at most 1 day to optimize the query efficiency.
- return getStartTimeSet(startCompletionTime, endCompletionTime, s -> HoodieInstantTimeGenerator.instantTimeMinusMillis(s, MILLI_SECONDS_IN_ONE_DAY));
+ return getStartTimes(readTimeline, rangeStart, rangeEnd, rangeType, s -> HoodieInstantTimeGenerator.instantTimeMinusMillis(s, MILLI_SECONDS_IN_ONE_DAY));
}
/**
* Queries the instant start time with given completion time range.
*
- * @param startCompletionTime The start completion time.
- * @param endCompletionTime The end completion time.
- * @param earliestStartTimeFunc The function to generate the earliest start time boundary
- * with the minimum completion time {@code startCompletionTime}.
+ * @param rangeStart The query range start completion time.
+ * @param rangeEnd The query range end completion time.
+ * @param earliestInstantTimeFunc The function to generate the earliest start time boundary
+ * with the minimum completion time.
*
- * @return The instant time set.
+ * @return The sorted instant time list.
*/
- public Set<String> getStartTimeSet(String startCompletionTime, String endCompletionTime, Function<String, String> earliestStartTimeFunc) {
- String startInstant = earliestStartTimeFunc.apply(startCompletionTime);
+ @VisibleForTesting
+ public List<String> getStartTimes(
+ String rangeStart,
+ String rangeEnd,
+ Function<String, String> earliestInstantTimeFunc) {
+ return getStartTimes(metaClient.getCommitsTimeline().filterCompletedInstants(), Option.ofNullable(rangeStart), Option.ofNullable(rangeEnd),
+ InstantRange.RangeType.CLOSE_CLOSE, earliestInstantTimeFunc);
+ }
+
+ /**
+ * Queries the instant start time with given completion time range.
+ *
+ * @param readTimeline The read timeline.
+ * @param rangeStart The query range start completion time.
+ * @param rangeEnd The query range end completion time.
+ * @param rangeType The range type.
+ * @param earliestInstantTimeFunc The function to generate the earliest start time boundary
+ * with the minimum completion time.
+ *
+ * @return The sorted instant time list.
+ */
+ public List<String> getStartTimes(
Review Comment:
This is confusing with `startTime` and `endTime` in `IncrementalQueryAnalyzer`. Let's assume each instant has start timestamp and completion timestamp. Then, we can rename this method to `getInstantStartTimestamps`?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "linliu-code (via GitHub)" <gi...@apache.org>.
linliu-code commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1427381908
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,429 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed before or on 'endTime';
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed in the given time range;
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * </table>
+ *
+ * <p> A range type is required for analyzing the query so that the query range boundary inclusiveness have clear semantics.
+ *
+ * <p>IMPORTANT: the reader may optionally choose to fall back to reading the latest snapshot if there are files missing from decoding the commit metadata.
+ */
+public class IncrementalQueryAnalyzer {
+ public static final String START_COMMIT_EARLIEST = "earliest";
+
+ private final HoodieTableMetaClient metaClient;
+ private final Option<String> startTime;
+ private final Option<String> endTime;
+ private final InstantRange.RangeType rangeType;
+ private final boolean skipCompaction;
+ private final boolean skipClustering;
+ private final int limit;
+
+ private IncrementalQueryAnalyzer(
+ HoodieTableMetaClient metaClient,
+ String startTime,
+ String endTime,
Review Comment:
Why do we use "String" type for time? Shouldn't "Long"?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "linliu-code (via GitHub)" <gi...@apache.org>.
linliu-code commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1428422995
##########
hudi-common/src/main/java/org/apache/hudi/common/table/timeline/CompletionTimeQueryView.java:
##########
@@ -175,42 +187,111 @@ public Option<String> getCompletionTime(String startTime) {
*
* <p>By default, assumes there is at most 1 day time of duration for an instant to accelerate the queries.
*
- * @param startCompletionTime The start completion time.
- * @param endCompletionTime The end completion time.
+ * @param readTimeline The read timeline.
+ * @param rangeStart The query range start completion time.
+ * @param rangeEnd The query range end completion time.
+ * @param rangeType The range type.
*
- * @return The instant time set.
+ * @return The sorted instant time list.
*/
- public Set<String> getStartTimeSet(String startCompletionTime, String endCompletionTime) {
+ public List<String> getStartTimes(
+ HoodieTimeline readTimeline,
+ Option<String> rangeStart,
+ Option<String> rangeEnd,
+ InstantRange.RangeType rangeType) {
// assumes any instant/transaction lasts at most 1 day to optimize the query efficiency.
- return getStartTimeSet(startCompletionTime, endCompletionTime, s -> HoodieInstantTimeGenerator.instantTimeMinusMillis(s, MILLI_SECONDS_IN_ONE_DAY));
+ return getStartTimes(readTimeline, rangeStart, rangeEnd, rangeType, s -> HoodieInstantTimeGenerator.instantTimeMinusMillis(s, MILLI_SECONDS_IN_ONE_DAY));
}
/**
* Queries the instant start time with given completion time range.
*
- * @param startCompletionTime The start completion time.
- * @param endCompletionTime The end completion time.
- * @param earliestStartTimeFunc The function to generate the earliest start time boundary
- * with the minimum completion time {@code startCompletionTime}.
+ * @param rangeStart The query range start completion time.
+ * @param rangeEnd The query range end completion time.
+ * @param earliestInstantTimeFunc The function to generate the earliest start time boundary
+ * with the minimum completion time.
*
- * @return The instant time set.
+ * @return The sorted instant time list.
*/
- public Set<String> getStartTimeSet(String startCompletionTime, String endCompletionTime, Function<String, String> earliestStartTimeFunc) {
- String startInstant = earliestStartTimeFunc.apply(startCompletionTime);
+ @VisibleForTesting
+ public List<String> getStartTimes(
+ String rangeStart,
+ String rangeEnd,
+ Function<String, String> earliestInstantTimeFunc) {
+ return getStartTimes(metaClient.getCommitsTimeline().filterCompletedInstants(), Option.ofNullable(rangeStart), Option.ofNullable(rangeEnd),
+ InstantRange.RangeType.CLOSE_CLOSE, earliestInstantTimeFunc);
+ }
+
+ /**
+ * Queries the instant start time with given completion time range.
+ *
+ * @param readTimeline The read timeline.
+ * @param rangeStart The query range start completion time.
+ * @param rangeEnd The query range end completion time.
+ * @param rangeType The range type.
+ * @param earliestInstantTimeFunc The function to generate the earliest start time boundary
+ * with the minimum completion time.
+ *
+ * @return The sorted instant time list.
+ */
+ public List<String> getStartTimes(
Review Comment:
Is it possible for us to return the completion time directly since the range time and range end is completion time, right?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "danny0405 (via GitHub)" <gi...@apache.org>.
danny0405 commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1428786843
##########
hudi-common/src/main/java/org/apache/hudi/common/table/log/InstantRange.java:
##########
@@ -166,6 +169,28 @@ public boolean isInRange(String instant) {
}
}
+ /**
+ * Composition of multiple instant ranges in disjunctive form.
Review Comment:
Yeah, the `conjunctive` and `disjunctive` is terminology for relation algebra(SQL), `disjunctive` usually means `OR`.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "linliu-code (via GitHub)" <gi...@apache.org>.
linliu-code commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1427389589
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,429 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
Review Comment:
Nvm, i have figured it out from the following code.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "linliu-code (via GitHub)" <gi...@apache.org>.
linliu-code commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1428382099
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,429 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed before or on 'endTime';
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed in the given time range;
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * </table>
+ *
+ * <p> A range type is required for analyzing the query so that the query range boundary inclusiveness have clear semantics.
+ *
+ * <p>IMPORTANT: the reader may optionally choose to fall back to reading the latest snapshot if there are files missing from decoding the commit metadata.
+ */
+public class IncrementalQueryAnalyzer {
+ public static final String START_COMMIT_EARLIEST = "earliest";
+
+ private final HoodieTableMetaClient metaClient;
+ private final Option<String> startTime;
+ private final Option<String> endTime;
+ private final InstantRange.RangeType rangeType;
+ private final boolean skipCompaction;
+ private final boolean skipClustering;
+ private final int limit;
+
+ private IncrementalQueryAnalyzer(
+ HoodieTableMetaClient metaClient,
+ String startTime,
+ String endTime,
+ InstantRange.RangeType rangeType,
+ boolean skipCompaction,
+ boolean skipClustering,
+ int limit) {
+ this.metaClient = metaClient;
+ this.startTime = Option.ofNullable(startTime);
+ this.endTime = Option.ofNullable(endTime);
+ this.rangeType = rangeType;
+ this.skipCompaction = skipCompaction;
+ this.skipClustering = skipClustering;
+ this.limit = limit;
+ }
+
+ /**
+ * Returns a builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Analyzes the incremental query context with given completion time range.
+ *
+ * @return An incremental query context including the instant time range info.
+ */
+ public QueryContext analyze() {
+ try (CompletionTimeQueryView completionTimeQueryView = new CompletionTimeQueryView(this.metaClient)) {
+ if (completionTimeQueryView.isEmptyTable()) {
+ // no dataset committed in the table
+ return QueryContext.EMPTY;
+ }
+ HoodieTimeline readTimeline = getReadTimeline(this.metaClient);
+ List<String> instantTimeList = completionTimeQueryView.getStartTimes(readTimeline, startTime, endTime, rangeType);
+ if (instantTimeList.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ // get hoodie instants
+ Pair<List<String>, List<String>> splitInstantTime = splitInstantByActiveness(instantTimeList, completionTimeQueryView);
+ Set<String> instantTimeSet = new HashSet<>(instantTimeList);
+ List<String> archivedInstantTime = splitInstantTime.getKey();
+ List<String> activeInstantTime = splitInstantTime.getValue();
+ List<HoodieInstant> archivedInstants = new ArrayList<>();
+ List<HoodieInstant> activeInstants = new ArrayList<>();
+ HoodieTimeline archivedReadTimeline = null;
+ if (!activeInstantTime.isEmpty()) {
+ activeInstants = readTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ if (limit > 0 && limit < activeInstants.size()) {
+ // streaming read speed limit, limits the maximum number of commits allowed to read for each run
+ activeInstants = activeInstants.subList(0, limit);
Review Comment:
Curious: Here do you mean the client will give a different start time to fetch the next batch of instants?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "hudi-bot (via GitHub)" <gi...@apache.org>.
hudi-bot commented on PR #10255:
URL: https://github.com/apache/hudi/pull/10255#issuecomment-1844707886
<!--
Meta data
{
"version" : 1,
"metaDataEntries" : [ {
"hash" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"status" : "FAILURE",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21338",
"triggerID" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"triggerType" : "PUSH"
}, {
"hash" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"status" : "PENDING",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21364",
"triggerID" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"triggerType" : "PUSH"
} ]
}-->
## CI report:
* 07b8dd347e0157ed848cddd4d5bb3b229d42db5a Azure: [FAILURE](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21338)
* bde657c2c091c062c342b0600c3269bbc15ebaaa Azure: [PENDING](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21364)
<details>
<summary>Bot commands</summary>
@hudi-bot supports the following commands:
- `@hudi-bot run azure` re-run the last Azure build
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "hudi-bot (via GitHub)" <gi...@apache.org>.
hudi-bot commented on PR #10255:
URL: https://github.com/apache/hudi/pull/10255#issuecomment-1846571904
<!--
Meta data
{
"version" : 1,
"metaDataEntries" : [ {
"hash" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21338",
"triggerID" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"triggerType" : "PUSH"
}, {
"hash" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"status" : "SUCCESS",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21364",
"triggerID" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"triggerType" : "PUSH"
}, {
"hash" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"status" : "PENDING",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21395",
"triggerID" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"triggerType" : "PUSH"
} ]
}-->
## CI report:
* bde657c2c091c062c342b0600c3269bbc15ebaaa Azure: [SUCCESS](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21364)
* 3787083e09a0bf7d754d095b5826657efd4f468e Azure: [PENDING](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21395)
<details>
<summary>Bot commands</summary>
@hudi-bot supports the following commands:
- `@hudi-bot run azure` re-run the last Azure build
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "hudi-bot (via GitHub)" <gi...@apache.org>.
hudi-bot commented on PR #10255:
URL: https://github.com/apache/hudi/pull/10255#issuecomment-1846746092
<!--
Meta data
{
"version" : 1,
"metaDataEntries" : [ {
"hash" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21338",
"triggerID" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"triggerType" : "PUSH"
}, {
"hash" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21364",
"triggerID" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"triggerType" : "PUSH"
}, {
"hash" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"status" : "FAILURE",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21395",
"triggerID" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"triggerType" : "PUSH"
} ]
}-->
## CI report:
* 3787083e09a0bf7d754d095b5826657efd4f468e Azure: [FAILURE](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21395)
<details>
<summary>Bot commands</summary>
@hudi-bot supports the following commands:
- `@hudi-bot run azure` re-run the last Azure build
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "vinothchandar (via GitHub)" <gi...@apache.org>.
vinothchandar merged PR #10255:
URL: https://github.com/apache/hudi/pull/10255
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "hudi-bot (via GitHub)" <gi...@apache.org>.
hudi-bot commented on PR #10255:
URL: https://github.com/apache/hudi/pull/10255#issuecomment-1939244338
<!--
Meta data
{
"version" : 1,
"metaDataEntries" : [ {
"hash" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21338",
"triggerID" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"triggerType" : "PUSH"
}, {
"hash" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21364",
"triggerID" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"triggerType" : "PUSH"
}, {
"hash" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21395",
"triggerID" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"triggerType" : "PUSH"
}, {
"hash" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21488",
"triggerID" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"triggerType" : "PUSH"
}, {
"hash" : "b39e93881c130fd8832e5c260740f8f59bb8033e",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21577",
"triggerID" : "b39e93881c130fd8832e5c260740f8f59bb8033e",
"triggerType" : "PUSH"
}, {
"hash" : "d2c7c5cd379c4c8ba4297fd5fa1e989f73e58b5d",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21854",
"triggerID" : "d2c7c5cd379c4c8ba4297fd5fa1e989f73e58b5d",
"triggerType" : "PUSH"
}, {
"hash" : "975fbb9de10066ac67a875f3484fe9f5922e197d",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=22346",
"triggerID" : "975fbb9de10066ac67a875f3484fe9f5922e197d",
"triggerType" : "PUSH"
}, {
"hash" : "a2cb2403ded21b17b991304af14eb57897b4100d",
"status" : "FAILURE",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=22357",
"triggerID" : "a2cb2403ded21b17b991304af14eb57897b4100d",
"triggerType" : "PUSH"
}, {
"hash" : "3153fe03616237ff7814c53923fec2a2504b6d01",
"status" : "UNKNOWN",
"url" : "TBD",
"triggerID" : "3153fe03616237ff7814c53923fec2a2504b6d01",
"triggerType" : "PUSH"
} ]
}-->
## CI report:
* a2cb2403ded21b17b991304af14eb57897b4100d Azure: [FAILURE](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=22357)
* 3153fe03616237ff7814c53923fec2a2504b6d01 UNKNOWN
<details>
<summary>Bot commands</summary>
@hudi-bot supports the following commands:
- `@hudi-bot run azure` re-run the last Azure build
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "hudi-bot (via GitHub)" <gi...@apache.org>.
hudi-bot commented on PR #10255:
URL: https://github.com/apache/hudi/pull/10255#issuecomment-1862087491
<!--
Meta data
{
"version" : 1,
"metaDataEntries" : [ {
"hash" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21338",
"triggerID" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"triggerType" : "PUSH"
}, {
"hash" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21364",
"triggerID" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"triggerType" : "PUSH"
}, {
"hash" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21395",
"triggerID" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"triggerType" : "PUSH"
}, {
"hash" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"status" : "SUCCESS",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21488",
"triggerID" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"triggerType" : "PUSH"
}, {
"hash" : "b39e93881c130fd8832e5c260740f8f59bb8033e",
"status" : "PENDING",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21577",
"triggerID" : "b39e93881c130fd8832e5c260740f8f59bb8033e",
"triggerType" : "PUSH"
} ]
}-->
## CI report:
* 3e12be6f351ee6da145de331af53c27c985b7d25 Azure: [SUCCESS](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21488)
* b39e93881c130fd8832e5c260740f8f59bb8033e Azure: [PENDING](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21577)
<details>
<summary>Bot commands</summary>
@hudi-bot supports the following commands:
- `@hudi-bot run azure` re-run the last Azure build
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "danny0405 (via GitHub)" <gi...@apache.org>.
danny0405 commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1428787710
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,429 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed before or on 'endTime';
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed in the given time range;
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * </table>
+ *
+ * <p> A range type is required for analyzing the query so that the query range boundary inclusiveness have clear semantics.
+ *
+ * <p>IMPORTANT: the reader may optionally choose to fall back to reading the latest snapshot if there are files missing from decoding the commit metadata.
+ */
+public class IncrementalQueryAnalyzer {
+ public static final String START_COMMIT_EARLIEST = "earliest";
+
+ private final HoodieTableMetaClient metaClient;
+ private final Option<String> startTime;
+ private final Option<String> endTime;
+ private final InstantRange.RangeType rangeType;
+ private final boolean skipCompaction;
+ private final boolean skipClustering;
+ private final int limit;
+
+ private IncrementalQueryAnalyzer(
+ HoodieTableMetaClient metaClient,
+ String startTime,
+ String endTime,
+ InstantRange.RangeType rangeType,
+ boolean skipCompaction,
+ boolean skipClustering,
+ int limit) {
+ this.metaClient = metaClient;
+ this.startTime = Option.ofNullable(startTime);
+ this.endTime = Option.ofNullable(endTime);
+ this.rangeType = rangeType;
+ this.skipCompaction = skipCompaction;
+ this.skipClustering = skipClustering;
+ this.limit = limit;
+ }
+
+ /**
+ * Returns a builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Analyzes the incremental query context with given completion time range.
+ *
+ * @return An incremental query context including the instant time range info.
+ */
+ public QueryContext analyze() {
+ try (CompletionTimeQueryView completionTimeQueryView = new CompletionTimeQueryView(this.metaClient)) {
+ if (completionTimeQueryView.isEmptyTable()) {
+ // no dataset committed in the table
+ return QueryContext.EMPTY;
+ }
+ HoodieTimeline readTimeline = getReadTimeline(this.metaClient);
+ List<String> instantTimeList = completionTimeQueryView.getStartTimes(readTimeline, startTime, endTime, rangeType);
+ if (instantTimeList.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ // get hoodie instants
+ Pair<List<String>, List<String>> splitInstantTime = splitInstantByActiveness(instantTimeList, completionTimeQueryView);
Review Comment:
The archived instant is deserialized from the archived log while the active instant is deserialzied from the commit metadata file. The former is more resource costly.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "danny0405 (via GitHub)" <gi...@apache.org>.
danny0405 commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1427515234
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,429 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed before or on 'endTime';
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed in the given time range;
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * </table>
+ *
+ * <p> A range type is required for analyzing the query so that the query range boundary inclusiveness have clear semantics.
+ *
+ * <p>IMPORTANT: the reader may optionally choose to fall back to reading the latest snapshot if there are files missing from decoding the commit metadata.
+ */
+public class IncrementalQueryAnalyzer {
+ public static final String START_COMMIT_EARLIEST = "earliest";
+
+ private final HoodieTableMetaClient metaClient;
+ private final Option<String> startTime;
+ private final Option<String> endTime;
+ private final InstantRange.RangeType rangeType;
+ private final boolean skipCompaction;
+ private final boolean skipClustering;
+ private final int limit;
+
+ private IncrementalQueryAnalyzer(
+ HoodieTableMetaClient metaClient,
+ String startTime,
+ String endTime,
Review Comment:
Both our instant time/completion time are all formatted string in format: `yyyyMMddHHmmssSSS`.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "hudi-bot (via GitHub)" <gi...@apache.org>.
hudi-bot commented on PR #10255:
URL: https://github.com/apache/hudi/pull/10255#issuecomment-1842679930
<!--
Meta data
{
"version" : 1,
"metaDataEntries" : [ {
"hash" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"status" : "PENDING",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21338",
"triggerID" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"triggerType" : "PUSH"
} ]
}-->
## CI report:
* 07b8dd347e0157ed848cddd4d5bb3b229d42db5a Azure: [PENDING](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21338)
<details>
<summary>Bot commands</summary>
@hudi-bot supports the following commands:
- `@hudi-bot run azure` re-run the last Azure build
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "hudi-bot (via GitHub)" <gi...@apache.org>.
hudi-bot commented on PR #10255:
URL: https://github.com/apache/hudi/pull/10255#issuecomment-1880016530
<!--
Meta data
{
"version" : 1,
"metaDataEntries" : [ {
"hash" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21338",
"triggerID" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"triggerType" : "PUSH"
}, {
"hash" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21364",
"triggerID" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"triggerType" : "PUSH"
}, {
"hash" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21395",
"triggerID" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"triggerType" : "PUSH"
}, {
"hash" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21488",
"triggerID" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"triggerType" : "PUSH"
}, {
"hash" : "b39e93881c130fd8832e5c260740f8f59bb8033e",
"status" : "SUCCESS",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21577",
"triggerID" : "b39e93881c130fd8832e5c260740f8f59bb8033e",
"triggerType" : "PUSH"
}, {
"hash" : "d2c7c5cd379c4c8ba4297fd5fa1e989f73e58b5d",
"status" : "UNKNOWN",
"url" : "TBD",
"triggerID" : "d2c7c5cd379c4c8ba4297fd5fa1e989f73e58b5d",
"triggerType" : "PUSH"
} ]
}-->
## CI report:
* b39e93881c130fd8832e5c260740f8f59bb8033e Azure: [SUCCESS](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21577)
* d2c7c5cd379c4c8ba4297fd5fa1e989f73e58b5d UNKNOWN
<details>
<summary>Bot commands</summary>
@hudi-bot supports the following commands:
- `@hudi-bot run azure` re-run the last Azure build
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "hudi-bot (via GitHub)" <gi...@apache.org>.
hudi-bot commented on PR #10255:
URL: https://github.com/apache/hudi/pull/10255#issuecomment-1844688303
<!--
Meta data
{
"version" : 1,
"metaDataEntries" : [ {
"hash" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"status" : "FAILURE",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21338",
"triggerID" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"triggerType" : "PUSH"
}, {
"hash" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"status" : "UNKNOWN",
"url" : "TBD",
"triggerID" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"triggerType" : "PUSH"
} ]
}-->
## CI report:
* 07b8dd347e0157ed848cddd4d5bb3b229d42db5a Azure: [FAILURE](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21338)
* bde657c2c091c062c342b0600c3269bbc15ebaaa UNKNOWN
<details>
<summary>Bot commands</summary>
@hudi-bot supports the following commands:
- `@hudi-bot run azure` re-run the last Azure build
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "danny0405 (via GitHub)" <gi...@apache.org>.
danny0405 commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1424804615
##########
hudi-common/src/main/java/org/apache/hudi/common/table/timeline/HoodieInstantTimeGenerator.java:
##########
@@ -108,6 +108,9 @@ public static String instantTimePlusMillis(String timestamp, long milliseconds)
public static String instantTimeMinusMillis(String timestamp, long milliseconds) {
try {
String timestampInMillis = fixInstantTimeCompatibility(timestamp);
+ if (timestampInMillis.length() < MILLIS_INSTANT_TIMESTAMP_FORMAT_LENGTH) {
Review Comment:
For testing compatibility.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "danny0405 (via GitHub)" <gi...@apache.org>.
danny0405 commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1424875356
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,425 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed before or on 'endTime';
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed in the given time range;
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * </table>
+ *
+ * <p> A range type is required for analyzing the query so that the query range boundary inclusiveness have clear semantics.
+ *
+ * <p>IMPORTANT: the reader may optionally choose to fall back to reading the latest snapshot if there are files missing from decoding the commit metadata.
+ */
+public class IncrementalQueryAnalyzer {
+ public static final String START_COMMIT_EARLIEST = "earliest";
+
+ private final HoodieTableMetaClient metaClient;
+ private final String startTime;
+ private final String endTime;
+ private final InstantRange.RangeType rangeType;
+ private final boolean skipCompaction;
+ private final boolean skipClustering;
+ private final int limit;
+
+ private IncrementalQueryAnalyzer(
+ HoodieTableMetaClient metaClient,
+ String startTime,
+ String endTime,
+ InstantRange.RangeType rangeType,
+ boolean skipCompaction,
+ boolean skipClustering,
+ int limit) {
+ this.metaClient = metaClient;
+ this.startTime = startTime;
+ this.endTime = endTime;
+ this.rangeType = rangeType;
+ this.skipCompaction = skipCompaction;
+ this.skipClustering = skipClustering;
+ this.limit = limit;
+ }
+
+ /**
+ * Returns a builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Analyzes the incremental query context with given completion time range.
+ *
+ * @return An incremental query context including the instant time range info.
+ */
+ public QueryContext analyze() {
+ try (CompletionTimeQueryView completionTimeQueryView = new CompletionTimeQueryView(this.metaClient)) {
+ if (completionTimeQueryView.isEmptyTable()) {
+ // no dataset committed in the table
+ return QueryContext.EMPTY;
+ }
+ HoodieTimeline readTimeline = getReadTimeline(this.metaClient);
+ List<String> instantTimeList = completionTimeQueryView.getStartTime(readTimeline, startTime, endTime, rangeType);
+ if (instantTimeList.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ // get hoodie instants
+ Pair<List<String>, List<String>> splitInstantTime = splitInstantByActiveness(instantTimeList, completionTimeQueryView);
+ Set<String> instantTimeSet = new HashSet<>(instantTimeList);
+ List<String> archivedInstantTime = splitInstantTime.getKey();
+ List<String> activeInstantTime = splitInstantTime.getValue();
+ List<HoodieInstant> archivedInstants = new ArrayList<>();
+ List<HoodieInstant> activeInstants = new ArrayList<>();
+ HoodieTimeline archivedReadTimeline = null;
+ if (!activeInstantTime.isEmpty()) {
+ activeInstants = readTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ if (limit > 0 && limit < activeInstants.size()) {
+ // streaming read speed limit, limits the maximum number of commits allowed to read for each run
+ activeInstants = activeInstants.subList(0, limit);
+ }
+ }
+ if (!archivedInstantTime.isEmpty()) {
+ archivedReadTimeline = getArchivedReadTimeline(metaClient, archivedInstantTime.get(0));
+ archivedInstants = archivedReadTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ }
+ List<String> instants = Stream.concat(archivedInstants.stream(), activeInstants.stream()).map(HoodieInstant::getTimestamp).collect(Collectors.toList());
+ if (instants.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ if (startTime == null && endTime != null) {
+ instants = Collections.singletonList(instants.get(instants.size() - 1));
+ }
+ String lastInstant = instants.get(instants.size() - 1);
+ // keep the same semantics with streaming read, default start from the latest commit
+ String startInstant = START_COMMIT_EARLIEST.equalsIgnoreCase(startTime) ? null : startTime == null ? lastInstant : instants.get(0);
+ String endInstant = endTime == null ? null : lastInstant;
+ return QueryContext.create(startInstant, endInstant, instants, archivedInstants, activeInstants, readTimeline, archivedReadTimeline);
+ }
+ }
+
+ /**
+ * Splits the given instant time list into a pair of archived instant list and active instant list.
+ */
+ private static Pair<List<String>, List<String>> splitInstantByActiveness(List<String> instantTimeList, CompletionTimeQueryView completionTimeQueryView) {
+ int firstActiveIdx = IntStream.range(0, instantTimeList.size()).filter(i -> !completionTimeQueryView.isArchived(instantTimeList.get(i))).findFirst().orElse(-1);
+ if (firstActiveIdx == -1) {
+ return Pair.of(instantTimeList, Collections.emptyList());
+ } else if (firstActiveIdx == 0) {
+ return Pair.of(Collections.emptyList(), instantTimeList);
+ } else {
+ return Pair.of(instantTimeList.subList(0, firstActiveIdx), instantTimeList.subList(firstActiveIdx, instantTimeList.size()));
+ }
+ }
+
+ private HoodieTimeline getReadTimeline(HoodieTableMetaClient metaClient) {
+ HoodieTimeline timeline = metaClient.getCommitsAndCompactionTimeline().filterCompletedAndCompactionInstants();
+ return filterInstantsAsPerUserConfigs(metaClient, timeline, this.skipCompaction, this.skipClustering);
+ }
+
+ private HoodieTimeline getArchivedReadTimeline(HoodieTableMetaClient metaClient, String startInstant) {
+ HoodieArchivedTimeline archivedTimeline = metaClient.getArchivedTimeline(startInstant, false);
+ HoodieTimeline archivedCompleteTimeline = archivedTimeline.getCommitsTimeline().filterCompletedInstants();
+ return filterInstantsAsPerUserConfigs(metaClient, archivedCompleteTimeline, this.skipCompaction, this.skipClustering);
+ }
+
+ /**
+ * Filters out the unnecessary instants as per user specified configs.
+ *
+ * @param timeline The timeline.
+ *
+ * @return the filtered timeline
+ */
+ @VisibleForTesting
+ public static HoodieTimeline filterInstantsAsPerUserConfigs(HoodieTableMetaClient metaClient, HoodieTimeline timeline, boolean skipCompaction, boolean skipClustering) {
+ final HoodieTimeline oriTimeline = timeline;
+ if (metaClient.getTableType() == HoodieTableType.MERGE_ON_READ & skipCompaction) {
+ // the compaction commit uses 'commit' as action which is tricky
+ timeline = timeline.filter(instant -> !instant.getAction().equals(HoodieTimeline.COMMIT_ACTION));
+ }
+ if (skipClustering) {
+ timeline = timeline.filter(instant -> !ClusteringUtils.isClusteringInstant(instant, oriTimeline));
+ }
+ return timeline;
+ }
+
+ // -------------------------------------------------------------------------
+ // Inner Class
+ // -------------------------------------------------------------------------
+ /**
+ * Builder for {@link IncrementalQueryAnalyzer}.
+ */
+ public static class Builder {
+ /**
+ * Start completion time.
+ */
+ private String startTime;
+ /**
+ * End completion time.
+ */
+ private String endTime;
+ private InstantRange.RangeType rangeType;
+ private HoodieTableMetaClient metaClient;
+ private boolean skipCompaction = false;
+ private boolean skipClustering = false;
+ /**
+ * Maximum number of instants to read per run.
+ */
+ private int limit = -1;
+
+ public Builder() {
+ }
+
+ public Builder startTime(String startTime) {
+ this.startTime = startTime;
+ return this;
+ }
+
+ public Builder endTime(String endTime) {
+ this.endTime = endTime;
+ return this;
+ }
+
+ public Builder rangeType(InstantRange.RangeType rangeType) {
+ this.rangeType = rangeType;
+ return this;
+ }
+
+ public Builder metaClient(HoodieTableMetaClient metaClient) {
+ this.metaClient = metaClient;
+ return this;
+ }
+
+ public Builder skipCompaction(boolean skipCompaction) {
Review Comment:
If we do not skip clustering or compaction for incremental/streaming read, there could be duplicates for downstream readers.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "danny0405 (via GitHub)" <gi...@apache.org>.
danny0405 commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1425037680
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,425 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed before or on 'endTime';
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed in the given time range;
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * </table>
+ *
+ * <p> A range type is required for analyzing the query so that the query range boundary inclusiveness have clear semantics.
+ *
+ * <p>IMPORTANT: the reader may optionally choose to fall back to reading the latest snapshot if there are files missing from decoding the commit metadata.
+ */
+public class IncrementalQueryAnalyzer {
+ public static final String START_COMMIT_EARLIEST = "earliest";
+
+ private final HoodieTableMetaClient metaClient;
+ private final String startTime;
+ private final String endTime;
+ private final InstantRange.RangeType rangeType;
+ private final boolean skipCompaction;
+ private final boolean skipClustering;
+ private final int limit;
+
+ private IncrementalQueryAnalyzer(
+ HoodieTableMetaClient metaClient,
+ String startTime,
+ String endTime,
+ InstantRange.RangeType rangeType,
+ boolean skipCompaction,
+ boolean skipClustering,
+ int limit) {
+ this.metaClient = metaClient;
+ this.startTime = startTime;
+ this.endTime = endTime;
+ this.rangeType = rangeType;
+ this.skipCompaction = skipCompaction;
+ this.skipClustering = skipClustering;
+ this.limit = limit;
+ }
+
+ /**
+ * Returns a builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Analyzes the incremental query context with given completion time range.
+ *
+ * @return An incremental query context including the instant time range info.
+ */
+ public QueryContext analyze() {
+ try (CompletionTimeQueryView completionTimeQueryView = new CompletionTimeQueryView(this.metaClient)) {
+ if (completionTimeQueryView.isEmptyTable()) {
+ // no dataset committed in the table
+ return QueryContext.EMPTY;
+ }
+ HoodieTimeline readTimeline = getReadTimeline(this.metaClient);
+ List<String> instantTimeList = completionTimeQueryView.getStartTime(readTimeline, startTime, endTime, rangeType);
+ if (instantTimeList.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ // get hoodie instants
+ Pair<List<String>, List<String>> splitInstantTime = splitInstantByActiveness(instantTimeList, completionTimeQueryView);
+ Set<String> instantTimeSet = new HashSet<>(instantTimeList);
+ List<String> archivedInstantTime = splitInstantTime.getKey();
+ List<String> activeInstantTime = splitInstantTime.getValue();
+ List<HoodieInstant> archivedInstants = new ArrayList<>();
+ List<HoodieInstant> activeInstants = new ArrayList<>();
+ HoodieTimeline archivedReadTimeline = null;
+ if (!activeInstantTime.isEmpty()) {
+ activeInstants = readTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ if (limit > 0 && limit < activeInstants.size()) {
+ // streaming read speed limit, limits the maximum number of commits allowed to read for each run
+ activeInstants = activeInstants.subList(0, limit);
+ }
+ }
+ if (!archivedInstantTime.isEmpty()) {
+ archivedReadTimeline = getArchivedReadTimeline(metaClient, archivedInstantTime.get(0));
+ archivedInstants = archivedReadTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ }
+ List<String> instants = Stream.concat(archivedInstants.stream(), activeInstants.stream()).map(HoodieInstant::getTimestamp).collect(Collectors.toList());
+ if (instants.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ if (startTime == null && endTime != null) {
+ instants = Collections.singletonList(instants.get(instants.size() - 1));
+ }
+ String lastInstant = instants.get(instants.size() - 1);
+ // keep the same semantics with streaming read, default start from the latest commit
+ String startInstant = START_COMMIT_EARLIEST.equalsIgnoreCase(startTime) ? null : startTime == null ? lastInstant : instants.get(0);
+ String endInstant = endTime == null ? null : lastInstant;
+ return QueryContext.create(startInstant, endInstant, instants, archivedInstants, activeInstants, readTimeline, archivedReadTimeline);
+ }
+ }
+
+ /**
+ * Splits the given instant time list into a pair of archived instant list and active instant list.
+ */
+ private static Pair<List<String>, List<String>> splitInstantByActiveness(List<String> instantTimeList, CompletionTimeQueryView completionTimeQueryView) {
+ int firstActiveIdx = IntStream.range(0, instantTimeList.size()).filter(i -> !completionTimeQueryView.isArchived(instantTimeList.get(i))).findFirst().orElse(-1);
+ if (firstActiveIdx == -1) {
+ return Pair.of(instantTimeList, Collections.emptyList());
+ } else if (firstActiveIdx == 0) {
+ return Pair.of(Collections.emptyList(), instantTimeList);
+ } else {
+ return Pair.of(instantTimeList.subList(0, firstActiveIdx), instantTimeList.subList(firstActiveIdx, instantTimeList.size()));
+ }
+ }
+
+ private HoodieTimeline getReadTimeline(HoodieTableMetaClient metaClient) {
+ HoodieTimeline timeline = metaClient.getCommitsAndCompactionTimeline().filterCompletedAndCompactionInstants();
+ return filterInstantsAsPerUserConfigs(metaClient, timeline, this.skipCompaction, this.skipClustering);
+ }
+
+ private HoodieTimeline getArchivedReadTimeline(HoodieTableMetaClient metaClient, String startInstant) {
+ HoodieArchivedTimeline archivedTimeline = metaClient.getArchivedTimeline(startInstant, false);
+ HoodieTimeline archivedCompleteTimeline = archivedTimeline.getCommitsTimeline().filterCompletedInstants();
+ return filterInstantsAsPerUserConfigs(metaClient, archivedCompleteTimeline, this.skipCompaction, this.skipClustering);
+ }
+
+ /**
+ * Filters out the unnecessary instants as per user specified configs.
+ *
+ * @param timeline The timeline.
+ *
+ * @return the filtered timeline
+ */
+ @VisibleForTesting
+ public static HoodieTimeline filterInstantsAsPerUserConfigs(HoodieTableMetaClient metaClient, HoodieTimeline timeline, boolean skipCompaction, boolean skipClustering) {
+ final HoodieTimeline oriTimeline = timeline;
+ if (metaClient.getTableType() == HoodieTableType.MERGE_ON_READ & skipCompaction) {
+ // the compaction commit uses 'commit' as action which is tricky
+ timeline = timeline.filter(instant -> !instant.getAction().equals(HoodieTimeline.COMMIT_ACTION));
+ }
+ if (skipClustering) {
+ timeline = timeline.filter(instant -> !ClusteringUtils.isClusteringInstant(instant, oriTimeline));
+ }
+ return timeline;
+ }
+
+ // -------------------------------------------------------------------------
+ // Inner Class
+ // -------------------------------------------------------------------------
+ /**
+ * Builder for {@link IncrementalQueryAnalyzer}.
+ */
+ public static class Builder {
+ /**
+ * Start completion time.
+ */
+ private String startTime;
+ /**
+ * End completion time.
+ */
+ private String endTime;
+ private InstantRange.RangeType rangeType;
+ private HoodieTableMetaClient metaClient;
+ private boolean skipCompaction = false;
+ private boolean skipClustering = false;
+ /**
+ * Maximum number of instants to read per run.
+ */
+ private int limit = -1;
+
+ public Builder() {
+ }
+
+ public Builder startTime(String startTime) {
+ this.startTime = startTime;
+ return this;
+ }
+
+ public Builder endTime(String endTime) {
+ this.endTime = endTime;
+ return this;
+ }
+
+ public Builder rangeType(InstantRange.RangeType rangeType) {
+ this.rangeType = rangeType;
+ return this;
+ }
+
+ public Builder metaClient(HoodieTableMetaClient metaClient) {
+ this.metaClient = metaClient;
+ return this;
+ }
+
+ public Builder skipCompaction(boolean skipCompaction) {
Review Comment:
I just add the interface here and the engine can decide whether to skip based on different use cases(batch or streaming).
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "danny0405 (via GitHub)" <gi...@apache.org>.
danny0405 commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1425055040
##########
hudi-common/src/main/java/org/apache/hudi/common/table/timeline/CompletionTimeQueryView.java:
##########
@@ -175,42 +190,109 @@ public Option<String> getCompletionTime(String startTime) {
*
* <p>By default, assumes there is at most 1 day time of duration for an instant to accelerate the queries.
*
- * @param startCompletionTime The start completion time.
- * @param endCompletionTime The end completion time.
+ * @param readTimeline The read timeline.
+ * @param startTime The start completion time.
+ * @param endTime The end completion time.
+ * @param rangeType The range type.
*
- * @return The instant time set.
+ * @return The sorted instant time list.
*/
- public Set<String> getStartTimeSet(String startCompletionTime, String endCompletionTime) {
+ public List<String> getStartTime(
+ HoodieTimeline readTimeline,
+ @Nullable String startTime,
+ @Nullable String endTime,
+ InstantRange.RangeType rangeType) {
// assumes any instant/transaction lasts at most 1 day to optimize the query efficiency.
- return getStartTimeSet(startCompletionTime, endCompletionTime, s -> HoodieInstantTimeGenerator.instantTimeMinusMillis(s, MILLI_SECONDS_IN_ONE_DAY));
+ return getStartTime(readTimeline, startTime, endTime, rangeType, s -> HoodieInstantTimeGenerator.instantTimeMinusMillis(s, MILLI_SECONDS_IN_ONE_DAY));
+ }
+
+ /**
+ * Queries the instant start time with given completion time range.
+ *
+ * @param startTime The start completion time.
+ * @param endTime The end completion time.
+ * @param earliestInstantTimeFunc The function to generate the earliest start time boundary
+ * with the minimum completion time.
+ *
+ * @return The sorted instant time list.
+ */
+ @VisibleForTesting
+ public List<String> getStartTime(
+ @Nullable String startTime,
+ @Nullable String endTime,
+ Function<String, String> earliestInstantTimeFunc) {
+ return getStartTime(metaClient.getCommitsTimeline().filterCompletedInstants(), startTime, endTime, InstantRange.RangeType.CLOSE_CLOSE, earliestInstantTimeFunc);
}
/**
* Queries the instant start time with given completion time range.
*
- * @param startCompletionTime The start completion time.
- * @param endCompletionTime The end completion time.
- * @param earliestStartTimeFunc The function to generate the earliest start time boundary
- * with the minimum completion time {@code startCompletionTime}.
+ * @param readTimeline The read timeline.
+ * @param startTime The start completion time.
+ * @param endTime The end completion time.
+ * @param rangeType The range type.
+ * @param earliestInstantTimeFunc The function to generate the earliest start time boundary
+ * with the minimum completion time.
*
- * @return The instant time set.
+ * @return The sorted instant time list.
*/
- public Set<String> getStartTimeSet(String startCompletionTime, String endCompletionTime, Function<String, String> earliestStartTimeFunc) {
- String startInstant = earliestStartTimeFunc.apply(startCompletionTime);
+ public List<String> getStartTime(
+ HoodieTimeline readTimeline,
+ @Nullable String startTime,
+ @Nullable String endTime,
+ InstantRange.RangeType rangeType,
+ Function<String, String> earliestInstantTimeFunc) {
+ final boolean startFromEarliest = START_COMMIT_EARLIEST.equalsIgnoreCase(startTime);
+ String earliestInstantToLoad = null;
+ if (startTime != null && !startFromEarliest) {
+ earliestInstantToLoad = earliestInstantTimeFunc.apply(startTime);
+ } else if (endTime != null) {
+ earliestInstantToLoad = earliestInstantTimeFunc.apply(endTime);
+ }
+
+ // ensure the earliest instant boundary be loaded.
+ if (earliestInstantToLoad != null && HoodieTimeline.compareTimestamps(this.cursorInstant, GREATER_THAN, earliestInstantToLoad)) {
+ loadCompletionTimeIncrementally(earliestInstantToLoad);
+ }
+
+ if (startTime == null && endTime != null) {
+ // returns the last instant that finished at or before the given completion time 'endTime'.
+ String maxInstantTime = readTimeline.getInstantsAsStream()
+ .filter(instant -> instant.isCompleted() && HoodieTimeline.compareTimestamps(instant.getCompletionTime(), LESSER_THAN_OR_EQUALS, endTime))
+ .max(Comparator.comparing(HoodieInstant::getCompletionTime)).map(HoodieInstant::getTimestamp).orElse(null);
+ if (maxInstantTime != null) {
+ return Collections.singletonList(maxInstantTime);
+ }
+ // fallback to archived timeline
+ return this.startToCompletionInstantTimeMap.entrySet().stream()
Review Comment:
We should, here we want to filter the timeline first then figure out the max instant, the `startToCompletionInstantTimeMap` constants all the completed instants(which is by design).
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "danny0405 (via GitHub)" <gi...@apache.org>.
danny0405 commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1442458819
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,428 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed before or on 'endTime';
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed in the given time range;
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * </table>
+ *
+ * <p> A range type is required for analyzing the query so that the query range boundary inclusiveness have clear semantics.
+ *
+ * <p>IMPORTANT: the reader may optionally choose to fall back to reading the latest snapshot if there are files missing from decoding the commit metadata.
+ */
+public class IncrementalQueryAnalyzer {
+ public static final String START_COMMIT_EARLIEST = "earliest";
+
+ private final HoodieTableMetaClient metaClient;
+ private final Option<String> startTime;
+ private final Option<String> endTime;
+ private final InstantRange.RangeType rangeType;
+ private final boolean skipCompaction;
+ private final boolean skipClustering;
+ private final int limit;
+
+ private IncrementalQueryAnalyzer(
+ HoodieTableMetaClient metaClient,
+ String startTime,
+ String endTime,
+ InstantRange.RangeType rangeType,
+ boolean skipCompaction,
+ boolean skipClustering,
+ int limit) {
+ this.metaClient = metaClient;
+ this.startTime = Option.ofNullable(startTime);
+ this.endTime = Option.ofNullable(endTime);
+ this.rangeType = rangeType;
+ this.skipCompaction = skipCompaction;
+ this.skipClustering = skipClustering;
+ this.limit = limit;
+ }
+
+ /**
+ * Returns a builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Analyzes the incremental query context with given completion time range.
+ *
+ * @return An incremental query context including the instant time range info.
+ */
+ public QueryContext analyze() {
+ try (CompletionTimeQueryView completionTimeQueryView = new CompletionTimeQueryView(this.metaClient)) {
+ if (completionTimeQueryView.isEmptyTable()) {
+ // no dataset committed in the table
+ return QueryContext.EMPTY;
+ }
+ HoodieTimeline readTimeline = getReadTimeline(this.metaClient);
+ List<String> instantTimeList = completionTimeQueryView.getStartTimes(readTimeline, startTime, endTime, rangeType);
+ if (instantTimeList.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ // get hoodie instants
+ Pair<List<String>, List<String>> splitInstantTime = splitInstantByActiveness(instantTimeList, completionTimeQueryView);
+ Set<String> instantTimeSet = new HashSet<>(instantTimeList);
+ List<String> archivedInstantTime = splitInstantTime.getKey();
+ List<String> activeInstantTime = splitInstantTime.getValue();
+ List<HoodieInstant> archivedInstants = new ArrayList<>();
+ List<HoodieInstant> activeInstants = new ArrayList<>();
+ HoodieTimeline archivedReadTimeline = null;
+ if (!activeInstantTime.isEmpty()) {
+ activeInstants = readTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ if (limit > 0 && limit < activeInstants.size()) {
+ // streaming read speed limit, limits the maximum number of commits allowed to read for each run
+ activeInstants = activeInstants.subList(0, limit);
+ }
+ }
+ if (!archivedInstantTime.isEmpty()) {
+ archivedReadTimeline = getArchivedReadTimeline(metaClient, archivedInstantTime.get(0));
+ archivedInstants = archivedReadTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ }
+ List<String> instants = Stream.concat(archivedInstants.stream(), activeInstants.stream()).map(HoodieInstant::getTimestamp).collect(Collectors.toList());
+ if (instants.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ if (startTime.isEmpty() && endTime.isPresent()) {
+ instants = Collections.singletonList(instants.get(instants.size() - 1));
+ }
+ String lastInstant = instants.get(instants.size() - 1);
+ // keep the same semantics with streaming read, default start from the latest commit
+ String startInstant = START_COMMIT_EARLIEST.equalsIgnoreCase(startTime.orElse(null)) ? null : startTime.isEmpty() ? lastInstant : instants.get(0);
+ String endInstant = endTime.isEmpty() ? null : lastInstant;
+ return QueryContext.create(startInstant, endInstant, instants, archivedInstants, activeInstants, readTimeline, archivedReadTimeline);
+ }
+ }
+
+ /**
+ * Splits the given instant time list into a pair of archived instant list and active instant list.
+ */
+ private static Pair<List<String>, List<String>> splitInstantByActiveness(List<String> instantTimeList, CompletionTimeQueryView completionTimeQueryView) {
Review Comment:
Pair is widely used, still think it’s better.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "danny0405 (via GitHub)" <gi...@apache.org>.
danny0405 commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1425028862
##########
hudi-common/src/main/java/org/apache/hudi/common/table/log/InstantRange.java:
##########
@@ -60,8 +63,8 @@ public String getEndInstant() {
@Override
public String toString() {
return "InstantRange{"
- + "startInstant='" + startInstant == null ? "null" : startInstant + '\''
- + ", endInstant='" + endInstant == null ? "null" : endInstant + '\''
+ + "startInstant='" + (startInstant == null ? "null" : startInstant) + '\''
+ + ", endInstant='" + (endInstant == null ? "null" : endInstant) + '\''
+ ", rangeType='" + this.getClass().getSimpleName() + '\''
Review Comment:
yeah, we do not hold the range type in the range object, so the class name makes sense, will refactor it when moving the package.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "danny0405 (via GitHub)" <gi...@apache.org>.
danny0405 commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1425028379
##########
hudi-common/src/main/java/org/apache/hudi/common/table/log/InstantRange.java:
##########
@@ -22,9 +22,12 @@
import org.apache.hudi.common.util.ValidationUtils;
import java.io.Serializable;
+import java.util.Arrays;
import java.util.Collections;
+import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.stream.Collectors;
/**
* A instant commits range used for incremental reader filtering.
Review Comment:
Will address it in another PR.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "hudi-bot (via GitHub)" <gi...@apache.org>.
hudi-bot commented on PR #10255:
URL: https://github.com/apache/hudi/pull/10255#issuecomment-1854029879
<!--
Meta data
{
"version" : 1,
"metaDataEntries" : [ {
"hash" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21338",
"triggerID" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"triggerType" : "PUSH"
}, {
"hash" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21364",
"triggerID" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"triggerType" : "PUSH"
}, {
"hash" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21395",
"triggerID" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"triggerType" : "PUSH"
}, {
"hash" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"status" : "SUCCESS",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21488",
"triggerID" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"triggerType" : "PUSH"
} ]
}-->
## CI report:
* 3e12be6f351ee6da145de331af53c27c985b7d25 Azure: [SUCCESS](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21488)
<details>
<summary>Bot commands</summary>
@hudi-bot supports the following commands:
- `@hudi-bot run azure` re-run the last Azure build
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "hudi-bot (via GitHub)" <gi...@apache.org>.
hudi-bot commented on PR #10255:
URL: https://github.com/apache/hudi/pull/10255#issuecomment-1853676119
<!--
Meta data
{
"version" : 1,
"metaDataEntries" : [ {
"hash" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21338",
"triggerID" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"triggerType" : "PUSH"
}, {
"hash" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"status" : "DELETED",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21364",
"triggerID" : "bde657c2c091c062c342b0600c3269bbc15ebaaa",
"triggerType" : "PUSH"
}, {
"hash" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"status" : "FAILURE",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21395",
"triggerID" : "3787083e09a0bf7d754d095b5826657efd4f468e",
"triggerType" : "PUSH"
}, {
"hash" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"status" : "PENDING",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21488",
"triggerID" : "3e12be6f351ee6da145de331af53c27c985b7d25",
"triggerType" : "PUSH"
} ]
}-->
## CI report:
* 3787083e09a0bf7d754d095b5826657efd4f468e Azure: [FAILURE](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21395)
* 3e12be6f351ee6da145de331af53c27c985b7d25 Azure: [PENDING](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21488)
<details>
<summary>Bot commands</summary>
@hudi-bot supports the following commands:
- `@hudi-bot run azure` re-run the last Azure build
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "linliu-code (via GitHub)" <gi...@apache.org>.
linliu-code commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1427374230
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,429 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
Review Comment:
instant == instant candidates? If yes, we should stick to use "instant". otherwise, please explain the difference between them.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "linliu-code (via GitHub)" <gi...@apache.org>.
linliu-code commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1427414475
##########
hudi-common/src/main/java/org/apache/hudi/common/table/timeline/CompletionTimeQueryView.java:
##########
@@ -175,42 +187,111 @@ public Option<String> getCompletionTime(String startTime) {
*
* <p>By default, assumes there is at most 1 day time of duration for an instant to accelerate the queries.
*
- * @param startCompletionTime The start completion time.
- * @param endCompletionTime The end completion time.
+ * @param readTimeline The read timeline.
+ * @param rangeStart The query range start completion time.
+ * @param rangeEnd The query range end completion time.
+ * @param rangeType The range type.
*
- * @return The instant time set.
+ * @return The sorted instant time list.
*/
- public Set<String> getStartTimeSet(String startCompletionTime, String endCompletionTime) {
+ public List<String> getStartTimes(
+ HoodieTimeline readTimeline,
+ Option<String> rangeStart,
+ Option<String> rangeEnd,
+ InstantRange.RangeType rangeType) {
// assumes any instant/transaction lasts at most 1 day to optimize the query efficiency.
Review Comment:
How does this assumption come from? For large data replication or sql queries, it can be more than one day.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "hudi-bot (via GitHub)" <gi...@apache.org>.
hudi-bot commented on PR #10255:
URL: https://github.com/apache/hudi/pull/10255#issuecomment-1843084912
<!--
Meta data
{
"version" : 1,
"metaDataEntries" : [ {
"hash" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"status" : "FAILURE",
"url" : "https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21338",
"triggerID" : "07b8dd347e0157ed848cddd4d5bb3b229d42db5a",
"triggerType" : "PUSH"
} ]
}-->
## CI report:
* 07b8dd347e0157ed848cddd4d5bb3b229d42db5a Azure: [FAILURE](https://dev.azure.com/apache-hudi-ci-org/785b6ef4-2f42-4a89-8f0e-5f0d7039a0cc/_build/results?buildId=21338)
<details>
<summary>Bot commands</summary>
@hudi-bot supports the following commands:
- `@hudi-bot run azure` re-run the last Azure build
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "danny0405 (via GitHub)" <gi...@apache.org>.
danny0405 commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1424806427
##########
hudi-common/src/main/java/org/apache/hudi/common/table/timeline/CompletionTimeQueryView.java:
##########
@@ -175,42 +190,109 @@ public Option<String> getCompletionTime(String startTime) {
*
* <p>By default, assumes there is at most 1 day time of duration for an instant to accelerate the queries.
*
- * @param startCompletionTime The start completion time.
- * @param endCompletionTime The end completion time.
+ * @param readTimeline The read timeline.
+ * @param startTime The start completion time.
+ * @param endTime The end completion time.
+ * @param rangeType The range type.
*
- * @return The instant time set.
+ * @return The sorted instant time list.
*/
- public Set<String> getStartTimeSet(String startCompletionTime, String endCompletionTime) {
+ public List<String> getStartTime(
+ HoodieTimeline readTimeline,
+ @Nullable String startTime,
+ @Nullable String endTime,
+ InstantRange.RangeType rangeType) {
// assumes any instant/transaction lasts at most 1 day to optimize the query efficiency.
- return getStartTimeSet(startCompletionTime, endCompletionTime, s -> HoodieInstantTimeGenerator.instantTimeMinusMillis(s, MILLI_SECONDS_IN_ONE_DAY));
+ return getStartTime(readTimeline, startTime, endTime, rangeType, s -> HoodieInstantTimeGenerator.instantTimeMinusMillis(s, MILLI_SECONDS_IN_ONE_DAY));
+ }
+
+ /**
+ * Queries the instant start time with given completion time range.
+ *
+ * @param startTime The start completion time.
+ * @param endTime The end completion time.
+ * @param earliestInstantTimeFunc The function to generate the earliest start time boundary
+ * with the minimum completion time.
+ *
+ * @return The sorted instant time list.
+ */
+ @VisibleForTesting
+ public List<String> getStartTime(
Review Comment:
Currently we are using start instant time, +1 to switch to 'begin'.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "linliu-code (via GitHub)" <gi...@apache.org>.
linliu-code commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1428371728
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,429 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed before or on 'endTime';
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed in the given time range;
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * </table>
+ *
+ * <p> A range type is required for analyzing the query so that the query range boundary inclusiveness have clear semantics.
+ *
+ * <p>IMPORTANT: the reader may optionally choose to fall back to reading the latest snapshot if there are files missing from decoding the commit metadata.
+ */
+public class IncrementalQueryAnalyzer {
+ public static final String START_COMMIT_EARLIEST = "earliest";
+
+ private final HoodieTableMetaClient metaClient;
+ private final Option<String> startTime;
+ private final Option<String> endTime;
+ private final InstantRange.RangeType rangeType;
+ private final boolean skipCompaction;
+ private final boolean skipClustering;
+ private final int limit;
+
+ private IncrementalQueryAnalyzer(
+ HoodieTableMetaClient metaClient,
+ String startTime,
+ String endTime,
+ InstantRange.RangeType rangeType,
+ boolean skipCompaction,
+ boolean skipClustering,
+ int limit) {
+ this.metaClient = metaClient;
+ this.startTime = Option.ofNullable(startTime);
+ this.endTime = Option.ofNullable(endTime);
+ this.rangeType = rangeType;
+ this.skipCompaction = skipCompaction;
+ this.skipClustering = skipClustering;
+ this.limit = limit;
+ }
+
+ /**
+ * Returns a builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Analyzes the incremental query context with given completion time range.
+ *
+ * @return An incremental query context including the instant time range info.
+ */
+ public QueryContext analyze() {
+ try (CompletionTimeQueryView completionTimeQueryView = new CompletionTimeQueryView(this.metaClient)) {
+ if (completionTimeQueryView.isEmptyTable()) {
+ // no dataset committed in the table
+ return QueryContext.EMPTY;
+ }
+ HoodieTimeline readTimeline = getReadTimeline(this.metaClient);
+ List<String> instantTimeList = completionTimeQueryView.getStartTimes(readTimeline, startTime, endTime, rangeType);
+ if (instantTimeList.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ // get hoodie instants
+ Pair<List<String>, List<String>> splitInstantTime = splitInstantByActiveness(instantTimeList, completionTimeQueryView);
+ Set<String> instantTimeSet = new HashSet<>(instantTimeList);
+ List<String> archivedInstantTime = splitInstantTime.getKey();
+ List<String> activeInstantTime = splitInstantTime.getValue();
+ List<HoodieInstant> archivedInstants = new ArrayList<>();
+ List<HoodieInstant> activeInstants = new ArrayList<>();
+ HoodieTimeline archivedReadTimeline = null;
+ if (!activeInstantTime.isEmpty()) {
+ activeInstants = readTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ if (limit > 0 && limit < activeInstants.size()) {
+ // streaming read speed limit, limits the maximum number of commits allowed to read for each run
+ activeInstants = activeInstants.subList(0, limit);
+ }
+ }
+ if (!archivedInstantTime.isEmpty()) {
+ archivedReadTimeline = getArchivedReadTimeline(metaClient, archivedInstantTime.get(0));
+ archivedInstants = archivedReadTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ }
+ List<String> instants = Stream.concat(archivedInstants.stream(), activeInstants.stream()).map(HoodieInstant::getTimestamp).collect(Collectors.toList());
+ if (instants.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ if (startTime.isEmpty() && endTime.isPresent()) {
+ instants = Collections.singletonList(instants.get(instants.size() - 1));
+ }
+ String lastInstant = instants.get(instants.size() - 1);
+ // keep the same semantics with streaming read, default start from the latest commit
+ String startInstant = START_COMMIT_EARLIEST.equalsIgnoreCase(startTime.orElse(null)) ? null : startTime.isEmpty() ? lastInstant : instants.get(0);
+ String endInstant = endTime.isEmpty() ? null : lastInstant;
+ return QueryContext.create(startInstant, endInstant, instants, archivedInstants, activeInstants, readTimeline, archivedReadTimeline);
+ }
+ }
Review Comment:
Just curious, this function compacts complex logics, and a bit hard to understand well. Can we wrap the logic into small functions? SRP?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "linliu-code (via GitHub)" <gi...@apache.org>.
linliu-code commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1428383262
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,429 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed before or on 'endTime';
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed in the given time range;
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * </table>
+ *
+ * <p> A range type is required for analyzing the query so that the query range boundary inclusiveness have clear semantics.
+ *
+ * <p>IMPORTANT: the reader may optionally choose to fall back to reading the latest snapshot if there are files missing from decoding the commit metadata.
+ */
+public class IncrementalQueryAnalyzer {
+ public static final String START_COMMIT_EARLIEST = "earliest";
+
+ private final HoodieTableMetaClient metaClient;
+ private final Option<String> startTime;
+ private final Option<String> endTime;
+ private final InstantRange.RangeType rangeType;
+ private final boolean skipCompaction;
+ private final boolean skipClustering;
+ private final int limit;
+
+ private IncrementalQueryAnalyzer(
+ HoodieTableMetaClient metaClient,
+ String startTime,
+ String endTime,
+ InstantRange.RangeType rangeType,
+ boolean skipCompaction,
+ boolean skipClustering,
+ int limit) {
+ this.metaClient = metaClient;
+ this.startTime = Option.ofNullable(startTime);
+ this.endTime = Option.ofNullable(endTime);
+ this.rangeType = rangeType;
+ this.skipCompaction = skipCompaction;
+ this.skipClustering = skipClustering;
+ this.limit = limit;
+ }
+
+ /**
+ * Returns a builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Analyzes the incremental query context with given completion time range.
+ *
+ * @return An incremental query context including the instant time range info.
+ */
+ public QueryContext analyze() {
+ try (CompletionTimeQueryView completionTimeQueryView = new CompletionTimeQueryView(this.metaClient)) {
+ if (completionTimeQueryView.isEmptyTable()) {
+ // no dataset committed in the table
+ return QueryContext.EMPTY;
+ }
+ HoodieTimeline readTimeline = getReadTimeline(this.metaClient);
+ List<String> instantTimeList = completionTimeQueryView.getStartTimes(readTimeline, startTime, endTime, rangeType);
+ if (instantTimeList.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ // get hoodie instants
+ Pair<List<String>, List<String>> splitInstantTime = splitInstantByActiveness(instantTimeList, completionTimeQueryView);
+ Set<String> instantTimeSet = new HashSet<>(instantTimeList);
+ List<String> archivedInstantTime = splitInstantTime.getKey();
+ List<String> activeInstantTime = splitInstantTime.getValue();
+ List<HoodieInstant> archivedInstants = new ArrayList<>();
+ List<HoodieInstant> activeInstants = new ArrayList<>();
+ HoodieTimeline archivedReadTimeline = null;
+ if (!activeInstantTime.isEmpty()) {
+ activeInstants = readTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ if (limit > 0 && limit < activeInstants.size()) {
+ // streaming read speed limit, limits the maximum number of commits allowed to read for each run
+ activeInstants = activeInstants.subList(0, limit);
+ }
+ }
+ if (!archivedInstantTime.isEmpty()) {
Review Comment:
Not limit for archived instants? Why?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "danny0405 (via GitHub)" <gi...@apache.org>.
danny0405 commented on PR #10255:
URL: https://github.com/apache/hudi/pull/10255#issuecomment-1858800572
> Overall it looks solid to me. Only concern is that this code is very critical and complex, do we have enough tests to ensure the correctness?
We have all the tests for each use case in `TestInputFormat` and `TestIncrementalInputSplits`, they are currently Flink UTs, somehow we need to refactoring the tests into the `hudi-common`. (an engine agnostic data set validation).
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "danny0405 (via GitHub)" <gi...@apache.org>.
danny0405 commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1442457312
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,428 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
Review Comment:
Archived instants included. From users standpoint, there is no notion for active or archived
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "vinothchandar (via GitHub)" <gi...@apache.org>.
vinothchandar commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1486523975
##########
hudi-common/src/main/java/org/apache/hudi/common/table/read/IncrementalQueryAnalyzer.java:
##########
@@ -0,0 +1,428 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hudi.common.table.read;
+
+import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
+import org.apache.hudi.common.table.log.InstantRange;
+import org.apache.hudi.common.table.timeline.CompletionTimeQueryView;
+import org.apache.hudi.common.table.timeline.HoodieArchivedTimeline;
+import org.apache.hudi.common.table.timeline.HoodieInstant;
+import org.apache.hudi.common.table.timeline.HoodieTimeline;
+import org.apache.hudi.common.util.ClusteringUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.VisibleForTesting;
+import org.apache.hudi.common.util.collection.Pair;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Analyzer for incremental queries.
+ *
+ * <p>The analyzer can supply info about the incremental queries including:
+ * <ul>
+ * <li>The archived instant candidates;</li>
+ * <li>The active instant candidates;</li>
+ * <li>The instant filtering predicate, e.g the instant range;</li>
+ * <li>Whether the query starts from the earliest;</li>
+ * <li>Whether the query ends to the latest;</li>
+ * <li>The max completion time used for fs view file slice version filtering.</li>
+ * </ul>
+ *
+ * <p><h2>Criteria for different query ranges:</h2>
+ *
+ * <table>
+ * <tr>
+ * <th>Query Range</th>
+ * <th>File Handles Decoding</th>
+ * <th>Instant Filtering Predicate</th>
+ * </tr>
+ * <tr>
+ * <td>[earliest, _]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>_</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>The latest snapshot files from table metadata</td>
+ * <td>'_hoodie_commit_time' in setA, setA is a collection of all the instants completed before or on 'endTime'</td>
+ * </tr>
+ * <tr>
+ * <td>[_, _]</td>
+ * <td>The latest completed instant metadata</td>
+ * <td>'_hoodie_commit_time' = i_n, i_n is the latest completed instant</td>
+ * </tr>
+ * <tr>
+ * <td>[_, endTime]</td>
+ * <td>i).find the last completed instant i_n before or on 'endTim;
+ * ii). read the latest snapshot from table metadata if i_n is archived or the commit metadata if it is still active</td>
+ * <td>'_hoodie_commit_time' = i_n</td>
+ * </tr>
+ * <tr>
+ * <td>[startTime, _]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed before or on 'endTime';
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * <tr>
+ * <td>[earliest, endTime]</td>
+ * <td>i).find the instant set setA, setA is a collection of all the instants completed in the given time range;
+ * ii). read the latest snapshot from table metadata if setA has archived instants or the commit metadata if all the instants are still active</td>
+ * <td>'_hoodie_commit_time' in setA</td>
+ * </tr>
+ * </table>
+ *
+ * <p> A range type is required for analyzing the query so that the query range boundary inclusiveness have clear semantics.
+ *
+ * <p>IMPORTANT: the reader may optionally choose to fall back to reading the latest snapshot if there are files missing from decoding the commit metadata.
+ */
+public class IncrementalQueryAnalyzer {
+ public static final String START_COMMIT_EARLIEST = "earliest";
+
+ private final HoodieTableMetaClient metaClient;
+ private final Option<String> startTime;
+ private final Option<String> endTime;
+ private final InstantRange.RangeType rangeType;
+ private final boolean skipCompaction;
+ private final boolean skipClustering;
+ private final int limit;
+
+ private IncrementalQueryAnalyzer(
+ HoodieTableMetaClient metaClient,
+ String startTime,
+ String endTime,
+ InstantRange.RangeType rangeType,
+ boolean skipCompaction,
+ boolean skipClustering,
+ int limit) {
+ this.metaClient = metaClient;
+ this.startTime = Option.ofNullable(startTime);
+ this.endTime = Option.ofNullable(endTime);
+ this.rangeType = rangeType;
+ this.skipCompaction = skipCompaction;
+ this.skipClustering = skipClustering;
+ this.limit = limit;
+ }
+
+ /**
+ * Returns a builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Analyzes the incremental query context with given completion time range.
+ *
+ * @return An incremental query context including the instant time range info.
+ */
+ public QueryContext analyze() {
+ try (CompletionTimeQueryView completionTimeQueryView = new CompletionTimeQueryView(this.metaClient)) {
+ if (completionTimeQueryView.isEmptyTable()) {
+ // no dataset committed in the table
+ return QueryContext.EMPTY;
+ }
+ HoodieTimeline readTimeline = getReadTimeline(this.metaClient);
+ List<String> instantTimeList = completionTimeQueryView.getStartTimes(readTimeline, startTime, endTime, rangeType);
+ if (instantTimeList.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ // get hoodie instants
+ Pair<List<String>, List<String>> splitInstantTime = splitInstantByActiveness(instantTimeList, completionTimeQueryView);
+ Set<String> instantTimeSet = new HashSet<>(instantTimeList);
+ List<String> archivedInstantTime = splitInstantTime.getKey();
+ List<String> activeInstantTime = splitInstantTime.getValue();
+ List<HoodieInstant> archivedInstants = new ArrayList<>();
+ List<HoodieInstant> activeInstants = new ArrayList<>();
+ HoodieTimeline archivedReadTimeline = null;
+ if (!activeInstantTime.isEmpty()) {
+ activeInstants = readTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ if (limit > 0 && limit < activeInstants.size()) {
+ // streaming read speed limit, limits the maximum number of commits allowed to read for each run
+ activeInstants = activeInstants.subList(0, limit);
+ }
+ }
+ if (!archivedInstantTime.isEmpty()) {
+ archivedReadTimeline = getArchivedReadTimeline(metaClient, archivedInstantTime.get(0));
+ archivedInstants = archivedReadTimeline.getInstantsAsStream().filter(instant -> instantTimeSet.contains(instant.getTimestamp())).collect(Collectors.toList());
+ }
+ List<String> instants = Stream.concat(archivedInstants.stream(), activeInstants.stream()).map(HoodieInstant::getTimestamp).collect(Collectors.toList());
+ if (instants.isEmpty()) {
+ // no instants completed within the give time range, returns early.
+ return QueryContext.EMPTY;
+ }
+ if (startTime.isEmpty() && endTime.isPresent()) {
Review Comment:
L187 should handle `endtTime` being empty?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "vinothchandar (via GitHub)" <gi...@apache.org>.
vinothchandar commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1482177137
##########
hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/configuration/OptionsResolver.java:
##########
@@ -407,4 +407,12 @@ public static List<ConfigOption<?>> allOptions(Class<?> clazz) {
}
return options;
}
+
+ /**
+ * Whether the reader only consumes new commit instants.
+ */
+ public static boolean isOnlyConsumingNewCommits(Configuration conf) {
Review Comment:
covered by UT?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [HUDI-7184] Add IncrementalQueryAnalyzer for completion time based in… [hudi]
Posted by "danny0405 (via GitHub)" <gi...@apache.org>.
danny0405 commented on code in PR #10255:
URL: https://github.com/apache/hudi/pull/10255#discussion_r1442460318
##########
hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/util/StreamerUtil.java:
##########
@@ -516,7 +517,7 @@ public static boolean fileExists(FileSystem fs, Path path) {
public static boolean isWriteCommit(HoodieTableType tableType, HoodieInstant instant, HoodieTimeline timeline) {
return tableType == HoodieTableType.MERGE_ON_READ
? !instant.getAction().equals(HoodieTimeline.COMMIT_ACTION) // not a compaction
- : !ClusteringUtil.isClusteringInstant(instant, timeline); // not a clustering
+ : !ClusteringUtils.isClusteringInstant(instant, timeline); // not a clustering
Review Comment:
Will do it in another PR.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org