com.googlecode.objectify.cache.CachingAsyncDatastoreService Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of objectify Show documentation
Show all versions of objectify Show documentation
*** THIS VERSION UPLOADED FOR USE WITH CEDAR-COMMON, TO AVOID DEPENDENCIES ON GOOGLE CODE-BASED MAVEN REPOSITORIES. *** The simplest convenient interface to the Google App Engine datastore
The newest version!
package com.googlecode.objectify.cache;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.google.appengine.api.datastore.AsyncDatastoreService;
import com.google.appengine.api.datastore.DatastoreAttributes;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.Index;
import com.google.appengine.api.datastore.Index.IndexState;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyRange;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Transaction;
import com.google.appengine.api.datastore.TransactionOptions;
import com.googlecode.objectify.cache.EntityMemcache.Bucket;
import com.googlecode.objectify.util.NowFuture;
import com.googlecode.objectify.util.SimpleFutureWrapper;
/**
* A write-through memcache for Entity objects that works for both transactional
* and nontransactional sessions.
*
*
* - Caches negative results as well as positive results.
* - Queries do not affect the cache in any way.
* - Transactional reads bypass the cache, but successful transaction commits will update the cache.
* - This cache has near-transactional integrity. As long as DeadlineExceededException is not hit, cache should
* not go out of sync even under heavy contention.
*
*
* Note: Until Google adds a hook that lets us wrap native Future implementations,
* you muse install the {@code AsyncCacheFilter} to use this cache asynchronously. This
* is not necessary for synchronous use of {@code CachingDatastoreService}, but asynchronous
* operation requires an extra hook for the end of a request when fired-and-forgotten put()s
* and delete()s get processed. If you use this cache asynchronously, and you do not
* use the {@code AsyncCacheFilter}, your cache will go out of sync.
*
* @author Jeff Schnitzer
*/
public class CachingAsyncDatastoreService implements AsyncDatastoreService
{
private static final Logger log = Logger.getLogger(CachingAsyncDatastoreService.class.getName());
/** The real datastore service objects - we need both */
AsyncDatastoreService rawAsync;
/** */
EntityMemcache memcache;
/**
*/
public CachingAsyncDatastoreService(AsyncDatastoreService rawAsync, EntityMemcache memcache)
{
this.rawAsync = rawAsync;
this.memcache = memcache;
}
/* (non-Javadoc)
* @see com.google.appengine.api.datastore.AsyncDatastoreService#allocateIds(java.lang.String, long)
*/
@Override
public Future allocateIds(String kind, long num)
{
return this.rawAsync.allocateIds(kind, num);
}
/* (non-Javadoc)
* @see com.google.appengine.api.datastore.AsyncDatastoreService#allocateIds(com.google.appengine.api.datastore.Key, java.lang.String, long)
*/
@Override
public Future allocateIds(Key parent, String kind, long num)
{
return this.rawAsync.allocateIds(parent, kind, num);
}
/**
* Need this for beingTransaction()
*/
private class TransactionFutureWrapper extends SimpleFutureWrapper
{
TransactionWrapper xact;
public TransactionFutureWrapper(Future base)
{
super(base);
}
@Override
protected Transaction wrap(Transaction t)
{
if (xact == null)
xact = new TransactionWrapper(memcache, t);
return xact;
}
}
/* (non-Javadoc)
* @see com.google.appengine.api.datastore.AsyncDatastoreService#beginTransaction()
*/
@Override
public Future beginTransaction()
{
return new TransactionFutureWrapper(this.rawAsync.beginTransaction());
}
/* (non-Javadoc)
* @see com.google.appengine.api.datastore.AsyncDatastoreService#beginTransaction(com.google.appengine.api.datastore.TransactionOptions)
*/
@Override
public Future beginTransaction(TransactionOptions options)
{
return new TransactionFutureWrapper(this.rawAsync.beginTransaction(options));
}
/**
* We don't allow implicit transactions, so throw an exception if the user is trying to use one.
*/
private void checkForImplicitTransaction()
{
if (this.rawAsync.getCurrentTransaction(null) != null)
throw new UnsupportedOperationException("Implicit, thread-local transactions are not supported by the cache. You must pass in an transaction explicitly.");
}
/* (non-Javadoc)
* @see com.google.appengine.api.datastore.AsyncDatastoreService#delete(com.google.appengine.api.datastore.Key[])
*/
@Override
public Future delete(Key... keys)
{
this.checkForImplicitTransaction();
return this.delete(null, keys);
}
/* (non-Javadoc)
* @see com.google.appengine.api.datastore.AsyncDatastoreService#delete(java.lang.Iterable)
*/
@Override
public Future delete(Iterable keys)
{
this.checkForImplicitTransaction();
return this.delete(null, keys);
}
/* (non-Javadoc)
* @see com.google.appengine.api.datastore.AsyncDatastoreService#delete(com.google.appengine.api.datastore.Transaction, com.google.appengine.api.datastore.Key[])
*/
@Override
public Future delete(Transaction txn, Key... keys)
{
return this.delete(txn, Arrays.asList(keys));
}
/* (non-Javadoc)
* @see com.google.appengine.api.datastore.AsyncDatastoreService#delete(com.google.appengine.api.datastore.Transaction, java.lang.Iterable)
*/
@Override
public Future delete(final Transaction txn, final Iterable keys)
{
// Always trigger, even on failure - the delete might have succeeded even though a timeout
// exception was thrown. We will always be safe emptying the key from the cache.
Future future = new TriggerFuture(this.rawAsync.delete(txn, keys)) {
@Override
protected void trigger()
{
if (txn != null)
{
for (Key key: keys)
((TransactionWrapper)txn).deferEmptyFromCache(key);
}
else
{
memcache.empty(keys);
}
}
};
if (txn instanceof TransactionWrapper)
((TransactionWrapper)txn).enlist(future);
return future;
}
/* (non-Javadoc)
* @see com.google.appengine.api.datastore.AsyncDatastoreService#get(com.google.appengine.api.datastore.Key)
*/
@Override
public Future get(Key key)
{
this.checkForImplicitTransaction();
return this.get(null, key);
}
/* (non-Javadoc)
* @see com.google.appengine.api.datastore.AsyncDatastoreService#get(java.lang.Iterable)
*/
@Override
public Future