Skip to content

Commit

Permalink
Fix #2847 Allow number of lock retries to be configured.
Browse files Browse the repository at this point in the history
  • Loading branch information
juliahayward committed Oct 26, 2020
1 parent 37c4aaa commit f77ed0e
Show file tree
Hide file tree
Showing 12 changed files with 165 additions and 12 deletions.
4 changes: 4 additions & 0 deletions flyway-commandline/src/main/assembly/flyway.conf
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,10 @@ flyway.locations=filesystem:sql
# Flyway Teams only
# flyway.dryRunOutput=

# When attempting to get a lock for migrating, the number of attempts (at 1 second intervals) to make before
# abandoning the migration. Specify -1 to try indefinitely. (default: 50)
# flyway.lockRetryCount=

# JDBC properties to pass to the JDBC driver when establishing a connection.
# Flyway Teams only
# flyway.jdbcProperties.myProperty=
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,7 @@ private static void printUsage() {
LOG.info("placeholders : Placeholders to replace in sql migrations");
LOG.info("placeholderPrefix : Prefix of every placeholder");
LOG.info("placeholderSuffix : Suffix of every placeholder");
LOG.info("lockRetryCount : The maximum number of retries when trying to obtain a lock");
LOG.info("jdbcProperties : Properties to pass to the JDBC driver object");
LOG.info("installedBy : Username that will be recorded in the schema history table");
LOG.info("target : Target version up to which Flyway should use migrations");
Expand Down
2 changes: 2 additions & 0 deletions flyway-core/src/main/java/org/flywaydb/core/Flyway.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.flywaydb.core.internal.clazz.NoopClassProvider;
import org.flywaydb.core.internal.command.*;
import org.flywaydb.core.internal.configuration.ConfigurationValidator;
import org.flywaydb.core.internal.strategy.RetryStrategy;
import org.flywaydb.core.internal.database.base.Database;
import org.flywaydb.core.internal.database.base.DatabaseType;
import org.flywaydb.core.internal.database.base.Schema;
Expand Down Expand Up @@ -473,6 +474,7 @@ private MigrationResolver createMigrationResolver(ResourceProvider resourceProvi
final DatabaseType databaseType = jdbcConnectionFactory.getDatabaseType();
final ParsingContext parsingContext = new ParsingContext();
final SqlScriptFactory sqlScriptFactory = databaseType.createSqlScriptFactory(configuration, parsingContext);
RetryStrategy.setNumberOfRetries(configuration.getLockRetryCount());

final SqlScriptExecutorFactory noCallbackSqlScriptExecutorFactory = databaseType.createSqlScriptExecutorFactory(
jdbcConnectionFactory,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,13 @@ public class ClassicConfiguration implements Configuration {




/**
* The maximum number of retries when trying to obtain a lock.
* (default: {@code 50})
*
*/
private int lockRetryCount = 50;



Expand Down Expand Up @@ -793,6 +800,11 @@ public String getLicenseKey() {



}

@Override
public int getLockRetryCount() {
return lockRetryCount;
}

/**
Expand Down Expand Up @@ -1918,6 +1930,10 @@ public void setJavaMigrationClassProvider(ClassProvider<JavaMigration> javaMigra
this.javaMigrationClassProvider = javaMigrationClassProvider;
}

public void setLockRetryCount(int lockRetryCount) {
this.lockRetryCount = lockRetryCount;
}

/**
* Properties to pass to the JDBC driver object
*
Expand Down Expand Up @@ -2008,6 +2024,7 @@ public void configure(Configuration configuration) {
setResourceProvider(configuration.getResourceProvider());
setJavaMigrationClassProvider(configuration.getJavaMigrationClassProvider());
setShouldCreateSchemas(configuration.getCreateSchemas());
setLockRetryCount(configuration.getLockRetryCount());

url = configuration.getUrl();
user = configuration.getUser();
Expand Down Expand Up @@ -2190,6 +2207,10 @@ public void configure(Map<String, String> props) {
if (cherryPickProp != null) {
setCherryPick(StringUtils.tokenizeToStringArray(cherryPickProp, ","));
}
Integer lockRetryCount = removeInteger(props, ConfigUtils.LOCK_RETRY_COUNT);
if (lockRetryCount != null) {
setLockRetryCount(lockRetryCount);
}
Boolean outOfOrderProp = removeBoolean(props, ConfigUtils.OUT_OF_ORDER);
if (outOfOrderProp != null) {
setOutOfOrder(outOfOrderProp);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,11 @@ public interface Configuration {
*/
boolean getCreateSchemas();

/**
* The maximum number of retries when trying to obtain a lock. -1 indicates attempting to repeat indefinitely.
*/
int getLockRetryCount();

/**
* Properties to pass to the JDBC driver object
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,11 @@ public boolean getCreateSchemas() {
return config.getCreateSchemas();
}

@Override
public int getLockRetryCount() {
return config.getLockRetryCount();
}

@Override
public Map<String, String> getJdbcProperties() {
return config.getJdbcProperties();
Expand Down Expand Up @@ -1080,6 +1085,11 @@ public FluentConfiguration batch(boolean batch) {
return this;
}

public FluentConfiguration lockRetryCount(int lockRetryCount) {
config.setLockRetryCount(lockRetryCount);
return this;
}

/**
* Properties to pass to the JDBC driver object
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ public class ConfigUtils {
public static final String PLACEHOLDER_REPLACEMENT = "flyway.placeholderReplacement";
public static final String PLACEHOLDER_SUFFIX = "flyway.placeholderSuffix";
public static final String PLACEHOLDERS_PROPERTY_PREFIX = "flyway.placeholders.";
public static final String LOCK_RETRY_COUNT = "flyway.lockRetryCount";
public static final String JDBC_PROPERTIES_PREFIX = "flyway.jdbcProperties.";
public static final String REPEATABLE_SQL_MIGRATION_PREFIX = "flyway.repeatableSqlMigrationPrefix";
public static final String RESOLVERS = "flyway.resolvers";
Expand Down Expand Up @@ -216,6 +217,9 @@ private static String convertKey(String key) {
if ("FLYWAY_PASSWORD".equals(key)) {
return PASSWORD;
}
if ("FLYWAY_LOCK_RETRY_COUNT".equals(key)) {
return LOCK_RETRY_COUNT;
}
if ("FLYWAY_PLACEHOLDER_PREFIX".equals(key)) {
return PLACEHOLDER_PREFIX;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package org.flywaydb.core.internal.database.postgresql;

import org.flywaydb.core.api.FlywayException;
import org.flywaydb.core.internal.strategy.RetryStrategy;
import org.flywaydb.core.internal.exception.FlywaySqlException;
import org.flywaydb.core.internal.jdbc.JdbcTemplate;
import org.flywaydb.core.api.logging.Log;
Expand Down Expand Up @@ -93,18 +94,10 @@ public <T> T execute(Callable<T> callable) {
}

private void lock() throws SQLException {
int retries = 0;
while (!tryLock()) {
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
throw new FlywayException("Interrupted while attempting to acquire PostgreSQL advisory lock", e);
}

if (++retries >= 50) {
throw new FlywayException("Number of retries exceeded while attempting to acquire PostgreSQL advisory lock");
}
}
RetryStrategy strategy = new RetryStrategy();
strategy.doWithRetries(() -> tryLock(),
"Interrupted while attempting to acquire PostgreSQL advisory lock",
"Number of retries exceeded while attempting to acquire PostgreSQL advisory lock");
}

private boolean tryLock() throws SQLException {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Copyright © Red Gate Software Ltd 2010-2020
*
* Licensed 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.flywaydb.core.internal.strategy;

import org.flywaydb.core.api.FlywayException;
import org.flywaydb.core.internal.util.SqlCallable;

import java.sql.SQLException;

/**
* A class that retries a Callable a given number of times until success is obtained.
*/
public class RetryStrategy {

/**
* We hard-code a default of 50 retries here, but this may be overridden by configuration.
*/
private static int numberOfRetries = 50;
private static boolean unlimitedRetries;

private int numberOfRetriesRemaining;

/**
* A class that retries a Callable a given number of times until success is obtained.
*/
public RetryStrategy() {
numberOfRetriesRemaining = numberOfRetries;
}

/**
* Set the number of retries that are to be attempted before giving up.
* @param retries The number of retries to attempt. To try forever, use -1.
*/
public static void setNumberOfRetries(int retries) {
numberOfRetries = retries;
unlimitedRetries = (retries < 0);
}

private boolean hasMoreRetries() {
return (unlimitedRetries || numberOfRetriesRemaining > 0);
}

private void nextRetry() {
if (!unlimitedRetries) {
numberOfRetriesRemaining--;
}
}

private int nextWaitInMilliseconds() {
return 1000;
}

/**
* Keep retrying a Callable with a potentially varying wait on each iteration, until one of the following happens:
* - the callable returns {@code true};
* - an InterruptedException happens
* - the number of retries is exceeded.
*
* @param callable The callable to retry
* @param interruptionMessage The message to relay if interruption happens
* @param retriesExceededMessage The message to relay if the number of retries is exceeded
*
* @throws SQLException
*/
public void doWithRetries(SqlCallable<Boolean> callable, String interruptionMessage, String retriesExceededMessage) throws SQLException {
while (!callable.call()) {
try {
Thread.sleep(nextWaitInMilliseconds());
} catch (InterruptedException e) {
throw new FlywayException(interruptionMessage, e);
}

if (!hasMoreRetries()) {
throw new FlywayException(retriesExceededMessage);
}
nextRetry();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,12 @@ public class FlywayExtension {
*/
public String encoding;

/**
* The maximum number of retries when trying to obtain a lock.
* (default: 50)
*/
public Integer lockRetryCount;

/**
* Placeholders to replace in Sql migrations
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,12 @@ public abstract class AbstractFlywayTask extends DefaultTask {
*/
public String licenseKey;

/**
* The maximum number of retries when trying to obtain a lock.
* (default: 50)
*/
public Integer lockRetryCount;

/**
* The encoding of the external config files specified with the {@code flyway.configFiles} property. (default: UTF-8).
* <p>Also configurable with Gradle or System Property: ${flyway.configFileEncoding}</p>
Expand Down Expand Up @@ -686,6 +692,7 @@ private Map<String, String> createFlywayConfig(Map<String, String> envVars) {
putIfSet(conf, ConfigUtils.GROUP, group, extension.group);
putIfSet(conf, ConfigUtils.INSTALLED_BY, installedBy, extension.installedBy);
putIfSet(conf, ConfigUtils.ENCODING, encoding, extension.encoding);
putIfSet(conf, ConfigUtils.LOCK_RETRY_COUNT, lockRetryCount, extension.lockRetryCount);
putIfSet(conf, ConfigUtils.PLACEHOLDER_REPLACEMENT, placeholderReplacement, extension.placeholderReplacement);
putIfSet(conf, ConfigUtils.PLACEHOLDER_PREFIX, placeholderPrefix, extension.placeholderPrefix);
putIfSet(conf, ConfigUtils.PLACEHOLDER_SUFFIX, placeholderSuffix, extension.placeholderSuffix);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,13 @@ abstract class AbstractFlywayMojo extends AbstractMojo {
@Parameter(property = ConfigUtils.ENCODING)
private String encoding;

/**
* The maximum number of retries when trying to obtain a lock. (default: 50)<br>
* <p>Also configurable with Maven or System Property: ${flyway.lockRetryCount}</p>
*/
@Parameter(property = ConfigUtils.LOCK_RETRY_COUNT)
private Integer lockRetryCount;

/**
* The file name prefix for versioned SQL migrations (default: V)
* <p>
Expand Down Expand Up @@ -762,6 +769,7 @@ public final void execute() throws MojoExecutionException {
putArrayIfSet(conf, ConfigUtils.CALLBACKS, callbacks);
putIfSet(conf, ConfigUtils.SKIP_DEFAULT_CALLBACKS, skipDefaultCallbacks);
putIfSet(conf, ConfigUtils.ENCODING, encoding);
putIfSet(conf, ConfigUtils.LOCK_RETRY_COUNT, lockRetryCount);
putIfSet(conf, ConfigUtils.SQL_MIGRATION_PREFIX, sqlMigrationPrefix);
putIfSet(conf, ConfigUtils.UNDO_SQL_MIGRATION_PREFIX, undoSqlMigrationPrefix);
putIfSet(conf, ConfigUtils.REPEATABLE_SQL_MIGRATION_PREFIX, repeatableSqlMigrationPrefix);
Expand Down

0 comments on commit f77ed0e

Please sign in to comment.