use of org.apache.flink.runtime.persistence.PossibleInconsistentStateException in project flink by apache.
the class KubernetesStateHandleStoreTest method testAddWithPossiblyInconsistentStateHandling.
@Test
public void testAddWithPossiblyInconsistentStateHandling() throws Exception {
new Context() {
{
runTest(() -> {
leaderCallbackGrantLeadership();
final FlinkKubeClient anotherFlinkKubeClient = createFlinkKubeClientBuilder().setCheckAndUpdateConfigMapFunction((configMapName, function) -> FutureUtils.completedExceptionally(new PossibleInconsistentStateException())).build();
final KubernetesStateHandleStore<TestingLongStateHandleHelper.LongStateHandle> store = new KubernetesStateHandleStore<>(anotherFlinkKubeClient, LEADER_CONFIGMAP_NAME, longStateStorage, filter, LOCK_IDENTITY);
try {
store.addAndLock(key, state);
fail("PossibleInconsistentStateException should have been thrown.");
} catch (PossibleInconsistentStateException ex) {
// PossibleInconsistentStateException is expected
}
assertThat(TestingLongStateHandleHelper.getGlobalStorageSize(), is(1));
assertThat(TestingLongStateHandleHelper.getGlobalDiscardCount(), is(0));
});
}
};
}
use of org.apache.flink.runtime.persistence.PossibleInconsistentStateException in project flink by apache.
the class KubernetesStateHandleStore method addAndLock.
/**
* Creates a state handle, stores it in ConfigMap. We could guarantee that only the leader could
* update the ConfigMap. Since “Get(check the leader)-and-Update(write back to the ConfigMap)”
* is a transactional operation.
*
* @param key Key in ConfigMap
* @param state State to be added
* @throws AlreadyExistException if the name already exists
* @throws PossibleInconsistentStateException if the write-to-Kubernetes operation failed. This
* indicates that it's not clear whether the new state was successfully written to
* Kubernetes or not. No state was discarded. Proper error handling has to be applied on the
* caller's side.
* @throws Exception if persisting state or writing state handle failed
*/
@Override
public RetrievableStateHandle<T> addAndLock(String key, T state) throws PossibleInconsistentStateException, Exception {
checkNotNull(key, "Key in ConfigMap.");
checkNotNull(state, "State.");
final RetrievableStateHandle<T> storeHandle = storage.store(state);
final byte[] serializedStoreHandle = serializeOrDiscard(storeHandle);
// initialize flag to serve the failure case
boolean discardState = true;
try {
// a successful operation will result in the state not being discarded
discardState = !kubeClient.checkAndUpdateConfigMap(configMapName, c -> {
if (isValidOperation(c)) {
if (!c.getData().containsKey(key)) {
c.getData().put(key, encodeStateHandle(serializedStoreHandle));
return Optional.of(c);
} else {
throw new CompletionException(getKeyAlreadyExistException(key));
}
}
return Optional.empty();
}).get();
return storeHandle;
} catch (Exception ex) {
final Optional<PossibleInconsistentStateException> possibleInconsistentStateException = ExceptionUtils.findThrowable(ex, PossibleInconsistentStateException.class);
if (possibleInconsistentStateException.isPresent()) {
// it's unclear whether the state handle metadata was written to the ConfigMap -
// hence, we don't discard the data
discardState = false;
throw possibleInconsistentStateException.get();
}
throw ExceptionUtils.findThrowable(ex, AlreadyExistException.class).orElseThrow(() -> ex);
} finally {
if (discardState) {
storeHandle.discardState();
}
}
}
use of org.apache.flink.runtime.persistence.PossibleInconsistentStateException in project flink by apache.
the class KubernetesStateHandleStore method replace.
/**
* Replaces a state handle in ConfigMap and discards the old state handle. Wo do not lock
* resource version and then replace in Kubernetes. Since the ConfigMap is periodically updated
* by leader, the resource version changes very fast. We use a "check-existence and update"
* transactional operation instead.
*
* @param key Key in ConfigMap
* @param resourceVersion resource version when checking existence via {@link #exists}.
* @param state State to be added
* @throws NotExistException if the name does not exist
* @throws PossibleInconsistentStateException if a failure occurred during the update operation.
* It's unclear whether the operation actually succeeded or not. No state was discarded. The
* method's caller should handle this case properly.
* @throws Exception if persisting state or writing state handle failed
*/
@Override
public void replace(String key, StringResourceVersion resourceVersion, T state) throws Exception {
checkNotNull(key, "Key in ConfigMap.");
checkNotNull(state, "State.");
final RetrievableStateHandle<T> oldStateHandle = getAndLock(key);
final RetrievableStateHandle<T> newStateHandle = storage.store(state);
final byte[] serializedStateHandle = serializeOrDiscard(newStateHandle);
// initialize flags to serve the failure case
boolean discardOldState = false;
boolean discardNewState = true;
try {
boolean success = kubeClient.checkAndUpdateConfigMap(configMapName, c -> {
if (isValidOperation(c)) {
// Check the existence
if (c.getData().containsKey(key)) {
c.getData().put(key, encodeStateHandle(serializedStateHandle));
} else {
throw new CompletionException(getKeyNotExistException(key));
}
return Optional.of(c);
}
return Optional.empty();
}).get();
// swap subject for deletion in case of success
discardOldState = success;
discardNewState = !success;
} catch (Exception ex) {
final Optional<PossibleInconsistentStateException> possibleInconsistentStateException = ExceptionUtils.findThrowable(ex, PossibleInconsistentStateException.class);
if (possibleInconsistentStateException.isPresent()) {
// it's unclear whether the state handle metadata was written to the ConfigMap -
// hence, we don't discard any data
discardNewState = false;
throw possibleInconsistentStateException.get();
}
throw ExceptionUtils.findThrowable(ex, NotExistException.class).orElseThrow(() -> ex);
} finally {
if (discardNewState) {
newStateHandle.discardState();
}
if (discardOldState) {
oldStateHandle.discardState();
}
}
}
use of org.apache.flink.runtime.persistence.PossibleInconsistentStateException in project flink by apache.
the class Fabric8FlinkKubeClient method attemptCheckAndUpdateConfigMap.
private CompletableFuture<Boolean> attemptCheckAndUpdateConfigMap(String configMapName, Function<KubernetesConfigMap, Optional<KubernetesConfigMap>> updateFunction) {
return CompletableFuture.supplyAsync(() -> {
final KubernetesConfigMap configMap = getConfigMap(configMapName).orElseThrow(() -> new CompletionException(new KubernetesException("Cannot retry checkAndUpdateConfigMap with configMap " + configMapName + " because it does not exist.")));
final Optional<KubernetesConfigMap> maybeUpdate = updateFunction.apply(configMap);
if (maybeUpdate.isPresent()) {
try {
internalClient.configMaps().withName(configMapName).lockResourceVersion(maybeUpdate.get().getResourceVersion()).replace(maybeUpdate.get().getInternalResource());
return true;
} catch (Throwable throwable) {
LOG.debug("Failed to update ConfigMap {} with data {}. Trying again.", configMap.getName(), configMap.getData());
// handling here
throw new CompletionException(new PossibleInconsistentStateException(throwable));
}
}
return false;
}, kubeClientExecutorService);
}
use of org.apache.flink.runtime.persistence.PossibleInconsistentStateException in project flink by apache.
the class ZooKeeperStateHandleStoreTest method testFailingAddWithPossiblyInconsistentState.
/**
* Tests that the created state handle is not discarded if ZooKeeper create fails with an
* generic exception.
*/
@Test
public void testFailingAddWithPossiblyInconsistentState() throws Exception {
final TestingLongStateHandleHelper stateHandleProvider = new TestingLongStateHandleHelper();
CuratorFramework client = spy(ZOOKEEPER.getClient());
when(client.inTransaction()).thenThrow(new RuntimeException("Expected test Exception."));
ZooKeeperStateHandleStore<TestingLongStateHandleHelper.LongStateHandle> store = new ZooKeeperStateHandleStore<>(client, stateHandleProvider);
// Config
final String pathInZooKeeper = "/testAddDiscardStateHandleAfterFailure";
final long state = 81282227L;
try {
// Test
store.addAndLock(pathInZooKeeper, new TestingLongStateHandleHelper.LongStateHandle(state));
fail("PossibleInconsistentStateException should have been thrown.");
} catch (PossibleInconsistentStateException ignored) {
// PossibleInconsistentStateException expected
}
// State handle created and not discarded
assertEquals(1, TestingLongStateHandleHelper.getGlobalStorageSize());
assertEquals(state, TestingLongStateHandleHelper.getStateHandleValueByIndex(0));
assertEquals(0, TestingLongStateHandleHelper.getDiscardCallCountForStateHandleByIndex(0));
}
Aggregations