mirror of
https://github.com/apache/openjpa.git
synced 2025-02-21 01:15:30 +00:00
OPENJPA-1739 Added basic stats tracking to query result cache. Updates to JMX provider. Added tests to jmx module for query cache and prepared SQL cache instruments.
git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@984633 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
0876c93ff6
commit
25ccc95d6c
@ -19,14 +19,22 @@
|
||||
package org.apache.openjpa.integration.jmx;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.ObjectName;
|
||||
import javax.persistence.Query;
|
||||
|
||||
import org.apache.openjpa.instrumentation.DataCacheInstrument;
|
||||
import org.apache.openjpa.instrumentation.InstrumentationManager;
|
||||
import org.apache.openjpa.instrumentation.PreparedQueryCacheInstrument;
|
||||
import org.apache.openjpa.instrumentation.QueryCacheInstrument;
|
||||
import org.apache.openjpa.instrumentation.jmx.JMXProvider;
|
||||
import org.apache.openjpa.lib.instrumentation.Instrument;
|
||||
import org.apache.openjpa.lib.instrumentation.InstrumentationProvider;
|
||||
@ -35,11 +43,10 @@ import org.apache.openjpa.persistence.OpenJPAEntityManagerSPI;
|
||||
import org.apache.openjpa.persistence.test.AbstractPersistenceTestCase;
|
||||
|
||||
public class TestJMXPlatformMBeans extends AbstractPersistenceTestCase {
|
||||
/**
|
||||
* Verifies the data cache metrics are available through simple instrumentation.
|
||||
*/
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
/**
|
||||
* Verifies data cache metrics are available through simple instrumentation.
|
||||
*/
|
||||
public void testDataCacheInstrument() {
|
||||
OpenJPAEntityManagerFactorySPI oemf = createNamedEMF("openjpa-integration-jmx");
|
||||
|
||||
@ -78,30 +85,167 @@ public class TestJMXPlatformMBeans extends AbstractPersistenceTestCase {
|
||||
// Thread out to do out-of-band MBean-based validation. This could
|
||||
// have been done on the same thread, but threading out makes for a
|
||||
// more realistic test.
|
||||
Thread thr = new Thread(new DCMBeanThread());
|
||||
thr.start();
|
||||
ExecutorService executor = Executors.newFixedThreadPool(1);
|
||||
Future<Boolean> result = executor.submit(new DCMBeanCallable());
|
||||
try {
|
||||
thr.join(60000); // Wait for 1 minute for the MBean thread to return.
|
||||
if (thr.isAlive()) {
|
||||
// MBean did not return within a minute, interrupt it.
|
||||
thr.interrupt();
|
||||
Thread.sleep(5000);
|
||||
if (thr.isAlive()) {
|
||||
// Attempt to hard kill the thread to prevent the test from hanging
|
||||
thr.stop();
|
||||
}
|
||||
fail("DataCache MBean verification thread failed.");
|
||||
}
|
||||
assertTrue(result.get());
|
||||
} catch (Throwable t) {
|
||||
fail("Caught unexpected throwable: " + t);
|
||||
fail("DataCache verification failed: " + t);
|
||||
}
|
||||
|
||||
closeEMF(oemf);
|
||||
}
|
||||
|
||||
public class DCMBeanThread implements Runnable {
|
||||
/**
|
||||
* Verifies query cache metrics are available through simple instrumentation.
|
||||
*/
|
||||
public void testQueryCacheInstrument() {
|
||||
OpenJPAEntityManagerFactorySPI oemf = createNamedEMF("openjpa-integration-jmx");
|
||||
|
||||
// Verify an EMF was created with the supplied instrumentation
|
||||
assertNotNull(oemf);
|
||||
|
||||
public void run() {
|
||||
// Verify an instrumentation manager is available
|
||||
InstrumentationManager mgr = oemf.getConfiguration().getInstrumentationManagerInstance();
|
||||
assertNotNull(mgr);
|
||||
|
||||
// Get the in-band data cache instrument
|
||||
Set<InstrumentationProvider> providers = mgr.getProviders();
|
||||
assertNotNull(providers);
|
||||
assertEquals(1, providers.size());
|
||||
InstrumentationProvider provider = providers.iterator().next();
|
||||
assertEquals(provider.getClass(), JMXProvider.class);
|
||||
|
||||
Instrument inst = provider.getInstrumentByName("QueryCache");
|
||||
assertNotNull(inst);
|
||||
assertTrue(inst instanceof QueryCacheInstrument);
|
||||
QueryCacheInstrument qci = (QueryCacheInstrument)inst;
|
||||
|
||||
assertEquals(0,qci.getExecutionCount());
|
||||
assertEquals(0,qci.getTotalExecutionCount());
|
||||
assertEquals(0,qci.getHitCount());
|
||||
assertEquals(0,qci.getTotalHitCount());
|
||||
|
||||
OpenJPAEntityManagerSPI oem = oemf.createEntityManager();
|
||||
|
||||
CachedEntity ce = new CachedEntity();
|
||||
int id = new Random().nextInt();
|
||||
ce.setId(id);
|
||||
CachedEntity ce2 = new CachedEntity();
|
||||
id = new Random().nextInt();
|
||||
ce2.setId(id);
|
||||
|
||||
oem.getTransaction().begin();
|
||||
oem.persist(ce);
|
||||
oem.persist(ce2);
|
||||
oem.getTransaction().commit();
|
||||
|
||||
Query q = oem.createQuery("SELECT ce FROM CachedEntity ce");
|
||||
|
||||
List<?> result = q.getResultList();
|
||||
assertNotNull(result);
|
||||
assertTrue(result.size() > 1);
|
||||
oem.clear();
|
||||
|
||||
result = q.getResultList();
|
||||
|
||||
assertTrue(qci.getExecutionCount() > 0);
|
||||
assertTrue(qci.getTotalExecutionCount() > 0);
|
||||
assertTrue(qci.getHitCount() > 0);
|
||||
assertTrue(qci.getTotalHitCount() > 0);
|
||||
|
||||
// Thread out to do out-of-band MBean-based validation. This could
|
||||
// have been done on the same thread, but threading out makes for a
|
||||
// more realistic test.
|
||||
ExecutorService executor = Executors.newFixedThreadPool(1);
|
||||
Future<Boolean> execResult = executor.submit(new QueryCachesMBeanCallable(
|
||||
QueryCachesMBeanCallable.QC_OBJNAME,
|
||||
QueryCachesMBeanCallable.QC_QM));
|
||||
try {
|
||||
assertTrue(execResult.get());
|
||||
} catch (Throwable t) {
|
||||
fail("QueryCache verification failed: " + t);
|
||||
}
|
||||
closeEMF(oemf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies prepared query cache metrics are available through simple instrumentation.
|
||||
*/
|
||||
public void testPreparedQueryCacheInstrument() {
|
||||
OpenJPAEntityManagerFactorySPI oemf = createNamedEMF("openjpa-integration-jmx-qsc");
|
||||
|
||||
// Verify an EMF was created with the supplied instrumentation
|
||||
assertNotNull(oemf);
|
||||
|
||||
// Verify an instrumentation manager is available
|
||||
InstrumentationManager mgr = oemf.getConfiguration().getInstrumentationManagerInstance();
|
||||
assertNotNull(mgr);
|
||||
|
||||
// Get the in-band data cache instrument
|
||||
Set<InstrumentationProvider> providers = mgr.getProviders();
|
||||
assertNotNull(providers);
|
||||
assertEquals(1, providers.size());
|
||||
InstrumentationProvider provider = providers.iterator().next();
|
||||
assertEquals(provider.getClass(), JMXProvider.class);
|
||||
|
||||
Instrument inst = provider.getInstrumentByName("QuerySQLCache");
|
||||
assertNotNull(inst);
|
||||
assertTrue(inst instanceof PreparedQueryCacheInstrument);
|
||||
PreparedQueryCacheInstrument qci = (PreparedQueryCacheInstrument)inst;
|
||||
|
||||
assertEquals(0,qci.getExecutionCount());
|
||||
assertEquals(0,qci.getTotalExecutionCount());
|
||||
assertEquals(0,qci.getHitCount());
|
||||
assertEquals(0,qci.getTotalHitCount());
|
||||
|
||||
OpenJPAEntityManagerSPI oem = oemf.createEntityManager();
|
||||
|
||||
CachedEntity ce = new CachedEntity();
|
||||
int id = new Random().nextInt();
|
||||
ce.setId(id);
|
||||
CachedEntity ce2 = new CachedEntity();
|
||||
id = new Random().nextInt();
|
||||
ce2.setId(id);
|
||||
|
||||
oem.getTransaction().begin();
|
||||
oem.persist(ce);
|
||||
oem.persist(ce2);
|
||||
oem.getTransaction().commit();
|
||||
|
||||
Query q = oem.createQuery("SELECT ce FROM CachedEntity ce");
|
||||
|
||||
List<?> result = q.getResultList();
|
||||
assertNotNull(result);
|
||||
assertTrue(result.size() > 1);
|
||||
oem.clear();
|
||||
|
||||
result = q.getResultList();
|
||||
|
||||
assertTrue(qci.getExecutionCount() > 0);
|
||||
assertTrue(qci.getTotalExecutionCount() > 0);
|
||||
assertTrue(qci.getHitCount() > 0);
|
||||
assertTrue(qci.getTotalHitCount() > 0);
|
||||
|
||||
// Thread out to do out-of-band MBean-based validation. This could
|
||||
// have been done on the same thread, but threading out makes for a
|
||||
// more realistic test.
|
||||
ExecutorService executor = Executors.newFixedThreadPool(1);
|
||||
Future<Boolean> execResult = executor.submit(new QueryCachesMBeanCallable(
|
||||
QueryCachesMBeanCallable.QSC_OBJNAME,
|
||||
QueryCachesMBeanCallable.QSC_QM));
|
||||
try {
|
||||
assertTrue(execResult.get());
|
||||
} catch (Throwable t) {
|
||||
fail("QueryCache verification failed: " + t);
|
||||
}
|
||||
|
||||
closeEMF(oemf);
|
||||
}
|
||||
|
||||
public class DCMBeanCallable implements Callable<Boolean> {
|
||||
|
||||
public Boolean call() {
|
||||
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
|
||||
assertNotNull(mbs);
|
||||
ObjectName objname = null;
|
||||
@ -144,7 +288,88 @@ public class TestJMXPlatformMBeans extends AbstractPersistenceTestCase {
|
||||
assertEquals(0, clsWriteCount);
|
||||
} catch (Exception e) {
|
||||
fail("Unexpected exception: " + e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public class QueryCachesMBeanCallable implements Callable<Boolean> {
|
||||
|
||||
public static final String QC_OBJNAME = "org.apache.openjpa:type=QueryCache,cfgid=openjpa-integration-jmx,*";
|
||||
public static final String QSC_OBJNAME = "org.apache.openjpa:type=QuerySQLCache,cfgid=openjpa-integration-jmx-qsc,*";
|
||||
public static final String QC_QM = "queryKeys";
|
||||
public static final String QSC_QM = "queries";
|
||||
|
||||
private String _objNameStr;
|
||||
private String _queryMethod;
|
||||
|
||||
public QueryCachesMBeanCallable(String objName, String queryMethod) {
|
||||
setObjName(objName);
|
||||
setQueryMethod(queryMethod);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Boolean call() {
|
||||
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
|
||||
assertNotNull(mbs);
|
||||
ObjectName objname = null;
|
||||
try {
|
||||
// Query for the QueryCache bean
|
||||
objname = new ObjectName(getObjName());
|
||||
Set<ObjectName> ons = mbs.queryNames(objname, null);
|
||||
assertEquals(1, ons.size());
|
||||
ObjectName on = ons.iterator().next();
|
||||
// Assert query cache attributes can be accessed and are being updated through the MBean
|
||||
long hitCount = (Long)mbs.getAttribute(on, "HitCount");
|
||||
long execCount = (Long)mbs.getAttribute(on, "ExecutionCount");
|
||||
assertTrue(hitCount > 0);
|
||||
assertTrue(execCount > 0);
|
||||
// Assert data cache MBean methods can be invoked
|
||||
|
||||
Set<String> keys = (Set<String>)mbs.invoke(on, getQueryMethod(), null, null);
|
||||
assertNotNull(keys);
|
||||
assertTrue(keys.size() > 0);
|
||||
String[] sigs = new String[] { "java.lang.String" };
|
||||
for (String key : keys) {
|
||||
Object[] parms = new Object[] { key };
|
||||
long queryHitCount = (Long)mbs.invoke(on, "getHitCount", parms, sigs);
|
||||
long queryReadCount = (Long)mbs.invoke(on, "getExecutionCount", parms, sigs);
|
||||
assertTrue(queryHitCount > 0);
|
||||
assertTrue(queryReadCount > 0);
|
||||
}
|
||||
// Invoke the reset method and recollect stats
|
||||
mbs.invoke(on, "reset", null, null);
|
||||
hitCount = (Long)mbs.getAttribute(on, "HitCount");
|
||||
execCount = (Long)mbs.getAttribute(on, "ExecutionCount");
|
||||
assertEquals(0, hitCount);
|
||||
assertEquals(0, execCount);
|
||||
|
||||
keys = (Set<String>)mbs.invoke(on, getQueryMethod(), null, null);
|
||||
assertNotNull(keys);
|
||||
assertEquals(0, keys.size());
|
||||
} catch (Exception e) {
|
||||
fail("Unexpected exception: " + e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setObjName(String objNameStr) {
|
||||
this._objNameStr = objNameStr;
|
||||
}
|
||||
|
||||
public String getObjName() {
|
||||
return _objNameStr;
|
||||
}
|
||||
|
||||
public void setQueryMethod(String _queryMethod) {
|
||||
this._queryMethod = _queryMethod;
|
||||
}
|
||||
|
||||
public String getQueryMethod() {
|
||||
return _queryMethod;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -24,11 +24,23 @@
|
||||
<description>PU for JMX Platform MBean Testing</description>
|
||||
<class>org.apache.openjpa.integration.jmx.CachedEntity</class>
|
||||
<properties>
|
||||
<property name="openjpa.Instrumentation" value="jmx(Instrument=DataCache)"/>
|
||||
<property name="openjpa.Instrumentation" value="jmx(Instrument='DataCache,QueryCache')"/>
|
||||
<property name="openjpa.DataCache" value="true(EnableStatistics=true)"/>
|
||||
<property name="openjpa.QueryCache" value="true(EnableStatistics=true)"/>
|
||||
<property name="openjpa.RemoteCommitProvider" value="sjvm"/>
|
||||
<property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema"/>
|
||||
<property name="openjpa.DynamicEnhancementAgent" value="false"/>
|
||||
</properties>
|
||||
</persistence-unit>
|
||||
|
||||
<persistence-unit name="openjpa-integration-jmx-qsc">
|
||||
<description>PU for JMX Platform MBean Testing with PreparedQueryCache/QuerySQLCache</description>
|
||||
<class>org.apache.openjpa.integration.jmx.CachedEntity</class>
|
||||
<properties>
|
||||
<property name="openjpa.Instrumentation" value="jmx(Instrument=QuerySQLCache)"/>
|
||||
<property name="openjpa.jdbc.QuerySQLCache" value="true(EnableStatistics=true)"/>
|
||||
<property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema"/>
|
||||
<property name="openjpa.DynamicEnhancementAgent" value="false"/>
|
||||
</properties>
|
||||
</persistence-unit>
|
||||
</persistence>
|
||||
|
@ -18,9 +18,11 @@
|
||||
*/
|
||||
package org.apache.openjpa.datacache;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.security.AccessController;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
@ -32,6 +34,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import org.apache.openjpa.conf.OpenJPAConfiguration;
|
||||
import org.apache.openjpa.event.RemoteCommitEvent;
|
||||
import org.apache.openjpa.event.RemoteCommitListener;
|
||||
import org.apache.openjpa.kernel.QueryStatistics;
|
||||
import org.apache.openjpa.lib.conf.Configurable;
|
||||
import org.apache.openjpa.lib.conf.Configuration;
|
||||
import org.apache.openjpa.lib.log.Log;
|
||||
@ -39,6 +42,7 @@ import org.apache.openjpa.lib.util.J2DoPrivHelper;
|
||||
import org.apache.openjpa.lib.util.Localizer;
|
||||
import org.apache.openjpa.lib.util.concurrent.AbstractConcurrentEventManager;
|
||||
import org.apache.openjpa.lib.util.concurrent.ConcurrentReferenceHashSet;
|
||||
import org.apache.openjpa.lib.util.concurrent.SizedConcurrentHashMap;
|
||||
import org.apache.openjpa.meta.ClassMetaData;
|
||||
import org.apache.openjpa.meta.MetaDataRepository;
|
||||
import org.apache.openjpa.util.Id;
|
||||
@ -77,7 +81,21 @@ public abstract class AbstractQueryCache
|
||||
|
||||
// default evict policy
|
||||
public EvictPolicy evictPolicy = EvictPolicy.DEFAULT;
|
||||
|
||||
private QueryStatistics<QueryKey> _stats;
|
||||
private boolean _statsEnabled = false;
|
||||
|
||||
public void setEnableStatistics(boolean enable){
|
||||
_statsEnabled = enable;
|
||||
}
|
||||
public boolean getEnableStatistics(){
|
||||
return _statsEnabled;
|
||||
}
|
||||
|
||||
public QueryStatistics<QueryKey> getStatistics() {
|
||||
return _stats;
|
||||
}
|
||||
|
||||
public void initialize(DataCacheManager manager) {
|
||||
if (evictPolicy == EvictPolicy.TIMESTAMP) {
|
||||
entityTimestampMap = new ConcurrentHashMap<String,Long>();
|
||||
@ -137,6 +155,9 @@ public abstract class AbstractQueryCache
|
||||
}
|
||||
|
||||
public QueryResult get(QueryKey key) {
|
||||
if (_statsEnabled) {
|
||||
_stats.recordExecution(key);
|
||||
}
|
||||
QueryResult o = getInternal(key);
|
||||
if (o != null && o.isTimedOut()) {
|
||||
o = null;
|
||||
@ -151,6 +172,9 @@ public abstract class AbstractQueryCache
|
||||
else
|
||||
log.trace(s_loc.get("cache-hit", key));
|
||||
}
|
||||
if (_statsEnabled && o != null) {
|
||||
((Default<QueryKey>)_stats).recordHit(key);
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
@ -200,6 +224,9 @@ public abstract class AbstractQueryCache
|
||||
clearInternal();
|
||||
if (log.isTraceEnabled())
|
||||
log.trace(s_loc.get("cache-clear", "<query-cache>"));
|
||||
if (_statsEnabled) {
|
||||
_stats.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
@ -339,6 +366,8 @@ public abstract class AbstractQueryCache
|
||||
}
|
||||
|
||||
public void endConfiguration() {
|
||||
_stats = _statsEnabled ? new Default<QueryKey>() :
|
||||
new QueryStatistics.None<QueryKey>();
|
||||
}
|
||||
|
||||
// ---------- AbstractEventManager implementation ----------
|
||||
@ -418,4 +447,159 @@ public abstract class AbstractQueryCache
|
||||
public String getName() {
|
||||
return _name;
|
||||
}
|
||||
|
||||
/**
|
||||
* A default implementation of query statistics for the Query result cache.
|
||||
*
|
||||
* Maintains statistics for only a fixed number of queries.
|
||||
* Statistical counts are approximate and not exact (to keep thread synchorization overhead low).
|
||||
*
|
||||
*/
|
||||
public static class Default<T> implements QueryStatistics<T> {
|
||||
|
||||
private static final long serialVersionUID = -7889619105916307055L;
|
||||
|
||||
private static final int FIXED_SIZE = 1000;
|
||||
private static final float LOAD_FACTOR = 0.75f;
|
||||
private static final int CONCURRENCY = 16;
|
||||
|
||||
private static final int ARRAY_SIZE = 2;
|
||||
private static final int READ = 0;
|
||||
private static final int HIT = 1;
|
||||
|
||||
private long[] astat = new long[ARRAY_SIZE];
|
||||
private long[] stat = new long[ARRAY_SIZE];
|
||||
private Map<T, long[]> stats = new SizedConcurrentHashMap(FIXED_SIZE, LOAD_FACTOR, CONCURRENCY);
|
||||
private Map<T, long[]> astats = new SizedConcurrentHashMap(FIXED_SIZE, LOAD_FACTOR, CONCURRENCY);
|
||||
private Date start = new Date();
|
||||
private Date since = start;
|
||||
|
||||
public Set<T> keys() {
|
||||
return stats.keySet();
|
||||
}
|
||||
|
||||
public long getExecutionCount() {
|
||||
return stat[READ];
|
||||
}
|
||||
|
||||
public long getTotalExecutionCount() {
|
||||
return astat[READ];
|
||||
}
|
||||
|
||||
public long getExecutionCount(T query) {
|
||||
return getCount(stats, query, READ);
|
||||
}
|
||||
|
||||
public long getTotalExecutionCount(T query) {
|
||||
return getCount(astats, query, READ);
|
||||
}
|
||||
|
||||
public long getHitCount() {
|
||||
return stat[HIT];
|
||||
}
|
||||
|
||||
public long getTotalHitCount() {
|
||||
return astat[HIT];
|
||||
}
|
||||
|
||||
public long getHitCount(T query) {
|
||||
return getCount(stats, query, HIT);
|
||||
}
|
||||
|
||||
public long getTotalHitCount(T query) {
|
||||
return getCount(astats, query, HIT);
|
||||
}
|
||||
|
||||
private long getCount(Map<T, long[]> target, T query, int i) {
|
||||
long[] row = target.get(query);
|
||||
return (row == null) ? 0 : row[i];
|
||||
}
|
||||
|
||||
public Date since() {
|
||||
return since;
|
||||
}
|
||||
|
||||
public Date start() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public synchronized void reset() {
|
||||
stat = new long[ARRAY_SIZE];
|
||||
stats.clear();
|
||||
since = new Date();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public synchronized void clear() {
|
||||
astat = new long[ARRAY_SIZE];
|
||||
stat = new long[ARRAY_SIZE];
|
||||
stats = new SizedConcurrentHashMap(FIXED_SIZE, LOAD_FACTOR, CONCURRENCY);
|
||||
astats = new SizedConcurrentHashMap(FIXED_SIZE, LOAD_FACTOR, CONCURRENCY);
|
||||
start = new Date();
|
||||
since = start;
|
||||
}
|
||||
|
||||
private void addSample(T query, int index) {
|
||||
stat[index]++;
|
||||
astat[index]++;
|
||||
addSample(stats, query, index);
|
||||
addSample(astats, query, index);
|
||||
}
|
||||
|
||||
private void addSample(Map<T, long[]> target, T query, int i) {
|
||||
long[] row = target.get(query);
|
||||
if (row == null) {
|
||||
row = new long[ARRAY_SIZE];
|
||||
}
|
||||
row[i]++;
|
||||
target.put(query, row);
|
||||
}
|
||||
|
||||
public void recordExecution(T query) {
|
||||
if (query == null)
|
||||
return;
|
||||
addSample(query, READ);
|
||||
}
|
||||
|
||||
public void recordHit(T query) {
|
||||
addSample(query, HIT);
|
||||
}
|
||||
|
||||
public void dump(PrintStream out) {
|
||||
String header = "Query Statistics starting from " + start;
|
||||
out.print(header);
|
||||
if (since == start) {
|
||||
out.println();
|
||||
out.println("Total Query Execution: " + toString(astat));
|
||||
out.println("\tTotal \t\tQuery");
|
||||
} else {
|
||||
out.println(" last reset on " + since);
|
||||
out.println("Total Query Execution since start " +
|
||||
toString(astat) + " since reset " + toString(stat));
|
||||
out.println("\tSince Start \tSince Reset \t\tQuery");
|
||||
}
|
||||
int i = 0;
|
||||
for (T key : stats.keySet()) {
|
||||
i++;
|
||||
long[] arow = astats.get(key);
|
||||
if (since == start) {
|
||||
out.println(i + ". \t" + toString(arow) + " \t" + key);
|
||||
} else {
|
||||
long[] row = stats.get(key);
|
||||
out.println(i + ". \t" + toString(arow) + " \t" + toString(row) + " \t\t" + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
long pct(long per, long cent) {
|
||||
if (cent <= 0)
|
||||
return 0;
|
||||
return (100*per)/cent;
|
||||
}
|
||||
|
||||
String toString(long[] row) {
|
||||
return row[READ] + ":" + row[HIT] + "(" + pct(row[HIT], row[READ]) + "%)";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
package org.apache.openjpa.datacache;
|
||||
|
||||
import org.apache.commons.lang.ObjectUtils;
|
||||
import org.apache.openjpa.kernel.QueryStatistics;
|
||||
import org.apache.openjpa.util.RuntimeExceptionTranslator;
|
||||
|
||||
/**
|
||||
@ -216,4 +217,14 @@ public class DelegatingQueryCache
|
||||
throw translate(re);
|
||||
}
|
||||
}
|
||||
|
||||
public QueryStatistics<QueryKey> getStatistics() {
|
||||
if (_cache == null)
|
||||
return null;
|
||||
try {
|
||||
return _cache.getStatistics();
|
||||
} catch (RuntimeException re) {
|
||||
throw translate(re);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ package org.apache.openjpa.datacache;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.openjpa.kernel.QueryStatistics;
|
||||
import org.apache.openjpa.lib.util.Closeable;
|
||||
|
||||
/**
|
||||
@ -135,4 +136,11 @@ public interface QueryCache
|
||||
* Free the resources used by this cache.
|
||||
*/
|
||||
public void close ();
|
||||
|
||||
/**
|
||||
* Gets the simple statistics for query results.
|
||||
* If the statistics gathering is disabled, an empty statistics is returned.
|
||||
* @since 2.1.0
|
||||
*/
|
||||
public QueryStatistics<QueryKey> getStatistics();
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
package org.apache.openjpa.instrumentation;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.openjpa.kernel.PreparedQueryCache;
|
||||
import org.apache.openjpa.kernel.QueryStatistics;
|
||||
@ -141,6 +142,13 @@ public abstract class AbstractPreparedQueryCacheInstrument extends AbstractInstr
|
||||
return null;
|
||||
}
|
||||
|
||||
public Set<String> queries() {
|
||||
QueryStatistics<String> stats = getStatistics();
|
||||
if (stats != null)
|
||||
return stats.keys();
|
||||
return null;
|
||||
}
|
||||
|
||||
public InstrumentationLevel getLevel() {
|
||||
return InstrumentationLevel.FACTORY;
|
||||
}
|
||||
|
@ -19,9 +19,12 @@
|
||||
package org.apache.openjpa.instrumentation;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.openjpa.datacache.CacheStatistics;
|
||||
import org.apache.openjpa.datacache.QueryCache;
|
||||
import org.apache.openjpa.datacache.QueryKey;
|
||||
import org.apache.openjpa.kernel.QueryStatistics;
|
||||
import org.apache.openjpa.lib.instrumentation.AbstractInstrument;
|
||||
import org.apache.openjpa.lib.instrumentation.InstrumentationLevel;
|
||||
|
||||
@ -46,94 +49,143 @@ public abstract class AbstractQueryCacheInstrument extends AbstractInstrument
|
||||
_qc = qc;
|
||||
}
|
||||
|
||||
// TODO : Cache stats must be added to query cache. They will likely be
|
||||
// tracked by a QueryStatistics type when that takes place.
|
||||
private CacheStatistics getStatistics() {
|
||||
if (_qc == null)
|
||||
return null;
|
||||
return null; // _qc.getStatistics();
|
||||
}
|
||||
|
||||
public long getHitCount() {
|
||||
CacheStatistics stats = getStatistics();
|
||||
if (stats != null)
|
||||
return stats.getHitCount();
|
||||
return NO_STATS;
|
||||
}
|
||||
|
||||
public long getReadCount() {
|
||||
CacheStatistics stats = getStatistics();
|
||||
if (stats != null)
|
||||
return stats.getReadCount();
|
||||
return NO_STATS;
|
||||
}
|
||||
|
||||
public long getTotalHitCount() {
|
||||
CacheStatistics stats = getStatistics();
|
||||
if (stats != null)
|
||||
return stats.getTotalHitCount();
|
||||
return NO_STATS;
|
||||
}
|
||||
|
||||
public long getTotalReadCount() {
|
||||
CacheStatistics stats = getStatistics();
|
||||
if (stats != null)
|
||||
return stats.getTotalReadCount();
|
||||
return NO_STATS;
|
||||
}
|
||||
|
||||
public long getTotalWriteCount() {
|
||||
CacheStatistics stats = getStatistics();
|
||||
if (stats != null)
|
||||
return stats.getTotalWriteCount();
|
||||
return NO_STATS;
|
||||
}
|
||||
|
||||
public long getWriteCount() {
|
||||
CacheStatistics stats = getStatistics();
|
||||
if (stats != null)
|
||||
return stats.getWriteCount();
|
||||
return NO_STATS;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
CacheStatistics stats = getStatistics();
|
||||
if (stats != null)
|
||||
stats.reset();
|
||||
}
|
||||
|
||||
public Date sinceDate() {
|
||||
CacheStatistics stats = getStatistics();
|
||||
if (stats != null)
|
||||
return stats.since();
|
||||
return null;
|
||||
}
|
||||
|
||||
public Date startDate() {
|
||||
CacheStatistics stats = getStatistics();
|
||||
if (stats != null)
|
||||
return stats.start();
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getConfigId() {
|
||||
return _configId;
|
||||
}
|
||||
|
||||
public void setConfigId(String cid) {
|
||||
_configId = cid;
|
||||
}
|
||||
|
||||
public String getContextRef() {
|
||||
return _configRef;
|
||||
}
|
||||
|
||||
public void setContextRef(String cref) {
|
||||
_configRef = cref;
|
||||
}
|
||||
|
||||
public String getConfigId() {
|
||||
return _configId;
|
||||
}
|
||||
|
||||
public String getContextRef() {
|
||||
return _configRef;
|
||||
}
|
||||
|
||||
public void setPreparedQueryCache(QueryCache qc) {
|
||||
_qc = qc;
|
||||
}
|
||||
|
||||
private QueryStatistics<QueryKey> getStatistics() {
|
||||
if (_qc == null)
|
||||
return null;
|
||||
return _qc.getStatistics();
|
||||
}
|
||||
|
||||
public long getExecutionCount() {
|
||||
QueryStatistics<QueryKey> stats = getStatistics();
|
||||
if (stats != null)
|
||||
return stats.getExecutionCount();
|
||||
return NO_STATS;
|
||||
}
|
||||
|
||||
public long getExecutionCount(String queryKey) {
|
||||
QueryStatistics<QueryKey> stats = getStatistics();
|
||||
if (stats != null) {
|
||||
QueryKey qk = findKey(queryKey);
|
||||
return stats.getExecutionCount(qk);
|
||||
}
|
||||
return NO_STATS;
|
||||
}
|
||||
|
||||
public long getTotalExecutionCount() {
|
||||
QueryStatistics<QueryKey> stats = getStatistics();
|
||||
if (stats != null)
|
||||
return stats.getTotalExecutionCount();
|
||||
return NO_STATS;
|
||||
}
|
||||
|
||||
public long getTotalExecutionCount(String queryKey) {
|
||||
QueryStatistics<QueryKey> stats = getStatistics();
|
||||
if (stats != null) {
|
||||
QueryKey qk = findKey(queryKey);
|
||||
return stats.getTotalExecutionCount(qk);
|
||||
}
|
||||
return NO_STATS;
|
||||
}
|
||||
|
||||
public long getHitCount() {
|
||||
QueryStatistics<QueryKey> stats = getStatistics();
|
||||
if (stats != null)
|
||||
return stats.getHitCount();
|
||||
return NO_STATS;
|
||||
}
|
||||
|
||||
public long getHitCount(String queryKey) {
|
||||
QueryStatistics<QueryKey> stats = getStatistics();
|
||||
if (stats != null) {
|
||||
QueryKey qk = findKey(queryKey);
|
||||
return stats.getHitCount(qk);
|
||||
}
|
||||
return NO_STATS;
|
||||
}
|
||||
|
||||
public long getTotalHitCount() {
|
||||
QueryStatistics<QueryKey> stats = getStatistics();
|
||||
if (stats != null)
|
||||
return stats.getTotalHitCount();
|
||||
return NO_STATS;
|
||||
}
|
||||
|
||||
public long getTotalHitCount(String queryKey) {
|
||||
QueryStatistics<QueryKey> stats = getStatistics();
|
||||
if (stats != null) {
|
||||
QueryKey qk = findKey(queryKey);
|
||||
return stats.getTotalHitCount(qk);
|
||||
}
|
||||
return NO_STATS;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
QueryStatistics<QueryKey> stats = getStatistics();
|
||||
if (stats != null)
|
||||
stats.reset();
|
||||
}
|
||||
|
||||
public Date sinceDate() {
|
||||
QueryStatistics<QueryKey> stats = getStatistics();
|
||||
if (stats != null)
|
||||
return stats.since();
|
||||
return null;
|
||||
}
|
||||
|
||||
public Date startDate() {
|
||||
QueryStatistics<QueryKey> stats = getStatistics();
|
||||
if (stats != null)
|
||||
return stats.start();
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all query keys currently tracked in the cache.
|
||||
* @return
|
||||
*/
|
||||
public Set<String> queryKeys() {
|
||||
QueryStatistics<QueryKey> stats = getStatistics();
|
||||
if (stats != null) {
|
||||
Set<String> keys = new HashSet<String>();
|
||||
for (QueryKey qk : stats.keys()) {
|
||||
keys.add(qk.toString());
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private QueryKey findKey(String key) {
|
||||
QueryStatistics<QueryKey> stats = getStatistics();
|
||||
for (QueryKey qk : stats.keys()) {
|
||||
if (qk.toString().equals(key.toString())) {
|
||||
return qk;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public InstrumentationLevel getLevel() {
|
||||
return InstrumentationLevel.FACTORY;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
package org.apache.openjpa.instrumentation;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Set;
|
||||
|
||||
public interface PreparedQueryCacheInstrument {
|
||||
|
||||
@ -63,18 +64,7 @@ public interface PreparedQueryCacheInstrument {
|
||||
* Returns number of total read requests that has been found since start.
|
||||
*/
|
||||
public long getTotalHitCount(String query);
|
||||
|
||||
/**
|
||||
* Returns the config id for the configuration attached to this cache
|
||||
*/
|
||||
public String getConfigId();
|
||||
|
||||
/**
|
||||
* Returns the context unique id for the configuration attached to this
|
||||
* cache
|
||||
*/
|
||||
public String getContextRef();
|
||||
|
||||
|
||||
/**
|
||||
* Resets cache statistics
|
||||
*/
|
||||
@ -89,4 +79,10 @@ public interface PreparedQueryCacheInstrument {
|
||||
* Returns date cache statistics collection started.
|
||||
*/
|
||||
public Date startDate();
|
||||
|
||||
/**
|
||||
* Returns all queries currently tracked in the cache.
|
||||
* @return
|
||||
*/
|
||||
public Set<String> queries();
|
||||
}
|
||||
|
@ -19,12 +19,33 @@
|
||||
package org.apache.openjpa.instrumentation;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Interface for providing instrumented data cache metrics and operations.
|
||||
*/
|
||||
public interface QueryCacheInstrument {
|
||||
|
||||
/**
|
||||
* Returns number of total exec requests since start.
|
||||
*/
|
||||
public long getTotalExecutionCount();
|
||||
|
||||
/**
|
||||
* Returns number of total exec requests since start.
|
||||
*/
|
||||
public long getTotalExecutionCount(String queryKey);
|
||||
|
||||
/**
|
||||
* Returns number of total execution requests since last reset
|
||||
*/
|
||||
public long getExecutionCount();
|
||||
|
||||
/**
|
||||
* Returns number of total execution requests since last reset
|
||||
*/
|
||||
public long getExecutionCount(String queryKey);
|
||||
|
||||
/**
|
||||
* Returns number of total read requests that have been found in cache since
|
||||
* last reset.
|
||||
@ -32,19 +53,10 @@ public interface QueryCacheInstrument {
|
||||
public long getHitCount();
|
||||
|
||||
/**
|
||||
* Returns number of total read requests since last reset
|
||||
* Returns number of total read requests that have been found in cache since
|
||||
* last reset.
|
||||
*/
|
||||
public long getReadCount();
|
||||
|
||||
/**
|
||||
* Returns number of total write requests since last reset.
|
||||
*/
|
||||
public long getWriteCount();
|
||||
|
||||
/**
|
||||
* Returns number of total read requests since start.
|
||||
*/
|
||||
public long getTotalReadCount();
|
||||
public long getHitCount(String queryKey);
|
||||
|
||||
/**
|
||||
* Returns number of total read requests that has been found since start.
|
||||
@ -52,21 +64,10 @@ public interface QueryCacheInstrument {
|
||||
public long getTotalHitCount();
|
||||
|
||||
/**
|
||||
* Returns number of total write requests for the given class since start.
|
||||
* Returns number of total read requests that has been found since start.
|
||||
*/
|
||||
public long getTotalWriteCount();
|
||||
|
||||
/**
|
||||
* Returns the config id for the configuration attached to this cache
|
||||
*/
|
||||
public String getConfigId();
|
||||
|
||||
/**
|
||||
* Returns the system unique id for the configuration attached to this
|
||||
* cache
|
||||
*/
|
||||
public String getContextRef();
|
||||
|
||||
public long getTotalHitCount(String queryKey);
|
||||
|
||||
/**
|
||||
* Resets cache statistics
|
||||
*/
|
||||
@ -81,4 +82,10 @@ public interface QueryCacheInstrument {
|
||||
* Returns date cache statistics collection started.
|
||||
*/
|
||||
public Date startDate();
|
||||
|
||||
/**
|
||||
* Returns all query keys currently in the cache.
|
||||
* @return
|
||||
*/
|
||||
public Set<String> queryKeys();
|
||||
}
|
||||
|
@ -20,9 +20,11 @@ package org.apache.openjpa.instrumentation.jmx;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerFactory;
|
||||
import javax.management.ObjectName;
|
||||
|
||||
import org.apache.openjpa.lib.instrumentation.AbstractInstrumentationProvider;
|
||||
@ -38,12 +40,12 @@ public class JMXProvider
|
||||
// Aliases for built-in JMX Instrumentation
|
||||
public static final String[] JMX_INSTRUMENT_ALIASES = {
|
||||
"DataCache", "org.apache.openjpa.instrumentation.jmx.DataCacheJMXInstrument",
|
||||
"QueryCache", "org.apache.openjpa.insrumentation.jmx.QueryCacheJMXInstrument",
|
||||
"QuerySQLCache", "org.apache.openjpa.insrumentation.jmx.PreparedQueryCacheJMXInstrument"
|
||||
"QueryCache", "org.apache.openjpa.instrumentation.jmx.QueryCacheJMXInstrument",
|
||||
"QuerySQLCache", "org.apache.openjpa.instrumentation.jmx.PreparedQueryCacheJMXInstrument"
|
||||
};
|
||||
|
||||
/**
|
||||
* The standard mbean package for OpenJPA
|
||||
* The MBean domain for OpenJPA
|
||||
*/
|
||||
public static final String MBEAN_DOMAIN = "org.apache.openjpa";
|
||||
|
||||
@ -80,14 +82,33 @@ public class JMXProvider
|
||||
if (mbs == null) {
|
||||
throw new UserException("jmx-server-failed-creation");
|
||||
}
|
||||
setStarted(true);
|
||||
} catch (Throwable t) {
|
||||
throw new UserException("jmx-server-unavailable",t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops all instruments registered with this provider and releases the
|
||||
* reference to the Platform MBean server instance.
|
||||
*/
|
||||
@Override
|
||||
public void stop() {
|
||||
// no-op with the built in MBean server
|
||||
if (isStarted()) {
|
||||
Set<Instrument> instruments = getInstruments();
|
||||
if (instruments != null && instruments.size() > 0) {
|
||||
for (Instrument inst : instruments) {
|
||||
stopInstrument(inst);
|
||||
}
|
||||
}
|
||||
// The MBean server factory does appear to ref count properly so the
|
||||
// platform server cannot released from the factory once it is acquired.
|
||||
// Multiple attempts to capture and release the server will result in a
|
||||
// runtime exception.
|
||||
// MBeanServerFactory.releaseMBeanServer(getMBeanServer());
|
||||
// _mbs = null;
|
||||
setStarted(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -125,6 +146,7 @@ public class JMXProvider
|
||||
public void startInstrument(Instrument instrument) {
|
||||
if (!instrument.isStarted()) {
|
||||
registerMBean((JMXInstrument)instrument);
|
||||
instrument.setStarted(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,6 +157,7 @@ public class JMXProvider
|
||||
if (instrument.isStarted() || force) {
|
||||
try {
|
||||
getMBeanServer().unregisterMBean(((JMXInstrument)instrument).getObjectName());
|
||||
instrument.setStarted(false);
|
||||
} catch (Exception e) {
|
||||
// If force, swallow the exception since the bean may not even
|
||||
// be registered.
|
||||
|
@ -86,6 +86,12 @@ public interface Instrument {
|
||||
*/
|
||||
public boolean isStarted();
|
||||
|
||||
/**
|
||||
* Sets whether the instrument is an available state.
|
||||
* @param started
|
||||
*/
|
||||
public void setStarted(boolean started);
|
||||
|
||||
/**
|
||||
* Starts the instrument. Typically this will be performed through the provider,
|
||||
* but in some cases an instrument will have its own specialized startup.
|
||||
|
Loading…
x
Reference in New Issue
Block a user