Merge branch 'master' into 3.0.0

# Conflicts:
#	pom.xml
#	redisson-all/pom.xml
#	redisson-tomcat/pom.xml
#	redisson-tomcat/redisson-tomcat-6/pom.xml
#	redisson-tomcat/redisson-tomcat-7/pom.xml
#	redisson-tomcat/redisson-tomcat-8/pom.xml
#	redisson-tomcat/redisson-tomcat-9/pom.xml
#	redisson/pom.xml
pull/1821/head
Nikita 7 years ago
commit 60816c0836

@ -4,6 +4,24 @@ Redisson Releases History
Try __[Redisson PRO](https://redisson.pro)__ version. Try __[Redisson PRO](https://redisson.pro)__ version.
### 15-Feb-2018 - versions 2.11.1 and 3.6.1 released
Feature - `RedissonClusteredSpringLocalCachedCacheManager` added. Please read [documentation](https://github.com/redisson/redisson/wiki/14.-Integration-with-frameworks/#1421-spring-cache-local-cache-and-data-partitioning) for more details
Feature - `rangeTailReversed`, `rangeHeadReversed` and `rangeReversed` methods added to `RLexSortedSet` object
Feature - `RBucketsAsync` interface added
Feature - `scanInterval` setting added for Sentinel mode
Feature - `RLocalCachedMap.clearLocalCache` method added
Fixed - remove `hset` command invocation during `RMapCache` entry loading
Fixed - buffer leak in `replace` and `remove` methods of `RLocalCachedMap` object
Fixed - `RRemoteService` object throws NPE
Fixed - Multimap cluster compatibility
Fixed - Enum support for Live Objects
Fixed - Jackson 2.9 compatibility
Fixed - `RTopic.removeAllListeners` got blocked on invocation
Fixed - possible pubsub listeners leak
Fixed - `RBatch` throws NPE with big pipeline in atomic mode
Fixed - Warning about `CommandDecoder.decode()` method
### 29-Jan-2018 - versions 2.11.0 and 3.6.0 released ### 29-Jan-2018 - versions 2.11.0 and 3.6.0 released
Feature - __`atomic` setting added to RBatch object__ Please read [documentation](https://github.com/redisson/redisson/wiki/10.-additional-features#103-execution-batches-of-commands) for more details Feature - __`atomic` setting added to RBatch object__ Please read [documentation](https://github.com/redisson/redisson/wiki/10.-additional-features#103-execution-batches-of-commands) for more details

@ -6,8 +6,8 @@ Based on high-performance async and lock-free Java Redis client and [Netty](http
| Stable <br/> Release Version | Release Date | JDK Version<br/> compatibility | `CompletionStage` <br/> support | `ProjectReactor` version<br/> compatibility | | Stable <br/> Release Version | Release Date | JDK Version<br/> compatibility | `CompletionStage` <br/> support | `ProjectReactor` version<br/> compatibility |
| ------------- | ------------- | ------------| -----------| -----------| | ------------- | ------------- | ------------| -----------| -----------|
| 3.6.0 | 29.01.2018 | 1.8+ | Yes | 3.1.x | | 3.6.1 | 15.02.2018 | 1.8, 1.9+ | Yes | 3.1.x |
| 2.11.0 | 29.01.2018 | 1.6, 1.7, 1.8 and Android | No | 2.0.8 | | 2.11.1 | 15.02.2018 | 1.6, 1.7, 1.8, 1.9 and Android | No | 2.0.8 |
Features Features
@ -88,23 +88,23 @@ Quick start
<dependency> <dependency>
<groupId>org.redisson</groupId> <groupId>org.redisson</groupId>
<artifactId>redisson</artifactId> <artifactId>redisson</artifactId>
<version>3.6.0</version> <version>3.6.1</version>
</dependency> </dependency>
<!-- JDK 1.6+ compatible --> <!-- JDK 1.6+ compatible -->
<dependency> <dependency>
<groupId>org.redisson</groupId> <groupId>org.redisson</groupId>
<artifactId>redisson</artifactId> <artifactId>redisson</artifactId>
<version>2.11.0</version> <version>2.11.1</version>
</dependency> </dependency>
#### Gradle #### Gradle
// JDK 1.8+ compatible // JDK 1.8+ compatible
compile 'org.redisson:redisson:3.6.0' compile 'org.redisson:redisson:3.6.1'
// JDK 1.6+ compatible // JDK 1.6+ compatible
compile 'org.redisson:redisson:2.11.0' compile 'org.redisson:redisson:2.11.1'
#### Java #### Java
@ -129,11 +129,11 @@ RExecutorService executor = redisson.getExecutorService("myExecutorService");
Downloads Downloads
=============================== ===============================
[Redisson 3.6.0](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson&v=3.6.0&e=jar), [Redisson 3.6.1](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson&v=3.6.1&e=jar),
[Redisson node 3.6.0](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-all&v=3.6.0&e=jar) [Redisson node 3.6.1](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-all&v=3.6.1&e=jar)
[Redisson 2.11.0](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson&v=2.11.0&e=jar), [Redisson 2.11.1](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson&v=2.11.1&e=jar),
[Redisson node 2.11.0](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-all&v=2.11.0&e=jar) [Redisson node 2.11.1](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-all&v=2.11.1&e=jar)
### Supported by ### Supported by

@ -33,23 +33,25 @@ Usage
**2** Copy two jars into `TOMCAT_BASE/lib` directory: **2** Copy two jars into `TOMCAT_BASE/lib` directory:
1. __For JDK 1.8+__ 1. __For JDK 1.8+__
[redisson-all-3.6.0.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-all&v=3.6.0&e=jar) [redisson-all-3.6.1.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-all&v=3.6.1&e=jar)
for Tomcat 6.x for Tomcat 6.x
[redisson-tomcat-6-3.6.0.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-6&v=3.6.0&e=jar) [redisson-tomcat-6-3.6.1.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-6&v=3.6.1&e=jar)
for Tomcat 7.x for Tomcat 7.x
[redisson-tomcat-7-3.6.0.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-7&v=3.6.0&e=jar) [redisson-tomcat-7-3.6.1.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-7&v=3.6.1&e=jar)
for Tomcat 8.x for Tomcat 8.x
[redisson-tomcat-8-3.6.0.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-8&v=3.6.0&e=jar) [redisson-tomcat-8-3.6.1.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-8&v=3.6.1&e=jar)
for Tomcat 9.x for Tomcat 9.x
[redisson-tomcat-9-3.6.0.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-9&v=3.6.0&e=jar) [redisson-tomcat-9-3.6.1.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-9&v=3.6.1&e=jar)
2. __For JDK 1.6+__ 2. __For JDK 1.6+__
[redisson-all-2.11.0.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-all&v=2.11.0&e=jar) [redisson-all-2.11.1.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-all&v=2.11.1&e=jar)
for Tomcat 6.x for Tomcat 6.x
[redisson-tomcat-6-2.11.0.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-6&v=2.11.0&e=jar) [redisson-tomcat-6-2.11.1.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-6&v=2.11.1&e=jar)
for Tomcat 7.x for Tomcat 7.x
[redisson-tomcat-7-2.11.0.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-7&v=2.11.0&e=jar) [redisson-tomcat-7-2.11.1.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-7&v=2.11.1&e=jar)
for Tomcat 8.x
[redisson-tomcat-8-2.11.1.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-8&v=2.11.1&e=jar)

@ -24,6 +24,7 @@ import java.util.concurrent.TimeUnit;
import org.apache.catalina.session.StandardSession; import org.apache.catalina.session.StandardSession;
import org.redisson.api.RMap; import org.redisson.api.RMap;
import org.redisson.tomcat.RedissonSessionManager.ReadMode; import org.redisson.tomcat.RedissonSessionManager.ReadMode;
import org.redisson.tomcat.RedissonSessionManager.UpdateMode;
/** /**
* Redisson Session object for Apache Tomcat * Redisson Session object for Apache Tomcat
@ -37,11 +38,13 @@ public class RedissonSession extends StandardSession {
private final Map<String, Object> attrs; private final Map<String, Object> attrs;
private RMap<String, Object> map; private RMap<String, Object> map;
private final RedissonSessionManager.ReadMode readMode; private final RedissonSessionManager.ReadMode readMode;
private final UpdateMode updateMode;
public RedissonSession(RedissonSessionManager manager, RedissonSessionManager.ReadMode readMode) { public RedissonSession(RedissonSessionManager manager, ReadMode readMode, UpdateMode updateMode) {
super(manager); super(manager);
this.redissonManager = manager; this.redissonManager = manager;
this.readMode = readMode; this.readMode = readMode;
this.updateMode = updateMode;
try { try {
Field attr = StandardSession.class.getDeclaredField("attributes"); Field attr = StandardSession.class.getDeclaredField("attributes");
@ -144,7 +147,7 @@ public class RedissonSession extends StandardSession {
public void setAttribute(String name, Object value, boolean notify) { public void setAttribute(String name, Object value, boolean notify) {
super.setAttribute(name, value, notify); super.setAttribute(name, value, notify);
if (map != null && value != null) { if (updateMode == UpdateMode.DEFAULT && map != null && value != null) {
map.fastPut(name, value); map.fastPut(name, value);
} }
} }
@ -153,7 +156,7 @@ public class RedissonSession extends StandardSession {
protected void removeAttributeInternal(String name, boolean notify) { protected void removeAttributeInternal(String name, boolean notify) {
super.removeAttributeInternal(name, notify); super.removeAttributeInternal(name, notify);
if (map != null) { if (updateMode == UpdateMode.DEFAULT && map != null) {
map.fastRemove(name); map.fastRemove(name);
} }
} }

@ -17,7 +17,6 @@ package org.redisson.tomcat;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Enumeration;
import java.util.Map; import java.util.Map;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
@ -159,7 +158,7 @@ public class RedissonSessionManager extends ManagerBase implements Lifecycle {
@Override @Override
public Session createEmptySession() { public Session createEmptySession() {
return new RedissonSession(this, readMode); return new RedissonSession(this, readMode, updateMode);
} }
@Override @Override
@ -218,18 +217,14 @@ public class RedissonSessionManager extends ManagerBase implements Lifecycle {
lifecycle.fireLifecycleEvent(STOP_EVENT, null); lifecycle.fireLifecycleEvent(STOP_EVENT, null);
} }
public void store(HttpSession session) { public void store(HttpSession session) throws IOException {
if (session == null) { if (session == null) {
return; return;
} }
if (updateMode == UpdateMode.AFTER_REQUEST) { if (updateMode == UpdateMode.AFTER_REQUEST) {
Enumeration<String> names = session.getAttributeNames(); RedissonSession sess = (RedissonSession) findSession(session.getId());
while (names.hasMoreElements()) { sess.save();
String name = names.nextElement();
Object value = session.getAttribute(name);
session.setAttribute(name, value);
}
} }
} }

@ -1,5 +1,7 @@
package org.redisson.tomcat; package org.redisson.tomcat;
import java.nio.file.Paths;
import org.apache.catalina.Engine; import org.apache.catalina.Engine;
import org.apache.catalina.Host; import org.apache.catalina.Host;
import org.apache.catalina.LifecycleException; import org.apache.catalina.LifecycleException;
@ -56,7 +58,8 @@ public class TomcatServer {
localHost.setAutoDeploy(false); localHost.setAutoDeploy(false);
StandardContext rootContext = (StandardContext) server.createContext(contextPath, "webapp"); StandardContext rootContext = (StandardContext) server.createContext(contextPath, "webapp");
rootContext.setDefaultWebXml("web.xml"); String s = Paths.get("").toAbsolutePath().resolve("src/test/webapp/META-INF/context.xml").toString();
rootContext.setDefaultContextXml(s);
localHost.addChild(rootContext); localHost.addChild(rootContext);
Engine engine = server.createEngine(); Engine engine = server.createEngine();

@ -24,6 +24,7 @@ import java.util.concurrent.TimeUnit;
import org.apache.catalina.session.StandardSession; import org.apache.catalina.session.StandardSession;
import org.redisson.api.RMap; import org.redisson.api.RMap;
import org.redisson.tomcat.RedissonSessionManager.ReadMode; import org.redisson.tomcat.RedissonSessionManager.ReadMode;
import org.redisson.tomcat.RedissonSessionManager.UpdateMode;
/** /**
* Redisson Session object for Apache Tomcat * Redisson Session object for Apache Tomcat
@ -37,11 +38,14 @@ public class RedissonSession extends StandardSession {
private final Map<String, Object> attrs; private final Map<String, Object> attrs;
private RMap<String, Object> map; private RMap<String, Object> map;
private final RedissonSessionManager.ReadMode readMode; private final RedissonSessionManager.ReadMode readMode;
private final UpdateMode updateMode;
public RedissonSession(RedissonSessionManager manager, RedissonSessionManager.ReadMode readMode) { public RedissonSession(RedissonSessionManager manager, ReadMode readMode, UpdateMode updateMode) {
super(manager); super(manager);
this.redissonManager = manager; this.redissonManager = manager;
this.readMode = readMode; this.readMode = readMode;
this.updateMode = updateMode;
try { try {
Field attr = StandardSession.class.getDeclaredField("attributes"); Field attr = StandardSession.class.getDeclaredField("attributes");
attrs = (Map<String, Object>) attr.get(this); attrs = (Map<String, Object>) attr.get(this);
@ -148,7 +152,7 @@ public class RedissonSession extends StandardSession {
public void setAttribute(String name, Object value, boolean notify) { public void setAttribute(String name, Object value, boolean notify) {
super.setAttribute(name, value, notify); super.setAttribute(name, value, notify);
if (map != null && value != null) { if (updateMode == UpdateMode.DEFAULT && map != null && value != null) {
map.fastPut(name, value); map.fastPut(name, value);
} }
} }
@ -157,7 +161,7 @@ public class RedissonSession extends StandardSession {
protected void removeAttributeInternal(String name, boolean notify) { protected void removeAttributeInternal(String name, boolean notify) {
super.removeAttributeInternal(name, notify); super.removeAttributeInternal(name, notify);
if (map != null) { if (updateMode == UpdateMode.DEFAULT && map != null) {
map.fastRemove(name); map.fastRemove(name);
} }
} }

@ -17,12 +17,11 @@ package org.redisson.tomcat;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Enumeration;
import java.util.Map; import java.util.Map;
import org.apache.catalina.Context;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException; import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleState; import org.apache.catalina.LifecycleState;
import org.apache.catalina.Session; import org.apache.catalina.Session;
@ -138,7 +137,7 @@ public class RedissonSessionManager extends ManagerBase {
@Override @Override
public Session createEmptySession() { public Session createEmptySession() {
return new RedissonSession(this, readMode); return new RedissonSession(this, readMode, updateMode);
} }
@Override @Override
@ -213,18 +212,14 @@ public class RedissonSessionManager extends ManagerBase {
} }
public void store(HttpSession session) { public void store(HttpSession session) throws IOException {
if (session == null) { if (session == null) {
return; return;
} }
if (updateMode == UpdateMode.AFTER_REQUEST) { if (updateMode == UpdateMode.AFTER_REQUEST) {
Enumeration<String> names = session.getAttributeNames(); RedissonSession sess = (RedissonSession) findSession(session.getId());
while (names.hasMoreElements()) { sess.save();
String name = names.nextElement();
Object value = session.getAttribute(name);
session.setAttribute(name, value);
}
} }
} }

@ -24,6 +24,7 @@ import java.util.concurrent.TimeUnit;
import org.apache.catalina.session.StandardSession; import org.apache.catalina.session.StandardSession;
import org.redisson.api.RMap; import org.redisson.api.RMap;
import org.redisson.tomcat.RedissonSessionManager.ReadMode; import org.redisson.tomcat.RedissonSessionManager.ReadMode;
import org.redisson.tomcat.RedissonSessionManager.UpdateMode;
/** /**
* Redisson Session object for Apache Tomcat * Redisson Session object for Apache Tomcat
@ -37,11 +38,13 @@ public class RedissonSession extends StandardSession {
private final Map<String, Object> attrs; private final Map<String, Object> attrs;
private RMap<String, Object> map; private RMap<String, Object> map;
private final RedissonSessionManager.ReadMode readMode; private final RedissonSessionManager.ReadMode readMode;
private final UpdateMode updateMode;
public RedissonSession(RedissonSessionManager manager, RedissonSessionManager.ReadMode readMode) { public RedissonSession(RedissonSessionManager manager, RedissonSessionManager.ReadMode readMode, UpdateMode updateMode) {
super(manager); super(manager);
this.redissonManager = manager; this.redissonManager = manager;
this.readMode = readMode; this.readMode = readMode;
this.updateMode = updateMode;
try { try {
Field attr = StandardSession.class.getDeclaredField("attributes"); Field attr = StandardSession.class.getDeclaredField("attributes");
@ -149,7 +152,7 @@ public class RedissonSession extends StandardSession {
public void setAttribute(String name, Object value, boolean notify) { public void setAttribute(String name, Object value, boolean notify) {
super.setAttribute(name, value, notify); super.setAttribute(name, value, notify);
if (map != null && value != null) { if (updateMode == UpdateMode.DEFAULT && map != null && value != null) {
map.fastPut(name, value); map.fastPut(name, value);
} }
} }
@ -158,7 +161,7 @@ public class RedissonSession extends StandardSession {
protected void removeAttributeInternal(String name, boolean notify) { protected void removeAttributeInternal(String name, boolean notify) {
super.removeAttributeInternal(name, notify); super.removeAttributeInternal(name, notify);
if (map != null) { if (updateMode == UpdateMode.DEFAULT && map != null) {
map.fastRemove(name); map.fastRemove(name);
} }
} }

@ -17,7 +17,6 @@ package org.redisson.tomcat;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Enumeration;
import java.util.Map; import java.util.Map;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
@ -138,7 +137,7 @@ public class RedissonSessionManager extends ManagerBase {
@Override @Override
public Session createEmptySession() { public Session createEmptySession() {
return new RedissonSession(this, readMode); return new RedissonSession(this, readMode, updateMode);
} }
@Override @Override
@ -208,18 +207,14 @@ public class RedissonSessionManager extends ManagerBase {
} }
public void store(HttpSession session) { public void store(HttpSession session) throws IOException {
if (session == null) { if (session == null) {
return; return;
} }
if (updateMode == UpdateMode.AFTER_REQUEST) { if (updateMode == UpdateMode.AFTER_REQUEST) {
Enumeration<String> names = session.getAttributeNames(); RedissonSession sess = (RedissonSession) findSession(session.getId());
while (names.hasMoreElements()) { sess.save();
String name = names.nextElement();
Object value = session.getAttribute(name);
session.setAttribute(name, value);
}
} }
} }

@ -24,6 +24,7 @@ import java.util.concurrent.TimeUnit;
import org.apache.catalina.session.StandardSession; import org.apache.catalina.session.StandardSession;
import org.redisson.api.RMap; import org.redisson.api.RMap;
import org.redisson.tomcat.RedissonSessionManager.ReadMode; import org.redisson.tomcat.RedissonSessionManager.ReadMode;
import org.redisson.tomcat.RedissonSessionManager.UpdateMode;
/** /**
* Redisson Session object for Apache Tomcat * Redisson Session object for Apache Tomcat
@ -37,11 +38,13 @@ public class RedissonSession extends StandardSession {
private final Map<String, Object> attrs; private final Map<String, Object> attrs;
private RMap<String, Object> map; private RMap<String, Object> map;
private final RedissonSessionManager.ReadMode readMode; private final RedissonSessionManager.ReadMode readMode;
private final UpdateMode updateMode;
public RedissonSession(RedissonSessionManager manager, RedissonSessionManager.ReadMode readMode) { public RedissonSession(RedissonSessionManager manager, RedissonSessionManager.ReadMode readMode, UpdateMode updateMode) {
super(manager); super(manager);
this.redissonManager = manager; this.redissonManager = manager;
this.readMode = readMode; this.readMode = readMode;
this.updateMode = updateMode;
try { try {
Field attr = StandardSession.class.getDeclaredField("attributes"); Field attr = StandardSession.class.getDeclaredField("attributes");
@ -149,7 +152,7 @@ public class RedissonSession extends StandardSession {
public void setAttribute(String name, Object value, boolean notify) { public void setAttribute(String name, Object value, boolean notify) {
super.setAttribute(name, value, notify); super.setAttribute(name, value, notify);
if (map != null && value != null) { if (updateMode == UpdateMode.DEFAULT && map != null && value != null) {
map.fastPut(name, value); map.fastPut(name, value);
} }
} }
@ -158,7 +161,7 @@ public class RedissonSession extends StandardSession {
protected void removeAttributeInternal(String name, boolean notify) { protected void removeAttributeInternal(String name, boolean notify) {
super.removeAttributeInternal(name, notify); super.removeAttributeInternal(name, notify);
if (map != null) { if (updateMode == UpdateMode.DEFAULT && map != null) {
map.fastRemove(name); map.fastRemove(name);
} }
} }

@ -17,7 +17,6 @@ package org.redisson.tomcat;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Enumeration;
import java.util.Map; import java.util.Map;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
@ -138,7 +137,7 @@ public class RedissonSessionManager extends ManagerBase {
@Override @Override
public Session createEmptySession() { public Session createEmptySession() {
return new RedissonSession(this, readMode); return new RedissonSession(this, readMode, updateMode);
} }
@Override @Override
@ -208,18 +207,14 @@ public class RedissonSessionManager extends ManagerBase {
} }
public void store(HttpSession session) { public void store(HttpSession session) throws IOException {
if (session == null) { if (session == null) {
return; return;
} }
if (updateMode == UpdateMode.AFTER_REQUEST) { if (updateMode == UpdateMode.AFTER_REQUEST) {
Enumeration<String> names = session.getAttributeNames(); RedissonSession sess = (RedissonSession) findSession(session.getId());
while (names.hasMoreElements()) { sess.save();
String name = names.nextElement();
Object value = session.getAttribute(name);
session.setAttribute(name, value);
}
} }
} }

@ -170,7 +170,7 @@
<dependency> <dependency>
<groupId>com.esotericsoftware</groupId> <groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId> <artifactId>kryo</artifactId>
<version>3.0.3</version> <version>4.0.1</version>
<scope>provided</scope> <scope>provided</scope>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
@ -248,6 +248,13 @@
<scope>provided</scope> <scope>provided</scope>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>[3.1,5.0)</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.session</groupId> <groupId>org.springframework.session</groupId>
<artifactId>spring-session-core</artifactId> <artifactId>spring-session-core</artifactId>

@ -19,6 +19,7 @@ import java.math.BigDecimal;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.redisson.api.RBitSetAsync; import org.redisson.api.RBitSetAsync;
import org.redisson.api.RBloomFilter; import org.redisson.api.RBloomFilter;
@ -54,15 +55,18 @@ public class RedissonBloomFilter<T> extends RedissonExpirable implements RBloomF
private volatile int hashIterations; private volatile int hashIterations;
private final CommandExecutor commandExecutor; private final CommandExecutor commandExecutor;
private final String configName;
protected RedissonBloomFilter(CommandExecutor commandExecutor, String name) { protected RedissonBloomFilter(CommandExecutor commandExecutor, String name) {
super(commandExecutor, name); super(commandExecutor, name);
this.commandExecutor = commandExecutor; this.commandExecutor = commandExecutor;
this.configName = suffixName(getName(), "config");
} }
protected RedissonBloomFilter(Codec codec, CommandExecutor commandExecutor, String name) { protected RedissonBloomFilter(Codec codec, CommandExecutor commandExecutor, String name) {
super(codec, commandExecutor, name); super(codec, commandExecutor, name);
this.commandExecutor = commandExecutor; this.commandExecutor = commandExecutor;
this.configName = suffixName(getName(), "config");
} }
private int optimalNumOfHashFunctions(long n, long m) { private int optimalNumOfHashFunctions(long n, long m) {
@ -179,18 +183,18 @@ public class RedissonBloomFilter<T> extends RedissonExpirable implements RBloomF
} }
private void addConfigCheck(int hashIterations, long size, CommandBatchService executorService) { private void addConfigCheck(int hashIterations, long size, CommandBatchService executorService) {
executorService.evalReadAsync(getConfigName(), codec, RedisCommands.EVAL_VOID, executorService.evalReadAsync(configName, codec, RedisCommands.EVAL_VOID,
"local size = redis.call('hget', KEYS[1], 'size');" + "local size = redis.call('hget', KEYS[1], 'size');" +
"local hashIterations = redis.call('hget', KEYS[1], 'hashIterations');" + "local hashIterations = redis.call('hget', KEYS[1], 'hashIterations');" +
"assert(size == ARGV[1] and hashIterations == ARGV[2], 'Bloom filter config has been changed')", "assert(size == ARGV[1] and hashIterations == ARGV[2], 'Bloom filter config has been changed')",
Arrays.<Object>asList(getConfigName()), size, hashIterations); Arrays.<Object>asList(configName), size, hashIterations);
} }
@Override @Override
public long count() { public long count() {
CommandBatchService executorService = new CommandBatchService(commandExecutor.getConnectionManager()); CommandBatchService executorService = new CommandBatchService(commandExecutor.getConnectionManager());
RFuture<Map<String, String>> configFuture = executorService.readAsync(getConfigName(), StringCodec.INSTANCE, RFuture<Map<String, String>> configFuture = executorService.readAsync(configName, StringCodec.INSTANCE,
new RedisCommand<Map<Object, Object>>("HGETALL", new ObjectMapReplayDecoder()), getConfigName()); new RedisCommand<Map<Object, Object>>("HGETALL", new ObjectMapReplayDecoder()), configName);
RBitSetAsync bs = createBitSet(executorService); RBitSetAsync bs = createBitSet(executorService);
RFuture<Long> cardinalityFuture = bs.cardinalityAsync(); RFuture<Long> cardinalityFuture = bs.cardinalityAsync();
executorService.execute(); executorService.execute();
@ -202,12 +206,12 @@ public class RedissonBloomFilter<T> extends RedissonExpirable implements RBloomF
@Override @Override
public RFuture<Boolean> deleteAsync() { public RFuture<Boolean> deleteAsync() {
return commandExecutor.writeAsync(getName(), RedisCommands.DEL_OBJECTS, getName(), getConfigName()); return commandExecutor.writeAsync(getName(), RedisCommands.DEL_OBJECTS, getName(), configName);
} }
private void readConfig() { private void readConfig() {
RFuture<Map<String, String>> future = commandExecutor.readAsync(getConfigName(), StringCodec.INSTANCE, RFuture<Map<String, String>> future = commandExecutor.readAsync(configName, StringCodec.INSTANCE,
new RedisCommand<Map<Object, Object>>("HGETALL", new ObjectMapReplayDecoder()), getConfigName()); new RedisCommand<Map<Object, Object>>("HGETALL", new ObjectMapReplayDecoder()), configName);
Map<String, String> config = commandExecutor.get(future); Map<String, String> config = commandExecutor.get(future);
readConfig(config); readConfig(config);
@ -245,13 +249,13 @@ public class RedissonBloomFilter<T> extends RedissonExpirable implements RBloomF
hashIterations = optimalNumOfHashFunctions(expectedInsertions, size); hashIterations = optimalNumOfHashFunctions(expectedInsertions, size);
CommandBatchService executorService = new CommandBatchService(commandExecutor.getConnectionManager()); CommandBatchService executorService = new CommandBatchService(commandExecutor.getConnectionManager());
executorService.evalReadAsync(getConfigName(), codec, RedisCommands.EVAL_VOID, executorService.evalReadAsync(configName, codec, RedisCommands.EVAL_VOID,
"local size = redis.call('hget', KEYS[1], 'size');" + "local size = redis.call('hget', KEYS[1], 'size');" +
"local hashIterations = redis.call('hget', KEYS[1], 'hashIterations');" + "local hashIterations = redis.call('hget', KEYS[1], 'hashIterations');" +
"assert(size == false and hashIterations == false, 'Bloom filter config has been changed')", "assert(size == false and hashIterations == false, 'Bloom filter config has been changed')",
Arrays.<Object>asList(getConfigName()), size, hashIterations); Arrays.<Object>asList(configName), size, hashIterations);
executorService.writeAsync(getConfigName(), StringCodec.INSTANCE, executorService.writeAsync(configName, StringCodec.INSTANCE,
new RedisCommand<Void>("HMSET", new VoidReplayConvertor()), getConfigName(), new RedisCommand<Void>("HMSET", new VoidReplayConvertor()), configName,
"size", size, "hashIterations", hashIterations, "size", size, "hashIterations", hashIterations,
"expectedInsertions", expectedInsertions, "falseProbability", BigDecimal.valueOf(falseProbability).toPlainString()); "expectedInsertions", expectedInsertions, "falseProbability", BigDecimal.valueOf(falseProbability).toPlainString());
try { try {
@ -267,31 +271,53 @@ public class RedissonBloomFilter<T> extends RedissonExpirable implements RBloomF
return true; return true;
} }
private String getConfigName() { @Override
return suffixName(getName(), "config"); public RFuture<Boolean> expireAsync(long timeToLive, TimeUnit timeUnit) {
return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return redis.call('pexpire', KEYS[2], ARGV[1]); ",
Arrays.<Object>asList(getName(), configName),
timeUnit.toMillis(timeToLive));
}
@Override
public RFuture<Boolean> expireAtAsync(long timestamp) {
return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
"redis.call('pexpireat', KEYS[1], ARGV[1]); " +
"return redis.call('pexpireat', KEYS[2], ARGV[1]); ",
Arrays.<Object>asList(getName(), configName),
timestamp);
}
@Override
public RFuture<Boolean> clearExpireAsync() {
return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
"redis.call('persist', KEYS[1]); " +
"return redis.call('persist', KEYS[2]); ",
Arrays.<Object>asList(getName(), configName));
} }
@Override @Override
public long getExpectedInsertions() { public long getExpectedInsertions() {
Long result = commandExecutor.read(getConfigName(), LongCodec.INSTANCE, RedisCommands.HGET, getConfigName(), "expectedInsertions"); Long result = commandExecutor.read(configName, LongCodec.INSTANCE, RedisCommands.HGET, configName, "expectedInsertions");
return check(result); return check(result);
} }
@Override @Override
public double getFalseProbability() { public double getFalseProbability() {
Double result = commandExecutor.read(getConfigName(), DoubleCodec.INSTANCE, RedisCommands.HGET, getConfigName(), "falseProbability"); Double result = commandExecutor.read(configName, DoubleCodec.INSTANCE, RedisCommands.HGET, configName, "falseProbability");
return check(result); return check(result);
} }
@Override @Override
public long getSize() { public long getSize() {
Long result = commandExecutor.read(getConfigName(), LongCodec.INSTANCE, RedisCommands.HGET, getConfigName(), "size"); Long result = commandExecutor.read(configName, LongCodec.INSTANCE, RedisCommands.HGET, configName, "size");
return check(result); return check(result);
} }
@Override @Override
public int getHashIterations() { public int getHashIterations() {
Integer result = commandExecutor.read(getConfigName(), IntegerCodec.INSTANCE, RedisCommands.HGET, getConfigName(), "hashIterations"); Integer result = commandExecutor.read(configName, IntegerCodec.INSTANCE, RedisCommands.HGET, configName, "hashIterations");
return check(result); return check(result);
} }

@ -33,7 +33,6 @@ import org.redisson.command.CommandAsyncExecutor;
import org.redisson.misc.RedissonPromise; import org.redisson.misc.RedissonPromise;
import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.ThreadLocalRandom;
/** /**
* *
@ -44,9 +43,15 @@ import io.netty.util.internal.ThreadLocalRandom;
public class RedissonDelayedQueue<V> extends RedissonExpirable implements RDelayedQueue<V> { public class RedissonDelayedQueue<V> extends RedissonExpirable implements RDelayedQueue<V> {
private final QueueTransferService queueTransferService; private final QueueTransferService queueTransferService;
private final String channelName;
private final String queueName;
private final String timeoutSetName;
protected RedissonDelayedQueue(QueueTransferService queueTransferService, Codec codec, final CommandAsyncExecutor commandExecutor, String name) { protected RedissonDelayedQueue(QueueTransferService queueTransferService, Codec codec, final CommandAsyncExecutor commandExecutor, String name) {
super(codec, commandExecutor, name); super(codec, commandExecutor, name);
channelName = prefixName("redisson_delay_queue_channel", getName());
queueName = prefixName("redisson_delay_queue", getName());
timeoutSetName = prefixName("redisson_delay_queue_timeout", getName());
QueueTransferTask task = new QueueTransferTask(commandExecutor.getConnectionManager()) { QueueTransferTask task = new QueueTransferTask(commandExecutor.getConnectionManager()) {
@ -68,33 +73,21 @@ public class RedissonDelayedQueue<V> extends RedissonExpirable implements RDelay
+ "return v[2]; " + "return v[2]; "
+ "end " + "end "
+ "return nil;", + "return nil;",
Arrays.<Object>asList(getName(), getTimeoutSetName(), getQueueName()), Arrays.<Object>asList(getName(), timeoutSetName, queueName),
System.currentTimeMillis(), 100); System.currentTimeMillis(), 100);
} }
@Override @Override
protected RTopic<Long> getTopic() { protected RTopic<Long> getTopic() {
return new RedissonTopic<Long>(LongCodec.INSTANCE, commandExecutor, getChannelName()); return new RedissonTopic<Long>(LongCodec.INSTANCE, commandExecutor, channelName);
} }
}; };
queueTransferService.schedule(getQueueName(), task); queueTransferService.schedule(queueName, task);
this.queueTransferService = queueTransferService; this.queueTransferService = queueTransferService;
} }
private String getChannelName() {
return prefixName("redisson_delay_queue_channel", getName());
}
private String getQueueName() {
return prefixName("redisson_delay_queue", getName());
}
private String getTimeoutSetName() {
return prefixName("redisson_delay_queue_timeout", getName());
}
public void offer(V e, long delay, TimeUnit timeUnit) { public void offer(V e, long delay, TimeUnit timeUnit) {
get(offerAsync(e, delay, timeUnit)); get(offerAsync(e, delay, timeUnit));
} }
@ -115,7 +108,7 @@ public class RedissonDelayedQueue<V> extends RedissonExpirable implements RDelay
+ "redis.call('publish', KEYS[4], ARGV[1]); " + "redis.call('publish', KEYS[4], ARGV[1]); "
+ "end;" + "end;"
, ,
Arrays.<Object>asList(getName(), getTimeoutSetName(), getQueueName(), getChannelName()), Arrays.<Object>asList(getName(), timeoutSetName, queueName, channelName),
timeout, randomId, encode(e)); timeout, randomId, encode(e));
} }
@ -180,7 +173,7 @@ public class RedissonDelayedQueue<V> extends RedissonExpirable implements RDelay
+ "return value; " + "return value; "
+ "end " + "end "
+ "return nil;", + "return nil;",
Arrays.<Object>asList(getQueueName()), index)); Arrays.<Object>asList(queueName), index));
} }
void remove(int index) { void remove(int index) {
@ -191,7 +184,7 @@ public class RedissonDelayedQueue<V> extends RedissonExpirable implements RDelay
"redis.call('lrem', KEYS[1], 1, v);" + "redis.call('lrem', KEYS[1], 1, v);" +
"redis.call('zrem', KEYS[2], v);" + "redis.call('zrem', KEYS[2], v);" +
"end; ", "end; ",
Arrays.<Object>asList(getQueueName(), getTimeoutSetName()), index)); Arrays.<Object>asList(queueName, timeoutSetName), index));
} }
@Override @Override
@ -268,7 +261,7 @@ public class RedissonDelayedQueue<V> extends RedissonExpirable implements RDelay
+ "table.insert(result, value);" + "table.insert(result, value);"
+ "end; " + "end; "
+ "return result; ", + "return result; ",
Collections.<Object>singletonList(getQueueName())); Collections.<Object>singletonList(queueName));
} }
@Override @Override
@ -294,7 +287,7 @@ public class RedissonDelayedQueue<V> extends RedissonExpirable implements RDelay
+ "end; " + "end; "
+ "end;" + + "end;" +
"return 0;", "return 0;",
Arrays.<Object>asList(getQueueName(), getTimeoutSetName()), encode(o)); Arrays.<Object>asList(queueName, timeoutSetName), encode(o));
} }
@Override @Override
@ -316,7 +309,7 @@ public class RedissonDelayedQueue<V> extends RedissonExpirable implements RDelay
+ "end; " + "end; "
+ "end;" + + "end;" +
"return #ARGV == 0 and 1 or 0;", "return #ARGV == 0 and 1 or 0;",
Collections.<Object>singletonList(getQueueName()), encode(c).toArray()); Collections.<Object>singletonList(queueName), encode(c).toArray());
} }
@Override @Override
@ -356,7 +349,7 @@ public class RedissonDelayedQueue<V> extends RedissonExpirable implements RDelay
+ "i = i + 1;" + "i = i + 1;"
+ "end; " + "end; "
+ "return result;", + "return result;",
Arrays.<Object>asList(getQueueName(), getTimeoutSetName()), encode(c).toArray()); Arrays.<Object>asList(queueName, timeoutSetName), encode(c).toArray());
} }
@Override @Override
@ -395,7 +388,7 @@ public class RedissonDelayedQueue<V> extends RedissonExpirable implements RDelay
+ "i = i + 1; " + "i = i + 1; "
+ "end; " + "end; "
+ "return changed; ", + "return changed; ",
Collections.<Object>singletonList(getQueueName()), encode(c).toArray()); Collections.<Object>singletonList(queueName), encode(c).toArray());
} }
@Override @Override
@ -405,9 +398,36 @@ public class RedissonDelayedQueue<V> extends RedissonExpirable implements RDelay
@Override @Override
public RFuture<Boolean> deleteAsync() { public RFuture<Boolean> deleteAsync() {
return commandExecutor.writeAsync(getName(), RedisCommands.DEL_OBJECTS, getQueueName(), getTimeoutSetName()); return commandExecutor.writeAsync(getName(), RedisCommands.DEL_OBJECTS, queueName, timeoutSetName);
}
@Override
public RFuture<Boolean> expireAsync(long timeToLive, TimeUnit timeUnit) {
return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return redis.call('pexpire', KEYS[2], ARGV[1]); ",
Arrays.<Object>asList(queueName, timeoutSetName),
timeUnit.toMillis(timeToLive));
}
@Override
public RFuture<Boolean> expireAtAsync(long timestamp) {
return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
"redis.call('pexpireat', KEYS[1], ARGV[1]); " +
"return redis.call('pexpireat', KEYS[2], ARGV[1]); ",
Arrays.<Object>asList(queueName, timeoutSetName),
timestamp);
} }
@Override
public RFuture<Boolean> clearExpireAsync() {
return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
"redis.call('persist', KEYS[1]); " +
"return redis.call('persist', KEYS[2]); ",
Arrays.<Object>asList(queueName, timeoutSetName));
}
@Override @Override
public RFuture<V> peekAsync() { public RFuture<V> peekAsync() {
return commandExecutor.evalReadAsync(getName(), codec, RedisCommands.EVAL_OBJECT, return commandExecutor.evalReadAsync(getName(), codec, RedisCommands.EVAL_OBJECT,
@ -417,7 +437,7 @@ public class RedissonDelayedQueue<V> extends RedissonExpirable implements RDelay
+ "return value; " + "return value; "
+ "end " + "end "
+ "return nil;", + "return nil;",
Arrays.<Object>asList(getQueueName())); Arrays.<Object>asList(queueName));
} }
@Override @Override
@ -430,7 +450,7 @@ public class RedissonDelayedQueue<V> extends RedissonExpirable implements RDelay
+ "return value; " + "return value; "
+ "end " + "end "
+ "return nil;", + "return nil;",
Arrays.<Object>asList(getQueueName(), getTimeoutSetName())); Arrays.<Object>asList(queueName, timeoutSetName));
} }
@Override @Override
@ -449,7 +469,7 @@ public class RedissonDelayedQueue<V> extends RedissonExpirable implements RDelay
+ "return value; " + "return value; "
+ "end " + "end "
+ "return nil;", + "return nil;",
Arrays.<Object>asList(getQueueName(), getTimeoutSetName(), queueName)); Arrays.<Object>asList(this.queueName, timeoutSetName, queueName));
} }
@Override @Override
@ -464,12 +484,12 @@ public class RedissonDelayedQueue<V> extends RedissonExpirable implements RDelay
+ "end; " + "end; "
+ "end;" + + "end;" +
"return 0;", "return 0;",
Collections.<Object>singletonList(getQueueName()), encode(o)); Collections.<Object>singletonList(queueName), encode(o));
} }
@Override @Override
public RFuture<Integer> sizeAsync() { public RFuture<Integer> sizeAsync() {
return commandExecutor.readAsync(getName(), codec, RedisCommands.LLEN_INT, getQueueName()); return commandExecutor.readAsync(getName(), codec, RedisCommands.LLEN_INT, queueName);
} }
@Override @Override
@ -489,7 +509,7 @@ public class RedissonDelayedQueue<V> extends RedissonExpirable implements RDelay
@Override @Override
public void destroy() { public void destroy() {
queueTransferService.remove(getQueueName()); queueTransferService.remove(queueName);
} }
} }

@ -42,18 +42,14 @@ public class RedissonFairLock extends RedissonLock implements RLock {
private final long threadWaitTime = 5000; private final long threadWaitTime = 5000;
private final CommandExecutor commandExecutor; private final CommandExecutor commandExecutor;
private final String threadsQueueName;
private final String timeoutSetName;
protected RedissonFairLock(CommandExecutor commandExecutor, String name, UUID id) { protected RedissonFairLock(CommandExecutor commandExecutor, String name, UUID id) {
super(commandExecutor, name, id); super(commandExecutor, name, id);
this.commandExecutor = commandExecutor; this.commandExecutor = commandExecutor;
} threadsQueueName = prefixName("redisson_lock_queue", name);
timeoutSetName = prefixName("redisson_lock_timeout", name);
String getThreadsQueueName() {
return prefixName("redisson_lock_queue", getName());
}
String getTimeoutSetName() {
return prefixName("redisson_lock_timeout", getName());
} }
@Override @Override
@ -85,7 +81,7 @@ public class RedissonFairLock extends RedissonLock implements RLock {
"end;" + "end;" +
"redis.call('zrem', KEYS[2], ARGV[1]); " + "redis.call('zrem', KEYS[2], ARGV[1]); " +
"redis.call('lrem', KEYS[1], 0, ARGV[1]); ", "redis.call('lrem', KEYS[1], 0, ARGV[1]); ",
Arrays.<Object>asList(getThreadsQueueName(), getTimeoutSetName()), Arrays.<Object>asList(threadsQueueName, timeoutSetName),
getLockName(threadId), threadWaitTime); getLockName(threadId), threadWaitTime);
} }
@ -126,7 +122,7 @@ public class RedissonFairLock extends RedissonLock implements RLock {
"return nil; " + "return nil; " +
"end; " + "end; " +
"return 1;", "return 1;",
Arrays.<Object>asList(getName(), getThreadsQueueName(), getTimeoutSetName()), Arrays.<Object>asList(getName(), threadsQueueName, timeoutSetName),
internalLockLeaseTime, getLockName(threadId), currentTime); internalLockLeaseTime, getLockName(threadId), currentTime);
} }
@ -174,7 +170,7 @@ public class RedissonFairLock extends RedissonLock implements RLock {
"redis.call('rpush', KEYS[2], ARGV[2]);" + "redis.call('rpush', KEYS[2], ARGV[2]);" +
"end; " + "end; " +
"return ttl;", "return ttl;",
Arrays.<Object>asList(getName(), getThreadsQueueName(), getTimeoutSetName()), Arrays.<Object>asList(getName(), threadsQueueName, timeoutSetName),
internalLockLeaseTime, getLockName(threadId), currentTime + threadWaitTime, currentTime); internalLockLeaseTime, getLockName(threadId), currentTime + threadWaitTime, currentTime);
} }
@ -221,7 +217,7 @@ public class RedissonFairLock extends RedissonLock implements RLock {
"redis.call('publish', KEYS[4] .. ':' .. nextThreadId, ARGV[1]); " + "redis.call('publish', KEYS[4] .. ':' .. nextThreadId, ARGV[1]); " +
"end; " + "end; " +
"return 1; ", "return 1; ",
Arrays.<Object>asList(getName(), getThreadsQueueName(), getTimeoutSetName(), getChannelName()), Arrays.<Object>asList(getName(), threadsQueueName, timeoutSetName, getChannelName()),
LockPubSub.unlockMessage, internalLockLeaseTime, getLockName(threadId), System.currentTimeMillis()); LockPubSub.unlockMessage, internalLockLeaseTime, getLockName(threadId), System.currentTimeMillis());
} }
@ -232,9 +228,39 @@ public class RedissonFairLock extends RedissonLock implements RLock {
@Override @Override
public RFuture<Boolean> deleteAsync() { public RFuture<Boolean> deleteAsync() {
return commandExecutor.writeAsync(getName(), RedisCommands.DEL_OBJECTS, getName(), getThreadsQueueName(), getTimeoutSetName()); return commandExecutor.writeAsync(getName(), RedisCommands.DEL_OBJECTS, getName(), threadsQueueName, timeoutSetName);
}
@Override
public RFuture<Boolean> expireAsync(long timeToLive, TimeUnit timeUnit) {
return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"redis.call('pexpire', KEYS[2], ARGV[1]); " +
"return redis.call('pexpire', KEYS[3], ARGV[1]); ",
Arrays.<Object>asList(getName(), threadsQueueName, timeoutSetName),
timeUnit.toMillis(timeToLive));
} }
@Override
public RFuture<Boolean> expireAtAsync(long timestamp) {
return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
"redis.call('pexpireat', KEYS[1], ARGV[1]); " +
"redis.call('pexpireat', KEYS[2], ARGV[1]); " +
"return redis.call('pexpireat', KEYS[3], ARGV[1]); ",
Arrays.<Object>asList(getName(), threadsQueueName, timeoutSetName),
timestamp);
}
@Override
public RFuture<Boolean> clearExpireAsync() {
return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
"redis.call('persist', KEYS[1]); " +
"redis.call('persist', KEYS[2]); " +
"return redis.call('persist', KEYS[3]); ",
Arrays.<Object>asList(getName(), threadsQueueName, timeoutSetName));
}
@Override @Override
public RFuture<Boolean> forceUnlockAsync() { public RFuture<Boolean> forceUnlockAsync() {
cancelExpirationRenewal(); cancelExpirationRenewal();
@ -263,7 +289,7 @@ public class RedissonFairLock extends RedissonLock implements RLock {
"return 1; " + "return 1; " +
"end; " + "end; " +
"return 0;", "return 0;",
Arrays.<Object>asList(getName(), getThreadsQueueName(), getTimeoutSetName(), getChannelName()), Arrays.<Object>asList(getName(), threadsQueueName, timeoutSetName, getChannelName()),
LockPubSub.unlockMessage, System.currentTimeMillis()); LockPubSub.unlockMessage, System.currentTimeMillis());
} }

@ -180,8 +180,8 @@ public class RedissonLocalCachedMap<K, V> extends RedissonMap<K, V> implements R
private RTopic<Object> invalidationTopic; private RTopic<Object> invalidationTopic;
private Cache<CacheKey, CacheValue> cache; private Cache<CacheKey, CacheValue> cache;
private int invalidateEntryOnChange; private int invalidateEntryOnChange;
private int invalidationListenerId; private int syncListenerId;
private int invalidationStatusListenerId; private int reconnectionListenerId;
private volatile long lastInvalidate; private volatile long lastInvalidate;
private SyncStrategy syncStrategy; private SyncStrategy syncStrategy;
private final Codec topicCodec = new LocalCachedMessageCodec(); private final Codec topicCodec = new LocalCachedMessageCodec();
@ -218,7 +218,7 @@ public class RedissonLocalCachedMap<K, V> extends RedissonMap<K, V> implements R
invalidationTopic = new RedissonTopic<Object>(topicCodec, commandExecutor, suffixName(name, "topic")); invalidationTopic = new RedissonTopic<Object>(topicCodec, commandExecutor, suffixName(name, "topic"));
if (options.getReconnectionStrategy() != ReconnectionStrategy.NONE) { if (options.getReconnectionStrategy() != ReconnectionStrategy.NONE) {
invalidationStatusListenerId = invalidationTopic.addListener(new BaseStatusListener() { reconnectionListenerId = invalidationTopic.addListener(new BaseStatusListener() {
@Override @Override
public void onSubscribe(String channel) { public void onSubscribe(String channel) {
if (options.getReconnectionStrategy() == ReconnectionStrategy.CLEAR) { if (options.getReconnectionStrategy() == ReconnectionStrategy.CLEAR) {
@ -272,7 +272,7 @@ public class RedissonLocalCachedMap<K, V> extends RedissonMap<K, V> implements R
} }
if (options.getSyncStrategy() != SyncStrategy.NONE) { if (options.getSyncStrategy() != SyncStrategy.NONE) {
invalidationListenerId = invalidationTopic.addListener(new MessageListener<Object>() { syncListenerId = invalidationTopic.addListener(new MessageListener<Object>() {
@Override @Override
public void onMessage(String channel, Object msg) { public void onMessage(String channel, Object msg) {
if (msg instanceof LocalCachedMapClear) { if (msg instanceof LocalCachedMapClear) {
@ -481,11 +481,11 @@ public class RedissonLocalCachedMap<K, V> extends RedissonMap<K, V> implements R
@Override @Override
public void destroy() { public void destroy() {
if (invalidationListenerId != 0) { if (syncListenerId != 0) {
invalidationTopic.removeListener(invalidationListenerId); invalidationTopic.removeListener(syncListenerId);
} }
if (invalidationStatusListenerId != 0) { if (reconnectionListenerId != 0) {
invalidationTopic.removeListener(invalidationStatusListenerId); invalidationTopic.removeListener(reconnectionListenerId);
} }
} }

@ -971,6 +971,47 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
return future; return future;
} }
@Override
public long remainTimeToLive(K key) {
return get(remainTimeToLiveAsync(key));
}
@Override
public RFuture<Long> remainTimeToLiveAsync(K key) {
checkKey(key);
return commandExecutor.evalWriteAsync(getName(key), codec, RedisCommands.EVAL_LONG,
"local value = redis.call('hget', KEYS[1], ARGV[2]); "
+ "if value == false then "
+ "return -2; "
+ "end; "
+ "local t, val = struct.unpack('dLc0', value); "
+ "local expireDate = 92233720368547758; " +
"local expireDateScore = redis.call('zscore', KEYS[2], ARGV[2]); "
+ "if expireDateScore ~= false then "
+ "expireDate = tonumber(expireDateScore) "
+ "end; "
+ "if t ~= 0 then "
+ "local expireIdle = redis.call('zscore', KEYS[3], ARGV[2]); "
+ "if expireIdle ~= false then "
+ "expireDate = math.min(expireDate, tonumber(expireIdle)) "
+ "end; "
+ "end; "
+ "if expireDate == 92233720368547758 then "
+ "return -1; "
+ "end;"
+ "if expireDate > tonumber(ARGV[1]) then "
+ "return ARGV[1] - expireDate; "
+ "else "
+ "return -2; "
+ "end; "
+ "return val; ",
Arrays.<Object>asList(getName(key), getTimeoutSetNameByKey(key), getIdleSetNameByKey(key)),
System.currentTimeMillis(), encodeMapKey(key));
}
String getTimeoutSetNameByKey(Object key) { String getTimeoutSetNameByKey(Object key) {
return prefixName("redisson__timeout__set", getName(key)); return prefixName("redisson__timeout__set", getName(key));
} }

@ -53,7 +53,7 @@ public class RedissonPermitExpirableSemaphore extends RedissonExpirable implemen
protected RedissonPermitExpirableSemaphore(CommandExecutor commandExecutor, String name, SemaphorePubSub semaphorePubSub) { protected RedissonPermitExpirableSemaphore(CommandExecutor commandExecutor, String name, SemaphorePubSub semaphorePubSub) {
super(commandExecutor, name); super(commandExecutor, name);
this.timeoutName = "{" + name + "}:timeout"; this.timeoutName = suffixName(name, "timeout");
this.commandExecutor = commandExecutor; this.commandExecutor = commandExecutor;
this.semaphorePubSub = semaphorePubSub; this.semaphorePubSub = semaphorePubSub;
} }
@ -635,6 +635,32 @@ public class RedissonPermitExpirableSemaphore extends RedissonExpirable implemen
return commandExecutor.writeAsync(getName(), RedisCommands.DEL_OBJECTS, getName(), timeoutName); return commandExecutor.writeAsync(getName(), RedisCommands.DEL_OBJECTS, getName(), timeoutName);
} }
@Override
public RFuture<Boolean> expireAsync(long timeToLive, TimeUnit timeUnit) {
return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return redis.call('pexpire', KEYS[2], ARGV[1]); ",
Arrays.<Object>asList(getName(), timeoutName),
timeUnit.toMillis(timeToLive));
}
@Override
public RFuture<Boolean> expireAtAsync(long timestamp) {
return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
"redis.call('pexpireat', KEYS[1], ARGV[1]); " +
"return redis.call('pexpireat', KEYS[2], ARGV[1]); ",
Arrays.<Object>asList(getName(), timeoutName),
timestamp);
}
@Override
public RFuture<Boolean> clearExpireAsync() {
return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
"redis.call('persist', KEYS[1]); " +
"return redis.call('persist', KEYS[2]); ",
Arrays.<Object>asList(getName(), timeoutName));
}
@Override @Override
public RFuture<Void> releaseAsync(final String permitId) { public RFuture<Void> releaseAsync(final String permitId) {
final RPromise<Void> result = new RedissonPromise<Void>(); final RPromise<Void> result = new RedissonPromise<Void>();

@ -64,9 +64,11 @@ public interface RExpirableAsync extends RObjectAsync {
RFuture<Boolean> clearExpireAsync(); RFuture<Boolean> clearExpireAsync();
/** /**
* Get remaining time to live of object in seconds. * Remaining time to live of Redisson object that has a timeout
* *
* @return <code>-1</code> if object does not exist or time in seconds * @return time in milliseconds
* -2 if the key does not exist.
* -1 if the key exists but has no associated expire.
*/ */
RFuture<Long> remainTimeToLiveAsync(); RFuture<Long> remainTimeToLiveAsync();

@ -261,4 +261,13 @@ public interface RMapCache<K, V> extends RMap<K, V>, RMapCacheAsync<K, V> {
*/ */
void removeListener(int listenerId); void removeListener(int listenerId);
/**
* Remaining time to live of map entry associated with a <code>key</code>.
*
* @return time in milliseconds
* -2 if the key does not exist.
* -1 if the key exists but has no associated expire.
*/
long remainTimeToLive(K key);
} }

@ -220,4 +220,14 @@ public interface RMapCacheAsync<K, V> extends RMapAsync<K, V> {
*/ */
@Override @Override
RFuture<Integer> sizeAsync(); RFuture<Integer> sizeAsync();
/**
* Remaining time to live of map entry associated with a <code>key</code>.
*
* @return time in milliseconds
* -2 if the key does not exist.
* -1 if the key exists but has no associated expire.
*/
RFuture<Long> remainTimeToLiveAsync(K key);
} }

@ -136,7 +136,14 @@ public class DNSMonitor {
log.info("Detected DNS change. Slave {} has changed ip from {} to {}", log.info("Detected DNS change. Slave {} has changed ip from {} to {}",
entry.getKey().getHost(), currentSlaveAddr.getAddress().getHostAddress(), newSlaveAddr.getAddress().getHostAddress()); entry.getKey().getHost(), currentSlaveAddr.getAddress().getHostAddress(), newSlaveAddr.getAddress().getHostAddress());
for (final MasterSlaveEntry masterSlaveEntry : connectionManager.getEntrySet()) { for (final MasterSlaveEntry masterSlaveEntry : connectionManager.getEntrySet()) {
if (masterSlaveEntry.hasSlave(currentSlaveAddr)) { if (!masterSlaveEntry.hasSlave(currentSlaveAddr)) {
continue;
}
if (masterSlaveEntry.hasSlave(newSlaveAddr)) {
masterSlaveEntry.slaveUp(newSlaveAddr, FreezeReason.MANAGER);
masterSlaveEntry.slaveDown(currentSlaveAddr, FreezeReason.MANAGER);
} else {
RFuture<Void> addFuture = masterSlaveEntry.addSlave(newSlaveAddr, entry.getKey()); RFuture<Void> addFuture = masterSlaveEntry.addSlave(newSlaveAddr, entry.getKey());
addFuture.addListener(new FutureListener<Void>() { addFuture.addListener(new FutureListener<Void>() {
@Override @Override
@ -149,8 +156,8 @@ public class DNSMonitor {
masterSlaveEntry.slaveDown(currentSlaveAddr, FreezeReason.MANAGER); masterSlaveEntry.slaveDown(currentSlaveAddr, FreezeReason.MANAGER);
} }
}); });
break;
} }
break;
} }
slaves.put(entry.getKey(), newSlaveAddr); slaves.put(entry.getKey(), newSlaveAddr);
} }

@ -428,7 +428,7 @@ public class MasterSlaveEntry {
// exclude master from slaves // exclude master from slaves
if (!config.checkSkipSlavesInit() if (!config.checkSkipSlavesInit()
&& !addr.equals(entry.getClient().getAddr())) { && !addr.equals(entry.getClient().getAddr())) {
slaveDown(masterEntry.getClient().getAddr(), FreezeReason.SYSTEM); slaveDown(addr, FreezeReason.SYSTEM);
log.info("master {} excluded from slaves", addr); log.info("master {} excluded from slaves", addr);
} }
return true; return true;
@ -443,12 +443,28 @@ public class MasterSlaveEntry {
// exclude master from slaves // exclude master from slaves
if (!config.checkSkipSlavesInit() if (!config.checkSkipSlavesInit()
&& !URIBuilder.compare(addr, address)) { && !URIBuilder.compare(addr, address)) {
slaveDown(masterEntry.getClient().getAddr(), FreezeReason.SYSTEM); slaveDown(addr, FreezeReason.SYSTEM);
log.info("master {} excluded from slaves", addr); log.info("master {} excluded from slaves", addr);
} }
return true; return true;
} }
public boolean slaveUp(InetSocketAddress address, FreezeReason freezeReason) {
if (!slaveBalancer.unfreeze(address, freezeReason)) {
return false;
}
InetSocketAddress addr = masterEntry.getClient().getAddr();
// exclude master from slaves
if (!config.checkSkipSlavesInit()
&& !addr.equals(address)) {
slaveDown(addr, FreezeReason.SYSTEM);
log.info("master {} excluded from slaves", addr);
}
return true;
}
/** /**
* Freeze slave with <code>redis(s)://host:port</code> from slaves list. * Freeze slave with <code>redis(s)://host:port</code> from slaves list.
* Re-attach pub/sub listeners from it to other slave. * Re-attach pub/sub listeners from it to other slave.

@ -16,7 +16,6 @@
package org.redisson.spring.cache; package org.redisson.spring.cache;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
@ -47,10 +46,9 @@ public class RedissonCache implements Cache {
private final AtomicLong misses = new AtomicLong(); private final AtomicLong misses = new AtomicLong();
public RedissonCache(RMapCache<Object, Object> mapCache, CacheConfig config, boolean allowNullValues) { public RedissonCache(RMapCache<Object, Object> mapCache, CacheConfig config, boolean allowNullValues) {
this(mapCache, allowNullValues);
this.mapCache = mapCache; this.mapCache = mapCache;
this.map = mapCache;
this.config = config; this.config = config;
this.allowNullValues = allowNullValues;
} }
public RedissonCache(RMap<Object, Object> map, boolean allowNullValues) { public RedissonCache(RMap<Object, Object> map, boolean allowNullValues) {
@ -98,11 +96,7 @@ public class RedissonCache implements Cache {
@Override @Override
public void put(Object key, Object value) { public void put(Object key, Object value) {
if (!allowNullValues && value == null) { if (!allowNullValues && value == null) {
if (mapCache != null) {
mapCache.remove(key);
} else {
map.remove(key); map.remove(key);
}
return; return;
} }
@ -117,11 +111,7 @@ public class RedissonCache implements Cache {
public ValueWrapper putIfAbsent(Object key, Object value) { public ValueWrapper putIfAbsent(Object key, Object value) {
Object prevValue; Object prevValue;
if (!allowNullValues && value == null) { if (!allowNullValues && value == null) {
if (mapCache != null) {
prevValue = mapCache.get(key);
} else {
prevValue = map.get(key); prevValue = map.get(key);
}
} else { } else {
value = toStoreValue(value); value = toStoreValue(value);
if (mapCache != null) { if (mapCache != null) {

@ -57,6 +57,19 @@ public class RedissonMapCacheTest extends BaseMapTest {
return redisson.getMapCache("test", options); return redisson.getMapCache("test", options);
} }
@Test
public void testRemainTimeToLive() {
RMapCache<String, String> map = redisson.getMapCache("test");
map.put("1", "2", 2, TimeUnit.SECONDS);
assertThat(map.remainTimeToLive("1")).isLessThan(1900);
map.put("3", "4");
assertThat(map.remainTimeToLive("3")).isEqualTo(-1);
assertThat(map.remainTimeToLive("0")).isEqualTo(-2);
map.put("5", "6", 20, TimeUnit.SECONDS, 10, TimeUnit.SECONDS);
assertThat(map.remainTimeToLive("1")).isLessThan(9900);
}
@Test @Test
public void testWriterPutIfAbsentTTL() { public void testWriterPutIfAbsentTTL() {
Map<String, String> store = new HashMap<>(); Map<String, String> store = new HashMap<>();

Loading…
Cancel
Save