Skip to content

Instantly share code, notes, and snippets.

@nking
Created March 12, 2011 04:44
Show Gist options
  • Save nking/867035 to your computer and use it in GitHub Desktop.
Save nking/867035 to your computer and use it in GitHub Desktop.
memcache implementation of sharded counters with Google's Appengine
The sharded counter approach to keeping track of the number of entities of a
given type is in Appengine article:
http://code.google.com/appengine/articles/sharding_counters.html
Google's Appengine cloud services are optimized for reads, so for high frequency
updates and to keep track of entities when there are more than 1000 (=count limit),
a sharded counter approach using the datastore and memcache is used below.
The sharded counter approach is to divide the container holding the sum of values
into multiple containers that are randomly chosen and less likely to be accessed
concurrently. The sharded counter approach can be applied to counters in the
datastore, but can also be applied to counters in memcache for immediate consistency.
Memcache is an in-memory key-value store that uses the memcached
protocol. The primary purpose of using it is to have an immediately accurate
value while the datastore has time to become consistent. Note that memcache is
infrequently volatile, so when it's empty, fresh values will be fetched from the
datastore. This has the side effect of keeping memcache and the datastore
eventually consistent.
The cache below is using a concurrent hashmap from the Java sdk, but
extensions such as MapMaker from Google Guice could be used instead.
http://code.google.com/p/google-collections/
*/
public class EventCounterDAO {
private final static transient Logger log =
Logger.getLogger(EventCounterDAO.class.getName());
/**
* the number of counters to use.
*/
public final static int numberOfCounters = 20;
private static Cache cache;
static {
try {
log.info("initializing cache");
CacheFactory cacheFactory = CacheManager.getInstance().getCacheFactory();
cache = cacheFactory.createCache(
new ConcurrentHashMap<String, Integer>());
} catch (CacheException e) {
log.severe(e.getMessage());
} catch (Throwable t) {
log.severe(t.getMessage());
} finally {
}
}
public static String createCacheKey(final int counterId) {
return "Event:" + Integer.valueOf(counterId).toString();
}
public static Key createDatastoreKey(int counterId) {
String name = Integer.toString(counterId);
return KeyFactory.createKey(EventCounter.class.getSimpleName(), name);
}
public static Integer getNumberOfEntities() {
log.info("getNumberOfEntities");
int sum = 0;
try {
sum = getNumberOfEntitiesFromCache();
if (sum != 0) {
log.log(Level.INFO,
"in memcache counter, have num Event entities={0}",
Integer.toString(sum));
} else {
log.info("no entries in memcache counter, so using datastore");
sum = getNumberOfEntitiesFromDatastore();
}
} finally {
}
return Integer.valueOf(sum);
}
static int getNumberOfEntitiesFromCache() {
log.info("getNumberOfEntitiesFromCache");
int sum = 0;
// get count from cache
for (int i = 0; i < numberOfCounters; i++) {
String key = createCacheKey(i);
if (cache.containsKey(key)) {
sum += (Integer) cache.get(key);
}
}
return sum;
}
static int getNumberOfEntitiesFromDatastore() {
log.info("getNumberOfEntitiesFromDatastore");
int sum = 0;
PersistenceManager pm = null;
Transaction tx = null;
try {
pm = PMF.get().getPersistenceManager();
tx = pm.currentTransaction();
tx.begin();
sum = Util.getCountInCounterEntities(EventCounter.class, pm);
tx.commit();
} finally {
if ((tx != null) && (tx.isActive())) {
tx.rollback();
}
if (pm != null) {
pm.close();
}
}
return sum;
}
public static void incrementNumberOfEntities() {
changeNumberOfEntities(1);
}
public static void decrementNumberOfEntities() {
changeNumberOfEntities( -1);
}
static void changeCountInCache(int counterId, int delta) {
log.log(Level.INFO, "changeCountInCache");
String cacheKey = createCacheKey(counterId);
Integer updated = null;
if (cache.containsKey(cacheKey)) {
Integer count = (Integer) cache.get(cacheKey);
updated = Integer.valueOf(count.intValue() + delta);
} else {
updated = Integer.valueOf(0 + delta);
}
log.log(Level.INFO, "setting memcachecounter({0}) count={1}",
new Object[]{Integer.toString(counterId), Integer.toString(updated)});
cache.put(cacheKey, updated);
}
static void changeCountInDatastore(int counterId, int delta) {
log.log(Level.INFO, "changeCountInDatastore {0}", Integer.toString(delta));
PersistenceManager pm = null;
Transaction tx = null;
try {
Key datastoreKey = createDatastoreKey(counterId);
pm = PMF.get().getPersistenceManager();
tx = pm.currentTransaction();
tx.begin();
EventCounter eventCounter = null;
try {
eventCounter = pm.getObjectById(EventCounter.class, datastoreKey);
} catch (JDOObjectNotFoundException e) {}
if (eventCounter == null) {
eventCounter = new EventCounter();
eventCounter.setKey(datastoreKey);
eventCounter.setCounterId(counterId);
}
Integer c = eventCounter.getCount();
Integer cn = Integer.valueOf( c.intValue() + delta);
log.log(Level.INFO, "setting shard({0}) count={1}",
new Object[]{Integer.toString(counterId), Integer.toString(cn)});
eventCounter.setCount(cn);
pm.makePersistent(eventCounter);
pm.flush();
tx.commit();
} finally {
if ((tx != null) && (tx.isActive())) {
tx.rollback();
}
if (pm != null) {
pm.close();
}
}
}
public static void changeNumberOfEntities(int delta) {
log.log(Level.INFO, "changeNumberOfEntities {0}", Integer.toString(delta));
double random = Math.random(); // value between 0 and 1
int counterId = (int) (random * numberOfCounters);
changeCountInCache(counterId, delta);
changeCountInDatastore(counterId, delta);
}
public static void resetAllEntitiesToZero() {
log.log(Level.INFO, "resetAllEntitiesToZero");
resetCacheValuesToZero();
resetDatastoreValuesToZero();
}
private static void resetCacheValuesToZero() {
log.info("resetCacheValuesToZero");
for (int i = 0; i < numberOfCounters; i++) {
String key = createCacheKey(i);
cache.remove(key);
}
}
private static void resetDatastoreValuesToZero() {
log.log(Level.INFO, "resetDatastoreValuesToZero");
PersistenceManager pm = null;
Query q = null;
try {
pm = PMF.get().getPersistenceManager();
q = pm.newQuery(EventCounter.class);
Object obj = q.execute();
if (obj != null && (((List<EventCounter>) obj).size() > 0)) {
List<EventCounter> counters = (List<EventCounter>) obj;
for (EventCounter counter : counters) {
counter.setCount(Integer.valueOf(0));
}
}
pm.flush();
} finally {
if (q != null) {
q.closeAll();
}
if (pm != null) {
pm.close();
}
}
}
public static Integer initializeAndRewrite() {
log.log(Level.INFO, "initializeAndRecount");
resetAllEntitiesToZero();
int numberOfEntities = 0;
PersistenceManager pm = null;
Query q = null;
boolean useCountThis = false;
try {
pm = PMF.get().getPersistenceManager();
if (useCountThis) {
numberOfEntities = Util.getSmallCount(Event.class);
} else {
numberOfEntities = Util.getCount(Event.class, 100);
}
log.log(Level.INFO, "there are {0} Event entities",
Integer.toString(numberOfEntities));
pm.flush();
// --- update counters -----
EventCounter c = new EventCounter();
c.setCounterId(0);
c.setCount(numberOfEntities);
pm.makePersistent(c);
pm.flush();
} finally {
if (q != null) {
q.closeAll();
}
if (pm != null) {
pm.close();
}
log.log(Level.INFO, "number of event entities: {0}",
Integer.toString(numberOfEntities));
}
return numberOfEntities;
}
The sharded counter approach to keeping track of the number of entities of a
given type is in Appengine article:
http://code.google.com/appengine/articles/sharding_counters.html
Google's Appengine cloud services are optimized for reads, so for high frequency
updates and to keep track of entities when there are more than 1000 (=count limit),
a sharded counter approach using the datastore and memcache is used below.
The sharded counter approach is to divide the container holding the sum of values
into multiple containers that are randomly chosen and less likely to be accessed
concurrently. The sharded counter approach can be applied to counters in the
datastore, but can also be applied to counters in memcache for immediate consistency.
Memcache is an in-memory key-value store that uses the memcached
protocol. The primary purpose of using it is to have an immediately accurate
value while the datastore has time to become consistent. Note that memcache is
infrequently volatile, so when it's empty, the code below fetches fresh values
from the datastore. This has the side effect of keeping memcache and the datastore
eventually consistent.
The cache below is using a concurrent hashmap from the Java sdk, but
extensions such as MapMaker from Google Guice could be used instead.
http://code.google.com/p/google-collections/
*/
public class EventCounterDAO {
private final static transient Logger log =
Logger.getLogger(EventCounterDAO.class.getName());
/**
* the number of counters to use.
*/
public final static int numberOfCounters = 20;
private static Cache cache;
static {
try {
log.info("initializing cache");
CacheFactory cacheFactory = CacheManager.getInstance().getCacheFactory();
cache = cacheFactory.createCache(
new ConcurrentHashMap<String, Integer>());
} catch (CacheException e) {
log.severe(e.getMessage());
} catch (Throwable t) {
log.severe(t.getMessage());
} finally {
}
}
public static String createCacheKey(final int counterId) {
return "Event:" + Integer.valueOf(counterId).toString();
}
public static Key createDatastoreKey(int counterId) {
String name = Integer.toString(counterId);
return KeyFactory.createKey(EventCounter.class.getSimpleName(), name);
}
public static Integer getNumberOfEntities() {
log.info("getNumberOfEntities");
int sum = 0;
try {
sum = getNumberOfEntitiesFromCache();
if (sum != 0) {
log.log(Level.INFO,
"in memcache counter, have num Event entities={0}",
Integer.toString(sum));
} else {
log.info("no entries in memcache counter, so using datastore");
sum = getNumberOfEntitiesFromDatastore();
}
} finally {
}
return Integer.valueOf(sum);
}
static int getNumberOfEntitiesFromCache() {
log.info("getNumberOfEntitiesFromCache");
int sum = 0;
// get count from cache
for (int i = 0; i < numberOfCounters; i++) {
String key = createCacheKey(i);
if (cache.containsKey(key)) {
sum += (Integer) cache.get(key);
}
}
return sum;
}
static int getNumberOfEntitiesFromDatastore() {
log.info("getNumberOfEntitiesFromDatastore");
int sum = 0;
PersistenceManager pm = null;
Transaction tx = null;
try {
pm = PMF.get().getPersistenceManager();
tx = pm.currentTransaction();
tx.begin();
sum = Util.getCountInCounterEntities(EventCounter.class, pm);
tx.commit();
} finally {
if ((tx != null) && (tx.isActive())) {
tx.rollback();
}
if (pm != null) {
pm.close();
}
}
return sum;
}
public static void incrementNumberOfEntities() {
changeNumberOfEntities(1);
}
public static void decrementNumberOfEntities() {
changeNumberOfEntities( -1);
}
static void changeCountInCache(int counterId, int delta) {
log.log(Level.INFO, "changeCountInCache");
String cacheKey = createCacheKey(counterId);
Integer updated = null;
if (cache.containsKey(cacheKey)) {
Integer count = (Integer) cache.get(cacheKey);
updated = Integer.valueOf(count.intValue() + delta);
} else {
updated = Integer.valueOf(0 + delta);
}
log.log(Level.INFO, "setting memcachecounter({0}) count={1}",
new Object[]{Integer.toString(counterId), Integer.toString(updated)});
cache.put(cacheKey, updated);
}
static void changeCountInDatastore(int counterId, int delta) {
log.log(Level.INFO, "changeCountInDatastore {0}", Integer.toString(delta));
PersistenceManager pm = null;
Transaction tx = null;
try {
Key datastoreKey = createDatastoreKey(counterId);
pm = PMF.get().getPersistenceManager();
tx = pm.currentTransaction();
tx.begin();
EventCounter eventCounter = null;
try {
eventCounter = pm.getObjectById(EventCounter.class, datastoreKey);
} catch (JDOObjectNotFoundException e) {}
if (eventCounter == null) {
eventCounter = new EventCounter();
eventCounter.setKey(datastoreKey);
eventCounter.setCounterId(counterId);
}
Integer c = eventCounter.getCount();
Integer cn = Integer.valueOf( c.intValue() + delta);
log.log(Level.INFO, "setting shard({0}) count={1}",
new Object[]{Integer.toString(counterId), Integer.toString(cn)});
eventCounter.setCount(cn);
pm.makePersistent(eventCounter);
pm.flush();
tx.commit();
} finally {
if ((tx != null) && (tx.isActive())) {
tx.rollback();
}
if (pm != null) {
pm.close();
}
}
}
public static void changeNumberOfEntities(int delta) {
log.log(Level.INFO, "changeNumberOfEntities {0}", Integer.toString(delta));
double random = Math.random(); // value between 0 and 1
int counterId = (int) (random * numberOfCounters);
changeCountInCache(counterId, delta);
changeCountInDatastore(counterId, delta);
}
public static void resetAllEntitiesToZero() {
log.log(Level.INFO, "resetAllEntitiesToZero");
resetCacheValuesToZero();
resetDatastoreValuesToZero();
}
private static void resetCacheValuesToZero() {
log.info("resetCacheValuesToZero");
for (int i = 0; i < numberOfCounters; i++) {
String key = createCacheKey(i);
cache.remove(key);
}
}
private static void resetDatastoreValuesToZero() {
log.log(Level.INFO, "resetDatastoreValuesToZero");
PersistenceManager pm = null;
Query q = null;
try {
pm = PMF.get().getPersistenceManager();
q = pm.newQuery(EventCounter.class);
Object obj = q.execute();
if (obj != null && (((List<EventCounter>) obj).size() > 0)) {
List<EventCounter> counters = (List<EventCounter>) obj;
for (EventCounter counter : counters) {
counter.setCount(Integer.valueOf(0));
}
}
pm.flush();
} finally {
if (q != null) {
q.closeAll();
}
if (pm != null) {
pm.close();
}
}
}
public static Integer initializeAndRewrite() {
log.log(Level.INFO, "initializeAndRecount");
resetAllEntitiesToZero();
int numberOfEntities = 0;
PersistenceManager pm = null;
Query q = null;
boolean useCountThis = false;
try {
pm = PMF.get().getPersistenceManager();
if (useCountThis) {
numberOfEntities = Util.getSmallCount(Event.class);
} else {
numberOfEntities = Util.getCount(Event.class, 100);
}
log.log(Level.INFO, "there are {0} Event entities",
Integer.toString(numberOfEntities));
pm.flush();
// --- update counters -----
EventCounter c = new EventCounter();
c.setCounterId(0);
c.setCount(numberOfEntities);
pm.makePersistent(c);
pm.flush();
} finally {
if (q != null) {
q.closeAll();
}
if (pm != null) {
pm.close();
}
log.log(Level.INFO, "number of event entities: {0}",
Integer.toString(numberOfEntities));
}
return numberOfEntities;
}
/*
The sharded counter approach to keeping track of the number of entities of a
given type is in Appengine article:
http://code.google.com/appengine/articles/sharding_counters.html
Google's Appengine cloud services are optimized for reads, so for high frequency
updates and to keep track of entities when there are more than 1000 (=count limit),
a sharded counter approach using the datastore and memcache is used below.
The sharded counter approach is to divide the container holding the sum of values
into multiple containers that are randomly chosen and less likely to be accessed
concurrently. The sharded counter approach can be applied to counters in the
datastore, but can also be applied to counters in memcache for immediate consistency.
Memcache is an in-memory key-value store that uses the memcached
protocol. The primary purpose of using it is to have an immediately accurate
value while the datastore has time to become consistent. Note that memcache is
infrequently volatile, so when it's empty, fresh values will be fetched from the
datastore. This has the side effect of keeping memcache and the datastore
eventually consistent.
The cache below is using a concurrent hashmap from the Java sdk, but
extensions such as MapMaker from Google Guice could be used instead.
http://code.google.com/p/google-collections/
*/
public class EventCounterDAO extends AbstractCounterDAO {
public EventCounterDAO() {
super();
}
@Override
protected Logger constructLogger() {
return Logger.getLogger(this.getClass().getName());
}
@Override
public String createCacheKey(final int counterId) {
return createCacheKey("Event", counterId);
}
@Override
public Key createDatastoreKey(int counterId) {
return createDatastoreKey(EventCounter.class, counterId);
}
@Override
public int getNumberOfEntitiesFromDatastore() {
return getNumberOfEntitiesFromDatastore(EventCounter.class);
}
@Override
protected void resetDatastoreValuesToZero() {
resetDatastoreValuesToZero(EventCounter.class);
}
@Override
void changeCountInDatastore(int counterId, int delta, PersistenceManager pm) {
changeCountInDatastore(EventCounter.class, counterId, delta, pm);
}
@Override
public Integer initializeAndRewrite() {
return initializeAndRewrite(Event.class);
}
}
public abstract class AbstractCounterDAO implements ICounterDAO {
protected static Cache cache = null;
// guards use of cache:
protected ReadWriteLock rwLock = new ReentrantReadWriteLock();
protected final Logger log;
/**
* the number of counters to use.
*/
public static final int numberOfCounters = 20;
public AbstractCounterDAO() {
log = constructLogger();
}
protected boolean initCache() throws CacheException {
boolean isInitialized = false;
try {
rwLock.readLock().lock();
if (cache != null) {
isInitialized = true;
} else {
CacheFactory cacheFactory =
CacheManager.getInstance().getCacheFactory();
cache = cacheFactory.createCache(
new ConcurrentHashMap<String, Integer>());
isInitialized = true;
}
} finally {
rwLock.readLock().unlock();
}
return isInitialized;
}
abstract void changeCountInDatastore(int counterId, int delta,
PersistenceManager pm);
protected abstract Logger constructLogger();
public abstract String createCacheKey(final int counterId);
public abstract Key createDatastoreKey(int counterId);
public abstract int getNumberOfEntitiesFromDatastore();
protected abstract void resetDatastoreValuesToZero();
protected abstract Integer initializeAndRewrite();
protected String createCacheKey(String entityKind, final int counterId) {
return entityKind + ":" + Integer.valueOf(counterId).toString();
}
protected Key createDatastoreKey(Class<? extends ICounter> counterClass,
int counterId) {
String name = Integer.toString(counterId);
return KeyFactory.createKey(counterClass.getSimpleName(), name);
}
public void decrementNumberOfEntities() {
changeNumberOfEntities(-1);
}
public void incrementNumberOfEntities() {
changeNumberOfEntities(1);
}
void changeCountInCache(int counterId, int delta) throws CacheException {
changeCountInCache(this.getClass(), counterId, delta);
}
void changeCountInCache(Class<? extends AbstractCounterDAO> daoCounterClass,
int counterId, int delta) throws CacheException {
if (daoCounterClass == null) {
throw new IllegalArgumentException("daoCounterClass cannot be null");
}
log.log(Level.INFO, "changeCountInCache({0},{1},{2})",
new Object[]{daoCounterClass.getName(), counterId, delta});
if (initCache()) {
try {
String cacheKey = createCacheKey(counterId);
Integer updated = null;
Object value = cache.get(cacheKey);
if (value != null) {
Integer count = (Integer) value;
updated = Integer.valueOf(count.intValue() + delta);
} else {
updated = Integer.valueOf(0 + delta);
}
log.log(Level.INFO, "setting memcachecounter({0}) count={1}",
new Object[]{Integer.toString(counterId),
Integer.toString(updated)});
Object put = cache.put(cacheKey, updated);
} finally {
}
}
}
void changeCountInDatastore(int counterId, int delta) {
log.log(Level.INFO, "changeCountInDatastore {0}", Integer.toString(delta));
PersistenceManager pm = null;
try {
pm = PMF.get().getPersistenceManager();
changeCountInDatastore(counterId, delta, pm);
pm.flush();
} finally {
if (pm != null) {
pm.close();
}
}
}
void changeCountInDatastore(Class<? extends ICounter> counterClass,
int counterId, int delta, PersistenceManager pm) {
if (counterClass == null) {
throw new IllegalArgumentException("counterClass cannot be null");
}
if (pm == null) {
throw new IllegalArgumentException("pm cannot be null");
}
log.log(Level.INFO, "changeCountInDatastore ({0}, {1}, {2})",
new Object[]{counterClass.getName(),
Integer.toString(counterId), Integer.toString(delta)});
Transaction tx = null;
try {
tx = pm.currentTransaction();
tx.begin();
Key datastoreKey = createDatastoreKey(counterId);
Object counter = null;
try {
counter = pm.getObjectById(counterClass, datastoreKey);
} catch (JDOObjectNotFoundException e) {
}
if (counter == null) {
// dynamic creation of classes not allowed in appengine currently,
// so use hard code the constructors...
if (counterClass.equals(EventCounter.class)) {
counter = new EventCounter();
} else if (counterClass.equals(VenueCounter.class)) {
counter = new VenueCounter();
}
((ICounter)counter).setKey(datastoreKey);
((ICounter)counter).setCounterId(counterId);
}
Integer c = ((ICounter)counter).getCount();
Integer cn = Integer.valueOf(c.intValue() + delta);
log.log(Level.INFO, "setting shard({0}) count={1}",
new Object[]{Integer.toString(counterId), Integer.toString(cn)});
((ICounter)counter).setCount(cn);
pm.makePersistent(counter);
pm.flush();
tx.commit();
} finally {
if ((tx != null) && (tx.isActive())) {
tx.rollback();
}
}
}
public void changeNumberOfEntities(int delta) {
log.log(Level.INFO, "changeNumberOfEntities {0}", Integer.toString(delta));
double random = Math.random(); // value between 0 and 1
int counterId = (int) (random * numberOfCounters);
try {
changeCountInCache(counterId, delta);
} catch (CacheException e) {
log.severe(e.getMessage());
} finally {
changeCountInDatastore(counterId, delta);
}
}
public Integer getNumberOfEntities() {
log.info("getNumberOfEntities");
int sum = 0;
try {
sum = getNumberOfEntitiesFromCache();
} catch (CacheException e) {
log.severe(e.getMessage());
} finally {
if (sum != 0) {
log.log(Level.INFO,
"in memcache counter, have num Event entities={0}",
Integer.toString(sum));
} else {
log.info("no entries in memcache counter, so using datastore");
sum = getNumberOfEntitiesFromDatastore();
}
}
return Integer.valueOf(sum);
}
public int getNumberOfEntitiesFromCache() throws CacheException {
log.info("getNumberOfEntitiesFromCache");
int sum = 0;
if (initCache()) {
try {
// get count from cache
for (int i = 0; i < numberOfCounters; i++) {
String key = createCacheKey(i);
if (cache.containsKey(key)) {
sum += (Integer) cache.get(key);
}
}
} finally {
}
}
return sum;
}
public int getNumberOfEntitiesFromDatastore(Class<? extends ICounter> counterClass) {
if (counterClass == null) {
throw new IllegalArgumentException("counterClass cannot be null");
}
log.log(Level.INFO,"getNumberOfEntitiesFromDatastore ({0})",
counterClass.getName());
int sum = 0;
PersistenceManager pm = null;
Transaction tx = null;
try {
pm = PMF.get().getPersistenceManager();
tx = pm.currentTransaction();
tx.begin();
sum = Util.getCountInCounterEntities(counterClass, pm);
tx.commit();
} finally {
if ((tx != null) && (tx.isActive())) {
tx.rollback();
}
if (pm != null) {
pm.close();
}
}
return sum;
}
public void resetAllEntitiesToZero() {
log.log(Level.INFO, "resetAllEntitiesToZero");
try {
resetCacheValuesToZero();
} catch (CacheException e) {
log.severe(e.getMessage());
} finally {
resetDatastoreValuesToZero();
}
}
protected void resetCacheValuesToZero() throws CacheException {
log.info("resetCacheValuesToZero");
if (initCache()) {
try {
for (int i = 0; i < numberOfCounters; i++) {
String key = createCacheKey(i);
cache.remove(key);
}
} finally {
}
}
}
protected void resetDatastoreValuesToZero(Class<? extends ICounter> counterClass) {
if (counterClass == null) {
throw new IllegalArgumentException("counterClass cannot be null");
}
log.log(Level.INFO, "resetDatastoreValuesToZero({0})", counterClass.getName());
PersistenceManager pm = null;
Query q = null;
try {
pm = PMF.get().getPersistenceManager();
q = pm.newQuery(counterClass);
List<ICounter> results = (List<ICounter>)q.execute();
for(ICounter counter : results) {
counter.setCount(Integer.valueOf(0));
}
pm.flush();
} finally {
if (q != null) {
q.closeAll();
}
if (pm != null) {
pm.close();
}
}
}
protected Integer initializeAndRewrite(Class<?> countedEntityClass) {
log.log(Level.INFO, "initializeAndRewrite({0})", countedEntityClass.getName());
resetAllEntitiesToZero();
int numberOfEntities = 0;
PersistenceManager pm = null;
boolean useCountThis = false;
try {
pm = PMF.get().getPersistenceManager();
if (useCountThis) {
numberOfEntities = Util.getSmallCount(countedEntityClass);
} else {
numberOfEntities = Util.getCount(countedEntityClass, 500);
}
log.log(Level.INFO, "there are {0} entities",
Integer.toString(numberOfEntities));
pm.flush();
pm.evictAll();
changeNumberOfEntities(numberOfEntities);
} finally {
if (pm != null) {
pm.close();
}
log.log(Level.INFO, "number of {0} entities: {1}",
new Object[]{countedEntityClass.getName(),
Integer.toString(numberOfEntities)});
}
return numberOfEntities;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment