io.camunda.operate.webapp.zeebe.operation.AbstractOperationHandler Maven / Gradle / Ivy
The newest version!
/*
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH under
* one or more contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright ownership.
* Licensed under the Camunda License 1.0. You may not use this file
* except in compliance with the Camunda License 1.0.
*/
package io.camunda.operate.webapp.zeebe.operation;
import io.camunda.operate.Metrics;
import io.camunda.operate.entities.OperationEntity;
import io.camunda.operate.entities.OperationState;
import io.camunda.operate.exceptions.PersistenceException;
import io.camunda.operate.property.OperateProperties;
import io.camunda.operate.util.OperationsManager;
import io.camunda.operate.webapp.writer.BatchOperationWriter;
import io.camunda.zeebe.client.ZeebeClient;
import io.grpc.Status;
import io.grpc.StatusRuntimeException;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
public abstract class AbstractOperationHandler implements OperationHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractOperationHandler.class);
private static final List RETRY_STATUSES =
Arrays.asList(
Status.UNAVAILABLE.getCode(),
Status.RESOURCE_EXHAUSTED.getCode(),
Status.DEADLINE_EXCEEDED.getCode());
@Autowired protected ZeebeClient zeebeClient;
@Autowired protected BatchOperationWriter batchOperationWriter;
@Autowired protected OperateProperties operateProperties;
@Autowired protected Metrics metrics;
@Autowired private OperationsManager operationsManager;
@Override
public void handle(final OperationEntity operation) {
try {
handleWithException(operation);
} catch (final Exception ex) {
if (isExceptionRetriable(ex)) {
// leave the operation locked -> when it expires, operation will be retried
LOGGER.error(
String.format(
"Unable to process operation with id %s. Reason: %s. Will be retried.",
operation.getId(), ex.getMessage()),
ex);
} else {
try {
failOperation(
operation, String.format("Unable to process operation: %s", ex.getMessage()));
} catch (final PersistenceException e) {
// noop
}
LOGGER.error(
String.format(
"Unable to process operation with id %s. Reason: %s. Will NOT be retried.",
operation.getId(), ex.getMessage()),
ex);
}
}
}
// Needed for tests
@Override
public void setZeebeClient(final ZeebeClient zeebeClient) {
this.zeebeClient = zeebeClient;
}
private boolean isExceptionRetriable(final Exception ex) {
final StatusRuntimeException cause = extractStatusRuntimeException(ex);
return cause != null && RETRY_STATUSES.contains(cause.getStatus().getCode());
}
private StatusRuntimeException extractStatusRuntimeException(final Throwable ex) {
if (ex.getCause() != null) {
if (ex.getCause() instanceof StatusRuntimeException) {
return (StatusRuntimeException) ex.getCause();
} else {
return extractStatusRuntimeException(ex.getCause());
}
}
return null;
}
protected void recordCommandMetric(final OperationEntity operation) {
metrics.recordCounts(
Metrics.COUNTER_NAME_COMMANDS,
1,
Metrics.TAG_KEY_STATUS,
operation.getState().name(),
Metrics.TAG_KEY_TYPE,
operation.getType().name());
}
protected boolean canForceFailOperation(final OperationEntity operation) {
return false;
}
protected void failOperation(final OperationEntity operation, final String errorMsg)
throws PersistenceException {
if (isLocked(operation) || canForceFailOperation(operation)) {
operation.setState(OperationState.FAILED);
operation.setLockExpirationTime(null);
operation.setLockOwner(null);
operation.setErrorMessage(StringUtils.trimWhitespace(errorMsg));
if (operation.getBatchOperationId() != null) {
operationsManager.updateFinishedInBatchOperation(operation.getBatchOperationId());
}
batchOperationWriter.updateOperation(operation);
LOGGER.debug(
"Operation {} failed with message: {} ", operation.getId(), operation.getErrorMessage());
}
recordCommandMetric(operation);
}
private boolean isLocked(final OperationEntity operation) {
return operation.getState().equals(OperationState.LOCKED)
&& operation.getLockOwner().equals(operateProperties.getOperationExecutor().getWorkerId())
&& getTypes().contains(operation.getType());
}
protected void markAsSent(final OperationEntity operation) throws PersistenceException {
markAsSent(operation, null);
}
protected void markAsSent(final OperationEntity operation, final Long zeebeCommandKey)
throws PersistenceException {
if (isLocked(operation)) {
operation.setState(OperationState.SENT);
operation.setLockExpirationTime(null);
operation.setLockOwner(null);
operation.setZeebeCommandKey(zeebeCommandKey);
batchOperationWriter.updateOperation(operation);
LOGGER.debug("Operation {} was sent to Zeebe", operation.getId());
}
recordCommandMetric(operation);
}
}