diff --git a/src/main/java/net/spy/memcached/ArcusClientPool.java b/src/main/java/net/spy/memcached/ArcusClientPool.java index a7100ad15..14660c876 100644 --- a/src/main/java/net/spy/memcached/ArcusClientPool.java +++ b/src/main/java/net/spy/memcached/ArcusClientPool.java @@ -242,6 +242,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 fe3696c79..a2eaa5a50 100644 --- a/src/main/java/net/spy/memcached/MemcachedClient.java +++ b/src/main/java/net/spy/memcached/MemcachedClient.java @@ -59,7 +59,9 @@ import net.spy.memcached.ops.CancelledOperationStatus; import net.spy.memcached.ops.ConcatenationType; import net.spy.memcached.ops.DeleteOperation; +import net.spy.memcached.ops.GetAndTouchOperation; import net.spy.memcached.ops.GetOperation; +import net.spy.memcached.ops.GetsAndTouchOperation; import net.spy.memcached.ops.GetsOperation; import net.spy.memcached.ops.Mutator; import net.spy.memcached.ops.Operation; @@ -986,6 +988,107 @@ 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 GetAndTouchOperation.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 GetsAndTouchOperation.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 1266edda8..9a279fe17 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 e86127de6..d2e7857a0 100644 --- a/src/main/java/net/spy/memcached/OperationFactory.java +++ b/src/main/java/net/spy/memcached/OperationFactory.java @@ -64,8 +64,10 @@ import net.spy.memcached.ops.ConcatenationType; import net.spy.memcached.ops.DeleteOperation; import net.spy.memcached.ops.FlushOperation; +import net.spy.memcached.ops.GetAndTouchOperation; import net.spy.memcached.ops.GetAttrOperation; import net.spy.memcached.ops.GetOperation; +import net.spy.memcached.ops.GetsAndTouchOperation; import net.spy.memcached.ops.GetsOperation; import net.spy.memcached.ops.KeyedOperation; import net.spy.memcached.ops.MultiOperationCallback; @@ -152,6 +154,28 @@ 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 + */ + GetAndTouchOperation getAndTouch(String key, int expiration, + GetAndTouchOperation.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 + */ + GetsAndTouchOperation getsAndTouch(String key, int expiration, + GetsAndTouchOperation.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 f6b3345c5..581a26067 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/BaseOperationFactory.java b/src/main/java/net/spy/memcached/ops/BaseOperationFactory.java index 28f24621e..f5e5bc992 100644 --- a/src/main/java/net/spy/memcached/ops/BaseOperationFactory.java +++ b/src/main/java/net/spy/memcached/ops/BaseOperationFactory.java @@ -60,6 +60,14 @@ public Collection clone(KeyedOperation op) { for (String k : op.getKeys()) { rv.add(gets(k, getsCb)); } + } else if (op instanceof GetAndTouchOperation) { + GetAndTouchOperation gt = (GetAndTouchOperation) op; + rv.add(getAndTouch(first(gt.getKeys()), gt.getExpiration(), + (GetAndTouchOperation.Callback) gt.getCallback())); + } else if (op instanceof GetsAndTouchOperation) { + GetsAndTouchOperation gt = (GetsAndTouchOperation) op; + rv.add(getsAndTouch(first(gt.getKeys()), gt.getExpiration(), + (GetsAndTouchOperation.Callback) gt.getCallback())); } else if (op instanceof CASOperation) { CASOperation cop = (CASOperation) op; rv.add(cas(cop.getStoreType(), first(op.getKeys()), diff --git a/src/main/java/net/spy/memcached/ops/GetAndTouchOperation.java b/src/main/java/net/spy/memcached/ops/GetAndTouchOperation.java new file mode 100644 index 000000000..30d7853f0 --- /dev/null +++ b/src/main/java/net/spy/memcached/ops/GetAndTouchOperation.java @@ -0,0 +1,44 @@ +/* + * 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.ops; + +/** + * Gat(GetAndTouch) operation. + */ +public interface GetAndTouchOperation extends KeyedOperation { + + /** + * Operation callback for the gat request. + */ + interface Callback extends OperationCallback { + /** + * Callback for each result from a gat. + * + * @param key the key that was retrieved + * @param flags the flags for this value + * @param data the data stored under this key + */ + void gotData(String key, int flags, byte[] data); + } + + /** + * Get the expiration to set in case of a new entry. + */ + int getExpiration(); +} diff --git a/src/main/java/net/spy/memcached/ops/GetsAndTouchOperation.java b/src/main/java/net/spy/memcached/ops/GetsAndTouchOperation.java new file mode 100644 index 000000000..7cc1c4c64 --- /dev/null +++ b/src/main/java/net/spy/memcached/ops/GetsAndTouchOperation.java @@ -0,0 +1,44 @@ +/* + * 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.ops; + +/** + * Gats(GetsAndTouch) operation. + */ +public interface GetsAndTouchOperation extends KeyedOperation { + /** + * Operation callback for the gats request. + */ + interface Callback extends OperationCallback { + /** + * Callback for each result from a gats. + * + * @param key the key that was retrieved + * @param flags the flags for this value + * @param cas the CAS value for this record + * @param data the data stored under this key + */ + void gotData(String key, int flags, long cas, byte[] data); + } + + /** + * Get the expiration to set in case of a new entry. + */ + int getExpiration(); +} diff --git a/src/main/java/net/spy/memcached/ops/OperationType.java b/src/main/java/net/spy/memcached/ops/OperationType.java index caab92ed2..76d5c2cd0 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 059818ef8..48f26f2d9 100644 --- a/src/main/java/net/spy/memcached/protocol/ascii/AsciiOperationFactory.java +++ b/src/main/java/net/spy/memcached/protocol/ascii/AsciiOperationFactory.java @@ -64,8 +64,10 @@ import net.spy.memcached.ops.ConcatenationType; import net.spy.memcached.ops.DeleteOperation; import net.spy.memcached.ops.FlushOperation; +import net.spy.memcached.ops.GetAndTouchOperation; import net.spy.memcached.ops.GetAttrOperation; import net.spy.memcached.ops.GetOperation; +import net.spy.memcached.ops.GetsAndTouchOperation; import net.spy.memcached.ops.GetsOperation; import net.spy.memcached.ops.Mutator; import net.spy.memcached.ops.MutatorOperation; @@ -109,6 +111,16 @@ public GetsOperation gets(Collection keys, GetsOperation.Callback cb, bo return new GetsOperationImpl(keys, cb, isMGet); } + public GetAndTouchOperation getAndTouch(String key, int expiration, + GetAndTouchOperation.Callback cb) { + return new GetAndTouchOperationImpl(key, expiration, cb); + } + + public GetsAndTouchOperation getsAndTouch(String key, int expiration, + GetsAndTouchOperation.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..220e6f8a3 100644 --- a/src/main/java/net/spy/memcached/protocol/ascii/BaseGetOpImpl.java +++ b/src/main/java/net/spy/memcached/protocol/ascii/BaseGetOpImpl.java @@ -22,7 +22,9 @@ import java.util.Collection; import java.util.Iterator; +import net.spy.memcached.ops.GetAndTouchOperation; import net.spy.memcached.ops.GetOperation; +import net.spy.memcached.ops.GetsAndTouchOperation; import net.spy.memcached.ops.GetsOperation; import net.spy.memcached.ops.OperationCallback; import net.spy.memcached.ops.OperationState; @@ -41,6 +43,7 @@ abstract class BaseGetOpImpl extends OperationImpl { private final String cmd; private final Collection keys; private String currentKey = null; + protected int exp; private long casValue = 0; private int currentFlags = 0; private byte[] data = null; @@ -58,6 +61,15 @@ public BaseGetOpImpl(String c, setOperationType(OperationType.READ); } + 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. */ @@ -66,7 +78,7 @@ public final Collection getKeys() { } @Override - public final void handleLine(String line) { + public void handleLine(String line) { /* VALUE []\r\n \r\n @@ -131,12 +143,21 @@ public final void handleRead(ByteBuffer bb) { if (readOffset == data.length && lookingFor == '\0') { // The callback is most likely a get callback. If it's not, then // it's a gets callback. - try { - GetOperation.Callback gcb = (GetOperation.Callback) getCallback(); + OperationCallback cb = getCallback(); + if (cb instanceof GetOperation.Callback) { + GetOperation.Callback gcb = (GetOperation.Callback) cb; + gcb.gotData(currentKey, currentFlags, data); + } else if (cb instanceof GetsOperation.Callback) { + GetsOperation.Callback gcb = (GetsOperation.Callback) cb; + gcb.gotData(currentKey, currentFlags, casValue, data); + } else if (cb instanceof GetAndTouchOperation.Callback) { + GetAndTouchOperation.Callback gcb = (GetAndTouchOperation.Callback) cb; gcb.gotData(currentKey, currentFlags, data); - } catch (ClassCastException e) { - GetsOperation.Callback gcb = (GetsOperation.Callback) getCallback(); + } else if (cb instanceof GetsAndTouchOperation.Callback) { + GetsAndTouchOperation.Callback gcb = (GetsAndTouchOperation.Callback) cb; gcb.gotData(currentKey, currentFlags, casValue, data); + } else { + throw new ClassCastException("Couldn't convert " + cb + "to a relevent op"); } lookingFor = '\r'; } @@ -184,6 +205,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; 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..62bf0a4fe --- /dev/null +++ b/src/main/java/net/spy/memcached/protocol/ascii/GetAndTouchOperationImpl.java @@ -0,0 +1,43 @@ +/* + * 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.GetAndTouchOperation; + +/** + * Implementation of the get and touch operation. + */ +class GetAndTouchOperationImpl extends BaseGetOpImpl + implements GetAndTouchOperation { + private static final String CMD = "gat"; + + public GetAndTouchOperationImpl(String k, int e, + GetAndTouchOperation.Callback cb) { + super(CMD, e, cb, Collections.singleton(k)); + setAPIType(APIType.GAT); + } + + @Override + public int getExpiration() { + return exp; + } +} 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..748b9399e --- /dev/null +++ b/src/main/java/net/spy/memcached/protocol/ascii/GetsAndTouchOperationImpl.java @@ -0,0 +1,43 @@ +/* + * 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.GetsAndTouchOperation; + +/** + * Implementation of the gets and touch operation. + */ +class GetsAndTouchOperationImpl extends BaseGetOpImpl + implements GetsAndTouchOperation { + private static final String CMD = "gats"; + + public GetsAndTouchOperationImpl(String k, int e, + GetsAndTouchOperation.Callback cb) { + super(CMD, e, cb, Collections.singleton(k)); + setAPIType(APIType.GATS); + } + + @Override + public int getExpiration() { + return exp; + } +} 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 e798a30b8..31a69d570 100644 --- a/src/main/java/net/spy/memcached/protocol/binary/BinaryOperationFactory.java +++ b/src/main/java/net/spy/memcached/protocol/binary/BinaryOperationFactory.java @@ -64,9 +64,11 @@ import net.spy.memcached.ops.ConcatenationType; import net.spy.memcached.ops.DeleteOperation; import net.spy.memcached.ops.FlushOperation; +import net.spy.memcached.ops.GetAndTouchOperation; import net.spy.memcached.ops.GetAttrOperation; import net.spy.memcached.ops.GetOperation; import net.spy.memcached.ops.GetOperation.Callback; +import net.spy.memcached.ops.GetsAndTouchOperation; import net.spy.memcached.ops.GetsOperation; import net.spy.memcached.ops.Mutator; import net.spy.memcached.ops.MutatorOperation; @@ -112,6 +114,18 @@ public GetsOperation gets(Collection keys, GetsOperation.Callback cb, bo "multiple key gets is not supported in binary protocol yet."); } + public GetAndTouchOperation getAndTouch(String key, int expiration, + GetAndTouchOperation.Callback cb) { + throw new RuntimeException( + "GetAndTouchOperation is not supported in binary protocol yet."); + } + + public GetsAndTouchOperation getsAndTouch(String key, int expiration, + GetsAndTouchOperation.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 fae6da9d9..d48437291 100644 --- a/src/test/java/net/spy/memcached/ProtocolBaseCase.java +++ b/src/test/java/net/spy/memcached/ProtocolBaseCase.java @@ -117,6 +117,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 e7a81c537..142992d68 100644 --- a/src/test/manual/net/spy/memcached/collection/attribute/GetAttrTest.java +++ b/src/test/manual/net/spy/memcached/collection/attribute/GetAttrTest.java @@ -23,6 +23,7 @@ import net.spy.memcached.collection.CollectionResponse; import net.spy.memcached.collection.CollectionType; import net.spy.memcached.internal.CollectionFuture; +import net.spy.memcached.internal.GetFuture; import org.junit.jupiter.api.Test; @@ -105,4 +106,17 @@ void testGetAttr_KeyNotFound() throws Exception { assertEquals(CollectionResponse.NOT_FOUND, future.getOperationStatus() .getResponse()); } + + @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); + } }