Skip to content

Repository files navigation

micronaut-distributed-lock

A Micronaut library to lock method executions in a distributed environment

Usage

Prerequisites

Add our Github Packages repository to your pom.xml or build.gradle

build.gradle

repositories {
maven {
url "https://maven.pkg.github.com/uberall/micronaut-distributed-lock" }
}

Installation

Add a dependency to micronaut-distributed-lock-core

implementation "com.uberall:micronaut-distributed-lock-core:3.5.0"

and one runtime implementation to your pom.xml/build.gradle

runtime "com.uberall:micronaut-distributed-lock-data-jdbc:3.5.0"

now you can annotate e.g. your @Scheduled methods with @com.uberall.annotations.DistributedLock

importcom.uberall.annotations.DistributedLockimportio.micronaut.scheduling.annotation.Scheduledimportjavax.inject.Injectimportjavax.inject.Singleton@SingletonclassFooJob {
@InjectFooService fooService
@DistributedLock(ttl="1m")
@Scheduled(fixedRate='5m')
voidperform() {
// some long running asynchornous task
}
}

The result is that no matter how often you call this method, as long as the first invocation is still running no further invocations will be executed, no matter who is trying to execute.

Annotation Parameters

namedefaultdescription
ttl"1m"The lock duration. If cleanup is set to false this prohibits any further execution for the given duration. Otherwise this is a mere "hint" when to allow execution again because e.g. the first execution was interrupted and the lock has never been cleaned up
name{methodName}The name of the lock, this is used to identify the lock in the repository and should be unique across you codebase. If not set the method name is used
appendParametersfalseWhether or not to append parameters to the lock name. If set to true a key/value map of the parameters will be appeneded to the lock name. This is helpful in environments where you only want to prohibit e.g. processing of the same database entitiy on 2 servers
cleanuptrueWhether or not to cleanup the lock after method execution

TTL and Cleanup

There can be some confusion with TTL and Cleanup so here is use cases of when to set cleanup and what to set ttl to in other cases.

Cleanup = true (default)

The Interceptor will always clean up the lock, no matter whether the method execution was successful or not, after method execution. You could see the TTL as a "hint" on execution time. If the lock is not cleaned up (e.g. server is shutdown during method execution) the next round is still running.

The typical use case is a distributed application where you want a scheduled job to only run once every X minutes even if you are running your micronaut application on 20 servers. TTL should be set to not block the next scheduled execution. E.g.:

importcom.uberall.annotations.DistributedLockimportio.micronaut.scheduling.annotation.Scheduledimportjavax.inject.Injectimportjavax.inject.Singleton@SingletonclassFooJob {
@InjectFooService fooService
@DistributedLock(ttl="4m50s")
@Scheduled(fixedRate='5m')
voidperform() {
fooService.runTheThingThatIsSupposedToHappenEveryFiveMinutes()
}
}

Cleanup = false

In this scenario the lock will not be released after method execution and no more invocations of the method are actually executed for the duration of the lock lifetime.

A typical example is a "rate limited" method e.g. You are doing some heavy analytics every 10 minutes and if the analytics haven't resulted in something unusual, you only want the result to be sent out once a day.

importcom.uberall.annotations.DistributedLockimportio.micronaut.scheduling.annotation.Scheduledimportjavax.inject.Injectimportjavax.inject.Singleton@SingletonclassFooJob {
@InjectFooService fooService
@Scheduled(fixedRate='10m')
voidperform() {
def result = fooService.getResult()
if (result.bad) {
fooService.sendResult(result) // will always happen
} else {
sendResult(result) // is only executed once a day
}
}
@DistributedLock(ttl="1d", cleanup=false)
voidsendResult(defresult) {
fooService.sendResult(result)
}
}

Configuration

To disable the library completely (recommended in tests) you can set micronaut.distributed-lock-enabled to false in application.groovy

micronaut:
distributed-lock:
enabled: false

Implementations

Redis

A simple implementation using micronaut-redis

Usage

runtime "com.uberall:micronaut-distributed-lock-data-redis:3.5.0"

Configuration

Check the documentation how to make sure lettuce has a redis server connection.

Micronaut Data JDBC

An implementation using micronaut-data-jdbc.

Usage

runtime "com.uberall:micronaut-distributed-lock-data-jdbc:3.5.0"

After adding the runtime dependency into build.gradle you'll have to tell the library which sql dialect to use

micronaut:
distributed-lock:
dialect: mysql # OR postgres

Additionally, You will need to add a table to your datasource that has 3 columns:

columntypedescription
idBIGINTThe unique id for each lock
nameVARCHARThe name of the lock, length depends on your usage, 255 should be safe
untilDATETIMEThe datetime until this lock is valid

For MySQL or MariaDB a compatible create statement would be:

CREATETABLEIF NOT EXISTS `distributed_lock`
(
`id`bigint(20) NOT NULL AUTO_INCREMENT,
`name`varchar(255) UNIQUE NOT NULL,
`until` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE = InnoDB
DEFAULT CHARSET = latin1;

Configuration

If you want to use a non-default datasource you can set the lock database name by setting micronaut.distributed.lock.datasource-name and defining a database with that name E.g.:

micronaut:
distributed-lock:
datasource-name: lockdialect: mysqldatasources:
default:
url: jdbc:mysql://production-server/all-the-important-datadriverClassName: com.mysql.cj.jdbc.Driverusername: userpassword: passschema-generate: nonedialect: MYSQLpooled: truelock:
url: jdbc:mysql://lock-server/the-lock-schemadriverClassName: com.mysql.cj.jdbc.Driverusername: resupassword: ssapdialect: MYSQLpooled: true

Development

No special setup is needed to start development. Just hack ahead and create a MR.

Publishing

You'll need to be in the uberall organisation in Github to be able to publish the latest version. also GITHUB_USER and GITHUB_TOKEN environment variables need to be set.

Simply run ./gradlew :publish --no-daemon to upload all artifacts to Github.

About

A Micronaut library to lock method executions in a distributed environment

Resources

Stars

6 stars

Watchers

17 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
GitHub - uberall/micronaut-distributed-lock: A Micronaut library to lock method executions in a distributed environment · GitHub
Skip to content

Repository files navigation

micronaut-distributed-lock

A Micronaut library to lock method executions in a distributed environment

Usage

Prerequisites

Add our Github Packages repository to your pom.xml or build.gradle

build.gradle

repositories {
maven {
url "https://maven.pkg.github.com/uberall/micronaut-distributed-lock" }
}

Installation

Add a dependency to micronaut-distributed-lock-core

implementation "com.uberall:micronaut-distributed-lock-core:3.5.0"

and one runtime implementation to your pom.xml/build.gradle

runtime "com.uberall:micronaut-distributed-lock-data-jdbc:3.5.0"

now you can annotate e.g. your @Scheduled methods with @com.uberall.annotations.DistributedLock

importcom.uberall.annotations.DistributedLockimportio.micronaut.scheduling.annotation.Scheduledimportjavax.inject.Injectimportjavax.inject.Singleton@SingletonclassFooJob {
@InjectFooService fooService
@DistributedLock(ttl="1m")
@Scheduled(fixedRate='5m')
voidperform() {
// some long running asynchornous task
}
}

The result is that no matter how often you call this method, as long as the first invocation is still running no further invocations will be executed, no matter who is trying to execute.

Annotation Parameters

namedefaultdescription
ttl"1m"The lock duration. If cleanup is set to false this prohibits any further execution for the given duration. Otherwise this is a mere "hint" when to allow execution again because e.g. the first execution was interrupted and the lock has never been cleaned up
name{methodName}The name of the lock, this is used to identify the lock in the repository and should be unique across you codebase. If not set the method name is used
appendParametersfalseWhether or not to append parameters to the lock name. If set to true a key/value map of the parameters will be appeneded to the lock name. This is helpful in environments where you only want to prohibit e.g. processing of the same database entitiy on 2 servers
cleanuptrueWhether or not to cleanup the lock after method execution

TTL and Cleanup

There can be some confusion with TTL and Cleanup so here is use cases of when to set cleanup and what to set ttl to in other cases.

Cleanup = true (default)

The Interceptor will always clean up the lock, no matter whether the method execution was successful or not, after method execution. You could see the TTL as a "hint" on execution time. If the lock is not cleaned up (e.g. server is shutdown during method execution) the next round is still running.

The typical use case is a distributed application where you want a scheduled job to only run once every X minutes even if you are running your micronaut application on 20 servers. TTL should be set to not block the next scheduled execution. E.g.:

importcom.uberall.annotations.DistributedLockimportio.micronaut.scheduling.annotation.Scheduledimportjavax.inject.Injectimportjavax.inject.Singleton@SingletonclassFooJob {
@InjectFooService fooService
@DistributedLock(ttl="4m50s")
@Scheduled(fixedRate='5m')
voidperform() {
fooService.runTheThingThatIsSupposedToHappenEveryFiveMinutes()
}
}

Cleanup = false

In this scenario the lock will not be released after method execution and no more invocations of the method are actually executed for the duration of the lock lifetime.

A typical example is a "rate limited" method e.g. You are doing some heavy analytics every 10 minutes and if the analytics haven't resulted in something unusual, you only want the result to be sent out once a day.

importcom.uberall.annotations.DistributedLockimportio.micronaut.scheduling.annotation.Scheduledimportjavax.inject.Injectimportjavax.inject.Singleton@SingletonclassFooJob {
@InjectFooService fooService
@Scheduled(fixedRate='10m')
voidperform() {
def result = fooService.getResult()
if (result.bad) {
fooService.sendResult(result) // will always happen
} else {
sendResult(result) // is only executed once a day
}
}
@DistributedLock(ttl="1d", cleanup=false)
voidsendResult(defresult) {
fooService.sendResult(result)
}
}

Configuration

To disable the library completely (recommended in tests) you can set micronaut.distributed-lock-enabled to false in application.groovy

micronaut:
distributed-lock:
enabled: false

Implementations

Redis

A simple implementation using micronaut-redis

Usage

runtime "com.uberall:micronaut-distributed-lock-data-redis:3.5.0"

Configuration

Check the documentation how to make sure lettuce has a redis server connection.

Micronaut Data JDBC

An implementation using micronaut-data-jdbc.

Usage

runtime "com.uberall:micronaut-distributed-lock-data-jdbc:3.5.0"

After adding the runtime dependency into build.gradle you'll have to tell the library which sql dialect to use

micronaut:
distributed-lock:
dialect: mysql # OR postgres

Additionally, You will need to add a table to your datasource that has 3 columns:

columntypedescription
idBIGINTThe unique id for each lock
nameVARCHARThe name of the lock, length depends on your usage, 255 should be safe
untilDATETIMEThe datetime until this lock is valid

For MySQL or MariaDB a compatible create statement would be:

CREATETABLEIF NOT EXISTS `distributed_lock`
(
`id`bigint(20) NOT NULL AUTO_INCREMENT,
`name`varchar(255) UNIQUE NOT NULL,
`until` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE = InnoDB
DEFAULT CHARSET = latin1;

Configuration

If you want to use a non-default datasource you can set the lock database name by setting micronaut.distributed.lock.datasource-name and defining a database with that name E.g.:

micronaut:
distributed-lock:
datasource-name: lockdialect: mysqldatasources:
default:
url: jdbc:mysql://production-server/all-the-important-datadriverClassName: com.mysql.cj.jdbc.Driverusername: userpassword: passschema-generate: nonedialect: MYSQLpooled: truelock:
url: jdbc:mysql://lock-server/the-lock-schemadriverClassName: com.mysql.cj.jdbc.Driverusername: resupassword: ssapdialect: MYSQLpooled: true

Development

No special setup is needed to start development. Just hack ahead and create a MR.

Publishing

You'll need to be in the uberall organisation in Github to be able to publish the latest version. also GITHUB_USER and GITHUB_TOKEN environment variables need to be set.

Simply run ./gradlew :publish --no-daemon to upload all artifacts to Github.

About

A Micronaut library to lock method executions in a distributed environment

Resources

Stars

6 stars

Watchers

17 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - uberall/micronaut-distributed-lock: A Micronaut library to lock method executions in a distributed environment · GitHub
Skip to content

Repository files navigation

micronaut-distributed-lock

A Micronaut library to lock method executions in a distributed environment

Usage

Prerequisites

Add our Github Packages repository to your pom.xml or build.gradle

build.gradle

repositories {
maven {
url "https://maven.pkg.github.com/uberall/micronaut-distributed-lock" }
}

Installation

Add a dependency to micronaut-distributed-lock-core

implementation "com.uberall:micronaut-distributed-lock-core:3.5.0"

and one runtime implementation to your pom.xml/build.gradle

runtime "com.uberall:micronaut-distributed-lock-data-jdbc:3.5.0"

now you can annotate e.g. your @Scheduled methods with @com.uberall.annotations.DistributedLock

importcom.uberall.annotations.DistributedLockimportio.micronaut.scheduling.annotation.Scheduledimportjavax.inject.Injectimportjavax.inject.Singleton@SingletonclassFooJob {
@InjectFooService fooService
@DistributedLock(ttl="1m")
@Scheduled(fixedRate='5m')
voidperform() {
// some long running asynchornous task
}
}

The result is that no matter how often you call this method, as long as the first invocation is still running no further invocations will be executed, no matter who is trying to execute.

Annotation Parameters

namedefaultdescription
ttl"1m"The lock duration. If cleanup is set to false this prohibits any further execution for the given duration. Otherwise this is a mere "hint" when to allow execution again because e.g. the first execution was interrupted and the lock has never been cleaned up
name{methodName}The name of the lock, this is used to identify the lock in the repository and should be unique across you codebase. If not set the method name is used
appendParametersfalseWhether or not to append parameters to the lock name. If set to true a key/value map of the parameters will be appeneded to the lock name. This is helpful in environments where you only want to prohibit e.g. processing of the same database entitiy on 2 servers
cleanuptrueWhether or not to cleanup the lock after method execution

TTL and Cleanup

There can be some confusion with TTL and Cleanup so here is use cases of when to set cleanup and what to set ttl to in other cases.

Cleanup = true (default)

The Interceptor will always clean up the lock, no matter whether the method execution was successful or not, after method execution. You could see the TTL as a "hint" on execution time. If the lock is not cleaned up (e.g. server is shutdown during method execution) the next round is still running.

The typical use case is a distributed application where you want a scheduled job to only run once every X minutes even if you are running your micronaut application on 20 servers. TTL should be set to not block the next scheduled execution. E.g.:

importcom.uberall.annotations.DistributedLockimportio.micronaut.scheduling.annotation.Scheduledimportjavax.inject.Injectimportjavax.inject.Singleton@SingletonclassFooJob {
@InjectFooService fooService
@DistributedLock(ttl="4m50s")
@Scheduled(fixedRate='5m')
voidperform() {
fooService.runTheThingThatIsSupposedToHappenEveryFiveMinutes()
}
}

Cleanup = false

In this scenario the lock will not be released after method execution and no more invocations of the method are actually executed for the duration of the lock lifetime.

A typical example is a "rate limited" method e.g. You are doing some heavy analytics every 10 minutes and if the analytics haven't resulted in something unusual, you only want the result to be sent out once a day.

importcom.uberall.annotations.DistributedLockimportio.micronaut.scheduling.annotation.Scheduledimportjavax.inject.Injectimportjavax.inject.Singleton@SingletonclassFooJob {
@InjectFooService fooService
@Scheduled(fixedRate='10m')
voidperform() {
def result = fooService.getResult()
if (result.bad) {
fooService.sendResult(result) // will always happen
} else {
sendResult(result) // is only executed once a day
}
}
@DistributedLock(ttl="1d", cleanup=false)
voidsendResult(defresult) {
fooService.sendResult(result)
}
}

Configuration

To disable the library completely (recommended in tests) you can set micronaut.distributed-lock-enabled to false in application.groovy

micronaut:
distributed-lock:
enabled: false

Implementations

Redis

A simple implementation using micronaut-redis

Usage

runtime "com.uberall:micronaut-distributed-lock-data-redis:3.5.0"

Configuration

Check the documentation how to make sure lettuce has a redis server connection.

Micronaut Data JDBC

An implementation using micronaut-data-jdbc.

Usage

runtime "com.uberall:micronaut-distributed-lock-data-jdbc:3.5.0"

After adding the runtime dependency into build.gradle you'll have to tell the library which sql dialect to use

micronaut:
distributed-lock:
dialect: mysql # OR postgres

Additionally, You will need to add a table to your datasource that has 3 columns:

columntypedescription
idBIGINTThe unique id for each lock
nameVARCHARThe name of the lock, length depends on your usage, 255 should be safe
untilDATETIMEThe datetime until this lock is valid

For MySQL or MariaDB a compatible create statement would be:

CREATETABLEIF NOT EXISTS `distributed_lock`
(
`id`bigint(20) NOT NULL AUTO_INCREMENT,
`name`varchar(255) UNIQUE NOT NULL,
`until` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE = InnoDB
DEFAULT CHARSET = latin1;

Configuration

If you want to use a non-default datasource you can set the lock database name by setting micronaut.distributed.lock.datasource-name and defining a database with that name E.g.:

micronaut:
distributed-lock:
datasource-name: lockdialect: mysqldatasources:
default:
url: jdbc:mysql://production-server/all-the-important-datadriverClassName: com.mysql.cj.jdbc.Driverusername: userpassword: passschema-generate: nonedialect: MYSQLpooled: truelock:
url: jdbc:mysql://lock-server/the-lock-schemadriverClassName: com.mysql.cj.jdbc.Driverusername: resupassword: ssapdialect: MYSQLpooled: true

Development

No special setup is needed to start development. Just hack ahead and create a MR.

Publishing

You'll need to be in the uberall organisation in Github to be able to publish the latest version. also GITHUB_USER and GITHUB_TOKEN environment variables need to be set.

Simply run ./gradlew :publish --no-daemon to upload all artifacts to Github.

About

A Micronaut library to lock method executions in a distributed environment

Resources

Stars

6 stars

Watchers

17 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - uberall/micronaut-distributed-lock: A Micronaut library to lock method executions in a distributed environment · GitHub
Skip to content

Repository files navigation

micronaut-distributed-lock

A Micronaut library to lock method executions in a distributed environment

Usage

Prerequisites

Add our Github Packages repository to your pom.xml or build.gradle

build.gradle

repositories {
maven {
url "https://maven.pkg.github.com/uberall/micronaut-distributed-lock" }
}

Installation

Add a dependency to micronaut-distributed-lock-core

implementation "com.uberall:micronaut-distributed-lock-core:3.5.0"

and one runtime implementation to your pom.xml/build.gradle

runtime "com.uberall:micronaut-distributed-lock-data-jdbc:3.5.0"

now you can annotate e.g. your @Scheduled methods with @com.uberall.annotations.DistributedLock

importcom.uberall.annotations.DistributedLockimportio.micronaut.scheduling.annotation.Scheduledimportjavax.inject.Injectimportjavax.inject.Singleton@SingletonclassFooJob {
@InjectFooService fooService
@DistributedLock(ttl="1m")
@Scheduled(fixedRate='5m')
voidperform() {
// some long running asynchornous task
}
}

The result is that no matter how often you call this method, as long as the first invocation is still running no further invocations will be executed, no matter who is trying to execute.

Annotation Parameters

namedefaultdescription
ttl"1m"The lock duration. If cleanup is set to false this prohibits any further execution for the given duration. Otherwise this is a mere "hint" when to allow execution again because e.g. the first execution was interrupted and the lock has never been cleaned up
name{methodName}The name of the lock, this is used to identify the lock in the repository and should be unique across you codebase. If not set the method name is used
appendParametersfalseWhether or not to append parameters to the lock name. If set to true a key/value map of the parameters will be appeneded to the lock name. This is helpful in environments where you only want to prohibit e.g. processing of the same database entitiy on 2 servers
cleanuptrueWhether or not to cleanup the lock after method execution

TTL and Cleanup

There can be some confusion with TTL and Cleanup so here is use cases of when to set cleanup and what to set ttl to in other cases.

Cleanup = true (default)

The Interceptor will always clean up the lock, no matter whether the method execution was successful or not, after method execution. You could see the TTL as a "hint" on execution time. If the lock is not cleaned up (e.g. server is shutdown during method execution) the next round is still running.

The typical use case is a distributed application where you want a scheduled job to only run once every X minutes even if you are running your micronaut application on 20 servers. TTL should be set to not block the next scheduled execution. E.g.:

importcom.uberall.annotations.DistributedLockimportio.micronaut.scheduling.annotation.Scheduledimportjavax.inject.Injectimportjavax.inject.Singleton@SingletonclassFooJob {
@InjectFooService fooService
@DistributedLock(ttl="4m50s")
@Scheduled(fixedRate='5m')
voidperform() {
fooService.runTheThingThatIsSupposedToHappenEveryFiveMinutes()
}
}

Cleanup = false

In this scenario the lock will not be released after method execution and no more invocations of the method are actually executed for the duration of the lock lifetime.

A typical example is a "rate limited" method e.g. You are doing some heavy analytics every 10 minutes and if the analytics haven't resulted in something unusual, you only want the result to be sent out once a day.

importcom.uberall.annotations.DistributedLockimportio.micronaut.scheduling.annotation.Scheduledimportjavax.inject.Injectimportjavax.inject.Singleton@SingletonclassFooJob {
@InjectFooService fooService
@Scheduled(fixedRate='10m')
voidperform() {
def result = fooService.getResult()
if (result.bad) {
fooService.sendResult(result) // will always happen
} else {
sendResult(result) // is only executed once a day
}
}
@DistributedLock(ttl="1d", cleanup=false)
voidsendResult(defresult) {
fooService.sendResult(result)
}
}

Configuration

To disable the library completely (recommended in tests) you can set micronaut.distributed-lock-enabled to false in application.groovy

micronaut:
distributed-lock:
enabled: false

Implementations

Redis

A simple implementation using micronaut-redis

Usage

runtime "com.uberall:micronaut-distributed-lock-data-redis:3.5.0"

Configuration

Check the documentation how to make sure lettuce has a redis server connection.

Micronaut Data JDBC

An implementation using micronaut-data-jdbc.

Usage

runtime "com.uberall:micronaut-distributed-lock-data-jdbc:3.5.0"

After adding the runtime dependency into build.gradle you'll have to tell the library which sql dialect to use

micronaut:
distributed-lock:
dialect: mysql # OR postgres

Additionally, You will need to add a table to your datasource that has 3 columns:

columntypedescription
idBIGINTThe unique id for each lock
nameVARCHARThe name of the lock, length depends on your usage, 255 should be safe
untilDATETIMEThe datetime until this lock is valid

For MySQL or MariaDB a compatible create statement would be:

CREATETABLEIF NOT EXISTS `distributed_lock`
(
`id`bigint(20) NOT NULL AUTO_INCREMENT,
`name`varchar(255) UNIQUE NOT NULL,
`until` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE = InnoDB
DEFAULT CHARSET = latin1;

Configuration

If you want to use a non-default datasource you can set the lock database name by setting micronaut.distributed.lock.datasource-name and defining a database with that name E.g.:

micronaut:
distributed-lock:
datasource-name: lockdialect: mysqldatasources:
default:
url: jdbc:mysql://production-server/all-the-important-datadriverClassName: com.mysql.cj.jdbc.Driverusername: userpassword: passschema-generate: nonedialect: MYSQLpooled: truelock:
url: jdbc:mysql://lock-server/the-lock-schemadriverClassName: com.mysql.cj.jdbc.Driverusername: resupassword: ssapdialect: MYSQLpooled: true

Development

No special setup is needed to start development. Just hack ahead and create a MR.

Publishing

You'll need to be in the uberall organisation in Github to be able to publish the latest version. also GITHUB_USER and GITHUB_TOKEN environment variables need to be set.

Simply run ./gradlew :publish --no-daemon to upload all artifacts to Github.

About

A Micronaut library to lock method executions in a distributed environment

Resources

Stars

6 stars

Watchers

17 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' GitHub - uberall/micronaut-distributed-lock: A Micronaut library to lock method executions in a distributed environment · GitHub
Skip to content

Repository files navigation

micronaut-distributed-lock

A Micronaut library to lock method executions in a distributed environment

Usage

Prerequisites

Add our Github Packages repository to your pom.xml or build.gradle

build.gradle

repositories {
maven {
url "https://maven.pkg.github.com/uberall/micronaut-distributed-lock" }
}

Installation

Add a dependency to micronaut-distributed-lock-core

implementation "com.uberall:micronaut-distributed-lock-core:3.5.0"

and one runtime implementation to your pom.xml/build.gradle

runtime "com.uberall:micronaut-distributed-lock-data-jdbc:3.5.0"

now you can annotate e.g. your @Scheduled methods with @com.uberall.annotations.DistributedLock

importcom.uberall.annotations.DistributedLockimportio.micronaut.scheduling.annotation.Scheduledimportjavax.inject.Injectimportjavax.inject.Singleton@SingletonclassFooJob {
@InjectFooService fooService
@DistributedLock(ttl="1m")
@Scheduled(fixedRate='5m')
voidperform() {
// some long running asynchornous task
}
}

The result is that no matter how often you call this method, as long as the first invocation is still running no further invocations will be executed, no matter who is trying to execute.

Annotation Parameters

namedefaultdescription
ttl"1m"The lock duration. If cleanup is set to false this prohibits any further execution for the given duration. Otherwise this is a mere "hint" when to allow execution again because e.g. the first execution was interrupted and the lock has never been cleaned up
name{methodName}The name of the lock, this is used to identify the lock in the repository and should be unique across you codebase. If not set the method name is used
appendParametersfalseWhether or not to append parameters to the lock name. If set to true a key/value map of the parameters will be appeneded to the lock name. This is helpful in environments where you only want to prohibit e.g. processing of the same database entitiy on 2 servers
cleanuptrueWhether or not to cleanup the lock after method execution

TTL and Cleanup

There can be some confusion with TTL and Cleanup so here is use cases of when to set cleanup and what to set ttl to in other cases.

Cleanup = true (default)

The Interceptor will always clean up the lock, no matter whether the method execution was successful or not, after method execution. You could see the TTL as a "hint" on execution time. If the lock is not cleaned up (e.g. server is shutdown during method execution) the next round is still running.

The typical use case is a distributed application where you want a scheduled job to only run once every X minutes even if you are running your micronaut application on 20 servers. TTL should be set to not block the next scheduled execution. E.g.:

importcom.uberall.annotations.DistributedLockimportio.micronaut.scheduling.annotation.Scheduledimportjavax.inject.Injectimportjavax.inject.Singleton@SingletonclassFooJob {
@InjectFooService fooService
@DistributedLock(ttl="4m50s")
@Scheduled(fixedRate='5m')
voidperform() {
fooService.runTheThingThatIsSupposedToHappenEveryFiveMinutes()
}
}

Cleanup = false

In this scenario the lock will not be released after method execution and no more invocations of the method are actually executed for the duration of the lock lifetime.

A typical example is a "rate limited" method e.g. You are doing some heavy analytics every 10 minutes and if the analytics haven't resulted in something unusual, you only want the result to be sent out once a day.

importcom.uberall.annotations.DistributedLockimportio.micronaut.scheduling.annotation.Scheduledimportjavax.inject.Injectimportjavax.inject.Singleton@SingletonclassFooJob {
@InjectFooService fooService
@Scheduled(fixedRate='10m')
voidperform() {
def result = fooService.getResult()
if (result.bad) {
fooService.sendResult(result) // will always happen
} else {
sendResult(result) // is only executed once a day
}
}
@DistributedLock(ttl="1d", cleanup=false)
voidsendResult(defresult) {
fooService.sendResult(result)
}
}

Configuration

To disable the library completely (recommended in tests) you can set micronaut.distributed-lock-enabled to false in application.groovy

micronaut:
distributed-lock:
enabled: false

Implementations

Redis

A simple implementation using micronaut-redis

Usage

runtime "com.uberall:micronaut-distributed-lock-data-redis:3.5.0"

Configuration

Check the documentation how to make sure lettuce has a redis server connection.

Micronaut Data JDBC

An implementation using micronaut-data-jdbc.

Usage

runtime "com.uberall:micronaut-distributed-lock-data-jdbc:3.5.0"

After adding the runtime dependency into build.gradle you'll have to tell the library which sql dialect to use

micronaut:
distributed-lock:
dialect: mysql # OR postgres

Additionally, You will need to add a table to your datasource that has 3 columns:

columntypedescription
idBIGINTThe unique id for each lock
nameVARCHARThe name of the lock, length depends on your usage, 255 should be safe
untilDATETIMEThe datetime until this lock is valid

For MySQL or MariaDB a compatible create statement would be:

CREATETABLEIF NOT EXISTS `distributed_lock`
(
`id`bigint(20) NOT NULL AUTO_INCREMENT,
`name`varchar(255) UNIQUE NOT NULL,
`until` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE = InnoDB
DEFAULT CHARSET = latin1;

Configuration

If you want to use a non-default datasource you can set the lock database name by setting micronaut.distributed.lock.datasource-name and defining a database with that name E.g.:

micronaut:
distributed-lock:
datasource-name: lockdialect: mysqldatasources:
default:
url: jdbc:mysql://production-server/all-the-important-datadriverClassName: com.mysql.cj.jdbc.Driverusername: userpassword: passschema-generate: nonedialect: MYSQLpooled: truelock:
url: jdbc:mysql://lock-server/the-lock-schemadriverClassName: com.mysql.cj.jdbc.Driverusername: resupassword: ssapdialect: MYSQLpooled: true

Development

No special setup is needed to start development. Just hack ahead and create a MR.

Publishing

You'll need to be in the uberall organisation in Github to be able to publish the latest version. also GITHUB_USER and GITHUB_TOKEN environment variables need to be set.

Simply run ./gradlew :publish --no-daemon to upload all artifacts to Github.

About

A Micronaut library to lock method executions in a distributed environment

Resources

Stars

6 stars

Watchers

17 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - uberall/micronaut-distributed-lock: A Micronaut library to lock method executions in a distributed environment · GitHub
Skip to content

Repository files navigation

micronaut-distributed-lock

A Micronaut library to lock method executions in a distributed environment

Usage

Prerequisites

Add our Github Packages repository to your pom.xml or build.gradle

build.gradle

repositories {
maven {
url "https://maven.pkg.github.com/uberall/micronaut-distributed-lock" }
}

Installation

Add a dependency to micronaut-distributed-lock-core

implementation "com.uberall:micronaut-distributed-lock-core:3.5.0"

and one runtime implementation to your pom.xml/build.gradle

runtime "com.uberall:micronaut-distributed-lock-data-jdbc:3.5.0"

now you can annotate e.g. your @Scheduled methods with @com.uberall.annotations.DistributedLock

importcom.uberall.annotations.DistributedLockimportio.micronaut.scheduling.annotation.Scheduledimportjavax.inject.Injectimportjavax.inject.Singleton@SingletonclassFooJob {
@InjectFooService fooService
@DistributedLock(ttl="1m")
@Scheduled(fixedRate='5m')
voidperform() {
// some long running asynchornous task
}
}

The result is that no matter how often you call this method, as long as the first invocation is still running no further invocations will be executed, no matter who is trying to execute.

Annotation Parameters

namedefaultdescription
ttl"1m"The lock duration. If cleanup is set to false this prohibits any further execution for the given duration. Otherwise this is a mere "hint" when to allow execution again because e.g. the first execution was interrupted and the lock has never been cleaned up
name{methodName}The name of the lock, this is used to identify the lock in the repository and should be unique across you codebase. If not set the method name is used
appendParametersfalseWhether or not to append parameters to the lock name. If set to true a key/value map of the parameters will be appeneded to the lock name. This is helpful in environments where you only want to prohibit e.g. processing of the same database entitiy on 2 servers
cleanuptrueWhether or not to cleanup the lock after method execution

TTL and Cleanup

There can be some confusion with TTL and Cleanup so here is use cases of when to set cleanup and what to set ttl to in other cases.

Cleanup = true (default)

The Interceptor will always clean up the lock, no matter whether the method execution was successful or not, after method execution. You could see the TTL as a "hint" on execution time. If the lock is not cleaned up (e.g. server is shutdown during method execution) the next round is still running.

The typical use case is a distributed application where you want a scheduled job to only run once every X minutes even if you are running your micronaut application on 20 servers. TTL should be set to not block the next scheduled execution. E.g.:

importcom.uberall.annotations.DistributedLockimportio.micronaut.scheduling.annotation.Scheduledimportjavax.inject.Injectimportjavax.inject.Singleton@SingletonclassFooJob {
@InjectFooService fooService
@DistributedLock(ttl="4m50s")
@Scheduled(fixedRate='5m')
voidperform() {
fooService.runTheThingThatIsSupposedToHappenEveryFiveMinutes()
}
}

Cleanup = false

In this scenario the lock will not be released after method execution and no more invocations of the method are actually executed for the duration of the lock lifetime.

A typical example is a "rate limited" method e.g. You are doing some heavy analytics every 10 minutes and if the analytics haven't resulted in something unusual, you only want the result to be sent out once a day.

importcom.uberall.annotations.DistributedLockimportio.micronaut.scheduling.annotation.Scheduledimportjavax.inject.Injectimportjavax.inject.Singleton@SingletonclassFooJob {
@InjectFooService fooService
@Scheduled(fixedRate='10m')
voidperform() {
def result = fooService.getResult()
if (result.bad) {
fooService.sendResult(result) // will always happen
} else {
sendResult(result) // is only executed once a day
}
}
@DistributedLock(ttl="1d", cleanup=false)
voidsendResult(defresult) {
fooService.sendResult(result)
}
}

Configuration

To disable the library completely (recommended in tests) you can set micronaut.distributed-lock-enabled to false in application.groovy

micronaut:
distributed-lock:
enabled: false

Implementations

Redis

A simple implementation using micronaut-redis

Usage

runtime "com.uberall:micronaut-distributed-lock-data-redis:3.5.0"

Configuration

Check the documentation how to make sure lettuce has a redis server connection.

Micronaut Data JDBC

An implementation using micronaut-data-jdbc.

Usage

runtime "com.uberall:micronaut-distributed-lock-data-jdbc:3.5.0"

After adding the runtime dependency into build.gradle you'll have to tell the library which sql dialect to use

micronaut:
distributed-lock:
dialect: mysql # OR postgres

Additionally, You will need to add a table to your datasource that has 3 columns:

columntypedescription
idBIGINTThe unique id for each lock
nameVARCHARThe name of the lock, length depends on your usage, 255 should be safe
untilDATETIMEThe datetime until this lock is valid

For MySQL or MariaDB a compatible create statement would be:

CREATETABLEIF NOT EXISTS `distributed_lock`
(
`id`bigint(20) NOT NULL AUTO_INCREMENT,
`name`varchar(255) UNIQUE NOT NULL,
`until` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE = InnoDB
DEFAULT CHARSET = latin1;

Configuration

If you want to use a non-default datasource you can set the lock database name by setting micronaut.distributed.lock.datasource-name and defining a database with that name E.g.:

micronaut:
distributed-lock:
datasource-name: lockdialect: mysqldatasources:
default:
url: jdbc:mysql://production-server/all-the-important-datadriverClassName: com.mysql.cj.jdbc.Driverusername: userpassword: passschema-generate: nonedialect: MYSQLpooled: truelock:
url: jdbc:mysql://lock-server/the-lock-schemadriverClassName: com.mysql.cj.jdbc.Driverusername: resupassword: ssapdialect: MYSQLpooled: true

Development

No special setup is needed to start development. Just hack ahead and create a MR.

Publishing

You'll need to be in the uberall organisation in Github to be able to publish the latest version. also GITHUB_USER and GITHUB_TOKEN environment variables need to be set.

Simply run ./gradlew :publish --no-daemon to upload all artifacts to Github.

About

A Micronaut library to lock method executions in a distributed environment

Resources

Stars

6 stars

Watchers

17 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - uberall/micronaut-distributed-lock: A Micronaut library to lock method executions in a distributed environment · GitHub
Skip to content

Repository files navigation

micronaut-distributed-lock

A Micronaut library to lock method executions in a distributed environment

Usage

Prerequisites

Add our Github Packages repository to your pom.xml or build.gradle

build.gradle

repositories {
maven {
url "https://maven.pkg.github.com/uberall/micronaut-distributed-lock" }
}

Installation

Add a dependency to micronaut-distributed-lock-core

implementation "com.uberall:micronaut-distributed-lock-core:3.5.0"

and one runtime implementation to your pom.xml/build.gradle

runtime "com.uberall:micronaut-distributed-lock-data-jdbc:3.5.0"

now you can annotate e.g. your @Scheduled methods with @com.uberall.annotations.DistributedLock

importcom.uberall.annotations.DistributedLockimportio.micronaut.scheduling.annotation.Scheduledimportjavax.inject.Injectimportjavax.inject.Singleton@SingletonclassFooJob {
@InjectFooService fooService
@DistributedLock(ttl="1m")
@Scheduled(fixedRate='5m')
voidperform() {
// some long running asynchornous task
}
}

The result is that no matter how often you call this method, as long as the first invocation is still running no further invocations will be executed, no matter who is trying to execute.

Annotation Parameters

namedefaultdescription
ttl"1m"The lock duration. If cleanup is set to false this prohibits any further execution for the given duration. Otherwise this is a mere "hint" when to allow execution again because e.g. the first execution was interrupted and the lock has never been cleaned up
name{methodName}The name of the lock, this is used to identify the lock in the repository and should be unique across you codebase. If not set the method name is used
appendParametersfalseWhether or not to append parameters to the lock name. If set to true a key/value map of the parameters will be appeneded to the lock name. This is helpful in environments where you only want to prohibit e.g. processing of the same database entitiy on 2 servers
cleanuptrueWhether or not to cleanup the lock after method execution

TTL and Cleanup

There can be some confusion with TTL and Cleanup so here is use cases of when to set cleanup and what to set ttl to in other cases.

Cleanup = true (default)

The Interceptor will always clean up the lock, no matter whether the method execution was successful or not, after method execution. You could see the TTL as a "hint" on execution time. If the lock is not cleaned up (e.g. server is shutdown during method execution) the next round is still running.

The typical use case is a distributed application where you want a scheduled job to only run once every X minutes even if you are running your micronaut application on 20 servers. TTL should be set to not block the next scheduled execution. E.g.:

importcom.uberall.annotations.DistributedLockimportio.micronaut.scheduling.annotation.Scheduledimportjavax.inject.Injectimportjavax.inject.Singleton@SingletonclassFooJob {
@InjectFooService fooService
@DistributedLock(ttl="4m50s")
@Scheduled(fixedRate='5m')
voidperform() {
fooService.runTheThingThatIsSupposedToHappenEveryFiveMinutes()
}
}

Cleanup = false

In this scenario the lock will not be released after method execution and no more invocations of the method are actually executed for the duration of the lock lifetime.

A typical example is a "rate limited" method e.g. You are doing some heavy analytics every 10 minutes and if the analytics haven't resulted in something unusual, you only want the result to be sent out once a day.

importcom.uberall.annotations.DistributedLockimportio.micronaut.scheduling.annotation.Scheduledimportjavax.inject.Injectimportjavax.inject.Singleton@SingletonclassFooJob {
@InjectFooService fooService
@Scheduled(fixedRate='10m')
voidperform() {
def result = fooService.getResult()
if (result.bad) {
fooService.sendResult(result) // will always happen
} else {
sendResult(result) // is only executed once a day
}
}
@DistributedLock(ttl="1d", cleanup=false)
voidsendResult(defresult) {
fooService.sendResult(result)
}
}

Configuration

To disable the library completely (recommended in tests) you can set micronaut.distributed-lock-enabled to false in application.groovy

micronaut:
distributed-lock:
enabled: false

Implementations

Redis

A simple implementation using micronaut-redis

Usage

runtime "com.uberall:micronaut-distributed-lock-data-redis:3.5.0"

Configuration

Check the documentation how to make sure lettuce has a redis server connection.

Micronaut Data JDBC

An implementation using micronaut-data-jdbc.

Usage

runtime "com.uberall:micronaut-distributed-lock-data-jdbc:3.5.0"

After adding the runtime dependency into build.gradle you'll have to tell the library which sql dialect to use

micronaut:
distributed-lock:
dialect: mysql # OR postgres

Additionally, You will need to add a table to your datasource that has 3 columns:

columntypedescription
idBIGINTThe unique id for each lock
nameVARCHARThe name of the lock, length depends on your usage, 255 should be safe
untilDATETIMEThe datetime until this lock is valid

For MySQL or MariaDB a compatible create statement would be:

CREATETABLEIF NOT EXISTS `distributed_lock`
(
`id`bigint(20) NOT NULL AUTO_INCREMENT,
`name`varchar(255) UNIQUE NOT NULL,
`until` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE = InnoDB
DEFAULT CHARSET = latin1;

Configuration

If you want to use a non-default datasource you can set the lock database name by setting micronaut.distributed.lock.datasource-name and defining a database with that name E.g.:

micronaut:
distributed-lock:
datasource-name: lockdialect: mysqldatasources:
default:
url: jdbc:mysql://production-server/all-the-important-datadriverClassName: com.mysql.cj.jdbc.Driverusername: userpassword: passschema-generate: nonedialect: MYSQLpooled: truelock:
url: jdbc:mysql://lock-server/the-lock-schemadriverClassName: com.mysql.cj.jdbc.Driverusername: resupassword: ssapdialect: MYSQLpooled: true

Development

No special setup is needed to start development. Just hack ahead and create a MR.

Publishing

You'll need to be in the uberall organisation in Github to be able to publish the latest version. also GITHUB_USER and GITHUB_TOKEN environment variables need to be set.

Simply run ./gradlew :publish --no-daemon to upload all artifacts to Github.

About

A Micronaut library to lock method executions in a distributed environment

Resources

Stars

6 stars

Watchers

17 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); GitHub - uberall/micronaut-distributed-lock: A Micronaut library to lock method executions in a distributed environment · GitHub
Skip to content

Repository files navigation

micronaut-distributed-lock

A Micronaut library to lock method executions in a distributed environment

Usage

Prerequisites

Add our Github Packages repository to your pom.xml or build.gradle

build.gradle

repositories {
maven {
url "https://maven.pkg.github.com/uberall/micronaut-distributed-lock" }
}

Installation

Add a dependency to micronaut-distributed-lock-core

implementation "com.uberall:micronaut-distributed-lock-core:3.5.0"

and one runtime implementation to your pom.xml/build.gradle

runtime "com.uberall:micronaut-distributed-lock-data-jdbc:3.5.0"

now you can annotate e.g. your @Scheduled methods with @com.uberall.annotations.DistributedLock

importcom.uberall.annotations.DistributedLockimportio.micronaut.scheduling.annotation.Scheduledimportjavax.inject.Injectimportjavax.inject.Singleton@SingletonclassFooJob {
@InjectFooService fooService
@DistributedLock(ttl="1m")
@Scheduled(fixedRate='5m')
voidperform() {
// some long running asynchornous task
}
}

The result is that no matter how often you call this method, as long as the first invocation is still running no further invocations will be executed, no matter who is trying to execute.

Annotation Parameters

namedefaultdescription
ttl"1m"The lock duration. If cleanup is set to false this prohibits any further execution for the given duration. Otherwise this is a mere "hint" when to allow execution again because e.g. the first execution was interrupted and the lock has never been cleaned up
name{methodName}The name of the lock, this is used to identify the lock in the repository and should be unique across you codebase. If not set the method name is used
appendParametersfalseWhether or not to append parameters to the lock name. If set to true a key/value map of the parameters will be appeneded to the lock name. This is helpful in environments where you only want to prohibit e.g. processing of the same database entitiy on 2 servers
cleanuptrueWhether or not to cleanup the lock after method execution

TTL and Cleanup

There can be some confusion with TTL and Cleanup so here is use cases of when to set cleanup and what to set ttl to in other cases.

Cleanup = true (default)

The Interceptor will always clean up the lock, no matter whether the method execution was successful or not, after method execution. You could see the TTL as a "hint" on execution time. If the lock is not cleaned up (e.g. server is shutdown during method execution) the next round is still running.

The typical use case is a distributed application where you want a scheduled job to only run once every X minutes even if you are running your micronaut application on 20 servers. TTL should be set to not block the next scheduled execution. E.g.:

importcom.uberall.annotations.DistributedLockimportio.micronaut.scheduling.annotation.Scheduledimportjavax.inject.Injectimportjavax.inject.Singleton@SingletonclassFooJob {
@InjectFooService fooService
@DistributedLock(ttl="4m50s")
@Scheduled(fixedRate='5m')
voidperform() {
fooService.runTheThingThatIsSupposedToHappenEveryFiveMinutes()
}
}

Cleanup = false

In this scenario the lock will not be released after method execution and no more invocations of the method are actually executed for the duration of the lock lifetime.

A typical example is a "rate limited" method e.g. You are doing some heavy analytics every 10 minutes and if the analytics haven't resulted in something unusual, you only want the result to be sent out once a day.

importcom.uberall.annotations.DistributedLockimportio.micronaut.scheduling.annotation.Scheduledimportjavax.inject.Injectimportjavax.inject.Singleton@SingletonclassFooJob {
@InjectFooService fooService
@Scheduled(fixedRate='10m')
voidperform() {
def result = fooService.getResult()
if (result.bad) {
fooService.sendResult(result) // will always happen
} else {
sendResult(result) // is only executed once a day
}
}
@DistributedLock(ttl="1d", cleanup=false)
voidsendResult(defresult) {
fooService.sendResult(result)
}
}

Configuration

To disable the library completely (recommended in tests) you can set micronaut.distributed-lock-enabled to false in application.groovy

micronaut:
distributed-lock:
enabled: false

Implementations

Redis

A simple implementation using micronaut-redis

Usage

runtime "com.uberall:micronaut-distributed-lock-data-redis:3.5.0"

Configuration

Check the documentation how to make sure lettuce has a redis server connection.

Micronaut Data JDBC

An implementation using micronaut-data-jdbc.

Usage

runtime "com.uberall:micronaut-distributed-lock-data-jdbc:3.5.0"

After adding the runtime dependency into build.gradle you'll have to tell the library which sql dialect to use

micronaut:
distributed-lock:
dialect: mysql # OR postgres

Additionally, You will need to add a table to your datasource that has 3 columns:

columntypedescription
idBIGINTThe unique id for each lock
nameVARCHARThe name of the lock, length depends on your usage, 255 should be safe
untilDATETIMEThe datetime until this lock is valid

For MySQL or MariaDB a compatible create statement would be:

CREATETABLEIF NOT EXISTS `distributed_lock`
(
`id`bigint(20) NOT NULL AUTO_INCREMENT,
`name`varchar(255) UNIQUE NOT NULL,
`until` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE = InnoDB
DEFAULT CHARSET = latin1;

Configuration

If you want to use a non-default datasource you can set the lock database name by setting micronaut.distributed.lock.datasource-name and defining a database with that name E.g.:

micronaut:
distributed-lock:
datasource-name: lockdialect: mysqldatasources:
default:
url: jdbc:mysql://production-server/all-the-important-datadriverClassName: com.mysql.cj.jdbc.Driverusername: userpassword: passschema-generate: nonedialect: MYSQLpooled: truelock:
url: jdbc:mysql://lock-server/the-lock-schemadriverClassName: com.mysql.cj.jdbc.Driverusername: resupassword: ssapdialect: MYSQLpooled: true

Development

No special setup is needed to start development. Just hack ahead and create a MR.

Publishing

You'll need to be in the uberall organisation in Github to be able to publish the latest version. also GITHUB_USER and GITHUB_TOKEN environment variables need to be set.

Simply run ./gradlew :publish --no-daemon to upload all artifacts to Github.

About

A Micronaut library to lock method executions in a distributed environment

Resources

Stars

6 stars

Watchers

17 watching

Forks

Releases

Packages

Used by

Contributors

Languages