mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
ENH: Add ProfileLocation - Part 2 - performance metrics for transactions with label and profileLocation
This commit is contained in:
@@ -17,17 +17,17 @@ public interface ProfileLocation {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a new ProfileLocation with a given lineNumber.
|
||||
* Create and return a new ProfileLocation with a given lineNumber and label.
|
||||
*/
|
||||
static ProfileLocation create(int lineNumber) {
|
||||
return XServiceProvider.profileLocationFactory().create(lineNumber);
|
||||
static ProfileLocation create(int lineNumber, String label) {
|
||||
return XServiceProvider.profileLocationFactory().create(lineNumber, label);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a new ProfileLocation with a given location.
|
||||
*/
|
||||
static ProfileLocation create(String location) {
|
||||
return XServiceProvider.profileLocationFactory().create(location);
|
||||
static ProfileLocation createAt(String location) {
|
||||
return XServiceProvider.profileLocationFactory().createAt(location);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -40,4 +40,8 @@ public interface ProfileLocation {
|
||||
*/
|
||||
String shortDescription();
|
||||
|
||||
/**
|
||||
* Add execution time.
|
||||
*/
|
||||
void add(long executionTime);
|
||||
}
|
||||
|
||||
@@ -59,6 +59,14 @@ public interface Transaction extends AutoCloseable {
|
||||
*/
|
||||
void register(TransactionCallback callback);
|
||||
|
||||
/**
|
||||
* Set a label on the transaction.
|
||||
* <p>
|
||||
* This label is used to group transaction execution times for performance metrics reporting.
|
||||
* </p>
|
||||
*/
|
||||
void setLabel(String label);
|
||||
|
||||
/**
|
||||
* Return true if this transaction is read only.
|
||||
*/
|
||||
|
||||
@@ -7,6 +7,11 @@ import java.util.List;
|
||||
*/
|
||||
public interface MetaInfoManager {
|
||||
|
||||
/**
|
||||
* Collect and return the transaction execution metrics.
|
||||
*/
|
||||
List<MetaTimedMetric> collectTransactionStatistics(boolean reset);
|
||||
|
||||
/**
|
||||
* Collect and return the non-empty query plan statistics for all the beans.
|
||||
* <p>
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package io.ebean.meta;
|
||||
|
||||
|
||||
/**
|
||||
* Timed execution statistics.
|
||||
*/
|
||||
public interface MetaTimedMetric {
|
||||
|
||||
/**
|
||||
* Return the metric name.
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Return the metric location if defined.
|
||||
*/
|
||||
String getLocation();
|
||||
|
||||
/**
|
||||
* Return the time the counters started from.
|
||||
*/
|
||||
long getStartTime();
|
||||
|
||||
/**
|
||||
* Return the total count.
|
||||
*/
|
||||
long getCount();
|
||||
|
||||
/**
|
||||
* Return the total execution time.
|
||||
*/
|
||||
long getTotal();
|
||||
|
||||
/**
|
||||
* Return the max execution time.
|
||||
*/
|
||||
long getMax();
|
||||
|
||||
/**
|
||||
* Return the mean execution time.
|
||||
*/
|
||||
long getMean();
|
||||
}
|
||||
@@ -15,10 +15,10 @@ public interface SpiProfileLocationFactory {
|
||||
/**
|
||||
* Create a profile location with a line number.
|
||||
*/
|
||||
ProfileLocation create(int lineNumber);
|
||||
ProfileLocation create(int lineNumber, String label);
|
||||
|
||||
/**
|
||||
* Create a known location.
|
||||
*/
|
||||
ProfileLocation create(String location);
|
||||
ProfileLocation createAt(String location);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,11 @@ import java.sql.SQLException;
|
||||
*/
|
||||
public interface SpiTransaction extends Transaction {
|
||||
|
||||
/**
|
||||
* Return the user defined label for the transaction.
|
||||
*/
|
||||
String getLabel();
|
||||
|
||||
/**
|
||||
* Return the string prefix with the transaction id and label used in logging.
|
||||
*/
|
||||
|
||||
@@ -30,6 +30,16 @@ abstract class SpiTransactionProxy implements SpiTransaction {
|
||||
return transaction.translate(message, cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLabel(String label) {
|
||||
transaction.setLabel(label);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return transaction.getLabel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commitAndContinue() {
|
||||
transaction.commitAndContinue();
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package io.ebeaninternal.metric;
|
||||
|
||||
/**
|
||||
* Factory to create timed metric counters.
|
||||
*/
|
||||
public interface MetricFactory {
|
||||
|
||||
/**
|
||||
* Return the factory instance.
|
||||
*/
|
||||
static MetricFactory get() {
|
||||
return MetricServiceProvider.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a timed metric group.
|
||||
*/
|
||||
TimedMetricMap createTimedMetricMap(String name);
|
||||
|
||||
/**
|
||||
* Create a Timed metric.
|
||||
*/
|
||||
TimedMetric createTimedMetric(String name);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package io.ebeaninternal.metric;
|
||||
|
||||
import io.ebeaninternal.server.profile.DMetricFactory;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
/**
|
||||
* Lookup MetricFactory service.
|
||||
*/
|
||||
class MetricServiceProvider {
|
||||
|
||||
private static MetricFactory metricFactory = init();
|
||||
|
||||
private static MetricFactory init() {
|
||||
|
||||
Iterator<MetricFactory> loader = ServiceLoader.load(MetricFactory.class).iterator();
|
||||
if (loader.hasNext()) {
|
||||
return loader.next();
|
||||
}
|
||||
return new DMetricFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the MetricFactory implementation.
|
||||
*/
|
||||
static MetricFactory get() {
|
||||
return metricFactory;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package io.ebeaninternal.metric;
|
||||
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Metric for timed events like transaction execution times.
|
||||
*/
|
||||
public interface TimedMetric {
|
||||
|
||||
/**
|
||||
* Add a time event (usually in microseconds).
|
||||
*/
|
||||
void add(long value);
|
||||
|
||||
/**
|
||||
* Return true if there are no metrics collected since the last collection.
|
||||
*/
|
||||
boolean isEmpty();
|
||||
|
||||
/**
|
||||
* Collect the timed metric statistics.
|
||||
*/
|
||||
TimedMetricStats collect(boolean reset);
|
||||
|
||||
/**
|
||||
* Add non empty metrics to the result.
|
||||
*/
|
||||
void collect(boolean reset, List<MetaTimedMetric> result);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package io.ebeaninternal.metric;
|
||||
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A map of timed metrics keyed by a string.
|
||||
*/
|
||||
public interface TimedMetricMap {
|
||||
|
||||
/**
|
||||
* Add an execution for the given key.
|
||||
*/
|
||||
void add(String key, long exeMicros);
|
||||
|
||||
/**
|
||||
* Add non empty metrics to the given result.
|
||||
*/
|
||||
void collect(boolean reset, List<MetaTimedMetric> result);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package io.ebeaninternal.metric;
|
||||
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
|
||||
/**
|
||||
* Extend public MetaTimedMetric with ability to set details from profile location.
|
||||
*/
|
||||
public interface TimedMetricStats extends MetaTimedMetric {
|
||||
|
||||
/**
|
||||
* Additionally set the location.
|
||||
*/
|
||||
void setLocation(String location);
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package io.ebeaninternal.server.core;
|
||||
import io.ebean.meta.MetaInfoManager;
|
||||
import io.ebean.meta.MetaObjectGraphNodeStats;
|
||||
import io.ebean.meta.MetaQueryPlanStatistic;
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.query.CQueryPlanStatsCollector;
|
||||
|
||||
@@ -20,6 +21,11 @@ public class DefaultMetaInfoManager implements MetaInfoManager {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaTimedMetric> collectTransactionStatistics(boolean reset) {
|
||||
return server.collectTransactionStatistics(reset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaQueryPlanStatistic> collectQueryPlanStatistics(boolean reset) {
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ import io.ebean.event.BeanPersistController;
|
||||
import io.ebean.event.readaudit.ReadAuditLogger;
|
||||
import io.ebean.event.readaudit.ReadAuditPrepare;
|
||||
import io.ebean.meta.MetaInfoManager;
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebean.plugin.BeanType;
|
||||
import io.ebean.plugin.Plugin;
|
||||
import io.ebean.plugin.SpiServer;
|
||||
@@ -2214,4 +2215,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<MetaTimedMetric> collectTransactionStatistics(boolean reset) {
|
||||
return transactionManager.collectTransactionStatistics(reset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,16 @@ class NoTransaction implements SpiTransaction {
|
||||
|
||||
static final NoTransaction INSTANCE = new NoTransaction();
|
||||
|
||||
@Override
|
||||
public void setLabel(String label) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
// always false
|
||||
|
||||
@@ -416,8 +416,8 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
this.name = InternString.intern(deploy.getName());
|
||||
this.baseTableAlias = "t0";
|
||||
this.fullName = InternString.intern(deploy.getFullName());
|
||||
this.locationById = ProfileLocation.create(fullName+".byId");
|
||||
this.locationAll = ProfileLocation.create(fullName+".all");
|
||||
this.locationById = ProfileLocation.createAt(fullName+".byId");
|
||||
this.locationAll = ProfileLocation.createAt(fullName+".all");
|
||||
this.profileBeanId = deploy.getProfileId();
|
||||
this.beanType = deploy.getBeanType();
|
||||
this.rootBeanType = PersistenceContextUtil.root(beanType);
|
||||
|
||||
@@ -19,6 +19,11 @@ class BasicProfileLocation implements ProfileLocation {
|
||||
return shortDescription;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(long executionTime) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
public String obtain() {
|
||||
return location;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebeaninternal.metric.MetricFactory;
|
||||
import io.ebeaninternal.metric.TimedMetric;
|
||||
import io.ebeaninternal.metric.TimedMetricMap;
|
||||
|
||||
/**
|
||||
* Default metric factory implementation.
|
||||
*/
|
||||
public class DMetricFactory implements MetricFactory {
|
||||
|
||||
@Override
|
||||
public TimedMetricMap createTimedMetricMap(String name) {
|
||||
return new DTimedMetricMap(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimedMetric createTimedMetric(String name) {
|
||||
return new DTimedMetric(name);
|
||||
}
|
||||
}
|
||||
@@ -18,9 +18,12 @@ class DProfileLocation implements ProfileLocation {
|
||||
private final int lineNumber;
|
||||
|
||||
DProfileLocation() {
|
||||
this.lineNumber = 0;
|
||||
this(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create with a given line number.
|
||||
*/
|
||||
DProfileLocation(int lineNumber) {
|
||||
this.lineNumber = lineNumber;
|
||||
}
|
||||
@@ -29,6 +32,11 @@ class DProfileLocation implements ProfileLocation {
|
||||
return "location: " + location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(long executionTime) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
public String obtain() {
|
||||
// atomic assignment so happy with this
|
||||
if (location == null) {
|
||||
|
||||
@@ -2,6 +2,8 @@ package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.service.SpiProfileLocationFactory;
|
||||
import io.ebeaninternal.metric.MetricFactory;
|
||||
import io.ebeaninternal.metric.TimedMetric;
|
||||
|
||||
/**
|
||||
* Default implementation of the profile location factory.
|
||||
@@ -14,12 +16,17 @@ public class DProfileLocationFactory implements SpiProfileLocationFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProfileLocation create(int lineNumber) {
|
||||
return new DProfileLocation(lineNumber);
|
||||
public ProfileLocation create(int lineNumber, String label) {
|
||||
|
||||
TimedMetric timedMetric = MetricFactory.get().createTimedMetric("txn.named." + label);
|
||||
|
||||
DTimedProfileLocation loc = new DTimedProfileLocation(lineNumber, label, timedMetric);
|
||||
TimedProfileLocationRegistry.register(loc);
|
||||
return loc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProfileLocation create(String location) {
|
||||
public ProfileLocation createAt(String location) {
|
||||
return new BasicProfileLocation(location);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebeaninternal.metric.TimedMetricStats;
|
||||
|
||||
/**
|
||||
* Snapshot of the current statistics for a Counter or TimeCounter.
|
||||
*/
|
||||
class DTimeMetricStats implements TimedMetricStats {
|
||||
|
||||
private final String name;
|
||||
|
||||
private String location;
|
||||
|
||||
private final long startTime;
|
||||
|
||||
private final long count;
|
||||
|
||||
private final long total;
|
||||
|
||||
private final long max;
|
||||
|
||||
DTimeMetricStats(String name, long collectionStart, long count, long total, long max) {
|
||||
this.name = name;
|
||||
this.startTime = collectionStart;
|
||||
this.count = count;
|
||||
this.total = total;
|
||||
// collection is racy so sanitize the max value if it has not been set
|
||||
// this most likely would happen when count = 1 so max = mean
|
||||
this.max = max != Long.MIN_VALUE ? max : (count < 1 ? 0 : Math.round(total / count));
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (location != null) {
|
||||
sb.append("loc:").append(location).append(" ");
|
||||
}
|
||||
if (name != null) {
|
||||
sb.append("name:").append(name).append(" ");
|
||||
}
|
||||
sb.append("count:").append(count)
|
||||
.append(" total:").append(total)
|
||||
.append(" max:").append(max);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public void setLocation(String location) {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the time the counter started statistics collection.
|
||||
*/
|
||||
@Override
|
||||
public long getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the count of values collected.
|
||||
*/
|
||||
@Override
|
||||
public long getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the total of all the values.
|
||||
*/
|
||||
@Override
|
||||
public long getTotal() {
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Max value collected.
|
||||
*/
|
||||
@Override
|
||||
public long getMax() {
|
||||
return max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mean value rounded up.
|
||||
*/
|
||||
@Override
|
||||
public long getMean() {
|
||||
return (count < 1) ? 0L : Math.round((double)(total / count));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebeaninternal.metric.TimedMetric;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.LongAccumulator;
|
||||
import java.util.concurrent.atomic.LongAdder;
|
||||
|
||||
/**
|
||||
* Used to collect timed execution statistics.
|
||||
* <p>
|
||||
* It is intended for high concurrent updates to the statistics and relatively infrequent reads.
|
||||
* </p>
|
||||
*/
|
||||
class DTimedMetric implements TimedMetric {
|
||||
|
||||
protected final String name;
|
||||
|
||||
protected final LongAdder count = new LongAdder();
|
||||
|
||||
protected final LongAdder total = new LongAdder();
|
||||
|
||||
protected final LongAccumulator max = new LongAccumulator(Math::max, Long.MIN_VALUE);
|
||||
|
||||
protected final AtomicLong startTime = new AtomicLong(System.currentTimeMillis());
|
||||
|
||||
DTimedMetric(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a value. Usually the value is Time or Bytes etc.
|
||||
*/
|
||||
@Override
|
||||
public void add(long value) {
|
||||
|
||||
count.increment();
|
||||
total.add(value);
|
||||
max.accumulate(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return count.sum() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collect(boolean reset, List<MetaTimedMetric> result) {
|
||||
DTimeMetricStats metric = collect(reset);
|
||||
if (metric != null) {
|
||||
result.add(metric);
|
||||
}
|
||||
}
|
||||
|
||||
// @Override
|
||||
public DTimeMetricStats collect(boolean reset) {
|
||||
boolean empty = count.sum() == 0;
|
||||
if (empty) {
|
||||
if (reset) {
|
||||
startTime.set(System.currentTimeMillis());
|
||||
}
|
||||
return null;
|
||||
} else {
|
||||
return getStatistics(reset);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current statistics resetting the internal values if reset is true.
|
||||
*/
|
||||
public DTimeMetricStats getStatistics(boolean reset) {
|
||||
|
||||
if (reset) {
|
||||
// Note these values are not guaranteed to be consistent wrt each other
|
||||
// but should be reasonably consistent (small time between count and total)
|
||||
final long maxVal = max.getThenReset();
|
||||
final long totalVal = total.sumThenReset();
|
||||
final long countVal = count.sumThenReset();
|
||||
final long startTimeVal = startTime.getAndSet(System.currentTimeMillis());
|
||||
return new DTimeMetricStats(name, startTimeVal, countVal, totalVal, maxVal);
|
||||
|
||||
} else {
|
||||
return new DTimeMetricStats(name, startTime.get(), count.sum(), total.sum(), max.get());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all the internal counters and start time.
|
||||
*/
|
||||
public void reset() {
|
||||
startTime.set(System.currentTimeMillis());
|
||||
max.reset();
|
||||
count.reset();
|
||||
total.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the start time.
|
||||
*/
|
||||
public long getStartTime() {
|
||||
return startTime.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the count of values.
|
||||
*/
|
||||
public long getCount() {
|
||||
return count.sum();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the total of values.
|
||||
*/
|
||||
public long getTotal() {
|
||||
return total.sum();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the max value.
|
||||
*/
|
||||
public long getMax() {
|
||||
return max.get();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebeaninternal.metric.TimedMetricMap;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
class DTimedMetricMap implements TimedMetricMap {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final ConcurrentHashMap<String, DTimedMetric> map = new ConcurrentHashMap<>();
|
||||
|
||||
DTimedMetricMap(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(String key, long exeMicros) {
|
||||
map.computeIfAbsent(key, (k)-> new DTimedMetric(name + key)).add(exeMicros);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collect(boolean reset, List<MetaTimedMetric> list) {
|
||||
for (DTimedMetric value : map.values()) {
|
||||
value.collect(reset, list);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebeaninternal.metric.TimedMetric;
|
||||
import io.ebeaninternal.metric.TimedMetricStats;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Default profile location that uses stack trace.
|
||||
*/
|
||||
class DTimedProfileLocation extends DProfileLocation implements TimedProfileLocation {
|
||||
|
||||
private final String label;
|
||||
|
||||
private final TimedMetric timedMetric;
|
||||
|
||||
DTimedProfileLocation(int lineNumber, String label, TimedMetric timedMetric) {
|
||||
super(lineNumber);
|
||||
this.label = label;
|
||||
this.timedMetric = timedMetric;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimedMetric getMetric() {
|
||||
return timedMetric;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(long executionTime) {
|
||||
timedMetric.add(executionTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collect(boolean reset, List<MetaTimedMetric> list) {
|
||||
|
||||
TimedMetricStats collect = timedMetric.collect(reset);
|
||||
if (collect != null) {
|
||||
collect.setLocation(obtain());
|
||||
list.add(collect);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebeaninternal.metric.TimedMetric;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* ProfileLocation that collects timing metrics.
|
||||
*/
|
||||
public interface TimedProfileLocation extends ProfileLocation {
|
||||
|
||||
/**
|
||||
* Return the label.
|
||||
*/
|
||||
String getLabel();
|
||||
|
||||
/**
|
||||
* Return the metric.
|
||||
*/
|
||||
TimedMetric getMetric();
|
||||
|
||||
/**
|
||||
* Collect the metrics adding to the given list if the metrics are non empty.
|
||||
*/
|
||||
void collect(boolean reset, List<MetaTimedMetric> list);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Global registry of the TimedProfileLocation instances created.
|
||||
*/
|
||||
public class TimedProfileLocationRegistry {
|
||||
|
||||
private static final List<TimedProfileLocation> list = Collections.synchronizedList(new ArrayList<TimedProfileLocation>());
|
||||
|
||||
/**
|
||||
* Register the timed profile location instance.
|
||||
*/
|
||||
public static void register(TimedProfileLocation location) {
|
||||
list.add(location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all the registered timed locations.
|
||||
*/
|
||||
public static List<TimedProfileLocation> registered() {
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,8 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
|
||||
|
||||
private static final String notExpectedMessage = "Not expected on read only transaction";
|
||||
|
||||
private final TransactionManager manager;
|
||||
|
||||
/**
|
||||
* The status of the transaction.
|
||||
*/
|
||||
@@ -61,23 +63,37 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
|
||||
|
||||
private Map<String, Object> userObjects;
|
||||
|
||||
private long startNanos;
|
||||
|
||||
/**
|
||||
* Create without a tenantId.
|
||||
*/
|
||||
ImplicitReadOnlyTransaction(Connection connection) {
|
||||
ImplicitReadOnlyTransaction(TransactionManager manager, Connection connection) {
|
||||
this.manager = manager;
|
||||
this.active = true;
|
||||
this.connection = connection;
|
||||
this.persistenceContext = new DefaultPersistenceContext();
|
||||
this.startNanos = System.nanoTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create with a tenantId.
|
||||
*/
|
||||
ImplicitReadOnlyTransaction(Connection connection, Object tenantId) {
|
||||
this(connection);
|
||||
ImplicitReadOnlyTransaction(TransactionManager manager, Connection connection, Object tenantId) {
|
||||
this(manager, connection);
|
||||
this.tenantId = tenantId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLabel(String label) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long profileOffset() {
|
||||
return 0;
|
||||
@@ -475,6 +491,8 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
|
||||
}
|
||||
connection = null;
|
||||
active = false;
|
||||
long exeMicros = (System.nanoTime() - startNanos) / 1000L;
|
||||
manager.collectMetricReadOnly(exeMicros);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -55,6 +55,11 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
*/
|
||||
protected final String id;
|
||||
|
||||
/**
|
||||
* The user defined label to group execution statistics.
|
||||
*/
|
||||
protected String label;
|
||||
|
||||
/**
|
||||
* Flag to indicate if this was an explicitly created Transaction.
|
||||
*/
|
||||
@@ -176,6 +181,8 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
|
||||
protected ProfileLocation profileLocation;
|
||||
|
||||
protected final long startNanos;
|
||||
|
||||
/**
|
||||
* Create without ProfileStream option (no profiling).
|
||||
*/
|
||||
@@ -196,6 +203,7 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
this.manager = manager;
|
||||
this.connection = connection;
|
||||
this.persistenceContext = new DefaultPersistenceContext();
|
||||
this.startNanos = System.nanoTime();
|
||||
|
||||
if (manager == null) {
|
||||
this.skipCacheAfterWrite = true;
|
||||
@@ -216,6 +224,16 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long profileOffset() {
|
||||
return (profileStream == null) ? 0 : profileStream.offset();
|
||||
@@ -980,8 +998,17 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
}
|
||||
|
||||
private void profileEnd() {
|
||||
if (profileStream != null) {
|
||||
profileStream.end(manager);
|
||||
if (manager != null) {
|
||||
long exeMicros = (System.nanoTime() - startNanos) / 1000L;
|
||||
if (profileLocation != null) {
|
||||
profileLocation.add(exeMicros);
|
||||
} else if (label != null) {
|
||||
manager.collectMetricNamed(exeMicros, label);
|
||||
}
|
||||
manager.collectMetric(exeMicros);
|
||||
if (profileStream != null) {
|
||||
profileStream.end(manager);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ class TransactionFactoryBasicWithRead extends TransactionFactoryBasic {
|
||||
Connection connection = null;
|
||||
try {
|
||||
connection = readOnlyDataSource.getConnection();
|
||||
return new ImplicitReadOnlyTransaction(connection);
|
||||
return new ImplicitReadOnlyTransaction(manager, connection);
|
||||
|
||||
} catch (PersistenceException ex) {
|
||||
JdbcClose.close(connection);
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ class TransactionFactoryTenantWithRead extends TransactionFactoryTenant {
|
||||
tenantId = tenantProvider.currentId();
|
||||
}
|
||||
connection = dataSourceSupplier.getReadOnlyConnection(tenantId);
|
||||
return new ImplicitReadOnlyTransaction(connection, tenantId);
|
||||
return new ImplicitReadOnlyTransaction(manager, connection, tenantId);
|
||||
|
||||
} catch (PersistenceException ex) {
|
||||
JdbcClose.close(connection);
|
||||
|
||||
@@ -8,13 +8,19 @@ import io.ebean.config.dbplatform.DatabasePlatform.OnQueryOnly;
|
||||
import io.ebean.event.changelog.ChangeLogListener;
|
||||
import io.ebean.event.changelog.ChangeLogPrepare;
|
||||
import io.ebean.event.changelog.ChangeSet;
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebeaninternal.api.SpiProfileHandler;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.api.TransactionEvent;
|
||||
import io.ebeaninternal.api.TransactionEventTable;
|
||||
import io.ebeaninternal.api.TransactionEventTable.TableIUD;
|
||||
import io.ebeaninternal.metric.MetricFactory;
|
||||
import io.ebeaninternal.metric.TimedMetric;
|
||||
import io.ebeaninternal.metric.TimedMetricMap;
|
||||
import io.ebeaninternal.server.cluster.ClusterManager;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
|
||||
import io.ebeaninternal.server.profile.TimedProfileLocation;
|
||||
import io.ebeaninternal.server.profile.TimedProfileLocationRegistry;
|
||||
import io.ebeanservice.docstore.api.DocStoreTransaction;
|
||||
import io.ebeanservice.docstore.api.DocStoreUpdateProcessor;
|
||||
import io.ebeanservice.docstore.api.DocStoreUpdates;
|
||||
@@ -25,6 +31,7 @@ import javax.persistence.PersistenceException;
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -113,6 +120,11 @@ public class TransactionManager {
|
||||
|
||||
private final SpiProfileHandler profileHandler;
|
||||
|
||||
private final MetricFactory metricFactory;
|
||||
private final TimedMetric txnMain;
|
||||
private final TimedMetric txnReadOnly;
|
||||
private final TimedMetricMap txnNamed;
|
||||
|
||||
/**
|
||||
* Create the TransactionManager
|
||||
*/
|
||||
@@ -142,6 +154,10 @@ public class TransactionManager {
|
||||
|
||||
CurrentTenantProvider tenantProvider = options.config.getCurrentTenantProvider();
|
||||
this.transactionFactory = TransactionFactoryBuilder.build(this, dataSourceSupplier, tenantProvider);
|
||||
this.metricFactory = MetricFactory.get();
|
||||
this.txnMain = metricFactory.createTimedMetric("txn.main");
|
||||
this.txnReadOnly = metricFactory.createTimedMetric("txn.readonly");
|
||||
this.txnNamed = metricFactory.createTimedMetricMap("txn.named.");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -266,7 +282,7 @@ public class TransactionManager {
|
||||
protected SpiTransaction createTransaction(int profileId, boolean explicit, Connection c, long id) {
|
||||
|
||||
ProfileStream profileStream = profileHandler.createProfileStream(profileId);
|
||||
return new JdbcTransaction(profileStream,prefix + id, explicit, c, this);
|
||||
return new JdbcTransaction(profileStream, prefix + id, explicit, c, this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -424,4 +440,42 @@ public class TransactionManager {
|
||||
public void profileCollect(TransactionProfile transactionProfile) {
|
||||
profileHandler.collectTransactionProfile(transactionProfile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect execution time for an explicit transaction.
|
||||
*/
|
||||
public void collectMetric(long exeMicros) {
|
||||
txnMain.add(exeMicros);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect execution time for implicit read only transaction.
|
||||
*/
|
||||
public void collectMetricReadOnly(long exeMicros) {
|
||||
txnReadOnly.add(exeMicros);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect execution time for a named transaction.
|
||||
*/
|
||||
public void collectMetricNamed(long exeMicros, String label) {
|
||||
txnNamed.add(label, exeMicros);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect the transaction execution statistics since the last reset.
|
||||
*/
|
||||
public List<MetaTimedMetric> collectTransactionStatistics(boolean reset) {
|
||||
|
||||
List<MetaTimedMetric> list = new ArrayList<>();
|
||||
|
||||
txnMain.collect(reset, list);
|
||||
txnReadOnly.collect(reset, list);
|
||||
for (TimedProfileLocation timedLocation : TimedProfileLocationRegistry.registered()) {
|
||||
timedLocation.collect(reset, list);
|
||||
}
|
||||
txnNamed.collect(reset, list);
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user