1 package com
.google
.appengine
.api
.datastore
;
3 import static com
.google
.common
.base
.Preconditions
.checkArgument
;
5 import com
.google
.appengine
.api
.memcache
.AsyncMemcacheService
;
6 import com
.google
.appengine
.api
.memcache
.ConsistentErrorHandler
;
7 import com
.google
.appengine
.api
.memcache
.ErrorHandlers
;
8 import com
.google
.appengine
.api
.memcache
.Expiration
;
9 import com
.google
.appengine
.api
.memcache
.InvalidValueException
;
10 import com
.google
.appengine
.api
.memcache
.MemcacheService
.CasValues
;
11 import com
.google
.appengine
.api
.memcache
.MemcacheService
.IdentifiableValue
;
12 import com
.google
.appengine
.api
.memcache
.MemcacheService
.SetPolicy
;
13 import com
.google
.appengine
.api
.memcache
.MemcacheServiceException
;
14 import com
.google
.appengine
.api
.memcache
.MemcacheServiceFactory
;
16 import java
.util
.Collection
;
19 import java
.util
.concurrent
.Future
;
20 import java
.util
.logging
.Level
;
21 import java
.util
.logging
.Logger
;
24 * Helper class for the memcache service.
27 class MemcacheServiceHelper
{
29 static final double DEFAULT_MEMCACHE_RPC_DEADLINE_SECS
= 1;
31 static Logger logger
= Logger
.getLogger(MemcacheServiceHelper
.class.getName());
32 private static final Level LOG_LEVEL
= Level
.INFO
;
34 static final String NAMESPACE
= "__ah-datastore-l2-v1__";
36 private double rpcDeadlineSecs
= DEFAULT_MEMCACHE_RPC_DEADLINE_SECS
;
37 private Expiration defaultValueExpirationTime
;
38 private final AsyncMemcacheService memcacheService
;
40 private MemcacheServiceHelper() {
41 memcacheService
= MemcacheServiceFactory
.getAsyncMemcacheService(NAMESPACE
);
42 memcacheService
.setErrorHandler(new MemcacheExceptionConvertingErrorHandler());
45 MemcacheServiceHelper(AsyncMemcacheService memcacheService
) {
46 this.memcacheService
= memcacheService
;
50 * Updates the memcache operation RPC deadline to use for memcache RPCs.
52 * @param rpcDeadlineSecs the memcache operation RPC deadline in seconds.
53 * @return {@code this} (for chaining).
54 * @throws IllegalArgumentException if the {@code rpcDeadlineSecs} is not greater than zero.
56 public MemcacheServiceHelper
rpcDeadlineSecs(double rpcDeadlineSecs
) {
57 checkArgument(rpcDeadlineSecs
> 0, "The rpcDeadlineSecs argument must be greater than 0");
58 this.rpcDeadlineSecs
= rpcDeadlineSecs
;
63 * @return the memcache operation RPC deadline in seconds.
65 public double getRpcDeadlineSecs() {
66 return rpcDeadlineSecs
;
70 * Updates the expiration time for values stored in the cache without an explicit expiration time.
71 * By default, values without an explicit expiration time are cached for as long of a duration as
74 * @param defaultValueExpirationTime the default value expiration time. Or {@code null} to specify
75 * an indefinite default expiration time.
76 * @return {@code this} (for chaining).
78 public MemcacheServiceHelper
defaultValueExpirationTime( Expiration defaultValueExpirationTime
) {
79 this.defaultValueExpirationTime
= defaultValueExpirationTime
;
84 * @return the default value expiration time for values stored in the cache without
85 * an explicit expiration time or {@code null} if no default expiration time has been
88 public Expiration
getDefaultValueExpirationTime() {
89 return defaultValueExpirationTime
;
93 * Updates the {@link MemcacheServiceHelper} instance to log and absorb any memcache
94 * service errors. By default, memcache service errors are converted to
95 * {@code DatastoreFailureException} exceptions and thrown from the memcache operation
98 * @return the newly created {@code MemcacheServiceHelper} instance.
100 public MemcacheServiceHelper
logAndAbsorbMemcacheServiceErrors() {
101 memcacheService
.setErrorHandler(ErrorHandlers
.getConsistentLogAndContinue(LOG_LEVEL
));
106 * Fetches previously-stored cache values. The values returned can be used in subsequent calls
107 * to {@link #putIfUntouched}.
109 * @param keys a collection of keys for which values should be retrieved
110 * @return a mapping from keys to values of any entries found. If a requested
111 * key is not found in the cache the key will not be in the returned Map.
112 * @throws IllegalArgumentException if any element of {@code keys} is {@code null}
113 * or not {@link Serializable}.
114 * @throws DatastoreFailureException if a memcache service error occurs and
115 * {@link #logAndAbsorbMemcacheServiceErrors} has not been specified.
117 public <K
> Future
<Map
<K
, IdentifiableValue
>> getIdentifiable(Collection
<K
> keys
) {
118 return memcacheService
.getIdentifiables(keys
);
122 * Sets the value in the cache for each key/value pair in the {@code values} map if the update
123 * satisfies the cache update {@code policy}.
125 * The expiration time for each value stored is specified by
126 * {@link #defaultValueExpirationTime(Expiration)}.
128 * @param values the key/value mappings to add to the cache
129 * @param policy what to do if the cache entry is or is not already present
130 * @return the set of keys for which entries were created. Keys in {@code values} may not be
131 * in the returned set because of the {@code policy} regarding pre-existing entries.
132 * @throws IllegalArgumentException if any of the keys or values are {@code null} or
133 * or are not {@link Serializable}.
134 * @throws DatastoreFailureException if a memcache service error occurs and
135 * {@link #logAndAbsorbMemcacheServiceErrors} has not been specified.
137 public <K
> Future
<Set
<K
>> put(Map
<K
, ?
> values
, SetPolicy policy
) {
138 return put(values
, policy
, defaultValueExpirationTime
);
142 * Sets the value in the cache for each key/value pair in the {@code values} map if the update
143 * satisfies the cache update {@code policy}.
145 * @param values the key/value mappings to add to the cache
146 * @param policy what to do if the cache entry is or is not already present
147 * @param valueExpirationTime the memcache value expiration time to use for each value updated
148 * in the cache. This overrides the default value expiration time. If {@code null} the
149 * expiration time is indefinite.
150 * @return the set of keys for which entries were created. Keys in {@code values} may not be
151 * in the returned set because of the {@code policy} regarding pre-existing entries.
152 * @throws IllegalArgumentException if any of the keys or values are {@code null} or
153 * or are not {@link Serializable}.
154 * @throws DatastoreFailureException if a memcache service error occurs and
155 * {@link #logAndAbsorbMemcacheServiceErrors} has not been specified.
157 public <K
> Future
<Set
<K
>> put(Map
<K
, ?
> values
, SetPolicy policy
, Expiration valueExpirationTime
) {
158 return memcacheService
.putAll(values
, valueExpirationTime
, policy
);
162 * Atomically stores the new value of each {@link CasValues} object in the {@code values} map if
163 * no other value has been stored in the cache since the {@code CasValues} object's old value
164 * was retrieved from {@link #getIdentifiable}. If another value in the cache for {@code key}
165 * has been stored, or if the cache entry has been evicted then nothing is stored by this call.
167 * The expiration time for each {@CasValues} object with a {@code null} expiration time is
168 * specified by {@link #defaultValueExpirationTime(Expiration)}.
170 * @param values the key/values mappings to compare and swap.
171 * @return the set of keys for which the new value was stored.
172 * @throws IllegalArgumentException If any of the keys or newValues are {@code null} or
173 * or are not {@link Serializable}. Also throws IllegalArgumentException if {@code values} has
175 * @throws DatastoreFailureException if a memcache service error occurs and
176 * {@link #logAndAbsorbMemcacheServiceErrors} has not been specified.
178 public <K
> Future
<Set
<K
>> putIfUntouched(Map
<K
, CasValues
> values
) {
179 return putIfUntouched(values
, defaultValueExpirationTime
);
183 * Atomically stores the new value of each {@link CasValues} object in the {@code values} map if
184 * no other value has been stored in the cache since the {@code CasValues} object's old value
185 * was retrieved from {@link #getIdentifiable}. If another value in the cache for {@code key}
186 * has been stored, or if the cache entry has been evicted then nothing is stored by this call.
189 * @param values the key/values mappings to compare and swap.
190 * @param valueExpirationTime the expiration time to use for all {@code values} with a
191 * {@code null} {@link Expiration expiration} value in the {@code CasValues} object.
192 * This overrides the default value expiration time. If this is {@code null} and the
193 * {@code CasValues} object's expiration time is {@code null} then an indefinite expiration
194 * time will be specified for that {@code CasValue} object.
195 * @return the set of keys for which the new value was stored.
196 * @throws IllegalArgumentException If any of the keys or newValues are {@code null} or
197 * or are not {@link Serializable}. Also throws IllegalArgumentException if {@code values} has
199 * @throws DatastoreFailureException if a memcache service error occurs and
200 * {@link #logAndAbsorbMemcacheServiceErrors} has not been specified.
202 public <K
> Future
<Set
<K
>> putIfUntouched(Map
<K
, CasValues
> values
, Expiration valueExpirationTime
) {
203 return memcacheService
.putIfUntouched(values
, valueExpirationTime
);
207 * Deletes cache entries for the specified keys.
209 * @param keys a collection of keys for entries to delete
210 * @return the set of keys successfully deleted. Any keys in {@code keys} but not in the
211 * returned set were not found in the cache. The iteration order of the returned set matches
212 * the iteration order of the provided {@code keys}.
213 * @throws IllegalArgumentException if any element of {@code keys} is not {@link Serializable}
214 * and is not {@code null}
215 * @throws DatastoreFailureException if a memcache service error occurs and
216 * {@link #logAndAbsorbMemcacheServiceErrors} has not been specified.
218 public <K
> Future
<Set
<K
>> delete(Collection
<K
> keys
) {
219 return memcacheService
.deleteAll(keys
);
223 * A memcache service error handler that converts memcache service errors into
224 * {@link DatastoreFailureException} exceptions.
226 private static class MemcacheExceptionConvertingErrorHandler
implements ConsistentErrorHandler
{
229 public void handleDeserializationError(InvalidValueException t
) {
230 throw new DatastoreFailureException("Memcache deserialization error", t
);
234 public void handleServiceError(MemcacheServiceException t
) {
235 throw new DatastoreFailureException("Memcache service error", t
);
240 * Contains static creation methods for {@link MemcacheServiceHelper} instances.
242 public static final class Builder
{
245 * Creates a {@link MemcacheServiceHelper} instance with the specified memcache RPC deadline to
246 * use for memcache RPCs.
248 * @param rpcDeadlineSecs the memcache operation RPC deadline in seconds.
249 * @return the newly created {@code MemcacheServiceHelper} instance.
250 * @throws IllegalArgumentException if the {@code rpcDeadlineSecs} is not greater than zero.
252 public static MemcacheServiceHelper
withRpcDeadlineSecs(double rpcDeadlineSecs
) {
253 return new MemcacheServiceHelper().rpcDeadlineSecs(rpcDeadlineSecs
);
257 * Creates a {@link MemcacheServiceHelper} instance with the specified expiration time for
258 * values stored in the cache without an explicit expiration time. By default, values without
259 * an explicit expiration time are cached for as long of a duration as possible.
261 * @param defaultValueExpirationTime the default value expiration time. Or {@code null} to
262 * specify an indefinite default expiration time.
263 * @return the newly created {@code MemcacheServiceHelper} instance.
265 public static MemcacheServiceHelper
withDefaultValueExpirationTime( Expiration defaultValueExpirationTime
) {
266 return new MemcacheServiceHelper().defaultValueExpirationTime(defaultValueExpirationTime
);
270 * Creates a {@link MemcacheServiceHelper} instance which logs and absorbs any memcache
271 * service errors. By default, memcache service errors are converted to
272 * {@code DatastoreFailureException} exceptions and thrown from the memcache operation methods.
274 * @return the newly created {@code MemcacheServiceHelper} instance.
276 public static MemcacheServiceHelper
withLogAndAbsorbMemcacheServiceErrors() {
277 return new MemcacheServiceHelper().logAndAbsorbMemcacheServiceErrors();
281 * Creates a {@link MemcacheServiceHelper} instance with the default configuration.
283 public static MemcacheServiceHelper
withDefaults() {
284 return new MemcacheServiceHelper();