GithubHelp home page GithubHelp logo

shedlock's Introduction

ShedLock

Apache License 2 Build Status Maven Central

ShedLock makes sure that your scheduled tasks are executed at most once at the same time. If a task is being executed on one node, it acquires a lock which prevents execution of the same task from another node (or thread). Please note, that if one task is already being executed on one node, execution on other nodes does not wait, it is simply skipped.

ShedLock uses an external store like Mongo, JDBC database, Redis, Hazelcast, ZooKeeper or others for coordination.

Feedback and pull-requests welcome!

ShedLock is not a distributed scheduler

Please note that ShedLock is not and will never be full-fledged scheduler, it's just a lock. If you need a distributed scheduler, please use another project (db-scheduler, JobRunr). ShedLock is designed to be used in situations where you have scheduled tasks that are not ready to be executed in parallel, but can be safely executed repeatedly. Moreover, the locks are time-based and ShedLock assumes that clocks on the nodes are synchronized.

Versions

If you are using JDK >17 and up-to-date libraries like Spring 6, use version 5.1.0 (Release Notes). If you are on older JDK or library, use version 4.44.0 (documentation).

Components

Shedlock consists of three parts

  • Core - The locking mechanism
  • Integration - integration with your application, using Spring AOP, Micronaut AOP or manual code
  • Lock provider - provides the lock using an external process like SQL database, Mongo, Redis and others

Usage

To use ShedLock, you do the following

  1. Enable and configure Scheduled locking
  2. Annotate your scheduled tasks
  3. Configure a Lock Provider

Enable and configure Scheduled locking (Spring)

First of all, we have to import the project

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-spring</artifactId>
    <version>5.14.0</version>
</dependency>

Now we need to integrate the library with Spring. In order to enable schedule locking use @EnableSchedulerLock annotation

@Configuration
@EnableScheduling
@EnableSchedulerLock(defaultLockAtMostFor = "10m")
class MySpringConfiguration {
    ...
}

Annotate your scheduled tasks

import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;

...

@Scheduled(...)
@SchedulerLock(name = "scheduledTaskName")
public void scheduledTask() {
    // To assert that the lock is held (prevents misconfiguration errors)
    LockAssert.assertLocked();
    // do something
}

The @SchedulerLock annotation has several purposes. First of all, only annotated methods are locked, the library ignores all other scheduled tasks. You also have to specify the name for the lock. Only one task with the same name can be executed at the same time.

You can also set lockAtMostFor attribute which specifies how long the lock should be kept in case the executing node dies. This is just a fallback, under normal circumstances the lock is released as soon the tasks finishes (unless lockAtLeastFor is specified, see below) You have to set lockAtMostFor to a value which is much longer than normal execution time. If the task takes longer than lockAtMostFor the resulting behavior may be unpredictable (more than one process will effectively hold the lock).

If you do not specify lockAtMostFor in @SchedulerLock default value from @EnableSchedulerLock will be used.

Lastly, you can set lockAtLeastFor attribute which specifies minimum amount of time for which the lock should be kept. Its main purpose is to prevent execution from multiple nodes in case of really short tasks and clock difference between the nodes.

All the annotations support Spring Expression Language (SpEL).

Example

Let's say you have a task which you execute every 15 minutes and which usually takes few minutes to run. Moreover, you want to execute it at most once per 15 minutes. In that case, you can configure it like this:

import net.javacrumbs.shedlock.core.SchedulerLock;


@Scheduled(cron = "0 */15 * * * *")
@SchedulerLock(name = "scheduledTaskName", lockAtMostFor = "14m", lockAtLeastFor = "14m")
public void scheduledTask() {
    // do something
}

By setting lockAtMostFor we make sure that the lock is released even if the node dies. By setting lockAtLeastFor we make sure it's not executed more than once in fifteen minutes. Please note that lockAtMostFor is just a safety net in case that the node executing the task dies, so set it to a time that is significantly larger than maximum estimated execution time. If the task takes longer than lockAtMostFor, it may be executed again and the results will be unpredictable (more processes will hold the lock).

Configure LockProvider

There are several implementations of LockProvider.

JdbcTemplate

First, create lock table (please note that name has to be primary key)

# MySQL, MariaDB
CREATE TABLE shedlock(name VARCHAR(64) NOT NULL, lock_until TIMESTAMP(3) NOT NULL,
    locked_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), locked_by VARCHAR(255) NOT NULL, PRIMARY KEY (name));

# Postgres
CREATE TABLE shedlock(name VARCHAR(64) NOT NULL, lock_until TIMESTAMP NOT NULL,
    locked_at TIMESTAMP NOT NULL, locked_by VARCHAR(255) NOT NULL, PRIMARY KEY (name));

# Oracle
CREATE TABLE shedlock(name VARCHAR(64) NOT NULL, lock_until TIMESTAMP(3) NOT NULL,
    locked_at TIMESTAMP(3) NOT NULL, locked_by VARCHAR(255) NOT NULL, PRIMARY KEY (name));

# MS SQL
CREATE TABLE shedlock(name VARCHAR(64) NOT NULL, lock_until datetime2 NOT NULL,
    locked_at datetime2 NOT NULL, locked_by VARCHAR(255) NOT NULL, PRIMARY KEY (name));

# DB2
CREATE TABLE shedlock(name VARCHAR(64) NOT NULL PRIMARY KEY, lock_until TIMESTAMP NOT NULL,
    locked_at TIMESTAMP NOT NULL, locked_by VARCHAR(255) NOT NULL);

Or use this liquibase change-set.

Add dependency

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-jdbc-template</artifactId>
    <version>5.14.0</version>
</dependency>

Configure:

import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider;

...
@Bean
public LockProvider lockProvider(DataSource dataSource) {
            return new JdbcTemplateLockProvider(
                JdbcTemplateLockProvider.Configuration.builder()
                .withJdbcTemplate(new JdbcTemplate(dataSource))
                .usingDbTime() // Works on Postgres, MySQL, MariaDb, MS SQL, Oracle, DB2, HSQL and H2
                .build()
            );
}

By specifying usingDbTime() the lock provider will use UTC time based on the DB server clock. If you do not specify this option, clock from the app server will be used (the clocks on app servers may not be synchronized thus leading to various locking issues).

It's strongly recommended to use usingDbTime() option as it uses DB engine specific SQL that prevents INSERT conflicts. See more details here.

For more fine-grained configuration use other options of the Configuration object

new JdbcTemplateLockProvider(builder()
    .withTableName("shdlck")
    .withColumnNames(new ColumnNames("n", "lck_untl", "lckd_at", "lckd_by"))
    .withJdbcTemplate(new JdbcTemplate(getDatasource()))
    .withLockedByValue("my-value")
    .withDbUpperCase(true)
    .build())

If you need to specify a schema, you can set it in the table name using the usual dot notation new JdbcTemplateLockProvider(datasource, "my_schema.shedlock")

To use a database with case-sensitive table and column names, the .withDbUpperCase(true) flag can be used. Default is false (lowercase).

Warning

Do not manually delete lock row from the DB table. ShedLock has an in-memory cache of existing lock rows so the row will NOT be automatically recreated until application restart. If you need to, you can edit the row/document, risking only that multiple locks will be held.

R2DBC

If you are really brave, you can try experimental R2DBC support. Please keep in mind that the capabilities of this lock provider are really limited and that the whole ecosystem around R2DBC is in flux and may easily break.

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-r2dbc</artifactId>
    <version>5.14.0</version>
</dependency>

and use it.

@Override
protected LockProvider getLockProvider() {
    return new R2dbcLockProvider(connectionFactory);
}

I recommend using R2DBC connection pool.

jOOQ lock provider

First, create lock table as described in the JdbcTemplate section above.

Add dependency

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-jooq</artifactId>
    <version>5.14.0</version>
</dependency>

Configure:

import net.javacrumbs.shedlock.provider.jooq;

...
@Bean
public LockProvider getLockProvider(DSLContext dslContext) {
    return new JooqLockProvider(dslContext);
}

jOOQ provider has a bit different transactional behavior. While the other JDBC lock providers create new transaction (with REQUIRES_NEW), jOOQ does not support setting it. ShedLock tries to create a new transaction, but depending on your set-up, ShedLock DB operations may end-up being part of the enclosing transaction.

If you need to configure the table name, schema or column names, you can use jOOQ render mapping as described here.

Micronaut Data Jdbc

If you are using Micronaut data and you do not want to add dependency on Spring JDBC, you can use Micronaut JDBC support. Just be aware that it has just a basic functionality when compared to the JdbcTemplate provider.

First, create lock table as described in the JdbcTemplate section above.

Add dependency

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-jdbc-micronaut</artifactId>
    <version>5.14.0</version>
</dependency>

Configure:

import net.javacrumbs.shedlock.provider.jdbc.micronaut.MicronautJdbcLockProvider;

...
@Singleton
public LockProvider lockProvider(TransactionOperations<Connection> transactionManager) {
    return new MicronautJdbcLockProvider(transactionManager);
}

Mongo

Import the project

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-mongo</artifactId>
    <version>5.14.0</version>
</dependency>

Configure:

import net.javacrumbs.shedlock.provider.mongo.MongoLockProvider;

...

@Bean
public LockProvider lockProvider(MongoClient mongo) {
    return new MongoLockProvider(mongo.getDatabase(databaseName))
}

Please note that MongoDB integration requires Mongo >= 2.4 and mongo-java-driver >= 3.7.0

Reactive Mongo

Import the project

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-mongo-reactivestreams</artifactId>
    <version>5.14.0</version>
</dependency>

Configure:

import net.javacrumbs.shedlock.provider.mongo.reactivestreams.ReactiveStreamsMongoLockProvider;

...

@Bean
public LockProvider lockProvider(MongoClient mongo) {
    return new ReactiveStreamsMongoLockProvider(mongo.getDatabase(databaseName))
}

Please note that MongoDB integration requires Mongo >= 4.x and mongodb-driver-reactivestreams 1.x

DynamoDB 2

Depends on AWS SDK v2.

Import the project

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-dynamodb2</artifactId>
    <version>5.14.0</version>
</dependency>

Configure:

import net.javacrumbs.shedlock.provider.dynamodb2.DynamoDBLockProvider;

...

@Bean
public LockProvider lockProvider(software.amazon.awssdk.services.dynamodb.DynamoDbClient dynamoDB) {
    return new DynamoDBLockProvider(dynamoDB, "Shedlock");
}

Please note that the lock table must be created externally with _id as a partition key. DynamoDBUtils#createLockTable may be used for creating it programmatically. A table definition is available from DynamoDBLockProvider's Javadoc.

ZooKeeper (using Curator)

Import

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-zookeeper-curator</artifactId>
    <version>5.14.0</version>
</dependency>

and configure

import net.javacrumbs.shedlock.provider.zookeeper.curator.ZookeeperCuratorLockProvider;

...

@Bean
public LockProvider lockProvider(org.apache.curator.framework.CuratorFramework client) {
    return new ZookeeperCuratorLockProvider(client);
}

By default, nodes for locks will be created under /shedlock node.

Redis (using Spring RedisConnectionFactory)

Import

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-redis-spring</artifactId>
    <version>5.14.0</version>
</dependency>

and configure

import net.javacrumbs.shedlock.provider.redis.spring.RedisLockProvider;
import org.springframework.data.redis.connection.RedisConnectionFactory;

...

@Bean
public LockProvider lockProvider(RedisConnectionFactory connectionFactory) {
    return new RedisLockProvider(connectionFactory, ENV);
}

Redis (using Spring ReactiveRedisConnectionFactory)

Import

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-redis-spring</artifactId>
    <version>5.14.0</version>
</dependency>

and configure

import net.javacrumbs.shedlock.provider.redis.spring.ReactiveRedisLockProvider;
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;

...

@Bean
public LockProvider lockProvider(ReactiveRedisConnectionFactory connectionFactory) {
    return new ReactiveRedisLockProvider.Builder(connectionFactory)
        .environment(ENV)
        .build();
}

Redis lock provider uses classical lock mechanism as described here which may not be reliable in case of Redis master failure.

Redis (using Jedis)

Import

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-redis-jedis4</artifactId>
    <version>5.14.0</version>
</dependency>

and configure

import net.javacrumbs.shedlock.provider.redis.jedis.JedisLockProvider;

...

@Bean
public LockProvider lockProvider(JedisPool jedisPool) {
    return new JedisLockProvider(jedisPool, ENV);
}

Hazelcast

Import the project

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-hazelcast4</artifactId>
    <version>5.14.0</version>
</dependency>

Configure:

import net.javacrumbs.shedlock.provider.hazelcast4.HazelcastLockProvider;

...

@Bean
public HazelcastLockProvider lockProvider(HazelcastInstance hazelcastInstance) {
    return new HazelcastLockProvider(hazelcastInstance);
}

Couchbase

Import the project

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-couchbase-javaclient3</artifactId>
    <version>5.14.0</version>
</dependency>

Configure:

import net.javacrumbs.shedlock.provider.couchbase.javaclient.CouchbaseLockProvider;

...

@Bean
public CouchbaseLockProvider lockProvider(Bucket bucket) {
    return new CouchbaseLockProvider(bucket);
}

For Couchbase 3 use shedlock-provider-couchbase-javaclient3 module and net.javacrumbs.shedlock.provider.couchbase3 package.

Elasticsearch

I am really not sure it's a good idea to use Elasticsearch as a lock provider. But if you have no other choice, you can. Import the project

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-elasticsearch8</artifactId>
    <version>5.14.0</version>
</dependency>

Configure:

import static net.javacrumbs.shedlock.provider.elasticsearch8.ElasticsearchLockProvider;

...

@Bean
public ElasticsearchLockProvider lockProvider(ElasticsearchClient client) {
    return new ElasticsearchLockProvider(client);
}

OpenSearch

Import the project

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-opensearch</artifactId>
    <version>4.36.1</version>
</dependency>

Configure:

import static net.javacrumbs.shedlock.provider.opensearch.OpenSearchLockProvider;

...

@Bean
public OpenSearchLockProvider lockProvider(RestHighLevelClient highLevelClient) {
    return new OpenSearchLockProvider(highLevelClient);
}

CosmosDB

CosmosDB support is provided by a third-party module available here

Cassandra

Import the project

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-cassandra</artifactId>
    <version>5.14.0</version>
</dependency>

Configure:

import net.javacrumbs.shedlock.provider.cassandra.CassandraLockProvider;
import net.javacrumbs.shedlock.provider.cassandra.CassandraLockProvider.Configuration;

...

@Bean
public CassandraLockProvider lockProvider(CqlSession cqlSession) {
    return new CassandraLockProvider(Configuration.builder().withCqlSession(cqlSession).withTableName("lock").build());
}

Example for creating default keyspace and table in local Cassandra instance:

CREATE KEYSPACE shedlock with replication={'class':'SimpleStrategy', 'replication_factor':1} and durable_writes=true;
CREATE TABLE shedlock.lock (name text PRIMARY KEY, lockUntil timestamp, lockedAt timestamp, lockedBy text);

Please, note that CassandraLockProvider uses Cassandra driver v4, which is part of Spring Boot since 2.3.

Consul

ConsulLockProvider has one limitation: lockAtMostFor setting will have a minimum value of 10 seconds. It is dictated by consul's session limitations.

Import the project

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-consul</artifactId>
    <version>5.14.0</version>
</dependency>

Configure:

import net.javacrumbs.shedlock.provider.consul.ConsulLockProvider;

...

@Bean // for micronaut please define preDestroy property @Bean(preDestroy="close")
public ConsulLockProvider lockProvider(com.ecwid.consul.v1.ConsulClient consulClient) {
    return new ConsulLockProvider(consulClient);
}

Please, note that Consul lock provider uses ecwid consul-api client, which is part of spring cloud consul integration (the spring-cloud-starter-consul-discovery package).

ArangoDB

Import the project

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-arangodb</artifactId>
    <version>5.14.0</version>
</dependency>

Configure:

import net.javacrumbs.shedlock.provider.arangodb.ArangoLockProvider;

...

@Bean
public ArangoLockProvider lockProvider(final ArangoOperations arangoTemplate) {
    return new ArangoLockProvider(arangoTemplate.driver().db(DB_NAME));
}

Please, note that ArangoDB lock provider uses ArangoDB driver v6.7, which is part of arango-spring-data in version 3.3.0.

Neo4j

Import the project

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-neo4j</artifactId>
    <version>5.14.0</version>
</dependency>

Configure:

import net.javacrumbs.shedlock.core.LockConfiguration;

...

@Bean
Neo4jLockProvider lockProvider(org.neo4j.driver.Driver driver) {
    return new Neo4jLockProvider(driver);
}

Please make sure that neo4j-java-driver version used by shedlock-provider-neo4j matches the driver version used in your project (if you use spring-boot-starter-data-neo4j, it is probably provided transitively).

Etcd

Import the project

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-etcd-jetcd</artifactId>
    <version>5.14.0</version>
</dependency>

Configure:

import net.javacrumbs.shedlock.provider.etcd.jetcd.EtcdLockProvider;

...

@Bean
public LockProvider lockProvider(Client client) {
    return new EtcdLockProvider(client);
}

Apache Ignite

Import the project

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-ignite</artifactId>
    <version>5.14.0</version>
</dependency>

Configure:

import net.javacrumbs.shedlock.provider.ignite.IgniteLockProvider;

...

@Bean
public LockProvider lockProvider(Ignite ignite) {
    return new IgniteLockProvider(ignite);
}

In-Memory

If you want to use a lock provider in tests there is an in-Memory implementation.

Import the project

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-inmemory</artifactId>
    <version>5.14.0</version>
    <scope>test</scope>
</dependency>
import net.javacrumbs.shedlock.provider.inmemory.InMemoryLockProvider;

...

@Bean
public LockProvider lockProvider() {
    return new InMemoryLockProvider();
}

Memcached (using spymemcached)

Please, be aware that memcached is not a database but a cache. It means that if the cache is full, the lock may be released prematurely Use only if you know what you are doing.

Import

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-memcached-spy</artifactId>
    <version>5.14.0</version>
</dependency>

and configure

import net.javacrumbs.shedlock.provider.memcached.spy.MemcachedLockProvider;

...

@Bean
public LockProvider lockProvider(net.spy.memcached.MemcachedClient client) {
    return new MemcachedLockProvider(client, ENV);
}

P.S.:

Memcached Standard Protocol:

  • A key (arbitrary string up to 250 bytes in length. No space or newlines for ASCII mode)
  • An expiration time, in seconds. '0' means never expire. Can be up to 30 days. After 30 days, is treated as a unix timestamp of an exact date. (support secondsใ€minutesใ€days, and less than 30 days)

Datastore

Import the project

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-datastore</artifactId>
    <version>5.14.0</version>
</dependency>

and configure

import net.javacrumbs.shedlock.provider.datastore.DatastoreLockProvider;

...

@Bean
public LockProvider lockProvider(com.google.cloud.datastore.Datastore datastore) {
    return new DatastoreLockProvider(datastore);
}

Spanner

Import the project

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-spanner</artifactId>
    <version>5.14.0</version>
</dependency>

Configure

import net.javacrumbs.shedlock.provider.spanner.SpannerLockProvider;

...

// Basic
@Bean
public LockProvider lockProvider(DatabaseClient databaseClient) {
    return new SpannerLockProvider(databaseClientSupplier);
}

// Custom host, table and column names
@Bean
public LockProvider lockProvider(DatabaseClient databaseClient) {
    var config = SpannerLockProvider.Configuration.builder()
        .withDatabaseClient(databaseClientSupplier)
        .withTableConfiguration(SpannerLockProvider.TableConfiguration.builder()
            ...
            // Custom table and column names
            .build())
        .withHostName("customHostName")
        .build();

    return new SpannerLockProvider(config);
}

Multi-tenancy

If you have multi-tenancy use-case you can use a lock provider similar to this one (see the full example)

private static abstract class MultiTenancyLockProvider implements LockProvider {
    private final ConcurrentHashMap<String, LockProvider> providers = new ConcurrentHashMap<>();

    @Override
    public @NonNull Optional<SimpleLock> lock(@NonNull LockConfiguration lockConfiguration) {
        String tenantName = getTenantName(lockConfiguration);
        return providers.computeIfAbsent(tenantName, this::createLockProvider).lock(lockConfiguration);
    }

    protected abstract LockProvider createLockProvider(String tenantName) ;

    protected abstract String getTenantName(LockConfiguration lockConfiguration);
}

Customization

You can customize the behavior of the library by implementing LockProvider interface. Let's say you want to implement a special behavior after a lock is obtained. You can do it like this:

public class MyLockProvider implements LockProvider {
    private final LockProvider delegate;

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

    @Override
    public Optional<SimpleLock> lock(LockConfiguration lockConfiguration) {
        Optional<SimpleLock> lock = delegate.lock(lockConfiguration);
        if (lock.isPresent()) {
            // do something
        }
        return lock;
    }
}

Duration specification

All the annotations where you need to specify a duration support the following formats

  • duration+unit - 1s, 5ms, 5m, 1d (Since 4.0.0)
  • duration in ms - 100 (only Spring integration)
  • ISO-8601 - PT15M (see Duration.parse() documentation)

Extending the lock

There are some use-cases which require to extend currently held lock. You can use LockExtender in the following way:

LockExtender.extendActiveLock(Duration.ofMinutes(5), ZERO);

Please note that not all lock provider implementations support lock extension.

KeepAliveLockProvider

There is also KeepAliveLockProvider that is able to keep the lock alive by periodically extending it. It can be used by wrapping the original lock provider. My personal opinion is that it should be used only in special cases, it adds more complexity to the library and the flow is harder to reason about so please use moderately.

@Bean
public LockProvider lockProvider(...) {
    return new  KeepAliveLockProvider(new XyzProvider(...), scheduler);
}

KeepAliveLockProvider extends the lock in the middle of the lockAtMostFor interval. For example, if the lockAtMostFor is 10 minutes the lock is extended every 5 minutes for 10 minutes until the lock is released. Please note that the minimal lockAtMostFor time supported by this provider is 30s. The scheduler is used only for the lock extension, single thread should be enough.

Micronaut integration

Since version 4.0.0, it's possible to use Micronaut framework for integration

Import the project:

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <!-- Micronaut 3 -->
    <artifactId>shedlock-micronaut</artifactId>
    <!-- For Micronaut 4 use -->
    <!-- <artifactId>shedlock-micronaut4</artifactId> -->
    <version>5.14.0</version>
</dependency>

Configure default lockAtMostFor value (application.yml):

shedlock:
  defaults:
    lock-at-most-for: 1m

Configure lock provider:

@Singleton
public LockProvider lockProvider() {
    ... select and configure your lock provider
}

Configure the scheduled task:

@Scheduled(fixedDelay = "1s")
@SchedulerLock(name = "myTask")
public void myTask() {
    assertLocked();
    ...
}

CDI integration

Since version 5.0.0, it's possible to use CDI for integration (tested only with Quarkus)

Import the project:

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <!-- use shedlock-cdi-vintage for quarkus 2.x -->
    <artifactId>shedlock-cdi</artifactId>
    <version>5.14.0</version>
</dependency>

Configure default lockAtMostFor value (application.properties):

shedlock.defaults.lock-at-most-for=PT30S

Configure lock provider:

@Produces
@Singleton
public LockProvider lockProvider() {
        ...
}

Configure the scheduled task:

@Scheduled(every = "1s")
@SchedulerLock(name = "myTask")
public void myTask() {
    assertLocked();
    ...
}

The implementation only depends on jakarta.enterprise.cdi-api and microprofile-config-api so it should be usable in other CDI compatible frameworks, but it has not been tested with anything else than Quarkus. It's built on top of javax annotation as Quarkus has not moved to Jakarta EE namespace yet.

The support is minimalistic, for example there is no support for expressions in the annotation parameters yet, if you need it, feel free to send a PR.

Locking without a framework

It is possible to use ShedLock without a framework

LockingTaskExecutor executor = new DefaultLockingTaskExecutor(lockProvider);

...

Instant lockAtMostUntil = Instant.now().plusSeconds(600);
executor.executeWithLock(runnable, new LockConfiguration("lockName", lockAtMostUntil));

Extending the lock

Some lock providers support extension of the lock. For the time being, it requires manual lock manipulation, directly using LockProvider and calling extend method on the SimpleLock.

Modes of Spring integration

ShedLock supports two modes of Spring integration. One that uses an AOP proxy around scheduled method (PROXY_METHOD) and one that proxies TaskScheduler (PROXY_SCHEDULER)

Scheduled Method proxy

Since version 4.0.0, the default mode of Spring integration is an AOP proxy around the annotated method.

The main advantage of this mode is that it plays well with other frameworks that want to somehow alter the default Spring scheduling mechanism. The disadvantage is that the lock is applied even if you call the method directly. If the method returns a value and the lock is held by another process, null or an empty Optional will be returned (primitive return types are not supported).

Final and non-public methods are not proxied so either you have to make your scheduled methods public and non-final or use TaskScheduler proxy.

Method proxy sequenceDiagram

TaskScheduler proxy

This mode wraps Spring TaskScheduler in an AOP proxy. This mode does not play well with instrumentation libraries like opentelementry that also wrap TaskScheduler. Please only use it if you know what you are doing. It can be switched-on like this (PROXY_SCHEDULER was the default method before 4.0.0):

@EnableSchedulerLock(interceptMode = PROXY_SCHEDULER)

If you do not specify your task scheduler, a default one is created for you. If you have special needs, just create a bean implementing TaskScheduler interface and it will get wrapped into the AOP proxy automatically.

@Bean
public TaskScheduler taskScheduler() {
    return new MySpecialTaskScheduler();
}

Alternatively, you can define a bean of type ScheduledExecutorService and it will automatically get used by the tasks scheduling mechanism.

TaskScheduler proxy sequence diagram

Spring XML configuration

Spring XML configuration is not supported as of version 3.0.0. If you need it, please use version 2.6.0 or file an issue explaining why it is needed.

Lock assert

To prevent misconfiguration errors, like AOP misconfiguration, missing annotation etc., you can assert that the lock works by using LockAssert:

@Scheduled(...)
@SchedulerLock(..)
public void scheduledTask() {
    // To assert that the lock is held (prevents misconfiguration errors)
    LockAssert.assertLocked();
    // do something
}

In unit tests you can switch-off the assertion by calling LockAssert.TestHelper.makeAllAssertsPass(true) on given thread (as in this example).

Kotlin gotchas

The library is tested with Kotlin and works fine. The only issue is Spring AOP which does not work on final method. If you use @SchedulerLock with @Component annotation, everything should work since Kotlin Spring compiler plugin will automatically 'open' the method for you. If @Component annotation is not present, you have to open the method by yourself. (see this issue for more details)

Caveats

Locks in ShedLock have an expiration time which leads to the following possible issues.

  1. If the task runs longer than lockAtMostFor, the task can be executed more than once
  2. If the clock difference between two nodes is more than lockAtLeastFor or minimal execution time the task can be executed more than once.

Troubleshooting

Help, ShedLock does not do what it's supposed to do!

  1. Upgrade to the newest version
  2. Use LockAssert to ensure that AOP is correctly configured.
    • If it does not work, please read about Spring AOP internals (for example here)
  3. Check the storage. If you are using JDBC, check the ShedLock table. If it's empty, ShedLock is not properly configured. If there is more than one record with the same name, you are missing a primary key.
  4. Use ShedLock debug log. ShedLock logs interesting information on DEBUG level with logger name net.javacrumbs.shedlock. It should help you to see what's going on.
  5. For short-running tasks consider using lockAtLeastFor. If the tasks are short-running, they could be executed one after another, lockAtLeastFor can prevent it.

Release notes

5.14.0 + 4.48.0 (2024-07-24)

  • RedisLockProvider made extensible (thanks @shubhajyoti-bagchi-groww)
  • Dependency updates

5.13.0 (2024-04-05)

  • #1779 Ability to rethrow unexpected exception in JdbcTemplateStorageAccessor
  • Dependency updates

5.12.0 (2024-02-29)

  • #1800 Enable lower case for database type when using usingDbTime()
  • #1804 Startup error with Neo4j 5.17.0
  • Dependency updates

4.47.0 (2024-03-01)

  • #1800 Enable lower case for database type when using usingDbTime() (thanks @yuagu1)

5.11.0 (2024-02-13)

  • #1753 Fix SpEL for methods with parameters
  • Dependency updates

5.10.2 (2023-12-07)

  • #1635 fix makeAllAssertsPass locks only once
  • Dependency updates

5.10.1 (2023-12-06)

  • #1635 fix makeAllAssertsPass(false) throws NoSuchElementException
  • Dependency updates

5.10.0 (2023-11-07)

  • SpannerLockProvider added (thanks @pXius)
  • Dependency updates

5.9.1 (2023-10-19)

  • QuarkusRedisLockProvider supports Redis 6.2 (thanks @ricardojlrufino)

5.9.0 (2023-10-15)

  • Support Quarkus 2 Redis client (thanks @ricardojlrufino)
  • Better handling of timeouts in ReactiveStreamsMongoLockProvider
  • Dependency updates

5.8.0 (2023-09-15)

  • Support for Micronaut 4
  • Use Merge instead of Insert for Oracle #1528 (thanks @xmojsic)
  • Dependency updates

5.7.0 (2023-08-25)

  • JedisLockProvider supports extending (thanks @shotmk)
  • Better behavior when locks are nested #1493

4.46.0 (2023-09-05)

  • JedisLockProvider (version 3) supports extending (thanks @shotmk)

4.45.0 (2023-09-04)

  • JedisLockProvider supports extending (thanks @shotmk)

5.6.0

  • Ability to explicitly set database product in JdbTemplateLockProvider (thanks @metron2)
  • Removed forgotten versions from BOM
  • Dependency updates

5.5.0 (2023-06-19)

  • Datastore support (thanks @mmastika)
  • Dependency updates

5.4.0 (2023-06-06)

5.3.0 (2023-05-13)

  • Added shedlock-cdi module (supports newest CDI version)
  • Dependency updates

5.2.0 (2023-03-06)

  • Uppercase in JdbcTemplateProvider (thanks @Ragin-LundF)
  • Dependency updates

5.1.0 (2023-01-07)

  • Added SpEL support to @SchedulerLock name attribute (thanks @ipalbeniz)
  • Dependency updates

5.0.1 (2022-12-10)

  • Work around broken Spring 6 exception translation #1272

4.44.0 (2022-12-29)

5.0.0 (2022-12-10)

  • Requires JDK 17
  • Tested with Spring 6 (Spring Boot 3)
  • Micronaut updated to 3.x.x
  • R2DBC 1.x.x (still sucks)
  • Spring Data 3.x.x
  • Rudimentary support for CDI (tested with quarkus)
  • New jOOQ lock provider
  • SLF4j 2
  • Deleted all deprecated code and support for old versions of libraries

4.43.0 (2022-12-04)

  • Better logging in JdbcTemplateProvider
  • Dependency updates

4.42.0 (2022-09-16)

  • Deprecate old Couchbase lock provider
  • Dependency updates

4.41.0 (2022-08-17)

  • Couchbase collection support (thanks @mesuutt)
  • Dependency updates

4.40.0 (2022-08-11)

  • Fixed caching issues when the app is started by the DB does not exist yet (#1129)
  • Dependency updates

4.39.0 (2022-07-26)

  • Introduced elasticsearch8 LockProvider and deperecated the orignal one (thanks @MarAra)
  • Dependency updates

4.38.0 (2022-07-02)

  • ReactiveRedisLockProvider added (thanks @ericwcc)
  • Dependency updates

4.37.0 (2022-06-14)

  • OpenSearch provider (thanks @Pinny3)
  • Fix wrong reference to reactive Mongo in BOM #1048
  • Dependency updates

4.36.0 (2022-05-28)

  • shedlock-bom module added
  • Dependency updates

4.35.0 (2022-05-16)

  • Neo4j allows to specify database thanks @SergeyPlatonov
  • Dependency updates

4.34.0 (2022-04-09)

  • Dropped support for Hazelcast <= 3 as it has unfixed vulnerability
  • Dropped support for Spring Data Redis 1 as it is not supported
  • Dependency updates

4.33.0

  • memcached provider added (thanks @pinkhello)
  • Dependency updates

4.32.0

  • JDBC provider does not change autocommit attribute
  • Dependency updates

4.31.0

  • Jedis 4 lock provider
  • Dependency updates

4.30.0

  • In-memory lock provider added (thanks @kkocel)
  • Dependency updates

4.29.0

  • R2DBC support added (thanks @sokomishalov)
  • Library upgrades

4.28.0

  • Neo4j lock provider added (thanks @thimmwork)
  • Library upgrades

4.27.0

  • Ability to set transaction isolation in JdbcTemplateLockProvider
  • Library upgrades

4.26.0

  • KeepAliveLockProvider introduced
  • Library upgrades

4.25.0

  • LockExtender added

4.24.0

  • Support for Apache Ignite (thanks @wirtsleg)
  • Library upgrades

4.23.0

  • Ability to set serialConsistencyLevel in Cassandra (thanks @DebajitKumarPhukan)
  • Introduced shedlock-provider-jdbc-micronaut module (thanks @drmaas)

4.22.1

  • Catching and logging Cassandra exception

4.22.0

  • Support for custom keyspace in Cassandra provider

4.21.0

  • Elastic unlock using IMMEDIATE refresh policy #422
  • DB2 JDBC lock provider uses microseconds in DB time
  • Various library upgrades

4.20.1

  • Fixed DB JDBC server time #378

4.20.0

  • Support for etcd (thanks grofoli)

4.19.1

  • Fixed devtools compatibility #368

4.19.0

  • Support for enhanced configuration in Cassandra provider (thanks DebajitKumarPhukan)
  • LockConfigurationExtractor exposed as a Spring bean #359
  • Handle CannotSerializeTransactionException #364

4.18.0

  • Fixed Consul support for tokens and added enhanced Consul configuration (thanks DrWifey)

4.17.0

  • Consul support for tokens

4.16.0

  • Spring - EnableSchedulerLock.order param added to specify AOP proxy order
  • JDBC - Log unexpected exceptions at ERROR level
  • Hazelcast upgraded to 4.1

4.15.1

  • Fix session leak in Consul provider #340 (thanks @haraldpusch)

4.15.0

  • ArangoDB lock provider added (thanks @patrick-birkle)

4.14.0

  • Support for Couchbase 3 driver (thanks @blitzenzzz)
  • Removed forgotten configuration files form micronaut package (thanks @drmaas)
  • Shutdown hook for Consul (thanks @kaliy)

4.13.0

  • Support for Consul (thanks @kaliy)
  • Various dependencies updated
  • Deprecated default LockConfiguration constructor

4.12.0

  • Lazy initialization of SqlStatementsSource #258

4.11.1

  • MongoLockProvider uses mongodb-driver-sync
  • Removed deprecated constructors from MongoLockProvider

4.10.1

  • New Mongo reactive streams driver (thanks @codependent)

4.9.3

  • Fixed JdbcTemplateLockProvider useDbTime() locking #244 thanks @gjorgievskivlatko

4.9.2

  • Do not fail on DB type determining code if DB connection is not available

4.9.1

  • Support for server time in DB2
  • removed shedlock-provider-jdbc-internal module

4.9.0

  • Support for server time in JdbcTemplateLockProvider
  • Using custom non-null annotations
  • Trimming time precision to milliseconds
  • Micronaut upgraded to 1.3.4
  • Add automatic DB tests for Oracle, MariaDB and MS SQL.

4.8.0

  • DynamoDB 2 module introduced (thanks Mark Egan)
  • JDBC template code refactored to not log error on failed insert in Postgres
    • INSERT .. ON CONFLICT UPDATE is used for Postgres

4.7.1

  • Make LockAssert.TestHelper public

4.7.0

  • New module for Hazelcasts 4
  • Ability to switch-off LockAssert in unit tests

4.6.0

  • Support for Meta annotations and annotation inheritance in Spring

4.5.2

  • Made compatible with PostgreSQL JDBC Driver 42.2.11

4.5.1

  • Inject redis template

4.5.0

  • ClockProvider introduced
  • MongoLockProvider(MongoDatabase) introduced

4.4.0

  • Support for non-void returning methods when PROXY_METHOD interception is used

4.3.1

  • Introduced shedlock-provider-redis-spring-1 to make it work around Spring Data Redis 1 issue #105 (thanks @rygh4775)

4.3.0

  • Jedis dependency upgraded to 3.2.0
  • Support for JedisCluster
  • Tests upgraded to JUnit 5

4.2.0

  • Cassandra provider (thanks @mitjag)

4.1.0

  • More configuration option for JdbcTemplateProvider

4.0.4

  • Allow configuration of key prefix in RedisLockProvider #181 (thanks @krm1312)

4.0.3

  • Fixed junit dependency scope #179

4.0.2

  • Fix NPE caused by Redisson #178

4.0.1

  • DefaultLockingTaskExecutor made reentrant #175

4.0.0

Version 4.0.0 is a major release changing quite a lot of stuff

  • net.javacrumbs.shedlock.core.SchedulerLock has been replaced by net.javacrumbs.shedlock.spring.annotation.SchedulerLock. The original annotation has been in wrong module and was too complex. Please use the new annotation, the old one still works, but in few years it will be removed.
  • Default intercept mode changed from PROXY_SCHEDULER to PROXY_METHOD. The reason is that there were a lot of issues with PROXY_SCHEDULER (for example #168). You can still use PROXY_SCHEDULER mode if you specify it manually.
  • Support for more readable duration strings
  • Support for lock assertion LockAssert.assertLocked()
  • Support for Micronaut added

3.0.1

  • Fixed bean definition configuration #171

3.0.0

  • EnableSchedulerLock.mode renamed to interceptMode
  • Use standard Spring AOP configuration to honor Spring Boot config (supports proxyTargetClass flag)
  • Removed deprecated SpringLockableTaskSchedulerFactoryBean and related classes
  • Removed support for XML configuration

2.6.0

  • Updated dependency to Spring 2.1.9
  • Support for lock extensions (beta)

2.5.0

  • Zookeeper supports lockAtMostFor and lockAtLeastFor params
  • Better debug logging

2.4.0

  • Fixed potential deadlock in Hazelcast (thanks @HubertTatar)
  • Finding class level annotation in proxy method mode (thanks @volkovs)
  • ScheduledLockConfigurationBuilder deprecated

2.3.0

  • LockProvides is initialized lazilly so it does not change DataSource initialization order

2.2.1

  • MongoLockProvider accepts MongoCollection as a constructor param

2.2.0

  • DynamoDBLockProvider added

2.1.0

  • MongoLockProvider rewritten to use upsert
  • ElasticsearchLockProvider added

2.0.1

  • AOP proxy and annotation configuration support

1.3.0

  • Can set Timezone to JdbcTemplateLock provider

1.2.0

  • Support for Couchbase (thanks to @MoranVaisberg)

1.1.1

  • Spring RedisLockProvider refactored to use RedisTemplate

1.1.0

  • Support for transaction manager in JdbcTemplateLockProvider (thanks to @grmblfrz)

1.0.0

  • Upgraded dependencies to Spring 5 and Spring Data 2
  • Removed deprecated net.javacrumbs.shedlock.provider.jedis.JedisLockProvider (use net.javacrumbs.shedlock.provider.redis.jedis.JedisLockProvide instead)
  • Removed deprecated SpringLockableTaskSchedulerFactory (use ScheduledLockConfigurationBuilder instead)

0.18.2

  • ablility to clean lock cache

0.18.1

  • shedlock-provider-redis-spring made compatible with spring-data-redis 1.x.x

0.18.0

  • Added shedlock-provider-redis-spring (thanks to @siposr)
  • shedlock-provider-jedis moved to shedlock-provider-redis-jedis

0.17.0

  • Support for SPEL in lock name annotation

0.16.1

  • Automatically closing TaskExecutor on Spring shutdown

0.16.0

  • Removed spring-test from shedlock-spring compile time dependencies
  • Added Automatic-Module-Names

0.15.1

  • Hazelcast works with remote cluster

0.15.0

  • Fixed ScheduledLockConfigurationBuilder interfaces #32
  • Hazelcast code refactoring

0.14.0

  • Support for Hazelcast (thanks to @peyo)

0.13.0

  • Jedis constructor made more generic (thanks to @mgrzeszczak)

0.12.0

  • Support for property placeholders in annotation lockAtMostForString/lockAtLeastForString
  • Support for composed annotations
  • ScheduledLockConfigurationBuilder introduced (deprecating SpringLockableTaskSchedulerFactory)

0.11.0

  • Support for Redis (thanks to @clamey)
  • Checking that lockAtMostFor is in the future
  • Checking that lockAtMostFor is larger than lockAtLeastFor

0.10.0

  • jdbc-template-provider does not participate in task transaction

0.9.0

  • Support for @SchedulerLock annotations on proxied classes

0.8.0

  • LockableTaskScheduler made AutoClosable so it's closed upon Spring shutdown

0.7.0

  • Support for lockAtLeastFor

0.6.0

  • Possible to configure defaultLockFor time so it does not have to be repeated in every annotation

0.5.0

  • ZooKeeper nodes created under /shedlock by default

0.4.1

  • JdbcLockProvider insert does not fail on DataIntegrityViolationException

0.4.0

  • Extracted LockingTaskExecutor
  • LockManager.executeIfNotLocked renamed to executeWithLock
  • Default table name in JDBC lock providers

0.3.0

  • @ShedlulerLock.name made obligatory
  • @ShedlulerLock.lockForMillis renamed to lockAtMostFor
  • Adding plain JDBC LockProvider
  • Adding ZooKeepr LockProvider

shedlock's People

Contributors

ae-govau avatar antarikshtiwary avatar clamey avatar ddworak avatar debajitkumarphukan avatar dependabot[bot] avatar drmaas avatar ericwcc avatar eurafa avatar faizanzaver avatar grofoli avatar jrehwaldt avatar kaliy avatar kennysoft avatar lukas-krecan avatar lukas-krecan-lt avatar marara avatar metron2 avatar mmastika avatar pbirkle avatar pierco avatar pinny3 avatar pxius avatar ragin-lundf avatar s7nio avatar shubhajyoti-bagchi-groww avatar snyk-bot avatar sokomishalov avatar svpr3m0 avatar thimmwork avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

shedlock's Issues

More of a concern

It looks like this doesn't take into consideration the next firing time. I believe schedulers like quartz knows when the next fire time should be and doesn't allow the job to be ran until that time comes. So I imagine it could be possible that a task could fire a little late and run the same task if the first one finishes quickly and releases the lock. Is this the case?

Issue shedlock-provider-jdbc

Hi
I set up shedlock with spring on my batches and my environment is: Database SQL Server , Jboss EAP 6.4 and java8. I turned on logs of shedlock and I catch some errors below:

[2017-04-19 13:05:00,013 - DEBUG - net.javacrumbs.shedlock.core.DefaultLockingTaskExecutor] Locked scheduledAlertaConciliacoes.
[2017-04-19 13:05:00,018 - DEBUG - net.javacrumbs.shedlock.core.DefaultLockingTaskExecutor] Unlocked scheduledAlertaConciliacoes.
[2017-04-19 13:15:00,064 - DEBUG - net.javacrumbs.shedlock.provider.jdbc.JdbcStorageAccessor] Exception thrown when inserting record
com.microsoft.sqlserver.jdbc.SQLServerException: Violaรงรฃo da restriรงรฃo PRIMARY KEY 'PK_shedlock'. Nรฃo รฉ possรญvel inserir a chave duplicada no objeto 'dbo.Spring_Batch_Locker'. O valor de chave duplicada รฉ (scheduledAlertaConciliacoes).
	at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDatabaseError(SQLServerException.java:232)
	at com.microsoft.sqlserver.jdbc.SQLServerStatement.getNextResult(SQLServerStatement.java:1672)
	at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement.doExecutePreparedStatement(SQLServerPreparedStatement.java:460)
	at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement$PrepStmtExecCmd.doExecute(SQLServerPreparedStatement.java:405)
	at com.microsoft.sqlserver.jdbc.TDSCommand.execute(IOBuffer.java:7535)
	at com.microsoft.sqlserver.jdbc.SQLServerConnection.executeCommand(SQLServerConnection.java:2438)
	at com.microsoft.sqlserver.jdbc.SQLServerStatement.executeCommand(SQLServerStatement.java:208)
	at com.microsoft.sqlserver.jdbc.SQLServerStatement.executeStatement(SQLServerStatement.java:183)
	at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement.executeUpdate(SQLServerPreparedStatement.java:348)
	at org.apache.commons.dbcp2.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:98)
	at org.apache.commons.dbcp2.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:98)
	at net.javacrumbs.shedlock.provider.jdbc.JdbcStorageAccessor.insertRecord(JdbcStorageAccessor.java:52)
	at net.javacrumbs.shedlock.support.StorageBasedLockProvider.doLock(StorageBasedLockProvider.java:70)
	at net.javacrumbs.shedlock.support.StorageBasedLockProvider.lock(StorageBasedLockProvider.java:54)
	at net.javacrumbs.shedlock.core.DefaultLockingTaskExecutor.executeWithLock(DefaultLockingTaskExecutor.java:38)
	at net.javacrumbs.shedlock.core.DefaultLockManager.executeWithLock(DefaultLockManager.java:51)
	at net.javacrumbs.shedlock.core.LockableRunnable.run(LockableRunnable.java:35)
	at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
	at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:745)
[2017-04-19 13:15:00,072 - DEBUG - net.javacrumbs.shedlock.core.DefaultLockingTaskExecutor] Locked scheduledAlertaConciliacoes.
[2017-04-19 13:15:00,078 - DEBUG - net.javacrumbs.shedlock.core.DefaultLockingTaskExecutor] Unlocked scheduledAlertaConciliacoes.

The shedlock-provider-jdbc tries to insert on table but PK_shedlock is PK.
I am using last version of shedock 0.9.0 both of them

Possible Transaction Issue

I haven't verified this, but have noticed when running two jobs at the same time on Postgres I'm getting the following error:
Caused by: org.postgresql.util.PSQLException: ERROR: out of shared memory Hint: You might need to increase max_locks_per_transaction.

The code is shown below

  @Scheduled(cron = "*/30 * * * * *") //every 30 secs
  @SchedulerLock(name = "update1", lockAtLeastFor = 15 * 1000)
  @Transactional(isolation = Isolation.READ_UNCOMMITTED)
  public void update1() {
    log.debug("Running update1: " + new Date());
    myDAO.update1();
    log.debug("Finished Running update1: " + new Date());
  }

  @Scheduled(cron = "*/30 * * * * *") //every 30 secs
  @SchedulerLock(name = "update2", lockAtLeastFor = 15 * 1000)
  @Transactional(isolation = Isolation.READ_UNCOMMITTED)
  public void update2() {

I'll try to confirm tomorrow, but thought I'd raise this in case you get a chance to test. We're using version 0.11.0.

Increase lock_until value when Spring scheduled task takes longer to finish

Is is possible to increase the lock_until value while the task is still running?

I could set the lockAtMostFor to some large value. This (mostly) ensures that the scheduled task is able to finish before the lock is released again. But I'm worried about what will happen if the node running this task is killed. Then other nodes in the cluster would have to wait until the large lockAtMostFor value times out.

Tomcat Issues

Is shedlock compatible with tomcat?
Iยดve been trying to run on tomcat but I got some errors below:

I used

<dependency>
    		<groupId>net.javacrumbs.shedlock</groupId>
    		<artifactId>shedlock-spring</artifactId>
    		<version>0.5.0</version>
		</dependency>
<dependency>
    		<groupId>net.javacrumbs.shedlock</groupId>
    		<artifactId>shedlock-provider-jdbc-template</artifactId>
    		<version>0.5.0</version>
		</dependency>
GRAVE: A child container failed during start
java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/BS-CONCILIA-PORTAL]]
	at java.util.concurrent.FutureTask.report(FutureTask.java:122)
	at java.util.concurrent.FutureTask.get(FutureTask.java:192)
	at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:915)
	at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:871)
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:147)
	at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1407)
	at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1397)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:745)
Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/BS-CONCILIA-PORTAL]]
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:153)
	... 6 more
Caused by: org.apache.catalina.LifecycleException: Failed to start component [org.apache.catalina.webresources.StandardRoot@7795bcd1]
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:153)
	at org.apache.catalina.core.StandardContext.resourcesStart(StandardContext.java:4929)
	at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5059)
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:147)
	... 6 more
Caused by: org.apache.catalina.LifecycleException: Failed to initialize component [org.apache.catalina.webresources.JarResourceSet@3eab89d9]
	at org.apache.catalina.util.LifecycleBase.init(LifecycleBase.java:106)
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:136)
	at org.apache.catalina.webresources.StandardRoot.startInternal(StandardRoot.java:699)
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:147)
	... 9 more
Caused by: java.lang.IllegalArgumentException: java.util.zip.ZipException: invalid LOC header (bad signature)
	at org.apache.catalina.webresources.JarResourceSet.initInternal(JarResourceSet.java:139)
	at org.apache.catalina.util.LifecycleBase.init(LifecycleBase.java:102)
	... 12 more
Caused by: java.util.zip.ZipException: invalid LOC header (bad signature)
	at java.util.zip.ZipFile.read(Native Method)
	at java.util.zip.ZipFile.access$1400(ZipFile.java:60)
	at java.util.zip.ZipFile$ZipFileInputStream.read(ZipFile.java:717)
	at java.util.zip.ZipFile$ZipFileInflaterInputStream.fill(ZipFile.java:419)
	at java.util.zip.InflaterInputStream.read(InflaterInputStream.java:158)
	at sun.misc.IOUtils.readFully(IOUtils.java:65)
	at java.util.jar.JarFile.getBytes(JarFile.java:425)
	at java.util.jar.JarFile.getManifestFromReference(JarFile.java:193)
	at java.util.jar.JarFile.getManifest(JarFile.java:180)
	at org.apache.catalina.webresources.JarResourceSet.initInternal(JarResourceSet.java:137)
	... 13 more

Oracle support

Have tested with Oracle, here is Oracle SQL script in case someone needs it:

CREATE TABLE SHEDLOCK (
  name VARCHAR2(64 CHAR),
  lock_until TIMESTAMP,
  locked_at TIMESTAMP,
  locked_by  VARCHAR2(255 CHAR),
  PRIMARY KEY (name)
);

Thanks for you work!

RedisConnectionFactory support

Hello there,

People who use SpringBoot and Redis have some problem with your Redis-provider. SpringBoot gives us some nice and easy configuration with RedisConnectionFactory. This CF uses the same RedisPool inside the implementation but that is private so no way to get it. Instead it gives us RedisConnection.

I created some nice implementation for your LockProvider interface. I can give it back to the community but I need some information about proper contributions here for that.

Thanks in advance.

Using LockProvider without Spring

Hi @lukas-krecan, thanks for the great library. I am looking for the correct implementation of a simple scheduler without Spring by relying only on ShedLock's LockProvider. To execute the Runnable at a scheduled rate I am using a java.util.concurrent Executor with fixed rate. I am not sure whether creating a new LockConfiguration at each execution will negatively impact, i.e., will another instance of the application running the same executor will be able to create a new lock since the LockConfiguration is created within the Runnable or not?

Following is my approach:

...
public void run() {
    executorService = Executors.newScheduledThreadPool(1);
    Runnable command = () -> {
            Instant lockAtMostUntil = Instant.now().plusSeconds(14);
            LockConfiguration lockConfiguration = new LockConfiguration("my_lock", lockAtMostUntil);
            Optional<SimpleLock> lock = lockProvider.lock(lockConfiguration);
            if (lock.isPresent()) {
                doUpdate();
                lock.get().unlock();
            }
    };
    executorService.scheduleAtFixedRate(command, 15_000, 15_000, TimeUnit.MILLISECONDS);
}

Ps.: Although that is not an issue, I had not found a user list to post such question.

Spring Integration InboundChannelAdapter

Is there a way of configuring ShedLock with Spring integration?

@Bean
@SchedulerLock( name = "fileProcessor")
@InboundChannelAdapter( channel = "fileInputChannel", poller = @Poller( fixedDelay = "${processor.poller.fixed-delay:5000}" ) )
public MessageSource<File> fileReadingMessageSource( ProcessorProperties processorProperties ) {

	...

}

Config documentation issue

I tried to configured my Spring env, as documented in the "Configure the task scheduler" paragraph (the ScheduledLockConfiguration bean provider), but this way, I noticed my Tomcat was unable to stop gracefully, due to the error
"The web application [xyz] appears to have started a thread named [ThreadPoolTaskScheduler-X] but has failed to stop it. This is very likely to create a memory leak."

I worked around it by using a different configuration:

@Bean
public TaskScheduler taskScheduler(LockProvider lockProvider) throws Exception {
    ScheduledLockConfiguration schedulerConfig = (ScheduledLockConfigurationBuilder
                    .withLockProvider(lockProvider)
                    .withPoolSize(10)
                    .withDefaultLockAtMostFor(Duration.ofMinutes(10))
                    .build());
    SpringLockableTaskSchedulerFactoryBean configBean = (SpringLockableTaskSchedulerFactoryBean) schedulerConfig;
    return configBean.getObject();
}

This way, it all looks working as expected.

Shedlock jdbc-template provider wont update existing locks created by other hosts

It fails with an exception when inserting lock due to existing entry by other host (which is not reflected in its in-memory set of locks it knows about), then fails to retry with update.

This prevents acquiring locks where another host left a stale lock entry - even if expired the other host wont be able to acquire the lock.

Possible Trigger Issues - Lock until time and manually deleting DB lock row

I might have found an issue through some slightly non-standard use. I'll try to confirm, but knowing the code, you might know if this is possible.

Firstly, I noticed a lock_until set for much longer (about an hour) than expected - it should only have locked for 15 seconds. I deleted that lock (via SQL delete) and then noticed the job stopped triggering - it kept saying it's already locked. It remained in that state for about 24 hours when I noticed it started up again (not sure if it was deleted while the job was running).

Apologies for the vagueness. I'll try to reproduce and debug the code to be more specific.

NoSuchMethodError on RedisLockProvider

After some tests scaling my application I start receiving this error

2018-02-15T11:15:00.00-0200 [APP/PROC/WEB/0] OUT java.lang.NoSuchMethodError: org.springframework.data.redis.core.types.Expiration.from(Ljava/time/Duration;)Lorg/springframework/data/redis/core/types/Expiration; 2018-02-15T11:15:00.00-0200 [APP/PROC/WEB/0] OUT at net.javacrumbs.shedlock.provider.redis.spring.RedisLockProvider.getExpiration(RedisLockProvider.java:72) ~[shedlock-provider-redis-spring-0.18.0.jar!/:na] 2018-02-15T11:15:00.00-0200 [APP/PROC/WEB/0] OUT at net.javacrumbs.shedlock.provider.redis.spring.RedisLockProvider.lock(RedisLockProvider.java:60) ~[shedlock-provider-redis-spring-0.18.0.jar!/:na] 2018-02-15T11:15:00.00-0200 [APP/PROC/WEB/0] OUT at net.javacrumbs.shedlock.core.DefaultLockManager.executeWithLock(DefaultLockManager.java:51) ~[shedlock-core-0.18.0.jar!/:na] 2018-02-15T11:15:00.00-0200 [APP/PROC/WEB/0] OUT at net.javacrumbs.shedlock.core.DefaultLockingTaskExecutor.executeWithLock(DefaultLockingTaskExecutor.java:38) ~[shedlock-core-0.18.0.jar!/:na] 2018-02-15T11:15:00.00-0200 [APP/PROC/WEB/0] OUT at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) ~[spring-context-4.3.9.RELEASE.jar!/:4.3.9.RELEASE]

Error creating bean shedlockConfiguration when used with Eureka and Spring Boot 2.x

Hey, I am having a lot of warnings in my logs referencing an error creating bean with name shedlockConfiguration. This only started happening when I migrated my applications to Spring Boot 2.x from 1.5. Any ideas?

17:10:28,752 WARN [com.netflix.discovery.InstanceInfoReplicator] (DiscoveryClient-InstanceInfoReplicator-0) There was a problem with the instance info replicator: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration$RefreshableEurekaClientConfiguration': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'shedlockConfiguration': Injection of autowired dependencies failed; nested exception is java.lang.NullPointerException
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:589)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:503)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:372)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1256)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1105)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:503)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$1(AbstractBeanFactory.java:353)
at org.springframework.cloud.context.scope.GenericScope$BeanLifecycleWrapper.getBean(GenericScope.java:390)
at org.springframework.cloud.context.scope.GenericScope.get(GenericScope.java:184)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:350)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:35)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:672)
at com.netflix.appinfo.ApplicationInfoManager$$EnhancerBySpringCGLIB$$a611a52b.refreshDataCenterInfoIfRequired()
at com.netflix.discovery.DiscoveryClient.refreshInstanceInfo(DiscoveryClient.java:1377)
at com.netflix.discovery.InstanceInfoReplicator.run(InstanceInfoReplicator.java:117)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'shedlockConfiguration': Injection of autowired dependencies failed; nested exception is java.lang.NullPointerException
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:379)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1350)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:580)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:503)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:372)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1256)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1105)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getSingletonFactoryBeanForTypeCheck(AbstractAutowireCapableBeanFactory.java:952)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryBean(AbstractAutowireCapableBeanFactory.java:835)
at org.springframework.beans.factory.support.AbstractBeanFactory.isTypeMatch(AbstractBeanFactory.java:562)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:427)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:398)
at org.springframework.beans.factory.BeanFactoryUtils.beanNamesForTypeIncludingAncestors(BeanFactoryUtils.java:208)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1276)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1101)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1065)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.resolvedCachedArgument(AutowiredAnnotationBeanPostProcessor.java:545)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.access$000(AutowiredAnnotationBeanPostProcessor.java:119)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:575)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:91)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:373)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1350)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:580)
... 27 more
Caused by: java.lang.NullPointerException

Possible to specify schema?

Heyo. I'm looking at ShedLock for locking tasks in a Spring environment. We use a setup where each tenant has their own schema and it's managed through Spring. We use SQL Server 2017 as the backing database.

Digging through ShedLock I wasn't able to find anything that would let me specify what schema to use. It would be great if we could set the schemaName in the @scheduled annotation, or at the very least specify it in the LockProvider.

Seems like I could customly implement StorageAccessor for the second use case of specifying a schema, but it doesn't seem like the first use-case is supported.

Can you advise me, what the best approach would be?

Spring4 support

Hi, I got an exception as follows:
java.lang.NoSuchMethodError:org.springframework.core.annotation.AnnotatedElementUtils.getMergedAnnotation

Spring version: 4.1.6.RELEASE.
ShedLock version: 1.0.0, 1.1.0(in fact all version after 0.11.0 will throw this exception cause spring 4.1.6 really no such method)
LockProvider: jdbc / jedis / zookeeper

Could anyone resolve this?

Thanks.

Row is not cleared during app restart or app goes down.

Hi Lukas, thanks for implementing this lock! I just tried using it(version:0.18.2) in my spring boot app with MS SQL server DB. I want to understand how the lock should behave in the event if the app is redeployed or server node goes down. In such cases, row in the table still exists though the task was already complete. When the app comes back up, it tries to INSERT row one more time and it fails till deleting the row manually.

What is the expected behavior? Have I missed any thing in the implementation?

@SchedulerLock(name = "someUniqueTaskName", lockAtMostFor = 60000)

Allow @SchedulerLock annotation on implementation

Nice tool. I've just been setting it up and noticed my @SchedulerLock wasn't taking effect until I moved it to the interface, but my @Scheduled annotation is on the implementation method. I can probably live with this, but it does make the code less readable. I'm not sure how Spring manages this, but perhaps this tool could do the same?

The relevant code is in net.javacrumbs.shedlock.spring.SpringLockConfigurationExtractor.java:

SchedulerLock annotation = AnnotationUtils.findAnnotation(method, SchedulerLock.class);

Configuration interface incorrect when setting withDefaultLockAtLeastFor

When I create a ScheduledLockConfiguration and try to set withDefaultLockAtLeastFor, I cannot build the configuration anymore without casting. The following example does not compile:

    @Bean
    public ScheduledLockConfiguration taskScheduler(LockProvider lockProvider) {
        return ScheduledLockConfigurationBuilder
                .withLockProvider(lockProvider)
                .withPoolSize(10)
                .withDefaultLockAtMostFor(Duration.ofMinutes(15))
                .withDefaultLockAtLeastFor(Duration.ofSeconds(5))
                .build();
    }

The workaround is casting the ConfigurationBuilder after calling withDefaultLockAtLeastFor:

    @Bean
    public ScheduledLockConfiguration taskScheduler(LockProvider lockProvider) {
        ScheduledLockConfigurationBuilder.ConfiguredScheduledLockConfigurationBuilder builder = 
                (ScheduledLockConfigurationBuilder.ConfiguredScheduledLockConfigurationBuilder) ScheduledLockConfigurationBuilder
                .withLockProvider(lockProvider)
                .withPoolSize(10)
                .withDefaultLockAtMostFor(Duration.ofMinutes(15))
                .withDefaultLockAtLeastFor(Duration.ofSeconds(5));
        return builder.build();
    }

In my opinion, the subinterfaces should be removed from the ScheduledLockConfigurationBuilder interface.

AWS DynamoDB

Hi,

Do you have any plan to support for DynamoDB?

Shutdown Hooks for Tomcat to prevent memory leaks

When using this library with Tomcat, upon shutdown I receive the following Warning:

WARNING [localhost-startStop-4] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [xyz] appears to have started a thread named [ThreadPoolTaskScheduler-7] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
sun.misc.Unsafe.park(Native Method)
java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1088)
java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1067)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
java.lang.Thread.run(Thread.java:745)

I believe this is related to the following stack overflow post: http://stackoverflow.com/questions/6603051/how-can-i-shutdown-spring-task-executor-scheduler-pools-before-all-other-beans-i

You are instantiating a new ThreadPoolTaskScheduler which has a shutdown() method:

ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();

Unfortunately the class you return only implements the TaskScheduler Interface which lacks the shutdown() method. Would it be possible to have this class extend ThreadPoolTaskScheduler instead of implementing TaskScheduler? That way we can get a hook to an autowired bean of this type and can call shutdown on this object as demonstrated in the stack overflow posting.

Regards,
grep-ir

SchedulerLock.name should support placeholders in form "${property.name}"

Currently, all the parameters of @SchedulerLock annotation support placeholders, with exception of name property. Name specified in annotation is not evaluated.
I would like to specify it like this:

@SchedulerLock(name = "${lock.name}")

so that it would be evaluated to a value defined in properties/yml file.

Cassandra support

Hi,
we need support for cassandra.
How expensive it is?
Can we implement it self?
Thanks

Support for composed annotations

It would be cool for the @SchedulerLock-annotation to be allowed for other annotations to make custom composed annotations possible:
https://sdqali.in/blog/2015/12/06/implementing-custom-annotations-for-spring-mvc/
The @Scheduled-annotation already allows this: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/scheduling/annotation/Scheduled.html

I would like to do something like this:

@Target(METHOD)
@Retention(RUNTIME)
@Documented
@Scheduled
@SchedulerLock
public @interface ScheduledLocked {
  @AliasFor(annotation = Scheduled.class, attribute = "cron")
  String cron() default "";

  @AliasFor(annotation = SchedulerLock.class, attribute = "lockAtMostFor")
  long lockAtMostFor() default 20L;

  @AliasFor(annotation = SchedulerLock.class, attribute = "name")
  String name() default "";
}

// usage:

@ScheduledLocked(cron="* * * * * *", name="batchrun")
public void runBatch() {
  // ...
}

Support for external config extractor

Hi

i would like to use the library programatically (not with annotations) but still leverage spring scheduler.
i could not find this in the examples (except for using it without spring)
so i looked at the code and could not find how to do it, specially giving a custom config extractor.
any tip would help
Shlomi

All jobs are executed initially on different nodes.

I've an application with two instances deployed having two scheduled tasks (with no delay) and I'm using shedlock-provider-mongo, when I start the applications the tasks are executed by both instances initially but it works fine after first execution, is this expected behavior? Or do i need to put more configuration to block tasks to be executed by both instances when the application start.

Duplicate key value violates unique constraint "shedlock_pkey"

Hello,

when application starts we have following error on PostgreSQL logs:

ERROR:  duplicate key value violates unique constraint "shedlock_pkey"
DETAIL:  Key (name)=(SomeScheduler) already exists.
STATEMENT:  INSERT INTO shedlock(name, lock_until, locked_at, locked_by) VALUES($1, $2, $3, $4)

Is there a possibility to avoid or fix above error?

Running in Docker/K8

Will this work in a container? If running in a clustered environment where multiple nodes have the same hostname, will this produce an issue?

DuplicateKeyException check in JdbcTemplateStorageAccessor is Insufficient

The insertRecord method in JdbcTemplateStorageAccessor catches only the DuplicateKeyException to ascertain whether the lock already exists. With Vertica DB this poses a problem as it throws a org.springframework.dao.DataIntegrityViolationException instead.
Note that this is the parent class of DuplicateKeyException. As such the catch block is skipped entirely and the exception is propagated to spring framework rendering ShedLock unusable.
I would like to propose that the DataIntegrityViolationException be caught instead so that this can work with Vertica.
IMHO maybe the base Exception class can be caught? Not sure if you have any other constraints around this so would leave that to you.
If you like, I can submit a pull request for this as well.

Exception encountered as below:
org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [INSERT INTO SHEDLOCK(name, lock_until, locked_at, locked_by) VALUES(?, ?, ?, ?)]; [Vertica]VJDBC ERROR: Duplicate key values: 'name=batchStatusEmailTask' -- violates constraint 'SHEDLOCK.C_PRIMARY'; nested exception is java.sql.SQLIntegrityConstraintViolationException: [Vertica]VJDBC ERROR: Duplicate key values: 'name=batchStatusEmailTask' -- violates constraint 'SHEDLOCK.C_PRIMARY'

Wrong @Bean/type in the README file

Hello, @lukas-krecan I'm trying to set up this library to use on a side project but the documentation in the README file doesn't seem to be correct.

It says:

if you already have an instance of ScheduledExecutorService

@Bean
public TaskScheduler taskScheduler(ScheduledExecutorService executorService, LockProvider lockProvider) {
    return ScheduledLockConfigurationBuilder
        .withLockProvider(lockProvider)
        .withExecutorService(executorService)
        .withDefaultLockAtMostFor(Duration.ofMinutes(10))
        .build();
}

However, the code above returns incompatible types.

not able to use ShedLock with spring's TaskScheduler

Hello,
this is my sample application where I'm trying to configure ShedLock to use Spring's TaskScheduler. But unfortunately with this setup I can't get it to work. Please can you suggest another configuration or tell me what am I doing wrong?

Thanks

changing column names on shedlock table

I am able to change the schema name and table name by specifying in the JdbcTemplateLockProvider
constructor.
Is there a way to rename the columns too?

thanks.

node1 unlock may overwrite node2's lock's info in MongoLockProvider?

In mongoProvider, if node1 running a task gets a lock at 9:00, lockuntil's value is 9:30, but the task is blocked until 9:40.
At 9:30, another node2 running the shcedule task can get the lock by the following code and set lockuntil is 10:00:

@Override public boolean updateRecord(LockConfiguration lockConfiguration) { Date now = now(); Bson update = combine( set(LOCK_UNTIL, Date.from(lockConfiguration.getLockAtMostUntil())), set(LOCKED_AT, now), set(LOCKED_BY, hostname) ); Document result = getCollection().findOneAndUpdate( and(eq(ID, lockConfiguration.getName()), lte(LOCK_UNTIL, now)), update ); return result != null; }
When the times comes to 9:40, node1 unlocks lock and update LOCK_UNTIL to now(9:40)
@Override public void unlock(LockConfiguration lockConfiguration) { // Set lockUtil to now or lockAtLeastUntil whichever is later getCollection().findOneAndUpdate( eq(ID, lockConfiguration.getName()), combine(set(LOCK_UNTIL, Date.from(lockConfiguration.getUnlockTime()))) ); }
public Instant getUnlockTime() { Instant now = Instant.now(); return lockAtLeastUntil.isAfter(now) ? lockAtLeastUntil : now; }
the node1 changes node2's lock info through unlock ? should add "lockedBy" check in unlock operation?

Support for async tasks

Hi it would be nice to implement also LockingTaskExecutor for async tasks since default one DefaultLockingTaskExecutor do not work in correct way.

Can't seem to fully clear the cache of locks when testing.

We are trying to use ShedLock in some integration tests. These tests need the locks cleared after each run, however when I use @DirtiesContext and set it to clear after tests, locks seem to be sticking around.

I need a way to manually clear all locks after each context.

Error using hazelcast lock provider

Hi!

We are trying to use hazelcast provider connecting to a remote cluster (not an embedded hazelcast instance), but we have an error:

2017-09-06 07:22:12.705 ERROR 8 --- [WATCHDOG_THREAD] o.s.s.s.TaskUtils$LoggingErrorHandler    : Unexpected error occurred in scheduled task.

java.lang.UnsupportedOperationException: Client has no local member!
        at com.hazelcast.client.proxy.ClientClusterProxy.getLocalMember(ClientClusterProxy.java:61)
        at net.javacrumbs.shedlock.provider.hazelcast.HazelcastLockProvider.getLocalMemberUuid(HazelcastLockProvider.java:144)
        at net.javacrumbs.shedlock.provider.hazelcast.HazelcastLockProvider.addNewLock(HazelcastLockProvider.java:127)
        at net.javacrumbs.shedlock.provider.hazelcast.HazelcastLockProvider.tryLock(HazelcastLockProvider.java:94)
        at net.javacrumbs.shedlock.provider.hazelcast.HazelcastLockProvider.lock(HazelcastLockProvider.java:79)
        at net.javacrumbs.shedlock.core.DefaultLockingTaskExecutor.executeWithLock(DefaultLockingTaskExecutor.java:38)
        at net.javacrumbs.shedlock.core.DefaultLockManager.executeWithLock(DefaultLockManager.java:51)
        at net.javacrumbs.shedlock.core.LockableRunnable.run(LockableRunnable.java:35)
        at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
        at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)

When we try against an embedded hazelcast, it works like a charm.

Thanks!

Disabling Scheduled Jobs

Thanks for the ShedLock feature, its really great. I would like to see if we can have a job disabling feature where in the next run can be disabled for a particular job. These help during application maintenance.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.