// Path: src/main/java/uk/sky/cqlmigrate/exception/CannotAcquireLockException.java
// public class CannotAcquireLockException extends LockException {
// 
//     public CannotAcquireLockException(String message) {
//         super(message);
//     }
// 
//     public CannotAcquireLockException(String message, Throwable cause) {
//         super(message, cause);
//     }
// }
// 
// Path: src/main/java/uk/sky/cqlmigrate/exception/CannotReleaseLockException.java
// public class CannotReleaseLockException extends LockException {
// 
//     public CannotReleaseLockException(String message, Throwable cause) {
//         super(message, cause);
//     }
// 
//     public CannotReleaseLockException(String message) {
//         super(message);
//     }
// }


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import uk.sky.cqlmigrate.exception.CannotAcquireLockException;
import uk.sky.cqlmigrate.exception.CannotReleaseLockException;
import java.util.concurrent.TimeoutException;
            Thread.currentThread().interrupt();
            throw new CannotAcquireLockException(String.format("Polling to acquire lock %s for client %s was interrupted", lockName, lockConfig.getClientId()), e);
        }
    }

    /**
     * Will release the lock using the locking mechanism. If a lock cannot
     * be released within the configured timeout interval, an exception is thrown.
     *
     * @param migrationFailed true if migration failed, false otherwise
     * @throws CannotReleaseLockException if this cannot release lock within the specified time interval or locking mechanism fails
     */
    public void unlock(boolean migrationFailed) throws CannotReleaseLockException {
        String lockName = lockingMechanism.getLockName();

        if (migrationFailed && !lockConfig.unlockOnFailure()) {
            log.info("Not releasing the lock for name '{}' and client id '{}' due to failure (use LockConfig.unlockOnFailure() to change that behavior)",
                    lockingMechanism.getLockName(), lockConfig.getClientId());
            return;
        }

        try {
            log.info("Attempting to release lock for '{}', using client id '{}'", lockName, lockConfig.getClientId());
            RetryTask.attempt(() -> lockingMechanism.release(lockConfig.getClientId()))
                    .withTimeout(lockConfig.getTimeout())
                    .withPollingInterval(lockConfig.getPollingInterval())
                    .untilSuccess();
        } catch (TimeoutException te) {
            log.warn("Unable to release lock for {}", lockConfig.getClientId(), te);
            throw new CannotReleaseLockException("Failed to release lock", te);
// Path: src/main/java/uk/sky/cqlmigrate/exception/CannotAcquireLockException.java
// public class CannotAcquireLockException extends LockException {
// 
//     public CannotAcquireLockException(String message) {
//         super(message);
//     }
// 
//     public CannotAcquireLockException(String message, Throwable cause) {
//         super(message, cause);
//     }
// }
// 
// Path: src/main/java/uk/sky/cqlmigrate/exception/CannotReleaseLockException.java
// public class CannotReleaseLockException extends LockException {
// 
//     public CannotReleaseLockException(String message, Throwable cause) {
//         super(message, cause);
//     }
// 
//     public CannotReleaseLockException(String message) {
//         super(message);
//     }
// }


// Path: src/main/java/uk/sky/cqlmigrate/Lock.java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import uk.sky.cqlmigrate.exception.CannotAcquireLockException;
import uk.sky.cqlmigrate.exception.CannotReleaseLockException;
import java.util.concurrent.TimeoutException;

            Thread.currentThread().interrupt();
            throw new CannotAcquireLockException(String.format("Polling to acquire lock %s for client %s was interrupted", lockName, lockConfig.getClientId()), e);
        }
    }

    /**
     * Will release the lock using the locking mechanism. If a lock cannot
     * be released within the configured timeout interval, an exception is thrown.
     *
     * @param migrationFailed true if migration failed, false otherwise
     * @throws CannotReleaseLockException if this cannot release lock within the specified time interval or locking mechanism fails
     */
    public void unlock(boolean migrationFailed) throws CannotReleaseLockException {
        String lockName = lockingMechanism.getLockName();

        if (migrationFailed && !lockConfig.unlockOnFailure()) {
            log.info("Not releasing the lock for name '{}' and client id '{}' due to failure (use LockConfig.unlockOnFailure() to change that behavior)",
                    lockingMechanism.getLockName(), lockConfig.getClientId());
            return;
        }

        try {
            log.info("Attempting to release lock for '{}', using client id '{}'", lockName, lockConfig.getClientId());
            RetryTask.attempt(() -> lockingMechanism.release(lockConfig.getClientId()))
                    .withTimeout(lockConfig.getTimeout())
                    .withPollingInterval(lockConfig.getPollingInterval())
                    .untilSuccess();
        } catch (TimeoutException te) {
            log.warn("Unable to release lock for {}", lockConfig.getClientId(), te);
            throw new CannotReleaseLockException("Failed to release lock", te);
        } catch (InterruptedException e) {