DeadlockLockProvider.java

package access.cron;

import net.javacrumbs.shedlock.core.LockConfiguration;
import net.javacrumbs.shedlock.core.LockProvider;
import net.javacrumbs.shedlock.core.SimpleLock;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.sql.SQLException;
import java.sql.SQLTransactionRollbackException;
import java.util.Optional;

public class DeadlockLockProvider implements LockProvider {

    private static final Log LOG = LogFactory.getLog(DeadlockLockProvider.class);

    private final LockProvider delegate;

    public DeadlockLockProvider(LockProvider delegate) {
        this.delegate = delegate;
    }

    @Override
    public Optional<SimpleLock> lock(LockConfiguration lockConfiguration) {
        try {
            return delegate.lock(lockConfiguration);
        } catch (Exception e) {
            if (isDeadlock(e)) {
                LOG.warn(String.format("Deadlock acquiring ShedLock '%s', skipping cycle",
                        lockConfiguration.getName()));
                return Optional.empty();  // treat as "already locked", skip this cycle
            } else {
                LOG.error(String.format("Unexpected error acquiring ShedLock '%s', skipping cycle",
                        lockConfiguration.getName()), e);
                return Optional.empty();  // treat as "already locked", skip this cycle, but make noise
            }
        }
    }

    private boolean isDeadlock(Exception e) {
        Throwable cause = e;
        while (cause != null) {
            if (cause instanceof SQLTransactionRollbackException) {
                return true;
            }
            // MariaDB/MySQL error code 1213
            if (cause instanceof SQLException sql && sql.getErrorCode() == 1213) {
                return true;
            }
            cause = cause.getCause();
        }
        return false;
    }

}