Skip to content

Commit 370ff9f

Browse files
committed
FEAT: Add gat(get and touch) command to get item & update item expiration
1 parent beeb9bb commit 370ff9f

File tree

10 files changed

+398
-0
lines changed

10 files changed

+398
-0
lines changed

src/main/java/net/spy/memcached/ArcusClientPool.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,16 @@ public GetFuture<CASValue<Object>> asyncGets(String key) {
242242
return this.getClient().asyncGets(key);
243243
}
244244

245+
@Override
246+
public Future<CASValue<Object>> asyncGetAndTouch(String key, int exp) {
247+
return this.getClient().asyncGetAndTouch(key, exp);
248+
}
249+
250+
@Override
251+
public <T> Future<CASValue<T>> asyncGetAndTouch(String key, int exp, Transcoder<T> tc) {
252+
return this.getClient().asyncGetAndTouch(key, exp, tc);
253+
}
254+
245255
@Override
246256
public <T> CASValue<T> gets(String key, Transcoder<T> tc)
247257
throws OperationTimeoutException {
@@ -264,6 +274,16 @@ public Object get(String key) throws OperationTimeoutException {
264274
return this.getClient().get(key);
265275
}
266276

277+
@Override
278+
public CASValue<Object> getAndTouch(String key, int exp) {
279+
return this.getClient().getAndTouch(key, exp);
280+
}
281+
282+
@Override
283+
public <T> CASValue<T> getAndTouch(String key, int exp, Transcoder<T> tc) {
284+
return this.getClient().getAndTouch(key, exp, tc);
285+
}
286+
267287
@Override
268288
public <T> BulkFuture<Map<String, T>> asyncGetBulk(Collection<String> keys,
269289
Iterator<Transcoder<T>> tcs) {

src/main/java/net/spy/memcached/MemcachedClient.java

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import java.util.List;
3333
import java.util.Map;
3434
import java.util.Set;
35+
import java.util.concurrent.CancellationException;
3536
import java.util.concurrent.ConcurrentHashMap;
3637
import java.util.concurrent.ConcurrentMap;
3738
import java.util.concurrent.CountDownLatch;
@@ -58,6 +59,7 @@
5859
import net.spy.memcached.ops.CancelledOperationStatus;
5960
import net.spy.memcached.ops.ConcatenationType;
6061
import net.spy.memcached.ops.DeleteOperation;
62+
import net.spy.memcached.ops.GetAndTouchOperation;
6163
import net.spy.memcached.ops.GetOperation;
6264
import net.spy.memcached.ops.GetsOperation;
6365
import net.spy.memcached.ops.Mutator;
@@ -980,6 +982,58 @@ public GetFuture<CASValue<Object>> asyncGets(final String key) {
980982
return asyncGets(key, transcoder);
981983
}
982984

985+
/**
986+
* Get the given key to reset its expiration time.
987+
*
988+
* @param key the key to fetch
989+
* @param exp the new expiration to set for the given key
990+
* @param tc the transcoder to serialize and unserialize value
991+
* @return a future that will hold the return value of the fetch
992+
* @throws IllegalStateException in the rare circumstance where queue is too
993+
* full to accept any more requests
994+
*/
995+
public <T> OperationFuture<CASValue<T>> asyncGetAndTouch(final String key,
996+
final int exp, final Transcoder<T> tc) {
997+
final CountDownLatch latch = new CountDownLatch(1);
998+
final OperationFuture<CASValue<T>> rv =
999+
new OperationFuture<CASValue<T>>(latch, operationTimeout);
1000+
1001+
Operation op = opFact.getAndTouch(key, exp,
1002+
new GetAndTouchOperation.Callback() {
1003+
private CASValue<T> val;
1004+
1005+
public void receivedStatus(OperationStatus status) {
1006+
rv.set(val, status);
1007+
}
1008+
1009+
public void complete() {
1010+
latch.countDown();
1011+
}
1012+
1013+
public void gotData(String k, int flags, long cas, byte[] data) {
1014+
assert k.equals(key) : "Wrong key returned";
1015+
val = new CASValue<T>(cas, tc.decode(new CachedData(flags, data, tc.getMaxSize())));
1016+
}
1017+
});
1018+
rv.setOperation(op);
1019+
addOp(key, op);
1020+
return rv;
1021+
}
1022+
1023+
/**
1024+
* Get the given key to reset its expiration time.
1025+
*
1026+
* @param key the key to fetch
1027+
* @param exp the new expiration to set for the given key
1028+
* @return a future that will hold the return value of the fetch
1029+
* @throws IllegalStateException in the rare circumstance where queue is too
1030+
* full to accept any more requests
1031+
*/
1032+
public OperationFuture<CASValue<Object>> asyncGetAndTouch(final String key,
1033+
final int exp) {
1034+
return asyncGetAndTouch(key, exp, transcoder);
1035+
}
1036+
9831037
/**
9841038
* Gets (with CAS support) with a single key.
9851039
*
@@ -1042,6 +1096,52 @@ public Object get(String key) {
10421096
return get(key, transcoder);
10431097
}
10441098

1099+
/**
1100+
* Get with a single key and reset its expiration.
1101+
*
1102+
* @param <T>
1103+
* @param key the key to get
1104+
* @param exp the new expiration for the key
1105+
* @param tc the transcoder to serialize and unserialize value
1106+
* @return the result from the cache (null if there is none)
1107+
* @throws OperationTimeoutException if the global operation timeout is
1108+
* exceeded
1109+
* @throws java.util.concurrent.CancellationException if operation was canceled
1110+
* @throws IllegalStateException in the rare circumstance where queue is too
1111+
* full to accept any more requests
1112+
*/
1113+
public <T> CASValue<T> getAndTouch(String key, int exp, Transcoder<T> tc) {
1114+
try {
1115+
return asyncGetAndTouch(key, exp, tc).get(operationTimeout,
1116+
TimeUnit.MILLISECONDS);
1117+
} catch (InterruptedException e) {
1118+
throw new RuntimeException("Interrupted waiting for value", e);
1119+
} catch (ExecutionException e) {
1120+
if (e.getCause() instanceof CancellationException) {
1121+
throw (CancellationException) e.getCause();
1122+
} else {
1123+
throw new RuntimeException("Exception waiting for value", e);
1124+
}
1125+
} catch (TimeoutException e) {
1126+
throw new OperationTimeoutException("Timeout waiting for value", e);
1127+
}
1128+
}
1129+
1130+
/**
1131+
* Get a single key and reset its expiration using the default transcoder.
1132+
*
1133+
* @param key the key to get
1134+
* @param exp the new expiration for the key
1135+
* @return the result from the cache and CAS id (null if there is none)
1136+
* @throws OperationTimeoutException if the global operation timeout is
1137+
* exceeded
1138+
* @throws IllegalStateException in the rare circumstance where queue is too
1139+
* full to accept any more requests
1140+
*/
1141+
public CASValue<Object> getAndTouch(String key, int exp) {
1142+
return getAndTouch(key, exp, transcoder);
1143+
}
1144+
10451145
/**
10461146
* Asynchronously get a bunch of objects from the cache.
10471147
*

src/main/java/net/spy/memcached/MemcachedClientIF.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ <T> Future<CASValue<T>> asyncGets(String key,
8282

8383
Future<CASValue<Object>> asyncGets(String key);
8484

85+
Future<CASValue<Object>> asyncGetAndTouch(final String key, final int exp);
86+
87+
<T> Future<CASValue<T>> asyncGetAndTouch(final String key, final int exp, final Transcoder<T> tc);
88+
8589
<T> CASValue<T> gets(String key, Transcoder<T> tc)
8690
throws OperationTimeoutException;
8791

@@ -92,6 +96,10 @@ <T> T get(String key, Transcoder<T> tc)
9296

9397
Object get(String key) throws OperationTimeoutException;
9498

99+
CASValue<Object> getAndTouch(String key, int exp);
100+
101+
<T> CASValue<T> getAndTouch(String key, int exp, Transcoder<T> tc);
102+
95103
<T> BulkFuture<Map<String, T>> asyncGetBulk(Collection<String> keys,
96104
Iterator<Transcoder<T>> tcs);
97105

src/main/java/net/spy/memcached/OperationFactory.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
import net.spy.memcached.ops.ConcatenationType;
6565
import net.spy.memcached.ops.DeleteOperation;
6666
import net.spy.memcached.ops.FlushOperation;
67+
import net.spy.memcached.ops.GetAndTouchOperation;
6768
import net.spy.memcached.ops.GetAttrOperation;
6869
import net.spy.memcached.ops.GetOperation;
6970
import net.spy.memcached.ops.GetsOperation;
@@ -142,6 +143,17 @@ public interface OperationFactory {
142143
*/
143144
GetOperation get(Collection<String> keys, GetOperation.Callback cb, boolean isMGet);
144145

146+
/**
147+
* Gets the value of a key and resets its timeout.
148+
*
149+
* @param key the key to get a value for and reset its timeout
150+
* @param expiration the new expiration for the key
151+
* @param cb the callback that will contain the result
152+
* @return a new GATOperation
153+
*/
154+
GetAndTouchOperation getAndTouch(String key, int expiration,
155+
GetAndTouchOperation.Callback cb);
156+
145157
/**
146158
* Create a gets operation.
147159
*

src/main/java/net/spy/memcached/ops/BaseOperationFactory.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ public Collection<Operation> clone(KeyedOperation op) {
8888
GetAttrOperation c = (GetAttrOperation) op;
8989
rv.add(getAttr(first(c.getKeys()),
9090
(GetAttrOperation.Callback) c.getCallback()));
91+
} else if (op instanceof GetAndTouchOperation) {
92+
GetAndTouchOperation gt = (GetAndTouchOperation) op;
93+
rv.add(getAndTouch(first(gt.getKeys()), gt.getExpiration(),
94+
(GetAndTouchOperation.Callback) gt.getCallback()));
9195
} else if (op instanceof CollectionInsertOperation) {
9296
CollectionInsertOperation c = (CollectionInsertOperation) op;
9397
rv.add(collectionInsert(first(c.getKeys()), c.getSubKey(),
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* arcus-java-client : Arcus Java client
3+
* Copyright 2010-2014 NAVER Corp.
4+
* Copyright 2014-present JaM2in Co., Ltd.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package net.spy.memcached.ops;
20+
21+
/**
22+
* Gat operation.
23+
*/
24+
public interface GetAndTouchOperation extends KeyedOperation {
25+
26+
/**
27+
* Operation callback for the gat request.
28+
*/
29+
interface Callback extends OperationCallback {
30+
/**
31+
* Callback for each result from a gat.
32+
*
33+
* @param key the key that was retrieved
34+
* @param flags the flags for this value
35+
* @param data the data stored under this key
36+
*/
37+
void gotData(String key, int flags, long cas, byte[] data);
38+
}
39+
40+
/**
41+
* Get the expiration to set in case of a new entry.
42+
*/
43+
int getExpiration();
44+
}

src/main/java/net/spy/memcached/protocol/ascii/AsciiOperationFactory.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
import net.spy.memcached.ops.ConcatenationType;
6565
import net.spy.memcached.ops.DeleteOperation;
6666
import net.spy.memcached.ops.FlushOperation;
67+
import net.spy.memcached.ops.GetAndTouchOperation;
6768
import net.spy.memcached.ops.GetAttrOperation;
6869
import net.spy.memcached.ops.GetOperation;
6970
import net.spy.memcached.ops.GetsOperation;
@@ -109,6 +110,11 @@ public GetsOperation gets(Collection<String> keys, GetsOperation.Callback cb, bo
109110
return new GetsOperationImpl(keys, cb, isMGet);
110111
}
111112

113+
public GetAndTouchOperation getAndTouch(String key, int expiration,
114+
GetAndTouchOperation.Callback cb) {
115+
return new GetAndTouchOperationImpl(key, expiration, cb, key);
116+
}
117+
112118
public MutatorOperation mutate(Mutator m, String key, int by,
113119
long def, int exp, OperationCallback cb) {
114120
return new MutatorOperationImpl(m, key, by, def, exp, cb);

0 commit comments

Comments
 (0)