Skip to content

Commit

Permalink
Merge branch 'test-zone-persistence' into mysql-maria-always-utc
Browse files Browse the repository at this point in the history
  • Loading branch information
kagkarlsson committed Feb 24, 2024
2 parents bb17380 + 3dbce37 commit 4a2edb7
Show file tree
Hide file tree
Showing 13 changed files with 95 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,10 @@ public Scheduler scheduler(DbSchedulerCustomizer customizer, StatsRegistry regis
.jdbcCustomization()
.orElse(new AutodetectJdbcCustomization(transactionalDataSource)));

if (config.isAlwaysPersistTimestampInUtc()) {
builder.alwaysPersistTimestampInUTC();
}

if (config.isImmediateExecutionEnabled()) {
builder.enableImmediateExecution();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ public class DbSchedulerProperties {
@DurationUnit(SECONDS)
private Duration shutdownMaxWait = SchedulerBuilder.SHUTDOWN_MAX_WAIT;

/**
* Store timestamps in UTC timezone even though the schema supports storing timezone information
*/
private boolean alwaysPersistTimestampInUtc = false;

/** Which log level to use when logging task failures. Defaults to {@link LogLevel#DEBUG}. */
private LogLevel failureLoggerLevel = SchedulerBuilder.DEFAULT_FAILURE_LOG_LEVEL;

Expand Down Expand Up @@ -228,4 +233,12 @@ public void setPollingStrategyUpperLimitFractionOfThreads(
double pollingStrategyUpperLimitFractionOfThreads) {
this.pollingStrategyUpperLimitFractionOfThreads = pollingStrategyUpperLimitFractionOfThreads;
}

public boolean isAlwaysPersistTimestampInUtc() {
return alwaysPersistTimestampInUtc;
}

public void setAlwaysPersistTimestampInUtc(boolean alwaysPersistTimestampInUTC) {
this.alwaysPersistTimestampInUtc = alwaysPersistTimestampInUTC;
}
}
4 changes: 2 additions & 2 deletions db-scheduler/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>8.2.1.jre8</version>
<version>12.4.2.jre8</version>
<scope>test</scope>
</dependency>
<dependency>
Expand All @@ -287,7 +287,7 @@
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>3.1.4</version>
<version>3.3.2</version>
<scope>test</scope>
</dependency>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public class SchedulerBuilder {
protected LogLevel logLevel = DEFAULT_FAILURE_LOG_LEVEL;
protected boolean logStackTrace = LOG_STACK_TRACE_ON_FAILURE;
private boolean registerShutdownHook = false;
private boolean alwaysPersistTimestampInUTC = false;

public SchedulerBuilder(DataSource dataSource, List<Task<?>> knownTasks) {
this.dataSource = dataSource;
Expand Down Expand Up @@ -156,6 +157,11 @@ public SchedulerBuilder jdbcCustomization(JdbcCustomization jdbcCustomization) {
return this;
}

public SchedulerBuilder alwaysPersistTimestampInUTC() {
this.alwaysPersistTimestampInUTC = true;
return this;
}

public SchedulerBuilder shutdownMaxWait(Duration shutdownMaxWait) {
this.shutdownMaxWait = shutdownMaxWait;
return this;
Expand Down Expand Up @@ -208,7 +214,8 @@ public Scheduler build() {
final TaskResolver taskResolver = new TaskResolver(statsRegistry, clock, knownTasks);
final JdbcCustomization jdbcCustomization =
ofNullable(this.jdbcCustomization)
.orElseGet(() -> new AutodetectJdbcCustomization(dataSource));
.orElseGet(
() -> new AutodetectJdbcCustomization(dataSource, alwaysPersistTimestampInUTC));
final JdbcTaskRepository schedulerTaskRepository =
new JdbcTaskRepository(
dataSource,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public AutodetectJdbcCustomization(DataSource dataSource, boolean persistTimesta

if (databaseProductName.equals(MICROSOFT_SQL_SERVER)) {
LOG.info("Using MSSQL jdbc-overrides.");
detectedCustomization = new MssqlJdbcCustomization(persistTimestampInUTC);
detectedCustomization = new MssqlJdbcCustomization(true);
} else if (databaseProductName.equals(POSTGRESQL)) {
LOG.info("Using PostgreSQL jdbc-overrides.");
detectedCustomization = new PostgreSqlJdbcCustomization(false, persistTimestampInUTC);
Expand All @@ -57,16 +57,16 @@ public AutodetectJdbcCustomization(DataSource dataSource, boolean persistTimesta
detectedCustomization = new OracleJdbcCustomization(persistTimestampInUTC);
} else if (databaseProductName.contains(MARIADB)) {
LOG.info("Using MariaDB jdbc-overrides.");
detectedCustomization = new MariaDBJdbcCustomization(persistTimestampInUTC);
detectedCustomization = new MariaDBJdbcCustomization(true);
} else if (databaseProductName.contains(MYSQL)) {
int databaseMajorVersion = c.getMetaData().getDatabaseMajorVersion();
String dbVersion = c.getMetaData().getDatabaseProductVersion();
if (databaseMajorVersion >= 8) {
LOG.info("Using MySQL jdbc-overrides version 8 and later. (v {})", dbVersion);
detectedCustomization = new MySQL8JdbcCustomization(persistTimestampInUTC);
detectedCustomization = new MySQL8JdbcCustomization(true);
} else {
LOG.info("Using MySQL jdbc-overrides for version older than 8. (v {})", dbVersion);
detectedCustomization = new MySQLJdbcCustomization(persistTimestampInUTC);
detectedCustomization = new MySQLJdbcCustomization(true);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import java.util.TimeZone;

public class DefaultJdbcCustomization implements JdbcCustomization {
private static final Calendar UTC = GregorianCalendar.getInstance(TimeZone.getTimeZone("CET"));
public static final Calendar UTC = GregorianCalendar.getInstance(TimeZone.getTimeZone("UTC"));
private final boolean persistTimestampInUTC;

public DefaultJdbcCustomization(boolean persistTimestampInUTC) {
Expand All @@ -36,10 +36,15 @@ public DefaultJdbcCustomization(boolean persistTimestampInUTC) {

@Override
public void setInstant(PreparedStatement p, int index, Instant value) throws SQLException {
if (value == null) {
p.setTimestamp(index, null);
return;
}

if (persistTimestampInUTC) {
p.setTimestamp(index, value != null ? Timestamp.from(value) : null, UTC);
p.setTimestamp(index, Timestamp.from(value), UTC);
} else {
p.setTimestamp(index, value != null ? Timestamp.from(value) : null);
p.setTimestamp(index, Timestamp.from(value));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,20 @@

import static com.github.kagkarlsson.scheduler.jdbc.Queries.selectForUpdate;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MariaDBJdbcCustomization extends DefaultJdbcCustomization {
private static final Logger LOG = LoggerFactory.getLogger(MariaDBJdbcCustomization.class);

public MariaDBJdbcCustomization(boolean persistTimestampInUTC) {
super(persistTimestampInUTC);
if (!persistTimestampInUTC) {
LOG.warn(
"{} does not support persistent timezones. "
+ "It is recommended to store time in UTC to avoid issues with for example DST",
getClass().getName());
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,36 +15,31 @@

import static com.github.kagkarlsson.scheduler.jdbc.Queries.selectForUpdate;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.time.Instant;
import java.util.Calendar;
import java.util.TimeZone;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MssqlJdbcCustomization extends DefaultJdbcCustomization {
private static final Logger LOG = LoggerFactory.getLogger(MssqlJdbcCustomization.class);

public MssqlJdbcCustomization() {
super(false);
super(true);
}

public MssqlJdbcCustomization(boolean persistTimestampInUTC) {
super(persistTimestampInUTC);
if (!persistTimestampInUTC) {
LOG.warn(
"{} must explicitly specify timezone when persisting a timestamp. "
+ "Persisting timestamp with undefined timezone is not recommended and will likely cause issues",
getClass().getName());
}
}

@Override
public String getName() {
return "MSSQL";
}

@Override
public void setInstant(PreparedStatement p, int index, Instant value) throws SQLException {
p.setTimestamp(
index,
value != null ? Timestamp.from(value) : null,
Calendar.getInstance(TimeZone.getTimeZone("UTC")));
}

@Override
public boolean supportsGenericLockAndFetch() {
// Currently supported, but not recommended because of deadlock issues
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,20 @@

import static com.github.kagkarlsson.scheduler.jdbc.Queries.selectForUpdate;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MySQL8JdbcCustomization extends DefaultJdbcCustomization {
private static final Logger LOG = LoggerFactory.getLogger(MySQL8JdbcCustomization.class);

public MySQL8JdbcCustomization(boolean persistTimestampInUTC) {
super(persistTimestampInUTC);
if (!persistTimestampInUTC) {
LOG.warn(
"{} does not support persistent timezones. "
+ "It is recommended to store time in UTC to avoid issues with for example DST",
getClass().getName());
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,20 @@
*/
package com.github.kagkarlsson.scheduler.jdbc;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MySQLJdbcCustomization extends DefaultJdbcCustomization {
private static final Logger LOG = LoggerFactory.getLogger(MySQLJdbcCustomization.class);

public MySQLJdbcCustomization(boolean persistTimestampInUTC) {
super(persistTimestampInUTC);
if (!persistTimestampInUTC) {
LOG.warn(
"{} does not support persistent timezones. "
+ "It is recommended to store time in UTC to avoid issues with for example DST",
getClass().getName());
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,6 @@
public class PostgreSqlJdbcCustomization extends DefaultJdbcCustomization {
private final boolean useGenericLockAndFetch;

public PostgreSqlJdbcCustomization() {
this(false, false);
}

public PostgreSqlJdbcCustomization(
boolean useGenericLockAndFetch, boolean persistTimestampInUTC) {
super(persistTimestampInUTC);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.slf4j.LoggerFactory;

@SuppressWarnings("ConstantConditions")
public abstract class CompatibilityTest {
Expand Down Expand Up @@ -225,32 +224,12 @@ public void test_has_peristent_time_zone() {
if (!shouldHavePersistentTimezone) {
return;
}

TaskResolver defaultTaskResolver = new TaskResolver(StatsRegistry.NOOP, new ArrayList<>());
defaultTaskResolver.addTask(oneTime);

JdbcTaskRepository winterTaskRepo =
new JdbcTaskRepository(
getDataSource(),
commitWhenAutocommitDisabled(),
new ZoneSpecificJdbcCustomization(
getJdbcCustomization().orElse(new AutodetectJdbcCustomization(getDataSource())),
GregorianCalendar.getInstance(TimeZone.getTimeZone("CET"))),
DEFAULT_TABLE_NAME,
defaultTaskResolver,
new SchedulerName.Fixed("scheduler1"),
new SystemClock());

JdbcTaskRepository summerTaskRepo =
new JdbcTaskRepository(
getDataSource(),
commitWhenAutocommitDisabled(),
new ZoneSpecificJdbcCustomization(
getJdbcCustomization().orElse(new AutodetectJdbcCustomization(getDataSource())),
GregorianCalendar.getInstance(TimeZone.getTimeZone("CEST"))),
DEFAULT_TABLE_NAME,
defaultTaskResolver,
new SchedulerName.Fixed("scheduler1"),
new SystemClock());
JdbcTaskRepository winterTaskRepo = createRepositoryForForZone(defaultTaskResolver, "CET");
JdbcTaskRepository summerTaskRepo = createRepositoryForForZone(defaultTaskResolver, "CEST");

Instant noonFirstJan = Instant.parse("2020-01-01T12:00:00.00Z");

Expand Down Expand Up @@ -347,11 +326,17 @@ public void test_jdbc_repository_compatibility_set_data() {
assertNull(round3.taskInstance.getData());
}

private void sleep(Duration duration) {
try {
Thread.sleep(duration.toMillis());
} catch (InterruptedException e) {
LoggerFactory.getLogger(CompatibilityTest.class).info("Interrupted");
}
private JdbcTaskRepository createRepositoryForForZone(
TaskResolver defaultTaskResolver, String zoneId) {
return new JdbcTaskRepository(
getDataSource(),
commitWhenAutocommitDisabled(),
new ZoneSpecificJdbcCustomization(
getJdbcCustomization().orElse(new AutodetectJdbcCustomization(getDataSource())),
GregorianCalendar.getInstance(TimeZone.getTimeZone(zoneId))),
DEFAULT_TABLE_NAME,
defaultTaskResolver,
new SchedulerName.Fixed("scheduler1"),
new SystemClock());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ db-scheduler.polling-strategy=fetch
db-scheduler.polling-strategy-lower-limit-fraction-of-threads=0.5
db-scheduler.polling-strategy-upper-limit-fraction-of-threads=3.0
db-scheduler.shutdown-max-wait=30m
db-scheduler.always-persist-timestamp-in-utc=false

0 comments on commit 4a2edb7

Please sign in to comment.