All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.flink.runtime.state.heap.HeapKeyedStateBackend Maven / Gradle / Ivy

/*
 * 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.flink.runtime.state.heap;

import org.apache.flink.annotation.VisibleForTesting;
import org.apache.flink.api.common.ExecutionConfig;
import org.apache.flink.api.common.state.AggregatingStateDescriptor;
import org.apache.flink.api.common.state.FoldingStateDescriptor;
import org.apache.flink.api.common.state.ListStateDescriptor;
import org.apache.flink.api.common.state.MapStateDescriptor;
import org.apache.flink.api.common.state.ReducingStateDescriptor;
import org.apache.flink.api.common.state.State;
import org.apache.flink.api.common.state.StateDescriptor;
import org.apache.flink.api.common.state.ValueStateDescriptor;
import org.apache.flink.api.common.typeutils.CompatibilityUtil;
import org.apache.flink.api.common.typeutils.TypeSerializer;
import org.apache.flink.api.common.typeutils.UnloadableDummyTypeSerializer;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.core.fs.FSDataInputStream;
import org.apache.flink.core.memory.DataInputViewStreamWrapper;
import org.apache.flink.core.memory.DataOutputViewStreamWrapper;
import org.apache.flink.runtime.checkpoint.CheckpointOptions;
import org.apache.flink.runtime.io.async.AbstractAsyncCallableWithResources;
import org.apache.flink.runtime.io.async.AsyncStoppableTaskWithCallback;
import org.apache.flink.runtime.query.TaskKvStateRegistry;
import org.apache.flink.runtime.state.AbstractKeyedStateBackend;
import org.apache.flink.runtime.state.CheckpointStreamFactory;
import org.apache.flink.runtime.state.CheckpointStreamWithResultProvider;
import org.apache.flink.runtime.state.CheckpointedStateScope;
import org.apache.flink.runtime.state.DoneFuture;
import org.apache.flink.runtime.state.KeyGroupRange;
import org.apache.flink.runtime.state.KeyGroupRangeOffsets;
import org.apache.flink.runtime.state.KeyGroupsStateHandle;
import org.apache.flink.runtime.state.KeyedBackendSerializationProxy;
import org.apache.flink.runtime.state.KeyedStateFunction;
import org.apache.flink.runtime.state.KeyedStateHandle;
import org.apache.flink.runtime.state.LocalRecoveryConfig;
import org.apache.flink.runtime.state.RegisteredKeyedBackendStateMetaInfo;
import org.apache.flink.runtime.state.SnappyStreamCompressionDecorator;
import org.apache.flink.runtime.state.SnapshotResult;
import org.apache.flink.runtime.state.SnapshotStrategy;
import org.apache.flink.runtime.state.StreamCompressionDecorator;
import org.apache.flink.runtime.state.StreamStateHandle;
import org.apache.flink.runtime.state.UncompressedStreamCompressionDecorator;
import org.apache.flink.runtime.state.internal.InternalAggregatingState;
import org.apache.flink.runtime.state.internal.InternalFoldingState;
import org.apache.flink.runtime.state.internal.InternalListState;
import org.apache.flink.runtime.state.internal.InternalMapState;
import org.apache.flink.runtime.state.internal.InternalReducingState;
import org.apache.flink.runtime.state.internal.InternalValueState;
import org.apache.flink.util.Preconditions;
import org.apache.flink.util.StateMigrationException;
import org.apache.flink.util.function.SupplierWithException;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.RunnableFuture;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * A {@link AbstractKeyedStateBackend} that keeps state on the Java Heap and will serialize state to
 * streams provided by a {@link CheckpointStreamFactory} upon checkpointing.
 *
 * @param  The key by which state is keyed.
 */
@Deprecated
public class HeapKeyedStateBackend extends AbstractKeyedStateBackend {

	private static final Logger LOG = LoggerFactory.getLogger(HeapKeyedStateBackend.class);

	/**
	 * Map of state tables that stores all state of key/value states. We store it centrally so
	 * that we can easily checkpoint/restore it.
	 *
	 * 

The actual parameters of StateTable are {@code StateTable>} * but we can't put them here because different key/value states with different types and * namespace types share this central list of tables. */ private final Map> stateTables = new HashMap<>(); /** * Map of state names to their corresponding restored state meta info. * *

* TODO this map can be removed when eager-state registration is in place. * TODO we currently need this cached to check state migration strategies when new serializers are registered. */ private final Map> restoredKvStateMetaInfos; /** * The configuration for local recovery. */ private final LocalRecoveryConfig localRecoveryConfig; /** * The snapshot strategy for this backend. This determines, e.g., if snapshots are synchronous or asynchronous. */ private final HeapSnapshotStrategy snapshotStrategy; public HeapKeyedStateBackend( TaskKvStateRegistry kvStateRegistry, TypeSerializer keySerializer, ClassLoader userCodeClassLoader, int numberOfKeyGroups, KeyGroupRange keyGroupRange, boolean asynchronousSnapshots, ExecutionConfig executionConfig, LocalRecoveryConfig localRecoveryConfig) { super(kvStateRegistry, keySerializer, userCodeClassLoader, numberOfKeyGroups, keyGroupRange, executionConfig); this.localRecoveryConfig = Preconditions.checkNotNull(localRecoveryConfig); SnapshotStrategySynchronicityBehavior synchronicityTrait = asynchronousSnapshots ? new AsyncSnapshotStrategySynchronicityBehavior() : new SyncSnapshotStrategySynchronicityBehavior(); this.snapshotStrategy = new HeapSnapshotStrategy(synchronicityTrait); LOG.info("Initializing heap keyed state backend with stream factory."); this.restoredKvStateMetaInfos = new HashMap<>(); } // ------------------------------------------------------------------------ // state backend operations // ------------------------------------------------------------------------ private StateTable tryRegisterStateTable( TypeSerializer namespaceSerializer, StateDescriptor stateDesc) throws StateMigrationException { @SuppressWarnings("unchecked") StateTable stateTable = (StateTable) stateTables.get(stateDesc.getName()); RegisteredKeyedBackendStateMetaInfo newMetaInfo; if (stateTable != null) { @SuppressWarnings("unchecked") RegisteredKeyedBackendStateMetaInfo.Snapshot restoredMetaInfoSnapshot = (RegisteredKeyedBackendStateMetaInfo.Snapshot) restoredKvStateMetaInfos.get(stateDesc.getName()); Preconditions.checkState( restoredMetaInfoSnapshot != null, "Requested to check compatibility of a restored RegisteredKeyedBackendStateMetaInfo," + " but its corresponding restored snapshot cannot be found."); newMetaInfo = RegisteredKeyedBackendStateMetaInfo.resolveKvStateCompatibility( restoredMetaInfoSnapshot, namespaceSerializer, stateDesc); stateTable.setMetaInfo(newMetaInfo); } else { newMetaInfo = new RegisteredKeyedBackendStateMetaInfo<>( stateDesc.getType(), stateDesc.getName(), namespaceSerializer, stateDesc.getSerializer()); stateTable = snapshotStrategy.newStateTable(newMetaInfo); stateTables.put(stateDesc.getName(), stateTable); } return stateTable; } @Override public Stream getKeys(String state, N namespace) { if (!stateTables.containsKey(state)) { return Stream.empty(); } StateTable table = (StateTable) stateTables.get(state); return table.getKeys(namespace); } private boolean hasRegisteredState() { return !stateTables.isEmpty(); } @Override public InternalValueState createValueState( TypeSerializer namespaceSerializer, ValueStateDescriptor stateDesc) throws Exception { StateTable stateTable = tryRegisterStateTable(namespaceSerializer, stateDesc); return new HeapValueState<>( stateTable, keySerializer, stateTable.getStateSerializer(), stateTable.getNamespaceSerializer(), stateDesc.getDefaultValue()); } @Override public InternalListState createListState( TypeSerializer namespaceSerializer, ListStateDescriptor stateDesc) throws Exception { StateTable> stateTable = tryRegisterStateTable(namespaceSerializer, stateDesc); return new HeapListState<>( stateTable, keySerializer, stateTable.getStateSerializer(), stateTable.getNamespaceSerializer(), stateDesc.getDefaultValue()); } @Override public InternalReducingState createReducingState( TypeSerializer namespaceSerializer, ReducingStateDescriptor stateDesc) throws Exception { StateTable stateTable = tryRegisterStateTable(namespaceSerializer, stateDesc); return new HeapReducingState<>( stateTable, keySerializer, stateTable.getStateSerializer(), stateTable.getNamespaceSerializer(), stateDesc.getDefaultValue(), stateDesc.getReduceFunction()); } @Override public InternalAggregatingState createAggregatingState( TypeSerializer namespaceSerializer, AggregatingStateDescriptor stateDesc) throws Exception { StateTable stateTable = tryRegisterStateTable(namespaceSerializer, stateDesc); return new HeapAggregatingState<>( stateTable, keySerializer, stateTable.getStateSerializer(), stateTable.getNamespaceSerializer(), stateDesc.getDefaultValue(), stateDesc.getAggregateFunction()); } @Override public InternalFoldingState createFoldingState( TypeSerializer namespaceSerializer, FoldingStateDescriptor stateDesc) throws Exception { StateTable stateTable = tryRegisterStateTable(namespaceSerializer, stateDesc); return new HeapFoldingState<>( stateTable, keySerializer, stateTable.getStateSerializer(), stateTable.getNamespaceSerializer(), stateDesc.getDefaultValue(), stateDesc.getFoldFunction()); } @Override protected InternalMapState createMapState( TypeSerializer namespaceSerializer, MapStateDescriptor stateDesc) throws Exception { StateTable> stateTable = tryRegisterStateTable(namespaceSerializer, stateDesc); return new HeapMapState<>( stateTable, keySerializer, stateTable.getStateSerializer(), stateTable.getNamespaceSerializer(), stateDesc.getDefaultValue()); } @Override @SuppressWarnings("unchecked") public RunnableFuture> snapshot( final long checkpointId, final long timestamp, final CheckpointStreamFactory streamFactory, CheckpointOptions checkpointOptions) { return snapshotStrategy.performSnapshot(checkpointId, timestamp, streamFactory, checkpointOptions); } @SuppressWarnings("deprecation") public void restore(Collection restoredState) throws Exception { if (restoredState == null || restoredState.isEmpty()) { return; } LOG.info("Initializing heap keyed state backend from snapshot."); if (LOG.isDebugEnabled()) { LOG.debug("Restoring snapshot from state handles: {}.", restoredState); } restorePartitionedState(restoredState); } @SuppressWarnings({"unchecked"}) private void restorePartitionedState(Collection state) throws Exception { final Map kvStatesById = new HashMap<>(); int numRegisteredKvStates = 0; stateTables.clear(); boolean keySerializerRestored = false; for (KeyedStateHandle keyedStateHandle : state) { if (keyedStateHandle == null) { continue; } if (!(keyedStateHandle instanceof KeyGroupsStateHandle)) { throw new IllegalStateException("Unexpected state handle type, " + "expected: " + KeyGroupsStateHandle.class + ", but found: " + keyedStateHandle.getClass()); } KeyGroupsStateHandle keyGroupsStateHandle = (KeyGroupsStateHandle) keyedStateHandle; FSDataInputStream fsDataInputStream = keyGroupsStateHandle.openInputStream(); cancelStreamRegistry.registerCloseable(fsDataInputStream); try { DataInputViewStreamWrapper inView = new DataInputViewStreamWrapper(fsDataInputStream); // isSerializerPresenceRequired flag is set to true, since for the heap state backend, // deserialization of state happens eagerly at restore time KeyedBackendSerializationProxy serializationProxy = new KeyedBackendSerializationProxy<>(userCodeClassLoader, true); serializationProxy.read(inView); if (!keySerializerRestored) { // check for key serializer compatibility; this also reconfigures the // key serializer to be compatible, if it is required and is possible if (CompatibilityUtil.resolveCompatibilityResult( serializationProxy.getKeySerializer(), UnloadableDummyTypeSerializer.class, serializationProxy.getKeySerializerConfigSnapshot(), keySerializer) .isRequiresMigration()) { // TODO replace with state migration; note that key hash codes need to remain the same after migration throw new StateMigrationException("The new key serializer is not compatible to read previous keys. " + "Aborting now since state migration is currently not available"); } keySerializerRestored = true; } List> restoredMetaInfos = serializationProxy.getStateMetaInfoSnapshots(); for (RegisteredKeyedBackendStateMetaInfo.Snapshot restoredMetaInfo : restoredMetaInfos) { restoredKvStateMetaInfos.put(restoredMetaInfo.getName(), restoredMetaInfo); StateTable stateTable = stateTables.get(restoredMetaInfo.getName()); //important: only create a new table we did not already create it previously if (null == stateTable) { RegisteredKeyedBackendStateMetaInfo registeredKeyedBackendStateMetaInfo = new RegisteredKeyedBackendStateMetaInfo<>( restoredMetaInfo.getStateType(), restoredMetaInfo.getName(), restoredMetaInfo.getNamespaceSerializer(), restoredMetaInfo.getStateSerializer()); stateTable = snapshotStrategy.newStateTable(registeredKeyedBackendStateMetaInfo); stateTables.put(restoredMetaInfo.getName(), stateTable); kvStatesById.put(numRegisteredKvStates, restoredMetaInfo.getName()); ++numRegisteredKvStates; } else { // TODO with eager state registration in place, check here for serializer migration strategies } } final StreamCompressionDecorator streamCompressionDecorator = serializationProxy.isUsingKeyGroupCompression() ? SnappyStreamCompressionDecorator.INSTANCE : UncompressedStreamCompressionDecorator.INSTANCE; for (Tuple2 groupOffset : keyGroupsStateHandle.getGroupRangeOffsets()) { int keyGroupIndex = groupOffset.f0; long offset = groupOffset.f1; // Check that restored key groups all belong to the backend. Preconditions.checkState(keyGroupRange.contains(keyGroupIndex), "The key group must belong to the backend."); fsDataInputStream.seek(offset); int writtenKeyGroupIndex = inView.readInt(); try (InputStream kgCompressionInStream = streamCompressionDecorator.decorateWithCompression(fsDataInputStream)) { DataInputViewStreamWrapper kgCompressionInView = new DataInputViewStreamWrapper(kgCompressionInStream); Preconditions.checkState(writtenKeyGroupIndex == keyGroupIndex, "Unexpected key-group in restore."); for (int i = 0; i < restoredMetaInfos.size(); i++) { int kvStateId = kgCompressionInView.readShort(); StateTable stateTable = stateTables.get(kvStatesById.get(kvStateId)); StateTableByKeyGroupReader keyGroupReader = StateTableByKeyGroupReaders.readerForVersion( stateTable, serializationProxy.getReadVersion()); keyGroupReader.readMappingsInKeyGroup(kgCompressionInView, keyGroupIndex); } } } } finally { if (cancelStreamRegistry.unregisterCloseable(fsDataInputStream)) { IOUtils.closeQuietly(fsDataInputStream); } } } } @Override public void notifyCheckpointComplete(long checkpointId) { //Nothing to do } @Override public void applyToAllKeys( final N namespace, final TypeSerializer namespaceSerializer, final StateDescriptor stateDescriptor, final KeyedStateFunction function) throws Exception { try (Stream keyStream = getKeys(stateDescriptor.getName(), namespace)) { // we copy the keys into list to avoid the concurrency problem // when state.clear() is invoked in function.process(). final List keys = keyStream.collect(Collectors.toList()); final S state = getPartitionedState( namespace, namespaceSerializer, stateDescriptor); for (K key : keys) { setCurrentKey(key); function.process(key, state); } } } @Override public String toString() { return "HeapKeyedStateBackend"; } /** * Returns the total number of state entries across all keys/namespaces. */ @VisibleForTesting @SuppressWarnings("unchecked") @Override public int numStateEntries() { int sum = 0; for (StateTable stateTable : stateTables.values()) { sum += stateTable.size(); } return sum; } /** * Returns the total number of state entries across all keys for the given namespace. */ @VisibleForTesting public int numStateEntries(Object namespace) { int sum = 0; for (StateTable stateTable : stateTables.values()) { sum += stateTable.sizeOfNamespace(namespace); } return sum; } @Override public boolean supportsAsynchronousSnapshots() { return snapshotStrategy.isAsynchronous(); } @VisibleForTesting public LocalRecoveryConfig getLocalRecoveryConfig() { return localRecoveryConfig; } private interface SnapshotStrategySynchronicityBehavior { default void finalizeSnapshotBeforeReturnHook(Runnable runnable) { } default void logOperationCompleted(CheckpointStreamFactory streamFactory, long startTime) { } boolean isAsynchronous(); StateTable newStateTable(RegisteredKeyedBackendStateMetaInfo newMetaInfo); } private class AsyncSnapshotStrategySynchronicityBehavior implements SnapshotStrategySynchronicityBehavior { @Override public void logOperationCompleted(CheckpointStreamFactory streamFactory, long startTime) { LOG.info("Heap backend snapshot ({}, asynchronous part) in thread {} took {} ms.", streamFactory, Thread.currentThread(), (System.currentTimeMillis() - startTime)); } @Override public boolean isAsynchronous() { return true; } @Override public StateTable newStateTable(RegisteredKeyedBackendStateMetaInfo newMetaInfo) { return new CopyOnWriteStateTable<>(HeapKeyedStateBackend.this, newMetaInfo); } } private class SyncSnapshotStrategySynchronicityBehavior implements SnapshotStrategySynchronicityBehavior { @Override public void finalizeSnapshotBeforeReturnHook(Runnable runnable) { // this triggers a synchronous execution from the main checkpointing thread. runnable.run(); } @Override public boolean isAsynchronous() { return false; } @Override public StateTable newStateTable(RegisteredKeyedBackendStateMetaInfo newMetaInfo) { return new NestedMapsStateTable<>(HeapKeyedStateBackend.this, newMetaInfo); } } /** * Base class for the snapshots of the heap backend that outlines the algorithm and offers some hooks to realize * the concrete strategies. Subclasses must be threadsafe. */ private class HeapSnapshotStrategy implements SnapshotStrategy>, SnapshotStrategySynchronicityBehavior { private final SnapshotStrategySynchronicityBehavior snapshotStrategySynchronicityTrait; public HeapSnapshotStrategy( SnapshotStrategySynchronicityBehavior snapshotStrategySynchronicityTrait) { this.snapshotStrategySynchronicityTrait = snapshotStrategySynchronicityTrait; } @Override public RunnableFuture> performSnapshot( long checkpointId, long timestamp, CheckpointStreamFactory primaryStreamFactory, CheckpointOptions checkpointOptions) { if (!hasRegisteredState()) { return DoneFuture.of(SnapshotResult.empty()); } long syncStartTime = System.currentTimeMillis(); Preconditions.checkState(stateTables.size() <= Short.MAX_VALUE, "Too many KV-States: " + stateTables.size() + ". Currently at most " + Short.MAX_VALUE + " states are supported"); List> metaInfoSnapshots = new ArrayList<>(stateTables.size()); final Map kVStateToId = new HashMap<>(stateTables.size()); final Map cowStateStableSnapshots = new HashMap<>(stateTables.size()); for (Map.Entry> kvState : stateTables.entrySet()) { String stateName = kvState.getKey(); kVStateToId.put(stateName, kVStateToId.size()); StateTable stateTable = kvState.getValue(); if (null != stateTable) { metaInfoSnapshots.add(stateTable.getMetaInfo().snapshot()); cowStateStableSnapshots.put(stateName, stateTable.createSnapshot()); } } final KeyedBackendSerializationProxy serializationProxy = new KeyedBackendSerializationProxy<>( // TODO: this code assumes that writing a serializer is threadsafe, we should support to // get a serialized form already at state registration time in the future keySerializer, metaInfoSnapshots, !Objects.equals(UncompressedStreamCompressionDecorator.INSTANCE, keyGroupCompressionDecorator)); final SupplierWithException checkpointStreamSupplier = localRecoveryConfig.isLocalRecoveryEnabled() ? () -> CheckpointStreamWithResultProvider.createDuplicatingStream( checkpointId, CheckpointedStateScope.EXCLUSIVE, primaryStreamFactory, localRecoveryConfig.getLocalStateDirectoryProvider()) : () -> CheckpointStreamWithResultProvider.createSimpleStream( checkpointId, CheckpointedStateScope.EXCLUSIVE, primaryStreamFactory); //--------------------------------------------------- this becomes the end of sync part // implementation of the async IO operation, based on FutureTask final AbstractAsyncCallableWithResources> ioCallable = new AbstractAsyncCallableWithResources>() { CheckpointStreamWithResultProvider streamAndResultExtractor = null; @Override protected void acquireResources() throws Exception { streamAndResultExtractor = checkpointStreamSupplier.get(); cancelStreamRegistry.registerCloseable(streamAndResultExtractor); } @Override protected void releaseResources() { unregisterAndCloseStreamAndResultExtractor(); for (StateTableSnapshot tableSnapshot : cowStateStableSnapshots.values()) { tableSnapshot.release(); } } @Override protected void stopOperation() { unregisterAndCloseStreamAndResultExtractor(); } private void unregisterAndCloseStreamAndResultExtractor() { if (cancelStreamRegistry.unregisterCloseable(streamAndResultExtractor)) { IOUtils.closeQuietly(streamAndResultExtractor); streamAndResultExtractor = null; } } @Nonnull @Override protected SnapshotResult performOperation() throws Exception { long startTime = System.currentTimeMillis(); CheckpointStreamFactory.CheckpointStateOutputStream localStream = this.streamAndResultExtractor.getCheckpointOutputStream(); DataOutputViewStreamWrapper outView = new DataOutputViewStreamWrapper(localStream); serializationProxy.write(outView); long[] keyGroupRangeOffsets = new long[keyGroupRange.getNumberOfKeyGroups()]; for (int keyGroupPos = 0; keyGroupPos < keyGroupRange.getNumberOfKeyGroups(); ++keyGroupPos) { int keyGroupId = keyGroupRange.getKeyGroupId(keyGroupPos); keyGroupRangeOffsets[keyGroupPos] = localStream.getPos(); outView.writeInt(keyGroupId); for (Map.Entry kvState : cowStateStableSnapshots.entrySet()) { try (OutputStream kgCompressionOut = keyGroupCompressionDecorator.decorateWithCompression(localStream)) { String stateName = kvState.getKey(); DataOutputViewStreamWrapper kgCompressionView = new DataOutputViewStreamWrapper(kgCompressionOut); kgCompressionView.writeShort(kVStateToId.get(stateName)); kvState.getValue().writeMappingsInKeyGroup(kgCompressionView, keyGroupId); } // this will just close the outer compression stream } } if (cancelStreamRegistry.unregisterCloseable(streamAndResultExtractor)) { KeyGroupRangeOffsets kgOffs = new KeyGroupRangeOffsets(keyGroupRange, keyGroupRangeOffsets); SnapshotResult result = streamAndResultExtractor.closeAndFinalizeCheckpointStreamResult(); streamAndResultExtractor = null; logOperationCompleted(primaryStreamFactory, startTime); return CheckpointStreamWithResultProvider.toKeyedStateHandleSnapshotResult(result, kgOffs); } return SnapshotResult.empty(); } }; AsyncStoppableTaskWithCallback> task = AsyncStoppableTaskWithCallback.from(ioCallable); finalizeSnapshotBeforeReturnHook(task); LOG.info("Heap backend snapshot (" + primaryStreamFactory + ", synchronous part) in thread " + Thread.currentThread() + " took " + (System.currentTimeMillis() - syncStartTime) + " ms."); return task; } @Override public void finalizeSnapshotBeforeReturnHook(Runnable runnable) { snapshotStrategySynchronicityTrait.finalizeSnapshotBeforeReturnHook(runnable); } @Override public void logOperationCompleted(CheckpointStreamFactory streamFactory, long startTime) { snapshotStrategySynchronicityTrait.logOperationCompleted(streamFactory, startTime); } @Override public boolean isAsynchronous() { return snapshotStrategySynchronicityTrait.isAsynchronous(); } @Override public StateTable newStateTable(RegisteredKeyedBackendStateMetaInfo newMetaInfo) { return snapshotStrategySynchronicityTrait.newStateTable(newMetaInfo); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy