前言
上一篇,我們瞭解了 checkpoint 全流程,現在我們具體講解一下 checkpoint 時 snapshot 的全過程。現在我們具體看一下 checkpoint 時是如何做 snapshot 的
正文
由 checkpoint 全流程 我們可以知道
public void executeCheckpointing() throws Exception {
startSyncPartNano = System.nanoTime();
try {
// 調用 StreamOperator 進行 snapshotState 的入口方法
// 先 sourceOperator (flatMap -> source) 再 sinkOperator (sink -> filter)
for (StreamOperator<?> op : allOperators) {
//對每一個算子進行 snapshotInProgress 並存儲至 operatorSnapshotsInProgress
// (存儲 是異步checkpoint的一個引用) 然後分別進行本地 checkpoint store and jobManager ack
// 捕獲 barrier 的過程其實就是處理 input 數據的過程,對應着 StreamInputProcessor.processInput() 方法
checkpointStreamOperator(op);
}
......
}
是做 snapshot 邏輯,具體如下( AbstractStreamOperator.snapshotState )
@Override
// 由此處統一持久化
public final OperatorSnapshotFutures snapshotState(long checkpointId, long timestamp, CheckpointOptions checkpointOptions,
CheckpointStreamFactory factory) throws Exception {
KeyGroupRange keyGroupRange = null != keyedStateBackend ?
keyedStateBackend.getKeyGroupRange() : KeyGroupRange.EMPTY_KEY_GROUP_RANGE;
OperatorSnapshotFutures snapshotInProgress = new OperatorSnapshotFutures();
try (StateSnapshotContextSynchronousImpl snapshotContext = new StateSnapshotContextSynchronousImpl(
checkpointId,
timestamp,
factory,
keyGroupRange,
getContainingTask().getCancelables())) {
snapshotState(snapshotContext);
snapshotInProgress.setKeyedStateRawFuture(snapshotContext.getKeyedStateStreamFuture());
snapshotInProgress.setOperatorStateRawFuture(snapshotContext.getOperatorStateStreamFuture());
// state 持久化
if (null != operatorStateBackend) {
snapshotInProgress.setOperatorStateManagedFuture(
// 觸發一個異步的 snapshot 至 DefaultOperatorStateBackend(內部的)
operatorStateBackend.snapshot(checkpointId, timestamp, factory, checkpointOptions));
}
// source -> flatMap --> rebance --> filter --> keyby --> sink
// 只有當 sink 的時候,keyedStateBackend 纔不爲 null , 纔會執行 backend 的 snapshot
if (null != keyedStateBackend) {
snapshotInProgress.setKeyedStateManagedFuture(
// 觸發一個異步的 snapshot 至 StateBacked
keyedStateBackend.snapshot(checkpointId, timestamp, factory, checkpointOptions));
}
} catch (Exception snapshotException) {
try {
snapshotInProgress.cancel();
} catch (Exception e) {
snapshotException.addSuppressed(e);
}
String snapshotFailMessage = "Could not complete snapshot " + checkpointId + " for operator " +
getOperatorName() + ".";
if (!getContainingTask().isCanceled()) {
LOG.info(snapshotFailMessage, snapshotException);
}
throw new Exception(snapshotFailMessage, snapshotException);
}
return snapshotInProgress;
}
由此可以知道,如果是 null != operatorStateBackend 則 operatorStateBackend.snapshot,如果 null != keyedStateBackend 則 keyedStateBackend.snapshot。
此處,我們以 RocksDBIncrementalSnapshotOperation 爲例 ( operatorStateBackend.snapshot 的代碼註釋已經很清楚了 )
@Nonnull
@Override
protected RunnableFuture<SnapshotResult<KeyedStateHandle>> doSnapshot(
long checkpointId,
long checkpointTimestamp,
@Nonnull CheckpointStreamFactory checkpointStreamFactory,
@Nonnull CheckpointOptions checkpointOptions) throws Exception {
final SnapshotDirectory snapshotDirectory = prepareLocalSnapshotDirectory(checkpointId);
LOG.trace("Local RocksDB checkpoint goes to backup path {}.", snapshotDirectory);
// RocksDBIncrementalRestoreOperation 中 kvStateInformation 賦值
// kvStateInformation.put(columnFamilyName, registeredColumn(RocksDBKeyedStateBackend.RocksDbKvStateInfo));
final List<StateMetaInfoSnapshot> stateMetaInfoSnapshots = new ArrayList<>(kvStateInformation.size());
final Set<StateHandleID> baseSstFiles = snapshotMetaData(checkpointId, stateMetaInfoSnapshots);
// 對 rocksdb 做 checkpoint 爲 RocksDBIncrementalSnapshotOperation.uploadSstFiles 做準備
takeDBNativeCheckpoint(snapshotDirectory);
// snapshot
final RocksDBIncrementalSnapshotOperation snapshotOperation =
new RocksDBIncrementalSnapshotOperation(
checkpointId,
checkpointStreamFactory,
snapshotDirectory,
baseSstFiles,
stateMetaInfoSnapshots);
return snapshotOperation.toAsyncSnapshotFutureTask(cancelStreamRegistry);
}
進入 RocksDBIncrementalSnapshotOperation 內部
@Override
protected SnapshotResult<KeyedStateHandle> callInternal() throws Exception {
boolean completed = false;
// Handle to the meta data file
SnapshotResult<StreamStateHandle> metaStateHandle = null;
// Handles to new sst files since the last completed checkpoint will go here
final Map<StateHandleID, StreamStateHandle> sstFiles = new HashMap<>();
// Handles to the misc files in the current snapshot will go here
final Map<StateHandleID, StreamStateHandle> miscFiles = new HashMap<>();
try {
// 寫 meta (全量) 到 hdfs
metaStateHandle = materializeMetaData();
// Sanity checks - they should never fail
Preconditions.checkNotNull(metaStateHandle, "Metadata was not properly created.");
Preconditions.checkNotNull(metaStateHandle.getJobManagerOwnedSnapshot(),
"Metadata for job manager was not properly created.");
// 將新產生的 sst file、misc file upload to checkpointFs
uploadSstFiles(sstFiles, miscFiles);
synchronized (materializedSstFiles) {
materializedSstFiles.put(checkpointId, sstFiles.keySet());
}
final IncrementalRemoteKeyedStateHandle jmIncrementalKeyedStateHandle =
new IncrementalRemoteKeyedStateHandle(
backendUID,
keyGroupRange,
checkpointId,
sstFiles,
miscFiles,
metaStateHandle.getJobManagerOwnedSnapshot());
//PermanentSnapshotDirectory
final DirectoryStateHandle directoryStateHandle = localBackupDirectory.completeSnapshotAndGetHandle();
final SnapshotResult<KeyedStateHandle> snapshotResult;
if (directoryStateHandle != null && metaStateHandle.getTaskLocalSnapshot() != null) {
// 增量的 localSnapshot
IncrementalLocalKeyedStateHandle localDirKeyedStateHandle =
new IncrementalLocalKeyedStateHandle(
backendUID,
checkpointId,
directoryStateHandle,
keyGroupRange,
metaStateHandle.getTaskLocalSnapshot(),
sstFiles.keySet());
// localSnapshot report to local state manager,
// jobManagerState(jmIncrementalKeyedStateHandle) report to job manager
snapshotResult = SnapshotResult.withLocalState(jmIncrementalKeyedStateHandle, localDirKeyedStateHandle);
} else {
//jobManagerState(jmIncrementalKeyedStateHandle) report to job manager
snapshotResult = SnapshotResult.of(jmIncrementalKeyedStateHandle);
}
completed = true;
return snapshotResult;
} finally {
if (!completed) {
final List<StateObject> statesToDiscard =
new ArrayList<>(1 + miscFiles.size() + sstFiles.size());
statesToDiscard.add(metaStateHandle);
statesToDiscard.addAll(miscFiles.values());
statesToDiscard.addAll(sstFiles.values());
cleanupIncompleteSnapshot(statesToDiscard);
}
}
}
元數據是全部持久化,而數據僅僅將新產生的 sst file、misc file upload to checkpointFs