diff --git a/src/main/java/net/spy/memcached/ArcusClientPool.java b/src/main/java/net/spy/memcached/ArcusClientPool.java index 660e2829c..6e97a1641 100644 --- a/src/main/java/net/spy/memcached/ArcusClientPool.java +++ b/src/main/java/net/spy/memcached/ArcusClientPool.java @@ -247,6 +247,26 @@ public GetFuture> asyncGets(String key) { return this.getClient().asyncGets(key); } + @Override + public GetFuture asyncGetAndTouch(String key, int exp, Transcoder tc) { + return this.getClient().asyncGetAndTouch(key, exp, tc); + } + + @Override + public GetFuture asyncGetAndTouch(String key, int exp) { + return this.getClient().asyncGetAndTouch(key, exp); + } + + @Override + public GetFuture> asyncGetsAndTouch(String key, int exp, Transcoder tc) { + return this.getClient().asyncGetsAndTouch(key, exp, tc); + } + + @Override + public GetFuture> asyncGetsAndTouch(String key, int exp) { + return this.getClient().asyncGetsAndTouch(key, exp); + } + @Override public CASValue gets(String key, Transcoder tc) throws OperationTimeoutException { diff --git a/src/main/java/net/spy/memcached/MemcachedClient.java b/src/main/java/net/spy/memcached/MemcachedClient.java index 0b0284a63..05a827905 100644 --- a/src/main/java/net/spy/memcached/MemcachedClient.java +++ b/src/main/java/net/spy/memcached/MemcachedClient.java @@ -980,6 +980,106 @@ public GetFuture> asyncGets(final String key) { return asyncGets(key, transcoder); } + /** + * Get the given key to reset its expiration time asynchronously. + * + * @param key the key to fetch + * @param exp the new expiration to set for the given key + * @param tc the transcoder to serialize and unserialize value + * @return a future that will hold the return value of the fetch + * @throws IllegalStateException in the rare circumstance where queue is too + * full to accept any more requests + */ + public GetFuture asyncGetAndTouch(final String key, final int exp, + final Transcoder tc) { + final CountDownLatch latch = new CountDownLatch(1); + final GetFuture future = new GetFuture<>(latch, operationTimeout); + + Operation op = opFact.getAndTouch(key, exp, + new GetOperation.Callback() { + private GetResult result = null; + + public void receivedStatus(OperationStatus status) { + future.set(result, status); + } + + public void gotData(String k, int flags, byte[] data) { + assert k.equals(key) : "Wrong key returned"; + result = new GetResultImpl<>(new CachedData(flags, data, tc.getMaxSize()), tc); + } + + public void complete() { + latch.countDown(); + } + }); + future.setOperation(op); + addOp(key, op); + return future; + } + + /** + * Get the given key to reset its expiration time asynchronously. + * + * @param key the key to fetch + * @param exp the new expiration to set for the given key + * @return a future that will hold the return value of the fetch + * @throws IllegalStateException in the rare circumstance where queue is too + * full to accept any more requests + */ + public GetFuture asyncGetAndTouch(final String key, final int exp) { + return asyncGetAndTouch(key, exp, transcoder); + } + + /** + * Gets (with CAS support) the given key to reset its expiration time asynchronously. + * + * @param key the key to fetch + * @param exp the new expiration to set for the given key + * @param tc the transcoder to serialize and unserialize value + * @return a future that will hold the return value of the fetch + * @throws IllegalStateException in the rare circumstance where queue is too + * full to accept any more requests + */ + public GetFuture> asyncGetsAndTouch(final String key, final int exp, + final Transcoder tc) { + final CountDownLatch latch = new CountDownLatch(1); + final GetFuture> rv = new GetFuture<>(latch, operationTimeout); + + Operation op = opFact.getsAndTouch(key, exp, new GetsOperation.Callback() { + private GetResult> val = null; + + public void receivedStatus(OperationStatus status) { + rv.set(val, status); + } + + public void gotData(String k, int flags, long cas, byte[] data) { + assert key.equals(k) : "Wrong key returned"; + assert cas > 0 : "CAS was less than zero: " + cas; + val = new GetsResultImpl<>(cas, new CachedData(flags, data, tc.getMaxSize()), tc); + } + + public void complete() { + latch.countDown(); + } + }); + rv.setOperation(op); + addOp(key, op); + return rv; + } + + /** + * Gets (with CAS support) the given key to reset its expiration time asynchronously. + * + * @param key the key to fetch + * @param exp the new expiration to set for the given key + * @return a future that will hold the return value of the fetch + * @throws IllegalStateException in the rare circumstance where queue is too + * full to accept any more requests + */ + public GetFuture> asyncGetsAndTouch(final String key, final int exp) { + return asyncGetsAndTouch(key, exp, transcoder); + } + /** * Gets (with CAS support) with a single key. * diff --git a/src/main/java/net/spy/memcached/MemcachedClientIF.java b/src/main/java/net/spy/memcached/MemcachedClientIF.java index fc96753c6..e5e8cb4d5 100644 --- a/src/main/java/net/spy/memcached/MemcachedClientIF.java +++ b/src/main/java/net/spy/memcached/MemcachedClientIF.java @@ -82,6 +82,15 @@ Future> asyncGets(String key, Future> asyncGets(String key); + Future asyncGetAndTouch(final String key, final int exp, final Transcoder tc); + + Future asyncGetAndTouch(final String key, final int exp); + + Future> asyncGetsAndTouch(final String key, final int exp, + final Transcoder tc); + + Future> asyncGetsAndTouch(final String key, final int exp); + CASValue gets(String key, Transcoder tc) throws OperationTimeoutException; diff --git a/src/main/java/net/spy/memcached/OperationFactory.java b/src/main/java/net/spy/memcached/OperationFactory.java index 296f251a4..3abbd4032 100644 --- a/src/main/java/net/spy/memcached/OperationFactory.java +++ b/src/main/java/net/spy/memcached/OperationFactory.java @@ -152,6 +152,26 @@ public interface OperationFactory { */ GetsOperation gets(Collection keys, GetsOperation.Callback cb, boolean isMGet); + /** + * Get the key and resets its timeout. + * + * @param key the key to get a value for and reset its timeout + * @param expiration the new expiration for the key + * @param cb the callback that will contain the result + * @return a new GetAndTouchOperation + */ + GetOperation getAndTouch(String key, int expiration, GetOperation.Callback cb); + + /** + * Gets (with CAS support) the key and resets its timeout. + * + * @param key the key to get a value for and reset its timeout + * @param expiration the new expiration for the key + * @param cb the callback that will contain the result + * @return a new GetsAndTouchOperation + */ + GetsOperation getsAndTouch(String key, int expiration, GetsOperation.Callback cb); + /** * Create a mutator operation. * diff --git a/src/main/java/net/spy/memcached/ops/APIType.java b/src/main/java/net/spy/memcached/ops/APIType.java index d81ecc940..9651a1eec 100644 --- a/src/main/java/net/spy/memcached/ops/APIType.java +++ b/src/main/java/net/spy/memcached/ops/APIType.java @@ -25,6 +25,7 @@ public enum APIType { INCR(OperationType.WRITE), DECR(OperationType.WRITE), DELETE(OperationType.WRITE), GET(OperationType.READ), GETS(OperationType.READ), + GAT(OperationType.WRITE), GATS(OperationType.WRITE), // List API Type LOP_CREATE(OperationType.WRITE), diff --git a/src/main/java/net/spy/memcached/ops/OperationType.java b/src/main/java/net/spy/memcached/ops/OperationType.java index 7bff99a17..243cdfdcc 100644 --- a/src/main/java/net/spy/memcached/ops/OperationType.java +++ b/src/main/java/net/spy/memcached/ops/OperationType.java @@ -25,6 +25,7 @@ public enum OperationType { * BTreeInsertAndGetOperationImpl (asyncBopInsertAndGetTrimmed) * FlushOperationImpl / FlushByPrefixOperationImpl (flush) * SetAttrOperationImpl (asyncSetAttr) + * GetAndTouchOperationImpl / GetsAndTouchOperationImpl (asyncGetAndTouch / asyncGetsAndTouch) */ WRITE, diff --git a/src/main/java/net/spy/memcached/protocol/ascii/AsciiOperationFactory.java b/src/main/java/net/spy/memcached/protocol/ascii/AsciiOperationFactory.java index 58c9ce924..1f4da20ae 100644 --- a/src/main/java/net/spy/memcached/protocol/ascii/AsciiOperationFactory.java +++ b/src/main/java/net/spy/memcached/protocol/ascii/AsciiOperationFactory.java @@ -109,6 +109,14 @@ public GetsOperation gets(Collection keys, GetsOperation.Callback cb, bo return new GetsOperationImpl(keys, cb, isMGet); } + public GetOperation getAndTouch(String key, int expiration, GetOperation.Callback cb) { + return new GetAndTouchOperationImpl(key, expiration, cb); + } + + public GetsOperation getsAndTouch(String key, int expiration, GetsOperation.Callback cb) { + return new GetsAndTouchOperationImpl(key, expiration, cb); + } + public MutatorOperation mutate(Mutator m, String key, int by, long def, int exp, OperationCallback cb) { return new MutatorOperationImpl(m, key, by, def, exp, cb); diff --git a/src/main/java/net/spy/memcached/protocol/ascii/BaseGetOpImpl.java b/src/main/java/net/spy/memcached/protocol/ascii/BaseGetOpImpl.java index 0277e57f1..f75f3376a 100644 --- a/src/main/java/net/spy/memcached/protocol/ascii/BaseGetOpImpl.java +++ b/src/main/java/net/spy/memcached/protocol/ascii/BaseGetOpImpl.java @@ -41,6 +41,7 @@ abstract class BaseGetOpImpl extends OperationImpl { private final String cmd; private final Collection keys; private String currentKey = null; + private final int exp; private long casValue = 0; private int currentFlags = 0; private byte[] data = null; @@ -55,9 +56,22 @@ public BaseGetOpImpl(String c, super(cb); cmd = c; keys = k; + exp = 0; setOperationType(OperationType.READ); } + /** + * For GetAndTouchOperationImpl, GetsAndTouchOperationImpl Only + */ + public BaseGetOpImpl(String c, int e, + OperationCallback cb, Collection k) { + super(cb); + cmd = c; + keys = k; + exp = e; + setOperationType(OperationType.WRITE); + } + /** * Get the keys this GetOperation is looking for. */ @@ -73,6 +87,12 @@ public final void handleLine(String line) { ... END\r\n */ + /* ENABLE_REPLICATION if */ + if (hasSwitchedOver(line)) { + prepareSwitchover(line); + return; + } + /* ENABLE_REPLICATION end */ if (line.equals("END")) { getLogger().debug("Get complete!"); /* ENABLE_MIGRATION if */ @@ -184,6 +204,14 @@ public final void initialize() { commandBuilder.append(' '); commandBuilder.append(keysString); commandBuilder.append(RN_STRING); + } else if (cmd.equals("gat") || cmd.equals("gats")) { + // syntax: gat || gats \r\n + commandBuilder.append(cmd); + commandBuilder.append(' '); + commandBuilder.append(exp); + commandBuilder.append(' '); + commandBuilder.append(keysString); + commandBuilder.append(RN_STRING); } else { assert (cmd.equals("mget") || cmd.equals("mgets")) : "Unknown Command " + cmd; @@ -231,4 +259,7 @@ public boolean isBulkOperation() { return keys.size() > 1; } + public int getExpiration() { + return exp; + } } diff --git a/src/main/java/net/spy/memcached/protocol/ascii/GetAndTouchOperationImpl.java b/src/main/java/net/spy/memcached/protocol/ascii/GetAndTouchOperationImpl.java new file mode 100644 index 000000000..85952777a --- /dev/null +++ b/src/main/java/net/spy/memcached/protocol/ascii/GetAndTouchOperationImpl.java @@ -0,0 +1,36 @@ +/* + * arcus-java-client : Arcus Java client + * Copyright 2010-2014 NAVER Corp. + * Copyright 2014-present JaM2in Co., Ltd. + * + * Licensed 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 net.spy.memcached.protocol.ascii; + +import java.util.Collections; + +import net.spy.memcached.ops.APIType; +import net.spy.memcached.ops.GetOperation; + +/** + * Implementation of the get and touch operation. + */ +class GetAndTouchOperationImpl extends BaseGetOpImpl implements GetOperation { + private static final String CMD = "gat"; + + public GetAndTouchOperationImpl(String k, int e, GetOperation.Callback cb) { + super(CMD, e, cb, Collections.singleton(k)); + setAPIType(APIType.GAT); + } +} diff --git a/src/main/java/net/spy/memcached/protocol/ascii/GetsAndTouchOperationImpl.java b/src/main/java/net/spy/memcached/protocol/ascii/GetsAndTouchOperationImpl.java new file mode 100644 index 000000000..0945c6242 --- /dev/null +++ b/src/main/java/net/spy/memcached/protocol/ascii/GetsAndTouchOperationImpl.java @@ -0,0 +1,36 @@ +/* + * arcus-java-client : Arcus Java client + * Copyright 2010-2014 NAVER Corp. + * Copyright 2014-present JaM2in Co., Ltd. + * + * Licensed 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 net.spy.memcached.protocol.ascii; + +import java.util.Collections; + +import net.spy.memcached.ops.APIType; +import net.spy.memcached.ops.GetsOperation; + +/** + * Implementation of the gets and touch operation. + */ +class GetsAndTouchOperationImpl extends BaseGetOpImpl implements GetsOperation { + private static final String CMD = "gats"; + + public GetsAndTouchOperationImpl(String k, int e, GetsOperation.Callback cb) { + super(CMD, e, cb, Collections.singleton(k)); + setAPIType(APIType.GATS); + } +} diff --git a/src/main/java/net/spy/memcached/protocol/binary/BinaryOperationFactory.java b/src/main/java/net/spy/memcached/protocol/binary/BinaryOperationFactory.java index 04e8e2215..6e8f159f7 100644 --- a/src/main/java/net/spy/memcached/protocol/binary/BinaryOperationFactory.java +++ b/src/main/java/net/spy/memcached/protocol/binary/BinaryOperationFactory.java @@ -112,6 +112,16 @@ public GetsOperation gets(Collection keys, GetsOperation.Callback cb, bo "multiple key gets is not supported in binary protocol yet."); } + public GetOperation getAndTouch(String key, int expiration, GetOperation.Callback cb) { + throw new RuntimeException( + "GetAndTouchOperation is not supported in binary protocol yet."); + } + + public GetsOperation getsAndTouch(String key, int expiration, GetsOperation.Callback cb) { + throw new RuntimeException( + "GetsAndTouchOperation is not supported in binary protocol yet."); + } + public MutatorOperation mutate(Mutator m, String key, int by, long def, int exp, OperationCallback cb) { return new MutatorOperationImpl(m, key, by, def, exp, cb); diff --git a/src/test/java/net/spy/memcached/ProtocolBaseCase.java b/src/test/java/net/spy/memcached/ProtocolBaseCase.java index e7bbaa4f4..c6f8b7afc 100644 --- a/src/test/java/net/spy/memcached/ProtocolBaseCase.java +++ b/src/test/java/net/spy/memcached/ProtocolBaseCase.java @@ -122,6 +122,25 @@ void testGetStatsCacheDump() throws Exception { assertTrue(val.matches("\\[acctime=\\d+, exptime=\\d+\\]"), val + "doesn't match"); } +// @Test +// void testGetAndTouch() throws Exception { +// assertNull(client.get("testgat")); +// assertTrue(client.set("testgat", 3, "testgatvalue").get()); +// GetFuture future = client.asyncGetAndTouch("testgat", 5); +// assertNotNull(future.get()); +// assertEquals("testgatvalue", future.get()); +// } +// +// @Test +// void testGetsAndTouch() throws Exception { +// assertNull(client.get("testgats")); +// assertTrue(client.set("testgats", 3, "testgatsvalue").get()); +// GetFuture> future = client.asyncGetsAndTouch("testgats", 5); +// assertNotNull(future.get()); +// assertEquals("testgatsvalue", future.get().getValue()); +// assertTrue(future.get().getCas() > 0); +// } + @Test void testDelayedFlush() throws Exception { assertNull(client.get("test1")); diff --git a/src/test/manual/net/spy/memcached/collection/attribute/GetAttrTest.java b/src/test/manual/net/spy/memcached/collection/attribute/GetAttrTest.java index 0f318727b..72d27bd92 100644 --- a/src/test/manual/net/spy/memcached/collection/attribute/GetAttrTest.java +++ b/src/test/manual/net/spy/memcached/collection/attribute/GetAttrTest.java @@ -118,4 +118,17 @@ void testGetAttr_Touch() throws Exception { CollectionAttributes rattrs = mc.asyncGetAttr(key).get(1000, TimeUnit.MILLISECONDS); assertTrue(rattrs.getExpireTime() > 10); } + +// @Test +// void testGetAttr_GetAndTouch() throws Exception { +// String key = "getattr_gat_attribute"; +// +// mc.set(key, 10, "v").get(); +// GetFuture future = mc.asyncGetAndTouch(key, 100); +// assertNotNull(future.get()); +// assertEquals(future.get(), "v"); +// +// CollectionAttributes rattrs = mc.asyncGetAttr(key).get(1000, TimeUnit.MILLISECONDS); +// assertTrue(rattrs.getExpireTime() > 10); +// } }