Skip to content

Releases: riido-git/locksmith

v3.0.1

13 Feb 21:25

Choose a tag to compare

v3.0.1

Fixed

  • Propagate InterruptedException from user methods inside @DistributedLock, @DistributedSemaphore, and template callbacks instead of silently swallowing it
  • Record rate-limit execution time metrics even when the method throws (wrap with try-finally in RateLimitAspect and LocksmithRateLimitTemplate)
  • Only warn about onLeaseExpired when THROW_EXCEPTION is used with autoRenew, not for the default LOG_WARNING value
  • Log full exception stack traces in semaphore permit release instead of just the message
  • Wrap DurationResolver parse errors with helpful context about the invalid input and expected formats

Changed

  • Document SpEL expression cache bounding semantics in SpELKeyResolver

Upgrade Guide

Update your dependency version:

<dependency>
    <groupId>in.riido</groupId>
    <artifactId>locksmith-spring-boot-starter</artifactId>
    <version>3.0.1</version>
</dependency>

Full Changelog: v3.0.0...v3.0.1

v3.0.0

06 Feb 19:23

Choose a tag to compare

v3.0.0

Added

  • Distributed Rate Limiting - New @RateLimit annotation for controlling request throughput across distributed systems (#44, #7)

    • Configurable permits and interval (e.g., 100 requests per minute)
    • Support for OVERALL (shared) and PER_CLIENT (per Redisson instance) rate types via RateType enum
    • SKIP_IMMEDIATELY and WAIT_AND_SKIP acquisition modes
    • RateLimitSkipHandler interface with RateLimitThrowExceptionHandler and RateLimitReturnDefaultHandler built-in handlers
    • RateLimitContext record for handler context
    • RateLimitExceededException for when rate limit is exceeded
    • LocksmithRateLimitTemplate for programmatic rate limiting with fluent builder
    • RateLimitCallback functional interface
    • RateLimitMetrics - Micrometer metrics integration (opt-in via locksmith.rate-limit.metrics-enabled)
    • SpEL expression support for dynamic keys
  • Enabled Property - Conditionally disable lock, semaphore, and rate-limit components

    • locksmith.lock.enabled (default: true)
    • locksmith.semaphore.enabled (default: true)
    • locksmith.rate-limit.enabled (default: true)
    • Uses @ConditionalOnProperty to conditionally create aspects and templates

Changed

  • Thread safety documentation added to Context records clarifying that args arrays are read-only by convention

Dependencies

  • Bumped Redisson from 4.1.0 to 4.2.0
  • Bumped maven-compiler-plugin from 3.14.1 to 3.15.0

Upgrade Guide

Update your dependency version:

<dependency>
    <groupId>in.riido</groupId>
    <artifactId>locksmith-spring-boot-starter</artifactId>
    <version>3.0.0</version>
</dependency>

New Rate Limiting Feature

Use the new @RateLimit annotation for throughput control:

// 100 requests per minute
@RateLimit(key = "heavy-api", permits = 100, interval = "1m")
public Response heavyOperation() {
    return processRequest();
}

// Per-user rate limiting with SpEL
@RateLimit(key = "#{#userId}", permits = 60, interval = "1m")
public Response userRequest(String userId) {
    return processRequest();
}

Or use the programmatic template:

@Service
public class MyService {

    private final LocksmithRateLimitTemplate rateLimitTemplate;

    public String example() throws Exception {
        return rateLimitTemplate.executeWithRateLimit("api", () -> {
            return "executed";
        });
    }

    public void builderExample() throws Exception {
        rateLimitTemplate.forKey("api")
            .permits(100)
            .interval(Duration.ofMinutes(1))
            .rateType(RateType.PER_CLIENT)
            .execute(() -> doWork());
    }
}

Disable Components

locksmith:
  lock:
    enabled: false      # Disable distributed locks
  semaphore:
    enabled: true
  rate-limit:
    enabled: true

Full Changelog: v2.1.0...v3.0.0

v2.1.0

25 Jan 21:39

Choose a tag to compare

v2.1.0

Added

  • Micrometer Metrics Integration - Optional observability for lock and semaphore operations (#30)

    • LockMetrics with counters, timers, and gauges for lock operations
    • SemaphoreMetrics with parallel metrics for semaphore operations
    • LocksmithMetricsAutoConfiguration for conditional bean creation
    • Metrics are opt-in via locksmith.lock.metrics-enabled and locksmith.semaphore.metrics-enabled properties
    • Graceful degradation when Micrometer is not on classpath
  • Programmatic Templates - Alternative to annotations for lock and semaphore operations (#42)

    • LocksmithLockTemplate for programmatic lock operations with fluent builder pattern
    • LocksmithSemaphoreTemplate for programmatic semaphore operations with builder pattern
    • LockCallback and SemaphoreCallback functional interfaces
    • Support for auto-renew, custom timing, and all lock types

Lock Metrics

Metric Type Description
locksmith.lock.acquired Counter Successful lock acquisitions
locksmith.lock.skipped Counter Skipped acquisitions (tagged by reason)
locksmith.lock.lease.expired Counter Lease expirations detected
locksmith.lock.acquisition.time Timer Time to acquire lock
locksmith.lock.held.time Timer Duration lock was held
locksmith.lock.autorenew.active Gauge Active auto-renewed locks

Semaphore Metrics

Metric Type Description
locksmith.semaphore.acquired Counter Successful permit acquisitions
locksmith.semaphore.skipped Counter Skipped acquisitions (tagged by reason)
locksmith.semaphore.lease.expired Counter Lease expirations detected
locksmith.semaphore.acquisition.time Timer Time to acquire permit
locksmith.semaphore.held.time Timer Duration permit was held

Fixed

  • Semaphore permit consistency validation in LocksmithSemaphoreTemplate
  • Metrics skip reason logic using explicit mode instead of waitTime proxy
  • LockOperationBuilder now warns when leaseTime() overrides autoRenew()

Upgrade Guide

Update your dependency version:

<dependency>
    <groupId>in.riido</groupId>
    <artifactId>locksmith-spring-boot-starter</artifactId>
    <version>2.1.0</version>
</dependency>

Enable Metrics

locksmith:
  lock:
    metrics-enabled: true
  semaphore:
    metrics-enabled: true

Programmatic Templates

@Service
public class MyService {

    private final LocksmithLockTemplate lockTemplate;

    public MyService(LocksmithLockTemplate lockTemplate) {
        this.lockTemplate = lockTemplate;
    }

    public String example() throws Exception {
        return lockTemplate.executeWithLock("my-key", () -> {
            return "executed";
        });
    }

    public void builderExample() throws Exception {
        lockTemplate.forKey("my-key")
            .waitTime(Duration.ofSeconds(5))
            .autoRenew()
            .execute(() -> doWork());
    }
}

Full Changelog: v2.0.0...v2.1.0

v2.0.0

15 Jan 18:58

Choose a tag to compare

v2.0.0

Added

  • Distributed Semaphores - New @DistributedSemaphore annotation for permit-based concurrency control (#8)
  • SemaphoreSkipHandler interface for custom semaphore skip behavior
  • SemaphoreThrowExceptionHandler and SemaphoreReturnDefaultHandler built-in handlers
  • SemaphoreContext record for handler context
  • SemaphoreNotAcquiredException and SemaphoreLeaseExpiredException exceptions
  • Spring bean dependency injection support for skip handlers - handlers can now be Spring @Component beans with @Autowired dependencies
  • DefaultValueResolver utility for shared default value resolution
  • SpELKeyResolver and DurationResolver shared utilities

Changed

  • Handler resolution now checks Spring ApplicationContext first, falls back to reflection
  • Extracted common utilities to support package
  • Improved handler caching with instance-level cache per aspect

Upgrade Guide

Update your dependency version:

<dependency>
    <groupId>in.riido</groupId>
    <artifactId>locksmith-spring-boot-starter</artifactId>
    <version>2.0.0</version>
</dependency>

New Semaphore Feature

Use the new @DistributedSemaphore annotation for permit-based concurrency control:

@DistributedSemaphore(key = "api-pool", permits = 10, leaseTime = "5m")
public void callExternalApi() {
    // Only 10 concurrent executions allowed
}

Handler Dependency Injection

Skip handlers can now be Spring beans with injected dependencies:

@Component
public class AlertingSkipHandler implements LockSkipHandler {
    private final AlertService alertService;

    public AlertingSkipHandler(AlertService alertService) {
        this.alertService = alertService;
    }

    @Override
    public Object handle(LockContext context) {
        alertService.sendAlert("Lock failed: " + context.lockKey());
        return null;
    }
}

// Usage
@DistributedLock(key = "my-task", skipHandler = AlertingSkipHandler.class)
public void myTask() { }

Full Changelog: v1.4.3...v2.0.0

v1.4.3

03 Jan 16:42

Choose a tag to compare

Added

  • Check isHeldByCurrentThread() before unlocking with warning log for expired locks (#36)

Changed

  • Improved virtual thread compatibility with better lock ownership verification (#36)

Upgrade Guide

Update your dependency version:

<dependency>
    <groupId>in.riido</groupId>
    <artifactId>locksmith-spring-boot-starter</artifactId>
    <version>1.4.3</version>
</dependency>

Full Changelog: v1.4.2...v1.4.3

v1.4.2

01 Jan 09:24

Choose a tag to compare

Added

  • Logging of Redisson and Spring Boot versions during initialization

Upgrade Guide

Update your dependency version:

<dependency>
    <groupId>in.riido</groupId>
    <artifactId>locksmith-spring-boot-starter</artifactId>
    <version>1.4.2</version>
</dependency>

Full Changelog: v1.4.1...v1.4.2

v1.4.1

27 Dec 17:17

Choose a tag to compare

Added

  • SpEL expression caching using ConcurrentHashMap (#34)

Performance

  • 43% reduction in P99 latency
  • 75% reduction in throughput variation
  • 17% increase in concurrent throughput
  • 14% reduction in CPU utilization

Changed

  • Improved CI/CD pipeline with Java 17, 21, and 25 matrix testing
  • Optimized test execution by excluding performance tests from CI

Upgrade Guide

Update your dependency version:

<dependency>
    <groupId>in.riido</groupId>
    <artifactId>locksmith-spring-boot-starter</artifactId>
    <version>1.4.1</version>
</dependency>

Full Changelog: v1.4.0...v1.4.1

v1.4.0

27 Dec 09:14

Choose a tag to compare

Breaking Changes

  • SpEL expressions now require #{...} wrapper syntax - change #userId to #{#userId} (#33)
  • Literal keys can now contain # character (e.g., order#123)

Added

  • Handler instance caching for better performance (#32)
  • Debug mode configuration: locksmith.debug=true
  • Comprehensive SpEL test coverage (38 tests)

Changed

  • Removed explicit version specs for Spring Boot and SLF4J
  • Improved Docker availability detection in tests

Upgrade Guide

Update your dependency version:

<dependency>
    <groupId>in.riido</groupId>
    <artifactId>locksmith-spring-boot-starter</artifactId>
    <version>1.4.0</version>
</dependency>

⚠️ Note: Update all SpEL expressions to use #{...} wrapper syntax.

Full Changelog: v1.3.1...v1.4.0

v1.3.1

24 Dec 18:29

Choose a tag to compare

Added

  • Virtual thread integration tests for Java 21+ (10 test cases)
  • Comprehensive test coverage for concurrent access and stress scenarios (#29)

Fixed

  • Javadoc warnings in LockContext compact constructor

Dependencies

  • Updated testcontainers to Spring Boot managed versions
  • Bumped actions/checkout from 4 to 6
  • Bumped actions/setup-java from 4 to 5
  • Bumped actions/upload-artifact from 4 to 6

Upgrade Guide

Update your dependency version:

<dependency>
    <groupId>in.riido</groupId>
    <artifactId>locksmith-spring-boot-starter</artifactId>
    <version>1.3.1</version>
</dependency>

Full Changelog: v1.3.0...v1.3.1

v1.3.0

23 Dec 17:21

Choose a tag to compare

Added

  • Auto-renew lease time support using Redisson watchdog mechanism (#20, #21)
  • Input validation for LockContext with null checks (#15, #19)

Fixed

  • Virtual thread compatibility by removing isHeldByCurrentThread() check to prevent lock leaks (#22, #23)

Upgrade Guide

Update your dependency version:

<dependency>
    <groupId>in.riido</groupId>
    <artifactId>locksmith-spring-boot-starter</artifactId>
    <version>1.3.0</version>
</dependency>

Full Changelog: v1.2.2...v1.3.0