!932 [I3FHGK] Improve snapshot stability

Merge pull request !932 from sandygao/improve-snapshot-stability
This commit is contained in:
i-robot 2021-06-15 08:49:23 +08:00 committed by Gitee
commit df01ea5e1c
8 changed files with 26 additions and 27 deletions

View File

@ -1,14 +0,0 @@
# Copyright (C) 2020. Huawei Technologies Co., Ltd. All rights reserved.
# Licensed 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.
connector.name=memory

View File

@ -30,7 +30,6 @@ To be able to resume execution from a previously saved snapshot, there must be a
- **Interaction with other features**: distributed snapshot does not yet work with the following features:
- Reuse exchange, i.e. `optimizer.reuse-table-scan`
- Reuse common table expression (CTE), i.e. `optimizer.cte-reuse-enabled`
- Spill, i.e. `experimental.spill-enabled`
When a query that does not meet the above requirements is submitted with distributed snapshot enabled, the query will be executed as if the distributed snapshot feature is _not_ turned on.

View File

@ -30,7 +30,6 @@
- **与其他功能的交互**:分布式快照目前无法与以下功能一起使用:
- 重用交换,即`optimizer.reuse-table-scan`
- 重用公用表表达式CTE即`optimizer.cte-reuse-enabled`
- 溢出,即`experimental.spill-enabled`
在启用分布式快照的情况下提交不满足上述要求的查询时,查询按未启用分布式快照功能的场景执行。

View File

@ -20,6 +20,7 @@ import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import io.airlift.http.client.HttpStatus;
import io.airlift.log.Logger;
import io.airlift.units.Duration;
import io.prestosql.Session;
@ -78,6 +79,7 @@ import static io.prestosql.SystemSessionProperties.isSnapshotEnabled;
import static io.prestosql.failuredetector.FailureDetector.State.GONE;
import static io.prestosql.operator.ExchangeOperator.REMOTE_CONNECTOR_ID;
import static io.prestosql.spi.StandardErrorCode.GENERIC_INTERNAL_ERROR;
import static io.prestosql.spi.StandardErrorCode.PAGE_TRANSPORT_ERROR;
import static io.prestosql.spi.StandardErrorCode.REMOTE_HOST_GONE;
import static java.util.Objects.requireNonNull;
@ -593,11 +595,16 @@ public final class SqlStageExecution
.map(this::rewriteTransportFailure)
.map(ExecutionFailureInfo::toException)
.orElse(new PrestoException(GENERIC_INTERNAL_ERROR, "A task failed for an unknown reason"));
// Snapshot: if remote task failed because they received 5xx from other tasks, then we treat it as resumable.
if (failure.getMessage() != null && failure.getMessage().contains("to be 200, but was 5")) {
log.debug(failure, "Task %s on node %s failed but is resumable. Triggering rescheduling.", taskStatus.getTaskId(), taskStatus.getNodeId());
stateMachine.transitionToResumableFailure();
return;
// Snapshot: if remote task failed because they received unexpecte response (5xx or missing/wrong header), then we treat it as resumable.
if (isSnapshotEnabled && failure instanceof Failure && ((Failure) failure).getErrorCode().equals(PAGE_TRANSPORT_ERROR.toErrorCode())) {
String message = failure.getMessage();
// message ends with "(response status code %d)"
int responseCode = Integer.parseInt(message.substring(message.lastIndexOf(' ') + 1, message.lastIndexOf(')')));
if (responseCode >= 500 || responseCode == HttpStatus.OK.code()) {
log.debug(failure, "Task %s on node %s failed but is resumable. Triggering rescheduling.", taskStatus.getTaskId(), taskStatus.getNodeId());
stateMachine.transitionToResumableFailure();
return;
}
}
stateMachine.transitionToFailed(failure);
}

View File

@ -638,7 +638,7 @@ public final class HttpPageBufferClient
return createEmptyPagesResponse(0, 0, false);
}
}
throw new PageTransportErrorException(format("Error fetching %s: %s", request.getUri().toASCIIString(), e.getMessage()), e);
throw new PageTransportErrorException(format("Error fetching %s: %s (response status code %d)", request.getUri().toASCIIString(), e.getMessage(), response.getStatusCode()), e);
}
}

View File

@ -383,7 +383,6 @@ public class MultiInputSnapshotState
// First time a marker is received for a snapshot. Store operator state and make marker available.
snapshot = new SnapshotState(marker);
pendingMarkers.add(marker);
// No need to take snapshot if not all input channels are known. The snapshot won't be complete.
try {
snapshot.states.add(restorable.capture(pagesSerde));
}

View File

@ -193,7 +193,14 @@ public class QuerySnapshotManager
}
}
saveQuerySnapshotResult();
try {
saveQuerySnapshotResult();
}
catch (Exception e) {
LOG.warn(e, "Failed to save query snapshot state for %s: %s", queryId, e.getMessage());
invalidateAllSnapshots();
result = OptionalLong.empty();
}
}
if (result.isPresent()) {
@ -211,7 +218,6 @@ public class QuerySnapshotManager
for (Long snapshotId : captureResults.keySet()) {
captureResults.put(snapshotId, SnapshotResult.NA);
}
saveQuerySnapshotResult();
}
}
@ -415,8 +421,6 @@ public class QuerySnapshotManager
SnapshotResult oldResult = captureResults.put(snapshotId, snapshotResult);
if (snapshotResult != oldResult && snapshotResult.isDone()) {
LOG.debug("Finished capturing snapshot %d for query %s. Result is %s.", snapshotId, queryId.getId(), snapshotResult);
// Store snapshot information for this query in filesystem, so it can be accessed by tasks, e.g. during backtrack state loading.
saveQuerySnapshotResult();
}
}
}

View File

@ -121,6 +121,7 @@ public class TestTaskSnapshotManager
QuerySnapshotManager querySnapshotManager = new QuerySnapshotManager(queryId, snapshotUtils, TEST_SNAPSHOT_SESSION);
querySnapshotManager.addNewTask(taskId);
querySnapshotManager.updateQueryCapture(taskId, Collections.singletonMap(1L, SnapshotResult.SUCCESSFUL));
querySnapshotManager.getResumeSnapshotId();
SnapshotStateId newStateId = stateId.withSnapshotId(2);
Optional<Object> loadedState = snapshotManager.loadState(newStateId);
@ -187,13 +188,17 @@ public class TestTaskSnapshotManager
// Try2: Previous snapshots are setup, so load should be successful
querySnapshotManager.updateQueryCapture(taskId, Collections.singletonMap(2L, SnapshotResult.SUCCESSFUL));
querySnapshotManager.getResumeSnapshotId();
assertTrue(snapshotManager.loadFile(id4load, targetPath));
String output = Files.readAllLines(targetPath).get(0);
Assert.assertEquals(output, fileContent);
// Try3: Previous snapshot failed
querySnapshotManager.updateQueryRestore(taskId, Optional.of(new RestoreResult(2, SnapshotResult.SUCCESSFUL)));
querySnapshotManager.updateQueryCapture(taskId, Collections.singletonMap(2L, SnapshotResult.FAILED));
querySnapshotManager.updateQueryCapture(taskId, Collections.singletonMap(3L, SnapshotResult.SUCCESSFUL));
querySnapshotManager.getResumeSnapshotId();
assertFalse(snapshotManager.loadFile(id4load, targetPath));
}