jt2594838 commented on code in PR #18132:
URL: https://github.com/apache/iotdb/pull/18132#discussion_r3567796846
##########
iotdb-client/session/src/main/java/org/apache/iotdb/session/Session.java:
##########
@@ -2857,6 +2867,491 @@ public void insertRelationalTablet(Tablet tablet)
}
}
+ /**
+ * insert relational Tablets.
+ *
+ * @param tablets data batches
+ */
+ public void insertRelationalTablets(List<Tablet> tablets)
+ throws IoTDBConnectionException, StatementExecutionException {
+ final boolean recordMergeTabletsCost = enableMergeTablets;
+ long mergeTabletsCost = 0;
+ if (enableMergeTablets) {
+ final long mergeTabletsStartTime = System.nanoTime();
+ tablets = mergeRelationalTablets(tablets);
+ mergeTabletsCost = System.nanoTime() - mergeTabletsStartTime;
+ }
+ if (tablets.isEmpty()) {
+ throw new BatchExecutionException(SessionMessages.NO_TABLET_INSERTING);
+ }
+ final long insertTabletsStartTime = System.nanoTime();
+ if (enableRedirection) {
+ insertRelationalTabletsWithLeaderCache(tablets);
+ } else {
+ TSInsertTabletsReq request = genTSInsertTabletsReq(tablets, false,
false);
+ request.setWriteToTable(true);
+ for (Tablet tablet : tablets) {
+
request.addToColumnCategoriesList(toEnumOrdinalsAsBytes(tablet.getColumnTypes()));
+ }
+ try {
+ getDefaultSessionConnection().insertTablets(request);
+ } catch (RedirectException ignored) {
+ }
+ }
+ if (recordMergeTabletsCost) {
+ recordMergeTabletsCost(mergeTabletsCost, System.nanoTime() -
insertTabletsStartTime);
+ }
+ }
+
+ private void insertRelationalTabletsWithLeaderCache(final List<Tablet>
tablets)
+ throws IoTDBConnectionException, StatementExecutionException {
+ final Map<SessionConnection, RelationalTabletsReq> tabletGroup = new
HashMap<>();
+ for (final Tablet tablet : tablets) {
+ addRelationalTabletToGroup(tabletGroup, tablet);
+ }
+ if (tabletGroup.size() == 1) {
+ insertRelationalTabletsOnce(tabletGroup);
+ } else {
+ insertRelationalTabletsByGroup(tabletGroup);
+ }
+ }
+
+ private void addRelationalTabletToGroup(
+ final Map<SessionConnection, RelationalTabletsReq> tabletGroup, final
Tablet tablet)
+ throws IoTDBConnectionException {
+ if (SessionUtils.isTabletContainsSingleDevice(tablet)) {
+ final SessionConnection connection =
getTableModelSessionConnection(tablet.getDeviceID(0));
+ updateRelationalTabletsReq(tabletGroup, connection, tablet);
+ return;
+ }
+ final Map<SessionConnection, Map<IDeviceID, Tablet>>
subTabletsByConnection =
+ new LinkedHashMap<>();
+ for (int row = 0; row < tablet.getRowSize(); row++) {
+ final IDeviceID deviceID = tablet.getDeviceID(row);
+ final SessionConnection connection =
getTableModelSessionConnection(deviceID);
+ final Tablet subTablet =
+ subTabletsByConnection
+ .computeIfAbsent(connection, ignored -> new LinkedHashMap<>())
+ .computeIfAbsent(deviceID, ignored ->
createEmptyRelationalTabletLike(tablet));
Review Comment:
Thanks for catching this. I changed the split logic to group rows directly
by target endpoint and pre-count the rows for each endpoint before allocating
sub-tablets. Each sub-tablet now has the exact required capacity, so the
empty-cache case creates one R-row tablet instead of D full-capacity tablets. I
also added a 1,000-distinct-device unit test that verifies only one
exact-capacity tablet is allocated when all rows use the default endpoint. This
will be included in the next push.
##########
iotdb-client/session/src/main/java/org/apache/iotdb/session/Session.java:
##########
@@ -2857,6 +2867,491 @@ public void insertRelationalTablet(Tablet tablet)
}
}
+ /**
+ * insert relational Tablets.
+ *
+ * @param tablets data batches
+ */
+ public void insertRelationalTablets(List<Tablet> tablets)
+ throws IoTDBConnectionException, StatementExecutionException {
+ final boolean recordMergeTabletsCost = enableMergeTablets;
+ long mergeTabletsCost = 0;
+ if (enableMergeTablets) {
+ final long mergeTabletsStartTime = System.nanoTime();
+ tablets = mergeRelationalTablets(tablets);
+ mergeTabletsCost = System.nanoTime() - mergeTabletsStartTime;
+ }
+ if (tablets.isEmpty()) {
+ throw new BatchExecutionException(SessionMessages.NO_TABLET_INSERTING);
+ }
+ final long insertTabletsStartTime = System.nanoTime();
+ if (enableRedirection) {
+ insertRelationalTabletsWithLeaderCache(tablets);
+ } else {
+ TSInsertTabletsReq request = genTSInsertTabletsReq(tablets, false,
false);
+ request.setWriteToTable(true);
+ for (Tablet tablet : tablets) {
+
request.addToColumnCategoriesList(toEnumOrdinalsAsBytes(tablet.getColumnTypes()));
+ }
+ try {
+ getDefaultSessionConnection().insertTablets(request);
+ } catch (RedirectException ignored) {
+ }
+ }
+ if (recordMergeTabletsCost) {
+ recordMergeTabletsCost(mergeTabletsCost, System.nanoTime() -
insertTabletsStartTime);
+ }
+ }
+
+ private void insertRelationalTabletsWithLeaderCache(final List<Tablet>
tablets)
+ throws IoTDBConnectionException, StatementExecutionException {
+ final Map<SessionConnection, RelationalTabletsReq> tabletGroup = new
HashMap<>();
+ for (final Tablet tablet : tablets) {
+ addRelationalTabletToGroup(tabletGroup, tablet);
+ }
+ if (tabletGroup.size() == 1) {
+ insertRelationalTabletsOnce(tabletGroup);
+ } else {
+ insertRelationalTabletsByGroup(tabletGroup);
+ }
+ }
+
+ private void addRelationalTabletToGroup(
+ final Map<SessionConnection, RelationalTabletsReq> tabletGroup, final
Tablet tablet)
+ throws IoTDBConnectionException {
+ if (SessionUtils.isTabletContainsSingleDevice(tablet)) {
+ final SessionConnection connection =
getTableModelSessionConnection(tablet.getDeviceID(0));
+ updateRelationalTabletsReq(tabletGroup, connection, tablet);
+ return;
+ }
+ final Map<SessionConnection, Map<IDeviceID, Tablet>>
subTabletsByConnection =
+ new LinkedHashMap<>();
+ for (int row = 0; row < tablet.getRowSize(); row++) {
+ final IDeviceID deviceID = tablet.getDeviceID(row);
+ final SessionConnection connection =
getTableModelSessionConnection(deviceID);
+ final Tablet subTablet =
+ subTabletsByConnection
+ .computeIfAbsent(connection, ignored -> new LinkedHashMap<>())
+ .computeIfAbsent(deviceID, ignored ->
createEmptyRelationalTabletLike(tablet));
+ for (int column = 0; column < subTablet.getSchemas().size(); column++) {
+ subTablet.addValue(
+ subTablet.getSchemas().get(column).getMeasurementName(),
+ subTablet.getRowSize(),
+ tablet.getValue(row, column));
+ }
+ subTablet.addTimestamp(subTablet.getRowSize(), tablet.getTimestamp(row));
+ }
+ subTabletsByConnection.forEach(
+ (connection, subTablets) ->
+ subTablets
+ .values()
+ .forEach(
+ subTablet -> updateRelationalTabletsReq(tabletGroup,
connection, subTablet)));
+ }
+
+ private SessionConnection getTableModelSessionConnection(final IDeviceID
deviceID)
+ throws IoTDBConnectionException {
+ return tableModelDeviceIdToEndpoint == null ||
tableModelDeviceIdToEndpoint.isEmpty()
+ ? getDefaultSessionConnection()
+ : getSessionConnection(deviceID);
+ }
+
+ private Tablet createEmptyRelationalTabletLike(final Tablet tablet) {
+ final List<String> measurements = new
ArrayList<>(tablet.getSchemas().size());
+ final List<TSDataType> dataTypes = new
ArrayList<>(tablet.getSchemas().size());
+ tablet
+ .getSchemas()
+ .forEach(
+ schema -> {
+ measurements.add(schema.getMeasurementName());
+ dataTypes.add(schema.getType());
+ });
+ return new Tablet(
+ tablet.getTableName(),
+ measurements,
+ dataTypes,
+ tablet.getColumnTypes(),
+ tablet.getRowSize());
+ }
+
+ private void updateRelationalTabletsReq(
+ final Map<SessionConnection, RelationalTabletsReq> tabletGroup,
+ final SessionConnection connection,
+ final Tablet tablet) {
+ tabletGroup
+ .computeIfAbsent(connection, ignored -> new RelationalTabletsReq())
+ .addTablet(tablet);
+ }
+
+ private void buildRelationalTabletsRequests(
+ final Map<SessionConnection, RelationalTabletsReq> tabletGroup) {
+ for (final RelationalTabletsReq relationalTabletsReq :
tabletGroup.values()) {
+ relationalTabletsReq.buildRequest();
+ }
+ }
+
+ private void insertRelationalTabletsOnce(
+ final Map<SessionConnection, RelationalTabletsReq> tabletGroup)
+ throws IoTDBConnectionException, StatementExecutionException {
+ buildRelationalTabletsRequests(tabletGroup);
+ final Map.Entry<SessionConnection, RelationalTabletsReq> entry =
+ tabletGroup.entrySet().iterator().next();
+ final SessionConnection connection = entry.getKey();
+ final RelationalTabletsReq relationalTabletsReq = entry.getValue();
+ try {
+ connection.insertTablets(relationalTabletsReq.request);
+ } catch (RedirectException e) {
+ handleRelationalTabletsRedirection(e, relationalTabletsReq.deviceIDs);
+ } catch (IoTDBConnectionException e) {
+ if (endPointToSessionConnection != null &&
endPointToSessionConnection.size() > 1) {
+ removeBrokenSessionConnection(connection);
+ try {
+
getDefaultSessionConnection().insertTablets(relationalTabletsReq.request);
+ } catch (RedirectException ignored) {
+ }
+ } else {
+ throw e;
+ }
+ }
+ }
+
+ @SuppressWarnings({
+ "squid:S3776"
+ }) // ignore Cognitive Complexity of methods should not be too high
+ private void insertRelationalTabletsByGroup(
+ final Map<SessionConnection, RelationalTabletsReq> tabletGroup)
+ throws IoTDBConnectionException, StatementExecutionException {
+ buildRelationalTabletsRequests(tabletGroup);
+ final List<CompletableFuture<Void>> completableFutures =
+ tabletGroup.entrySet().stream()
+ .map(
+ entry -> {
+ final SessionConnection connection = entry.getKey();
+ final RelationalTabletsReq relationalTabletsReq =
entry.getValue();
+ return CompletableFuture.runAsync(
+ () -> {
+ try {
+
connection.insertTablets(relationalTabletsReq.request);
+ } catch (RedirectException e) {
+ handleRelationalTabletsRedirection(e,
relationalTabletsReq.deviceIDs);
+ } catch (StatementExecutionException e) {
+ throw new CompletionException(e);
+ } catch (IoTDBConnectionException e) {
+ removeBrokenSessionConnection(connection);
+ try {
+ getDefaultSessionConnection()
+ .insertTablets(relationalTabletsReq.request);
+ } catch (IoTDBConnectionException |
StatementExecutionException ex) {
+ throw new CompletionException(ex);
+ } catch (RedirectException ignored) {
+ }
+ }
+ },
+ OPERATION_EXECUTOR);
+ })
+ .collect(Collectors.toList());
+
+ final StringBuilder errMsgBuilder = new StringBuilder();
+ for (final CompletableFuture<Void> completableFuture : completableFutures)
{
+ try {
+ completableFuture.join();
+ } catch (CompletionException completionException) {
+ final Throwable cause = completionException.getCause();
+ logger.error(SessionMessages.MEET_ERROR_WHEN_ASYNC_INSERT, cause);
+ if (cause instanceof IoTDBConnectionException) {
+ throw (IoTDBConnectionException) cause;
+ }
+ if (errMsgBuilder.length() > 0) {
+ errMsgBuilder.append(";");
+ }
+ errMsgBuilder.append(cause.getMessage());
+ }
+ }
+ if (errMsgBuilder.length() > 0) {
+ throw new StatementExecutionException(errMsgBuilder.toString());
+ }
+ }
+
+ private void handleRelationalTabletsRedirection(
+ final RedirectException redirectException, final List<IDeviceID>
deviceIDs) {
+ final List<TEndPoint> endPointList = redirectException.getEndPointList();
+ if (endPointList == null) {
+ return;
+ }
+ for (int i = 0; i < endPointList.size() && i < deviceIDs.size(); i++) {
+ if (endPointList.get(i) != null) {
+ handleRedirection(deviceIDs.get(i), endPointList.get(i));
+ }
+ }
+ }
+
+ private List<Tablet> mergeRelationalTablets(final List<Tablet> tablets) {
+ if (consecutiveRelationalTabletMergeMissCount
+ >= MAX_CONSECUTIVE_RELATIONAL_TABLET_MERGE_MISS_COUNT) {
+ return tablets;
+ }
+ final List<Tablet> sortedTablets = new ArrayList<>(tablets);
+ sortedTablets.sort(
+ Comparator.comparing(
+ Tablet::getTableName,
Comparator.nullsFirst(Comparator.naturalOrder())));
+ final List<RelationalTabletWithColumnIndexMap> mergedTablets = new
ArrayList<>(tablets.size());
+ boolean anyMerged = false;
+ for (final Tablet tablet : sortedTablets) {
+ final RelationalTabletWithColumnIndexMap relationalTablet =
+ new RelationalTabletWithColumnIndexMap(tablet,
getColumnIndexMap(tablet));
+ if (!mergedTablets.isEmpty()) {
+ final int lastIndex = mergedTablets.size() - 1;
+ final RelationalTabletWithColumnIndexMap candidate =
mergedTablets.get(lastIndex);
+ if (canMergeRelationalTablets(candidate, relationalTablet)) {
+ mergedTablets.set(lastIndex, mergeRelationalTablets(candidate,
relationalTablet.tablet));
Review Comment:
Agreed. I replaced the pairwise merge-and-recopy loop with compatible-run
collection. The final schema and row count are accumulated first, then the
merged tablet is allocated once and every source tablet is copied once. The old
intermediate-tablet wrapper and pairwise merge implementation have been
removed. I also added a scaling test with 5,000 compatible tablets, plus a test
covering type conflicts on columns introduced by an earlier tablet in the run.
This will be included in the next push.
##########
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/schema/SchemaValidator.java:
##########
@@ -85,6 +87,30 @@ public static void validate(
}
}
+ public static void validate(
+ final Metadata metadata,
+ final InsertTablets insertStatement,
+ final MPPQueryContext context,
+ final AccessControl accessControl) {
+ try {
+ for (InsertTabletStatement tabletStatement :
+
insertStatement.getInnerTreeStatement().getInsertTabletStatementList()) {
+ accessControl.checkCanInsertIntoTable(
+ context.getSession().getUserName(),
+ new QualifiedObjectName(
+ unQualifyDatabaseName(insertStatement.getDatabase()),
Review Comment:
Fixed. The database is now resolved and unqualified once before the
permission loop, and table names are deduplicated with a set so each table is
checked only once. This removes the repeated InsertMultiTabletsStatement
database scan and duplicate permission checks. This will be included in the
next push.
##########
iotdb-client/session/src/main/java/org/apache/iotdb/session/Session.java:
##########
@@ -2857,6 +2867,491 @@ public void insertRelationalTablet(Tablet tablet)
}
}
+ /**
+ * insert relational Tablets.
+ *
+ * @param tablets data batches
+ */
+ public void insertRelationalTablets(List<Tablet> tablets)
+ throws IoTDBConnectionException, StatementExecutionException {
+ final boolean recordMergeTabletsCost = enableMergeTablets;
+ long mergeTabletsCost = 0;
+ if (enableMergeTablets) {
+ final long mergeTabletsStartTime = System.nanoTime();
+ tablets = mergeRelationalTablets(tablets);
+ mergeTabletsCost = System.nanoTime() - mergeTabletsStartTime;
+ }
+ if (tablets.isEmpty()) {
+ throw new BatchExecutionException(SessionMessages.NO_TABLET_INSERTING);
+ }
+ final long insertTabletsStartTime = System.nanoTime();
+ if (enableRedirection) {
+ insertRelationalTabletsWithLeaderCache(tablets);
+ } else {
+ TSInsertTabletsReq request = genTSInsertTabletsReq(tablets, false,
false);
+ request.setWriteToTable(true);
+ for (Tablet tablet : tablets) {
+
request.addToColumnCategoriesList(toEnumOrdinalsAsBytes(tablet.getColumnTypes()));
+ }
+ try {
+ getDefaultSessionConnection().insertTablets(request);
+ } catch (RedirectException ignored) {
+ }
+ }
+ if (recordMergeTabletsCost) {
+ recordMergeTabletsCost(mergeTabletsCost, System.nanoTime() -
insertTabletsStartTime);
+ }
+ }
+
+ private void insertRelationalTabletsWithLeaderCache(final List<Tablet>
tablets)
+ throws IoTDBConnectionException, StatementExecutionException {
+ final Map<SessionConnection, RelationalTabletsReq> tabletGroup = new
HashMap<>();
+ for (final Tablet tablet : tablets) {
+ addRelationalTabletToGroup(tabletGroup, tablet);
+ }
+ if (tabletGroup.size() == 1) {
+ insertRelationalTabletsOnce(tabletGroup);
+ } else {
+ insertRelationalTabletsByGroup(tabletGroup);
+ }
+ }
+
+ private void addRelationalTabletToGroup(
+ final Map<SessionConnection, RelationalTabletsReq> tabletGroup, final
Tablet tablet)
+ throws IoTDBConnectionException {
+ if (SessionUtils.isTabletContainsSingleDevice(tablet)) {
+ final SessionConnection connection =
getTableModelSessionConnection(tablet.getDeviceID(0));
+ updateRelationalTabletsReq(tabletGroup, connection, tablet);
+ return;
+ }
+ final Map<SessionConnection, Map<IDeviceID, Tablet>>
subTabletsByConnection =
+ new LinkedHashMap<>();
+ for (int row = 0; row < tablet.getRowSize(); row++) {
+ final IDeviceID deviceID = tablet.getDeviceID(row);
+ final SessionConnection connection =
getTableModelSessionConnection(deviceID);
+ final Tablet subTablet =
+ subTabletsByConnection
+ .computeIfAbsent(connection, ignored -> new LinkedHashMap<>())
+ .computeIfAbsent(deviceID, ignored ->
createEmptyRelationalTabletLike(tablet));
+ for (int column = 0; column < subTablet.getSchemas().size(); column++) {
+ subTablet.addValue(
+ subTablet.getSchemas().get(column).getMeasurementName(),
+ subTablet.getRowSize(),
+ tablet.getValue(row, column));
+ }
+ subTablet.addTimestamp(subTablet.getRowSize(), tablet.getTimestamp(row));
+ }
+ subTabletsByConnection.forEach(
+ (connection, subTablets) ->
+ subTablets
+ .values()
+ .forEach(
+ subTablet -> updateRelationalTabletsReq(tabletGroup,
connection, subTablet)));
+ }
+
+ private SessionConnection getTableModelSessionConnection(final IDeviceID
deviceID)
+ throws IoTDBConnectionException {
+ return tableModelDeviceIdToEndpoint == null ||
tableModelDeviceIdToEndpoint.isEmpty()
+ ? getDefaultSessionConnection()
+ : getSessionConnection(deviceID);
+ }
+
+ private Tablet createEmptyRelationalTabletLike(final Tablet tablet) {
+ final List<String> measurements = new
ArrayList<>(tablet.getSchemas().size());
+ final List<TSDataType> dataTypes = new
ArrayList<>(tablet.getSchemas().size());
+ tablet
+ .getSchemas()
+ .forEach(
+ schema -> {
+ measurements.add(schema.getMeasurementName());
+ dataTypes.add(schema.getType());
+ });
+ return new Tablet(
+ tablet.getTableName(),
+ measurements,
+ dataTypes,
+ tablet.getColumnTypes(),
+ tablet.getRowSize());
+ }
+
+ private void updateRelationalTabletsReq(
+ final Map<SessionConnection, RelationalTabletsReq> tabletGroup,
+ final SessionConnection connection,
+ final Tablet tablet) {
+ tabletGroup
+ .computeIfAbsent(connection, ignored -> new RelationalTabletsReq())
+ .addTablet(tablet);
+ }
+
+ private void buildRelationalTabletsRequests(
+ final Map<SessionConnection, RelationalTabletsReq> tabletGroup) {
+ for (final RelationalTabletsReq relationalTabletsReq :
tabletGroup.values()) {
+ relationalTabletsReq.buildRequest();
+ }
+ }
+
+ private void insertRelationalTabletsOnce(
+ final Map<SessionConnection, RelationalTabletsReq> tabletGroup)
+ throws IoTDBConnectionException, StatementExecutionException {
+ buildRelationalTabletsRequests(tabletGroup);
+ final Map.Entry<SessionConnection, RelationalTabletsReq> entry =
+ tabletGroup.entrySet().iterator().next();
+ final SessionConnection connection = entry.getKey();
+ final RelationalTabletsReq relationalTabletsReq = entry.getValue();
+ try {
+ connection.insertTablets(relationalTabletsReq.request);
+ } catch (RedirectException e) {
+ handleRelationalTabletsRedirection(e, relationalTabletsReq.deviceIDs);
+ } catch (IoTDBConnectionException e) {
+ if (endPointToSessionConnection != null &&
endPointToSessionConnection.size() > 1) {
+ removeBrokenSessionConnection(connection);
+ try {
+
getDefaultSessionConnection().insertTablets(relationalTabletsReq.request);
+ } catch (RedirectException ignored) {
+ }
+ } else {
+ throw e;
+ }
+ }
+ }
+
+ @SuppressWarnings({
+ "squid:S3776"
+ }) // ignore Cognitive Complexity of methods should not be too high
+ private void insertRelationalTabletsByGroup(
+ final Map<SessionConnection, RelationalTabletsReq> tabletGroup)
+ throws IoTDBConnectionException, StatementExecutionException {
+ buildRelationalTabletsRequests(tabletGroup);
+ final List<CompletableFuture<Void>> completableFutures =
+ tabletGroup.entrySet().stream()
+ .map(
+ entry -> {
+ final SessionConnection connection = entry.getKey();
+ final RelationalTabletsReq relationalTabletsReq =
entry.getValue();
+ return CompletableFuture.runAsync(
+ () -> {
+ try {
+
connection.insertTablets(relationalTabletsReq.request);
+ } catch (RedirectException e) {
+ handleRelationalTabletsRedirection(e,
relationalTabletsReq.deviceIDs);
+ } catch (StatementExecutionException e) {
+ throw new CompletionException(e);
+ } catch (IoTDBConnectionException e) {
+ removeBrokenSessionConnection(connection);
+ try {
+ getDefaultSessionConnection()
+ .insertTablets(relationalTabletsReq.request);
+ } catch (IoTDBConnectionException |
StatementExecutionException ex) {
+ throw new CompletionException(ex);
+ } catch (RedirectException ignored) {
+ }
+ }
+ },
+ OPERATION_EXECUTOR);
+ })
+ .collect(Collectors.toList());
+
+ final StringBuilder errMsgBuilder = new StringBuilder();
+ for (final CompletableFuture<Void> completableFuture : completableFutures)
{
+ try {
+ completableFuture.join();
+ } catch (CompletionException completionException) {
+ final Throwable cause = completionException.getCause();
+ logger.error(SessionMessages.MEET_ERROR_WHEN_ASYNC_INSERT, cause);
+ if (cause instanceof IoTDBConnectionException) {
+ throw (IoTDBConnectionException) cause;
+ }
+ if (errMsgBuilder.length() > 0) {
+ errMsgBuilder.append(";");
+ }
+ errMsgBuilder.append(cause.getMessage());
+ }
+ }
+ if (errMsgBuilder.length() > 0) {
+ throw new StatementExecutionException(errMsgBuilder.toString());
+ }
+ }
+
+ private void handleRelationalTabletsRedirection(
+ final RedirectException redirectException, final List<IDeviceID>
deviceIDs) {
+ final List<TEndPoint> endPointList = redirectException.getEndPointList();
Review Comment:
Thanks, addressed on both sides. RelationalInsertMultiTabletsNode now
concatenates the per-row redirect metadata produced by each relational tablet,
and TableModelPlanner emits redirect sub-statuses for
InsertMultiTabletsStatement as well. The session passes the row-aligned device
IDs to the multi-device redirect verifier and consumes deviceEndPointMap to
update tableModelDeviceIdToEndpoint. REDIRECTION_RECOMMEND is treated as a
successful write: the client updates the cache without retrying. I added tests
for server-side redirect-list aggregation and client-side cache updates/no
retry. This will be included in the next push.
--
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: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]