Skip to content

Commit 2ac1311

Browse files
committed
FEAT: Add gat(get and touch) command to get item & update item expiration
1 parent eff557c commit 2ac1311

File tree

13 files changed

+283
-4
lines changed

13 files changed

+283
-4
lines changed

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

Lines changed: 10 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 <T> Future<T> asyncGetAndTouch(String key, int exp, Transcoder<T> tc) {
247+
return this.getClient().asyncGetAndTouch(key, exp, tc);
248+
}
249+
250+
@Override
251+
public Future<Object> asyncGetAndTouch(String key, int exp) {
252+
return this.getClient().asyncGetAndTouch(key, exp);
253+
}
254+
245255
@Override
246256
public <T> CASValue<T> gets(String key, Transcoder<T> tc)
247257
throws OperationTimeoutException {

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

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
import net.spy.memcached.ops.CancelledOperationStatus;
6060
import net.spy.memcached.ops.ConcatenationType;
6161
import net.spy.memcached.ops.DeleteOperation;
62+
import net.spy.memcached.ops.GetAndTouchOperation;
6263
import net.spy.memcached.ops.GetOperation;
6364
import net.spy.memcached.ops.GetsOperation;
6465
import net.spy.memcached.ops.Mutator;
@@ -986,6 +987,56 @@ public GetFuture<CASValue<Object>> asyncGets(final String key) {
986987
return asyncGets(key, transcoder);
987988
}
988989

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

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

Lines changed: 4 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+
<T> Future<T> asyncGetAndTouch(final String key, final int exp, final Transcoder<T> tc);
86+
87+
Future<Object> asyncGetAndTouch(final String key, final int exp);
88+
8589
<T> CASValue<T> gets(String key, Transcoder<T> tc)
8690
throws OperationTimeoutException;
8791

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;
@@ -152,6 +153,17 @@ public interface OperationFactory {
152153
*/
153154
GetsOperation gets(Collection<String> keys, GetsOperation.Callback cb, boolean isMGet);
154155

156+
/**
157+
* Gets the value of a key and resets its timeout.
158+
*
159+
* @param key the key to get a value for and reset its timeout
160+
* @param expiration the new expiration for the key
161+
* @param cb the callback that will contain the result
162+
* @return a new GATOperation
163+
*/
164+
GetAndTouchOperation getAndTouch(String key, int expiration,
165+
GetAndTouchOperation.Callback cb);
166+
155167
/**
156168
* Create a mutator operation.
157169
*

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public enum APIType {
2525
INCR(OperationType.WRITE), DECR(OperationType.WRITE),
2626
DELETE(OperationType.WRITE),
2727
GET(OperationType.READ), GETS(OperationType.READ),
28+
GAT(OperationType.WRITE),
2829

2930
// List API Type
3031
LOP_CREATE(OperationType.WRITE),

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ public Collection<Operation> clone(KeyedOperation op) {
6060
for (String k : op.getKeys()) {
6161
rv.add(gets(k, getsCb));
6262
}
63+
} else if (op instanceof GetAndTouchOperation) {
64+
GetAndTouchOperation gt = (GetAndTouchOperation) op;
65+
rv.add(getAndTouch(first(gt.getKeys()), gt.getExpiration(),
66+
(GetAndTouchOperation.Callback) gt.getCallback()));
6367
} else if (op instanceof CASOperation) {
6468
CASOperation cop = (CASOperation) op;
6569
rv.add(cas(cop.getStoreType(), first(op.getKeys()),
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, 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);
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);

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

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.util.Collection;
2323
import java.util.Iterator;
2424

25+
import net.spy.memcached.ops.GetAndTouchOperation;
2526
import net.spy.memcached.ops.GetOperation;
2627
import net.spy.memcached.ops.GetsOperation;
2728
import net.spy.memcached.ops.OperationCallback;
@@ -41,6 +42,7 @@ abstract class BaseGetOpImpl extends OperationImpl {
4142
private final String cmd;
4243
private final Collection<String> keys;
4344
private String currentKey = null;
45+
protected final int exp;
4446
private long casValue = 0;
4547
private int currentFlags = 0;
4648
private byte[] data = null;
@@ -55,9 +57,19 @@ public BaseGetOpImpl(String c,
5557
super(cb);
5658
cmd = c;
5759
keys = k;
60+
exp = 0;
5861
setOperationType(OperationType.READ);
5962
}
6063

64+
public BaseGetOpImpl(String c, int e,
65+
OperationCallback cb, Collection<String> k) {
66+
super(cb);
67+
cmd = c;
68+
keys = k;
69+
exp = e;
70+
setOperationType(OperationType.WRITE);
71+
}
72+
6173
/**
6274
* Get the keys this GetOperation is looking for.
6375
*/
@@ -131,12 +143,18 @@ public final void handleRead(ByteBuffer bb) {
131143
if (readOffset == data.length && lookingFor == '\0') {
132144
// The callback is most likely a get callback. If it's not, then
133145
// it's a gets callback.
134-
try {
135-
GetOperation.Callback gcb = (GetOperation.Callback) getCallback();
146+
OperationCallback cb = getCallback();
147+
if (cb instanceof GetOperation.Callback) {
148+
GetOperation.Callback gcb = (GetOperation.Callback) cb;
136149
gcb.gotData(currentKey, currentFlags, data);
137-
} catch (ClassCastException e) {
138-
GetsOperation.Callback gcb = (GetsOperation.Callback) getCallback();
150+
} else if (cb instanceof GetsOperation.Callback) {
151+
GetsOperation.Callback gcb = (GetsOperation.Callback) cb;
139152
gcb.gotData(currentKey, currentFlags, casValue, data);
153+
} else if (cb instanceof GetAndTouchOperation.Callback) {
154+
GetAndTouchOperation.Callback gcb = (GetAndTouchOperation.Callback) cb;
155+
gcb.gotData(currentKey, currentFlags, data);
156+
} else {
157+
throw new ClassCastException("Couldn't convert " + cb + "to a relevent op");
140158
}
141159
lookingFor = '\r';
142160
}
@@ -184,6 +202,14 @@ public final void initialize() {
184202
commandBuilder.append(' ');
185203
commandBuilder.append(keysString);
186204
commandBuilder.append(RN_STRING);
205+
} else if (cmd.equals("gat")) {
206+
// syntax: get <exp> <key>\r\n
207+
commandBuilder.append(cmd);
208+
commandBuilder.append(' ');
209+
commandBuilder.append(exp);
210+
commandBuilder.append(' ');
211+
commandBuilder.append(keysString);
212+
commandBuilder.append(RN_STRING);
187213
} else {
188214
assert (cmd.equals("mget") || cmd.equals("mgets"))
189215
: "Unknown Command " + cmd;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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.protocol.ascii;
20+
21+
import java.util.Collections;
22+
23+
import net.spy.memcached.ops.APIType;
24+
import net.spy.memcached.ops.GetAndTouchOperation;
25+
26+
/**
27+
* Implementation of the get and touch operation.
28+
*/
29+
public class GetAndTouchOperationImpl extends BaseGetOpImpl
30+
implements GetAndTouchOperation {
31+
private static final String CMD = "gat";
32+
33+
public GetAndTouchOperationImpl(String k, int e,
34+
GetAndTouchOperation.Callback cb) {
35+
super(CMD, e, cb, Collections.singleton(k));
36+
setAPIType(APIType.GAT);
37+
}
38+
39+
@Override
40+
public int getExpiration() {
41+
return exp;
42+
}
43+
}

0 commit comments

Comments
 (0)