ShedLock
Distributed lock for your scheduled tasks
Install / Use
/learn @lukas-krecan/ShedLockREADME
ShedLock
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.
- Components
- Usage
- Lock Providers
- JdbcTemplate
- R2DBC
- jOOQ
- Micronaut Data Jdbc
- Mongo
- DynamoDB 2
- ZooKeeper (using Curator)
- Redis (using Spring RedisConnectionFactory)
- Redis (using Spring ReactiveRedisConnectionFactory)
- Redis (using Jedis)
- Hazelcast
- Couchbase
- ElasticSearch
- OpenSearch
- CosmosDB
- Cassandra
- ArangoDB
- Neo4j
- Etcd
- Apache Ignite
- In-Memory
- Memcached
- Datastore
- Firestore
- GCS
- S3
- NATS Jetstream
- Multi-tenancy
- Customization
- Duration specification
- Extending the lock
- Micronaut integration
- CDI integration
- Locking without a framework
- Troubleshooting
- Micrometer integration (Spring)
- Modes of Spring integration
- Compatibility matrix
- Release notes
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
- Enable and configure Scheduled locking
- Annotate your scheduled tasks
- 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>7.7.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
}
Behavior
- Only methods annotated by
@SchedulerLockare locked, the library ignores all other scheduled tasks. By default, the lock will be applied even if the method is called directly, not only thorough the scheduler. - Only one task with the same name can be executed at the same time.
- If the lock is being held by a task, other tasks protected by the same lock are not blocked, but are simply skipped.
- The lock is released as soon as the task is finished (unless
lockAtLeastForis specified, see below) - If the JVM crashes before the task finishes,
lockAtMostForattribute comes to play. The lock is always released afterlockAtMostFor. You have to setlockAtMostForto a value which is much longer than normal execution time. If the task takes longer thanlockAtMostForthe resulting behavior may be unpredictable (more than one process will effectively hold the lock). - If you don't specify
lockAtMostForin@SchedulerLock, the default value from@EnableSchedulerLockwill be used. - You can set
lockAtLeastForattribute 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.spring.annotation.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>7.7.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, risk
Related Skills
node-connect
339.3kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
83.9kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
339.3kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
83.9kCommit, push, and open a PR
