IGNITE-5912: Redis EXPIRE/PEXPIRE commands. - Fixes #2383. Signed-off-by: shroman <rsht...@yahoo.com>
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/988ffe3e Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/988ffe3e Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/988ffe3e Branch: refs/heads/ignite-5872 Commit: 988ffe3e131792072e743e103f524ec20394f8e0 Parents: aa81dd1 Author: shroman <rsht...@yahoo.com> Authored: Thu Aug 10 09:51:05 2017 +0900 Committer: shroman <rsht...@yahoo.com> Committed: Thu Aug 10 09:51:05 2017 +0900 ---------------------------------------------------------------------- .../tcp/redis/RedisProtocolStringSelfTest.java | 44 ++++++++ .../processors/rest/GridRestCommand.java | 3 + .../handlers/cache/GridCacheCommandHandler.java | 75 +++++++++++++- .../key/GridRedisExpireCommandHandler.java | 101 +++++++++++++++++++ .../protocols/tcp/redis/GridRedisCommand.java | 4 + .../tcp/redis/GridRedisNioListener.java | 2 + 6 files changed, 228 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/988ffe3e/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/RedisProtocolStringSelfTest.java ---------------------------------------------------------------------- diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/RedisProtocolStringSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/RedisProtocolStringSelfTest.java index 68b42c4..21a9882 100644 --- a/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/RedisProtocolStringSelfTest.java +++ b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/RedisProtocolStringSelfTest.java @@ -423,4 +423,48 @@ public class RedisProtocolStringSelfTest extends RedisCommonAbstractTest { Assert.assertEquals(2, (long)jedis.exists("existsKey1", "existsKey2")); } } + + /** + * @throws Exception If failed. + */ + public void testExpire() throws Exception { + testExpire(new Expiration() { + @Override public long expire(Jedis jedis, String key) { + return jedis.expire("k1", 2); + } + }); + } + + /** + * @throws Exception If failed. + */ + public void testExpireMs() throws Exception { + testExpire(new Expiration() { + @Override public long expire(Jedis jedis, String key) { + return jedis.pexpire("k1", 2000); + } + }); + } + + private void testExpire(Expiration exp) throws Exception { + try (Jedis jedis = pool.getResource()) { + jedis.set("k1", "v1"); + + Assert.assertTrue(jedis.exists("k1")); + + Assert.assertEquals(1L, exp.expire(jedis, "k1")); + + Assert.assertEquals("v1", jedis.get("k1")); + + Thread.sleep(2100); + + Assert.assertFalse(jedis.exists("k1")); + + Assert.assertEquals(0L, (long)jedis.expire("k1", 2)); + } + } + + private interface Expiration { + long expire(Jedis jedis, String key); + } } http://git-wip-us.apache.org/repos/asf/ignite/blob/988ffe3e/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/GridRestCommand.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/GridRestCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/GridRestCommand.java index 2ed370d..24b4bda 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/GridRestCommand.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/GridRestCommand.java @@ -99,6 +99,9 @@ public enum GridRestCommand { /** Cache size. */ CACHE_SIZE("size"), + /** Set TTL for the key. */ + CACHE_UPDATE_TLL("updatettl"), + /** Cache metadata. */ CACHE_METADATA("metadata"), http://git-wip-us.apache.org/repos/asf/ignite/blob/988ffe3e/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/cache/GridCacheCommandHandler.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/cache/GridCacheCommandHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/cache/GridCacheCommandHandler.java index f2ca071..53342c9 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/cache/GridCacheCommandHandler.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/cache/GridCacheCommandHandler.java @@ -52,7 +52,10 @@ import org.apache.ignite.internal.IgniteEx; import org.apache.ignite.internal.IgniteInternalFuture; import org.apache.ignite.internal.IgniteKernal; import org.apache.ignite.internal.managers.discovery.GridDiscoveryManager; +import org.apache.ignite.internal.processors.cache.CacheInvokeEntry; import org.apache.ignite.internal.processors.cache.GridCacheAdapter; +import org.apache.ignite.internal.processors.cache.GridCacheEntryEx; +import org.apache.ignite.internal.processors.cache.GridCacheEntryRemovedException; import org.apache.ignite.internal.processors.cache.IgniteInternalCache; import org.apache.ignite.internal.processors.cache.query.GridCacheSqlMetadata; import org.apache.ignite.internal.processors.rest.GridRestCommand; @@ -83,6 +86,7 @@ import static org.apache.ignite.internal.processors.rest.GridRestCommand.CACHE_C import static org.apache.ignite.internal.processors.rest.GridRestCommand.CACHE_CLEAR; import static org.apache.ignite.internal.processors.rest.GridRestCommand.CACHE_CONTAINS_KEY; import static org.apache.ignite.internal.processors.rest.GridRestCommand.CACHE_CONTAINS_KEYS; +import static org.apache.ignite.internal.processors.rest.GridRestCommand.CACHE_UPDATE_TLL; import static org.apache.ignite.internal.processors.rest.GridRestCommand.CACHE_GET; import static org.apache.ignite.internal.processors.rest.GridRestCommand.CACHE_GET_ALL; import static org.apache.ignite.internal.processors.rest.GridRestCommand.CACHE_GET_AND_PUT; @@ -136,6 +140,7 @@ public class GridCacheCommandHandler extends GridRestCommandHandlerAdapter { CACHE_PREPEND, CACHE_METRICS, CACHE_SIZE, + CACHE_UPDATE_TLL, CACHE_METADATA ); @@ -158,7 +163,8 @@ public class GridCacheCommandHandler extends GridRestCommandHandlerAdapter { ATOMIC_DECREMENT, CACHE_CAS, CACHE_APPEND, - CACHE_PREPEND + CACHE_PREPEND, + CACHE_UPDATE_TLL ); /** @@ -634,6 +640,15 @@ public class GridCacheCommandHandler extends GridRestCommandHandlerAdapter { break; } + case CACHE_UPDATE_TLL: { + if (ttl == null) + throw new IgniteCheckedException(GridRestCommandHandlerAdapter.missingParameter("ttl")); + + fut = executeCommand(req.destinationId(), req.clientId(), cacheName, key, new UpdateTllCommand(key, ttl)); + + break; + } + default: throw new IllegalArgumentException("Invalid command for cache handler: " + req); } @@ -1621,4 +1636,62 @@ public class GridCacheCommandHandler extends GridRestCommandHandlerAdapter { return c.sizeAsync(new CachePeekMode[] {CachePeekMode.PRIMARY}); } } + + /** Update TTL on key. */ + private static class UpdateTllCommand extends CacheCommand { + /** */ + private static final long serialVersionUID = 0L; + + /** */ + private final Object key; + + /** */ + private final Long ttl; + + /** + * @param key Key. + * @param ttl TTL. + */ + UpdateTllCommand(Object key, Long ttl) { + this.key = key; + this.ttl = ttl; + } + + /** {@inheritDoc} */ + @Override public IgniteInternalFuture<?> applyx(final IgniteInternalCache<Object, Object> c, + GridKernalContext ctx) { + assert c != null; + + return ctx.closure().callLocalSafe(new Callable<Object>() { + @Override public Object call() throws Exception { + EntryProcessorResult<Boolean> res = c.invoke(key, new EntryProcessor<Object, Object, Boolean>() { + @Override + public Boolean process(MutableEntry<Object, Object> entry, + Object... objects) throws EntryProcessorException { + GridCacheEntryEx ex = ((CacheInvokeEntry)entry).entry(); + + if (entry.getValue() == null) + return false; + + try { + ex.updateTtl(ex.version(), ttl); + } + catch (GridCacheEntryRemovedException e) { + throw new EntryProcessorException(e.getCause()); + } + + return true; + } + }); + + try { + return res.get(); + } + catch (EntryProcessorException e) { + throw new IgniteCheckedException(e.getCause()); + } + } + }, false); + } + } } http://git-wip-us.apache.org/repos/asf/ignite/blob/988ffe3e/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/redis/key/GridRedisExpireCommandHandler.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/redis/key/GridRedisExpireCommandHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/redis/key/GridRedisExpireCommandHandler.java new file mode 100644 index 0000000..70ca504 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/redis/key/GridRedisExpireCommandHandler.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.internal.processors.rest.handlers.redis.key; + +import java.nio.ByteBuffer; +import java.util.Collection; +import java.util.List; +import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.IgniteLogger; +import org.apache.ignite.internal.processors.rest.GridRestProtocolHandler; +import org.apache.ignite.internal.processors.rest.GridRestResponse; +import org.apache.ignite.internal.processors.rest.handlers.redis.GridRedisRestCommandHandler; +import org.apache.ignite.internal.processors.rest.handlers.redis.exception.GridRedisGenericException; +import org.apache.ignite.internal.processors.rest.protocols.tcp.redis.GridRedisCommand; +import org.apache.ignite.internal.processors.rest.protocols.tcp.redis.GridRedisMessage; +import org.apache.ignite.internal.processors.rest.protocols.tcp.redis.GridRedisProtocolParser; +import org.apache.ignite.internal.processors.rest.request.GridRestCacheRequest; +import org.apache.ignite.internal.processors.rest.request.GridRestRequest; +import org.apache.ignite.internal.util.typedef.internal.U; + +import static org.apache.ignite.internal.processors.rest.GridRestCommand.CACHE_UPDATE_TLL; +import static org.apache.ignite.internal.processors.rest.protocols.tcp.redis.GridRedisCommand.EXPIRE; +import static org.apache.ignite.internal.processors.rest.protocols.tcp.redis.GridRedisCommand.PEXPIRE; + +/** + * Redis EXPIRE/PEXPIRE command handler. + */ +public class GridRedisExpireCommandHandler extends GridRedisRestCommandHandler { + /** Supported commands. */ + private static final Collection<GridRedisCommand> SUPPORTED_COMMANDS = U.sealList( + EXPIRE, + PEXPIRE + ); + + /** TTL position in Redis message. */ + private static final int TTL_POS = 2; + + /** + * Handler constructor. + * + * @param log Logger to use. + * @param hnd Rest handler. + */ + public GridRedisExpireCommandHandler(final IgniteLogger log, final GridRestProtocolHandler hnd) { + super(log, hnd); + } + + /** {@inheritDoc} */ + @Override public Collection<GridRedisCommand> supportedCommands() { + return SUPPORTED_COMMANDS; + } + + /** {@inheritDoc} */ + @Override public GridRestRequest asRestRequest(GridRedisMessage msg) throws IgniteCheckedException { + assert msg != null; + + if (msg.messageSize() < 2) + throw new GridRedisGenericException("Wrong number of arguments (key is missing)"); + else if (msg.messageSize() < 3) + throw new GridRedisGenericException("Wrong number of arguments (timeout value is missing)"); + + GridRestCacheRequest restReq = new GridRestCacheRequest(); + + restReq.clientId(msg.clientId()); + restReq.key(msg.key()); + restReq.command(CACHE_UPDATE_TLL); + restReq.cacheName(msg.cacheName()); + + switch (msg.command()) { + case EXPIRE: + restReq.ttl(Long.valueOf(msg.aux(TTL_POS)) * 1000); + break; + default: + // PEXPIRE + restReq.ttl(Long.valueOf(msg.aux(TTL_POS))); + } + + return restReq; + } + + /** {@inheritDoc} */ + @Override public ByteBuffer makeResponse(final GridRestResponse restRes, List<String> params) { + return ((Boolean)restRes.getResponse() == true ? GridRedisProtocolParser.toInteger("1") + : GridRedisProtocolParser.toInteger("0")); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/988ffe3e/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisCommand.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisCommand.java index bc3b9a2..bc32fb4 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisCommand.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisCommand.java @@ -67,6 +67,10 @@ public enum GridRedisCommand { DEL("DEL"), /** EXISTS. */ EXISTS("EXISTS"), + /** EXPIRE. */ + EXPIRE("EXPIRE"), + /** PEXPIRE. */ + PEXPIRE("PEXPIRE"), // Server commands. /** DBSIZE. */ http://git-wip-us.apache.org/repos/asf/ignite/blob/988ffe3e/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisNioListener.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisNioListener.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisNioListener.java index 9436369..bc2daf2 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisNioListener.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisNioListener.java @@ -28,6 +28,7 @@ import org.apache.ignite.internal.processors.rest.handlers.redis.GridRedisComman import org.apache.ignite.internal.processors.rest.handlers.redis.GridRedisConnectionCommandHandler; import org.apache.ignite.internal.processors.rest.handlers.redis.key.GridRedisDelCommandHandler; import org.apache.ignite.internal.processors.rest.handlers.redis.key.GridRedisExistsCommandHandler; +import org.apache.ignite.internal.processors.rest.handlers.redis.key.GridRedisExpireCommandHandler; import org.apache.ignite.internal.processors.rest.handlers.redis.server.GridRedisDbSizeCommandHandler; import org.apache.ignite.internal.processors.rest.handlers.redis.server.GridRedisFlushCommandHandler; import org.apache.ignite.internal.processors.rest.handlers.redis.string.GridRedisAppendCommandHandler; @@ -87,6 +88,7 @@ public class GridRedisNioListener extends GridNioServerListenerAdapter<GridRedis // key commands. addCommandHandler(new GridRedisDelCommandHandler(log, hnd)); addCommandHandler(new GridRedisExistsCommandHandler(log, hnd)); + addCommandHandler(new GridRedisExpireCommandHandler(log, hnd)); // server commands. addCommandHandler(new GridRedisDbSizeCommandHandler(log, hnd));