GH-3655: Add automatically delete for Redis Locks

Fixes https://github.com/spring-projects/spring-integration/issues/3655

* support automatically clean up cache
* RedisLockRegistry.capacity desc
This commit is contained in:
unseok kim
2021-11-08 09:13:37 -05:00
committed by Artem Bilan
parent 25100d1ac7
commit e51513230a
3 changed files with 217 additions and 11 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2020 the original author or authors.
* Copyright 2014-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,9 +19,10 @@ package org.springframework.integration.redis.util;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -68,6 +69,7 @@ import org.springframework.util.ReflectionUtils;
* @author Konstantin Yakimov
* @author Artem Bilan
* @author Vedran Pavic
* @author Unseok Kim
*
* @since 4.0
*
@@ -78,6 +80,8 @@ public final class RedisLockRegistry implements ExpirableLockRegistry, Disposabl
private static final long DEFAULT_EXPIRE_AFTER = 60000L;
private static final int DEFAULT_CAPACITY = 1_000_000;
private static final String OBTAIN_LOCK_SCRIPT =
"local lockClientId = redis.call('GET', KEYS[1])\n" +
"if lockClientId == ARGV[1] then\n" +
@@ -90,7 +94,15 @@ public final class RedisLockRegistry implements ExpirableLockRegistry, Disposabl
"return false";
private final Map<String, RedisLock> locks = new ConcurrentHashMap<>();
private final Map<String, RedisLock> locks =
new LinkedHashMap<String, RedisLock>(16, 0.75F, true) {
@Override
protected boolean removeEldestEntry(Entry<String, RedisLock> eldest) {
return size() > RedisLockRegistry.this.capacity;
}
};
private final String clientId = UUID.randomUUID().toString();
@@ -102,6 +114,8 @@ public final class RedisLockRegistry implements ExpirableLockRegistry, Disposabl
private final long expireAfter;
private int capacity = DEFAULT_CAPACITY;
/**
* An {@link ExecutorService} to call {@link StringRedisTemplate#delete} in
* the separate thread when the current one is interrupted.
@@ -152,21 +166,34 @@ public final class RedisLockRegistry implements ExpirableLockRegistry, Disposabl
this.executorExplicitlySet = true;
}
/**
* Set the capacity of cached locks.
* @param capacity The capacity of cached lock, (default 1_000_000).
* @since 5.5.6
*/
public void setCapacity(int capacity) {
this.capacity = capacity;
}
@Override
public Lock obtain(Object lockKey) {
Assert.isInstanceOf(String.class, lockKey);
String path = (String) lockKey;
return this.locks.computeIfAbsent(path, RedisLock::new);
synchronized (this.locks) {
return this.locks.computeIfAbsent(path, RedisLock::new);
}
}
@Override
public void expireUnusedOlderThan(long age) {
long now = System.currentTimeMillis();
this.locks.entrySet()
.removeIf((entry) -> {
RedisLock lock = entry.getValue();
return now - lock.getLockedAt() > age && !lock.isAcquiredInThisProcess();
});
synchronized (this.locks) {
this.locks.entrySet()
.removeIf((entry) -> {
RedisLock lock = entry.getValue();
return now - lock.getLockedAt() > age && !lock.isAcquiredInThisProcess();
});
}
}
@Override