mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#1183 - ENH: Add support for transaction profiling
This commit is contained in:
+1
-1
@@ -13,7 +13,7 @@ ebean-profiling*.xml
|
||||
/db
|
||||
/mydb.db
|
||||
!src/test/ddl-review/*.sql
|
||||
|
||||
profiling/
|
||||
|
||||
# Intellij project files
|
||||
*.iml
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean</artifactId>
|
||||
<version>11.2.4-SNAPSHOT</version>
|
||||
<version>11.3.1-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>ebean</name>
|
||||
@@ -87,7 +87,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-annotation</artifactId>
|
||||
<version>3.1</version>
|
||||
<version>3.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -1331,6 +1331,17 @@ public interface Query<T> {
|
||||
return setUseQueryCache(enabled ? CacheMode.ON : CacheMode.OFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an id to identify this query for profiling purposes.
|
||||
* <p>
|
||||
* The profileId is expected to be unique for a given bean type.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that the profileId is treated as a short internally and has a MAX value of 32,767.
|
||||
* </p>
|
||||
*/
|
||||
Query<T> setProfileId(int profileId);
|
||||
|
||||
/**
|
||||
* Set to true if this query should execute against the doc store.
|
||||
* <p>
|
||||
|
||||
@@ -25,6 +25,8 @@ import java.util.concurrent.Callable;
|
||||
*/
|
||||
public final class TxScope {
|
||||
|
||||
int profileId;
|
||||
|
||||
TxType type;
|
||||
|
||||
String serverName;
|
||||
@@ -168,6 +170,21 @@ public final class TxScope {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the transaction profile id.
|
||||
*/
|
||||
public int getProfileId() {
|
||||
return profileId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the transaction profile id.
|
||||
*/
|
||||
public TxScope setProfileId(int profileId) {
|
||||
this.profileId = profileId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the batch mode.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
package io.ebean.config;
|
||||
|
||||
/**
|
||||
* Configuration for transaction profiling.
|
||||
*/
|
||||
public class ProfilingConfig {
|
||||
|
||||
/**
|
||||
* When true transaction profiling is enabled.
|
||||
*/
|
||||
private boolean transactionProfiling;
|
||||
|
||||
/**
|
||||
* The minimum transaction execution time to be included in profiling.
|
||||
*/
|
||||
private long minimumTransactionMicros;
|
||||
|
||||
/**
|
||||
* A specific set of profileIds to include in profiling.
|
||||
*/
|
||||
private int[] includeProfileIds = {};
|
||||
|
||||
/**
|
||||
* The number of profiles to write per file.
|
||||
*/
|
||||
private long profilesPerFile = 1000;
|
||||
|
||||
private String directory = "profiling";
|
||||
|
||||
/**
|
||||
* Return true if transaction profiling is enabled.
|
||||
*/
|
||||
public boolean isTransactionProfiling() {
|
||||
return transactionProfiling;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to enable transaction profiling.
|
||||
*/
|
||||
public void setTransactionProfiling(boolean transactionProfiling) {
|
||||
this.transactionProfiling = transactionProfiling;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the minimum transaction execution to be included in profiling.
|
||||
*/
|
||||
public long getMinimumTransactionMicros() {
|
||||
return minimumTransactionMicros;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the minimum transaction execution to be included in profiling.
|
||||
*/
|
||||
public void setMinimumTransactionMicros(long minimumTransactionMicros) {
|
||||
this.minimumTransactionMicros = minimumTransactionMicros;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the specific set of profileIds to include in profiling.
|
||||
* When not set all transactions with profileIds are included.
|
||||
*/
|
||||
public int[] getIncludeProfileIds() {
|
||||
return includeProfileIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a specific set of profileIds to include in profiling.
|
||||
* When not set all transactions with profileIds are included.
|
||||
*/
|
||||
public void setIncludeProfileIds(int[] includeProfileIds) {
|
||||
this.includeProfileIds = includeProfileIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of profiles to write to a single file.
|
||||
*/
|
||||
public long getProfilesPerFile() {
|
||||
return profilesPerFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of profiles to write to a single file.
|
||||
*/
|
||||
public void setProfilesPerFile(long profilesPerFile) {
|
||||
this.profilesPerFile = profilesPerFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the directory profiling files are put into.
|
||||
*/
|
||||
public String getDirectory() {
|
||||
return directory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the directory profiling files are put into.
|
||||
*/
|
||||
public void setDirectory(String directory) {
|
||||
this.directory = directory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load setting from properties.
|
||||
*/
|
||||
public void loadSettings(PropertiesWrapper p, String name) {
|
||||
transactionProfiling = p.getBoolean("profiling.transactionProfiling", transactionProfiling);
|
||||
directory = p.get("profiling.directory", directory);
|
||||
profilesPerFile = p.getLong("profiling.profilesPerFile", profilesPerFile);
|
||||
minimumTransactionMicros = p.getLong("profiling.minimumTransactionMicros", minimumTransactionMicros);
|
||||
|
||||
String includeIds = p.get("profiling.includeProfileIds");
|
||||
if (includeIds != null) {
|
||||
includeProfileIds = parseIds(includeIds);
|
||||
}
|
||||
}
|
||||
|
||||
private int[] parseIds(String includeIds) {
|
||||
|
||||
String[] ids = includeIds.split(",");
|
||||
int[] vals = new int[ids.length];
|
||||
for (int i = 0; i < ids.length; i++) {
|
||||
vals[i] = Integer.parseInt(ids[i]);
|
||||
}
|
||||
return vals;
|
||||
}
|
||||
}
|
||||
@@ -450,6 +450,9 @@ public class ServerConfig {
|
||||
*/
|
||||
private SlowQueryListener slowQueryListener;
|
||||
|
||||
|
||||
private ProfilingConfig profilingConfig = new ProfilingConfig();
|
||||
|
||||
/**
|
||||
* Construct a Server Configuration for programmatically creating an EbeanServer.
|
||||
*/
|
||||
@@ -1021,6 +1024,20 @@ public class ServerConfig {
|
||||
this.readAuditPrepare = readAuditPrepare;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configuration for profiling.
|
||||
*/
|
||||
public ProfilingConfig getProfilingConfig() {
|
||||
return profilingConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the configuration for profiling.
|
||||
*/
|
||||
public void setProfilingConfig(ProfilingConfig profilingConfig) {
|
||||
this.profilingConfig = profilingConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DB migration configuration.
|
||||
*/
|
||||
@@ -2546,6 +2563,7 @@ public class ServerConfig {
|
||||
*/
|
||||
protected void loadSettings(PropertiesWrapper p) {
|
||||
|
||||
profilingConfig.loadSettings(p, name);
|
||||
migrationConfig.loadSettings(p, name);
|
||||
|
||||
boolean quotedIdentifiers = p.getBoolean("allQuotedIdentifiers", allQuotedIdentifiers);
|
||||
|
||||
@@ -20,6 +20,11 @@ public interface BeanType<T> {
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Return the profileId of the bean type.
|
||||
*/
|
||||
short getProfileId();
|
||||
|
||||
/**
|
||||
* Return the full name of the bean type.
|
||||
*/
|
||||
|
||||
@@ -10,6 +10,7 @@ import io.ebeaninternal.server.core.PersistDeferredRelationship;
|
||||
import io.ebeaninternal.server.core.PersistRequest;
|
||||
import io.ebeaninternal.server.core.PersistRequestBean;
|
||||
import io.ebeaninternal.server.persist.BatchControl;
|
||||
import io.ebeaninternal.server.transaction.ProfileStream;
|
||||
import io.ebeanservice.docstore.api.DocStoreTransaction;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
@@ -79,6 +80,21 @@ public class ScopedTransaction implements SpiTransaction {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int profileOffset() {
|
||||
return transaction.profileOffset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void profileEvent(SpiProfileTransactionEvent event) {
|
||||
transaction.profileEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProfileStream profileStream() {
|
||||
return transaction.profileStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTenantId(Object tenantId) {
|
||||
transaction.setTenantId(tenantId);
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebeaninternal.server.transaction.ProfileStream;
|
||||
import io.ebeaninternal.server.transaction.TransactionProfile;
|
||||
|
||||
/**
|
||||
* Handle the logging or processing of transaction profiling information that is collected.
|
||||
*/
|
||||
public interface SpiProfileHandler {
|
||||
|
||||
/**
|
||||
* Process the collected transaction profiling information.
|
||||
* <p>
|
||||
* Note that profileId and totalMicros are part of the profilingData but passed separately as the handler
|
||||
* may filter what it processed based on this information (ignore short transactions, only process specific
|
||||
* profileId transactions etc).
|
||||
* </p>
|
||||
*
|
||||
* @param transactionProfile The transaction profile that has just been collected
|
||||
*/
|
||||
void collectTransactionProfile(TransactionProfile transactionProfile);
|
||||
|
||||
/**
|
||||
* Create a profiling stream if we are profiling this transaction.
|
||||
* Return null if we are not profiling this transaction.
|
||||
*
|
||||
* @param profileId The transaction profileId
|
||||
*/
|
||||
ProfileStream createProfileStream(int profileId);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
/**
|
||||
* Event that adds to a profiling transaction.
|
||||
*/
|
||||
public interface SpiProfileTransactionEvent {
|
||||
|
||||
/**
|
||||
* Add the event information to the profiling transaction.
|
||||
*/
|
||||
void profile();
|
||||
}
|
||||
@@ -29,7 +29,7 @@ import java.util.Set;
|
||||
/**
|
||||
* Object Relational query - Internal extension to Query object.
|
||||
*/
|
||||
public interface SpiQuery<T> extends Query<T> {
|
||||
public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
|
||||
enum Mode {
|
||||
NORMAL(false), LAZYLOAD_MANY(false), LAZYLOAD_BEAN(true), REFRESH_BEAN(true);
|
||||
@@ -53,57 +53,67 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
/**
|
||||
* Find by Id or unique returning a single bean.
|
||||
*/
|
||||
BEAN,
|
||||
|
||||
/**
|
||||
* Find iterate type query - findEach(), findIterate() etc.
|
||||
*/
|
||||
ITERATE,
|
||||
BEAN(FIND_ONE),
|
||||
|
||||
/**
|
||||
* Find returning a List.
|
||||
*/
|
||||
LIST,
|
||||
LIST(FIND_MANY),
|
||||
|
||||
/**
|
||||
* Find returning a Set.
|
||||
*/
|
||||
SET,
|
||||
SET(FIND_MANY),
|
||||
|
||||
/**
|
||||
* Find returning a Map.
|
||||
*/
|
||||
MAP,
|
||||
MAP(FIND_MANY),
|
||||
|
||||
/**
|
||||
* Find iterate type query - findEach(), findIterate() etc.
|
||||
*/
|
||||
ITERATE(FIND_ITERATE),
|
||||
|
||||
/**
|
||||
* Find the Id's.
|
||||
*/
|
||||
ID_LIST,
|
||||
ID_LIST(FIND_ID_LIST),
|
||||
|
||||
/**
|
||||
* Find single attribute.
|
||||
*/
|
||||
ATTRIBUTE,
|
||||
ATTRIBUTE(FIND_ATTRIBUTE),
|
||||
|
||||
/**
|
||||
* Find rowCount.
|
||||
*/
|
||||
COUNT,
|
||||
COUNT(FIND_COUNT),
|
||||
|
||||
/**
|
||||
* A subquery used as part of a where clause.
|
||||
*/
|
||||
SUBQUERY,
|
||||
SUBQUERY(FIND_SUBQUERY),
|
||||
|
||||
/**
|
||||
* Delete query.
|
||||
*/
|
||||
DELETE,
|
||||
DELETE(FIND_DELETE),
|
||||
|
||||
/**
|
||||
* Update query.
|
||||
*/
|
||||
UPDATE,
|
||||
UPDATE(FIND_UPDATE);
|
||||
|
||||
byte profileEventId;
|
||||
|
||||
Type(byte profileEventId) {
|
||||
this.profileEventId = profileEventId;
|
||||
}
|
||||
|
||||
public byte profileEventId() {
|
||||
return profileEventId;
|
||||
}
|
||||
}
|
||||
|
||||
enum TemporalMode {
|
||||
@@ -140,6 +150,16 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the profile event id based on query mode and type.
|
||||
*/
|
||||
byte profileEventId();
|
||||
|
||||
/**
|
||||
* Return the id used to identify a particular query for the given bean type.
|
||||
*/
|
||||
short getProfileId();
|
||||
|
||||
/**
|
||||
* Check for a single "equal to" expression for the Id.
|
||||
*/
|
||||
|
||||
@@ -9,6 +9,7 @@ import io.ebeaninternal.server.core.PersistDeferredRelationship;
|
||||
import io.ebeaninternal.server.core.PersistRequest;
|
||||
import io.ebeaninternal.server.core.PersistRequestBean;
|
||||
import io.ebeaninternal.server.persist.BatchControl;
|
||||
import io.ebeaninternal.server.transaction.ProfileStream;
|
||||
import io.ebeanservice.docstore.api.DocStoreTransaction;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
@@ -282,4 +283,19 @@ public interface SpiTransaction extends Transaction {
|
||||
* Return the current Tenant Id.
|
||||
*/
|
||||
Object getTenantId();
|
||||
|
||||
/**
|
||||
* Return the offset time from the start of the transaction.
|
||||
*/
|
||||
int profileOffset();
|
||||
|
||||
/**
|
||||
* Check if the event should be added to a profiling transaction.
|
||||
*/
|
||||
void profileEvent(SpiProfileTransactionEvent event);
|
||||
|
||||
/**
|
||||
* Return the stream that profiling events are written to.
|
||||
*/
|
||||
ProfileStream profileStream();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
/**
|
||||
* Event codes used in transaction profiling.
|
||||
*/
|
||||
public interface TxnProfileEventCodes {
|
||||
|
||||
byte EVT_END = 0;
|
||||
byte EVT_COMMIT = 1;
|
||||
byte EVT_ROLLBACK = 2;
|
||||
|
||||
byte EVT_INSERT = 10;
|
||||
byte EVT_UPDATE = 11;
|
||||
byte EVT_DELETE = 12;
|
||||
byte EVT_SOFT_DELETE = 13;
|
||||
byte EVT_DELETE_PERMANENT = 14;
|
||||
byte EVT_ORMUPDATE = 15;
|
||||
byte FIND_UPDATE = 16;
|
||||
byte FIND_DELETE = 17;
|
||||
|
||||
byte EVT_UPDATESQL = 20;
|
||||
byte EVT_CALLABLESQL = 21;
|
||||
|
||||
byte FIND_ONE = 30;
|
||||
byte FIND_MANY = 31;
|
||||
byte FIND_ITERATE = 34;
|
||||
byte FIND_ID_LIST = 35;
|
||||
byte FIND_ATTRIBUTE = 36;
|
||||
byte FIND_COUNT = 37;
|
||||
byte FIND_SUBQUERY = 38;
|
||||
|
||||
byte FIND_ONE_LAZY = 40;
|
||||
byte FIND_MANY_LAZY = 41;
|
||||
|
||||
}
|
||||
@@ -659,7 +659,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
@Override
|
||||
public Transaction createTransaction() {
|
||||
|
||||
return transactionManager.createTransaction(true, -1);
|
||||
return transactionManager.createTransaction(0,true, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -671,7 +671,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
@Override
|
||||
public Transaction createTransaction(TxIsolation isolation) {
|
||||
|
||||
return transactionManager.createTransaction(true, isolation.getLevel());
|
||||
return transactionManager.createTransaction(0,true, isolation.getLevel());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -796,7 +796,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
if (isolation != null) {
|
||||
isoLevel = isolation.getLevel();
|
||||
}
|
||||
t = transactionManager.createTransaction(true, isoLevel);
|
||||
t = transactionManager.createTransaction(txScope.getProfileId(), true, isoLevel);
|
||||
// note ScopeTrans.onFinally() restores the suspended transaction
|
||||
transactionScopeManager.replace(t);
|
||||
}
|
||||
@@ -842,7 +842,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
@Override
|
||||
public Transaction beginTransaction(TxIsolation isolation) {
|
||||
// start an explicit transaction
|
||||
SpiTransaction t = transactionManager.createTransaction(true, isolation.getLevel());
|
||||
SpiTransaction t = transactionManager.createTransaction(0,true, isolation.getLevel());
|
||||
try {
|
||||
transactionScopeManager.set(t);
|
||||
} catch (PersistenceException existingTransactionError) {
|
||||
@@ -2094,7 +2094,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public SpiTransaction beginServerTransaction() {
|
||||
SpiTransaction t = transactionManager.createTransaction(false, -1);
|
||||
SpiTransaction t = transactionManager.createTransaction(0,false, -1);
|
||||
transactionScopeManager.set(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import io.ebean.ExpressionFactory;
|
||||
import io.ebean.annotation.Platform;
|
||||
import io.ebean.cache.ServerCacheManager;
|
||||
import io.ebean.config.ExternalTransactionManager;
|
||||
import io.ebean.config.ProfilingConfig;
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.config.dbplatform.DbHistorySupport;
|
||||
@@ -19,6 +20,7 @@ import io.ebean.plugin.SpiServer;
|
||||
import io.ebeaninternal.api.SpiBackgroundExecutor;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiJsonContext;
|
||||
import io.ebeaninternal.api.SpiProfileHandler;
|
||||
import io.ebeaninternal.dbmigration.DbOffline;
|
||||
import io.ebeaninternal.server.autotune.AutoTuneService;
|
||||
import io.ebeaninternal.server.autotune.service.AutoTuneServiceFactory;
|
||||
@@ -49,11 +51,13 @@ import io.ebeaninternal.server.readaudit.DefaultReadAuditPrepare;
|
||||
import io.ebeaninternal.server.text.json.DJsonContext;
|
||||
import io.ebeaninternal.server.transaction.AutoCommitTransactionManager;
|
||||
import io.ebeaninternal.server.transaction.DataSourceSupplier;
|
||||
import io.ebeaninternal.server.transaction.DefaultProfileHandler;
|
||||
import io.ebeaninternal.server.transaction.DefaultTransactionScopeManager;
|
||||
import io.ebeaninternal.server.transaction.DocStoreTransactionManager;
|
||||
import io.ebeaninternal.server.transaction.ExplicitTransactionManager;
|
||||
import io.ebeaninternal.server.transaction.ExternalTransactionScopeManager;
|
||||
import io.ebeaninternal.server.transaction.JtaTransactionManager;
|
||||
import io.ebeaninternal.server.transaction.NoopProfileHandler;
|
||||
import io.ebeaninternal.server.transaction.TransactionManager;
|
||||
import io.ebeaninternal.server.transaction.TransactionManagerOptions;
|
||||
import io.ebeaninternal.server.transaction.TransactionScopeManager;
|
||||
@@ -352,7 +356,7 @@ public class InternalConfiguration {
|
||||
|
||||
TransactionManagerOptions options =
|
||||
new TransactionManagerOptions(localL2, serverConfig, clusterManager, backgroundExecutor,
|
||||
indexUpdateProcessor, beanDescriptorManager, dataSource());
|
||||
indexUpdateProcessor, beanDescriptorManager, dataSource(), profileHandler());
|
||||
|
||||
if (serverConfig.isExplicitTransactionBeginMode()) {
|
||||
return new ExplicitTransactionManager(options);
|
||||
@@ -366,6 +370,19 @@ public class InternalConfiguration {
|
||||
return new TransactionManager(options);
|
||||
}
|
||||
|
||||
private SpiProfileHandler profileHandler() {
|
||||
|
||||
ProfilingConfig profilingConfig = serverConfig.getProfilingConfig();
|
||||
if (!profilingConfig.isTransactionProfiling()) {
|
||||
return new NoopProfileHandler();
|
||||
}
|
||||
SpiProfileHandler handler = serverConfig.service(SpiProfileHandler.class);
|
||||
if (handler == null) {
|
||||
handler = new DefaultProfileHandler(profilingConfig);
|
||||
}
|
||||
return plugin(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DataSource supplier based on the tenancy mode.
|
||||
*/
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.api.TxnProfileEventCodes;
|
||||
import io.ebeaninternal.server.persist.BatchControl;
|
||||
import io.ebeaninternal.server.persist.BatchPostExecute;
|
||||
import io.ebeaninternal.server.persist.BatchedSqlException;
|
||||
@@ -10,10 +11,20 @@ import io.ebeaninternal.server.persist.PersistExecute;
|
||||
/**
|
||||
* Wraps all the objects used to persist a bean.
|
||||
*/
|
||||
public abstract class PersistRequest extends BeanRequest implements BatchPostExecute {
|
||||
public abstract class PersistRequest extends BeanRequest implements BatchPostExecute, TxnProfileEventCodes {
|
||||
|
||||
public enum Type {
|
||||
INSERT, UPDATE, DELETE, SOFT_DELETE, DELETE_PERMANENT, UPDATESQL, CALLABLESQL
|
||||
INSERT(EVT_INSERT),
|
||||
UPDATE(EVT_UPDATE),
|
||||
DELETE(EVT_DELETE),
|
||||
SOFT_DELETE(EVT_SOFT_DELETE),
|
||||
DELETE_PERMANENT(EVT_DELETE_PERMANENT),
|
||||
UPDATESQL(EVT_UPDATESQL),
|
||||
CALLABLESQL(EVT_CALLABLESQL);
|
||||
byte profileEventId;
|
||||
Type(byte profileEventId) {
|
||||
this.profileEventId = profileEventId;
|
||||
}
|
||||
}
|
||||
|
||||
boolean persistCascade;
|
||||
@@ -43,6 +54,10 @@ public abstract class PersistRequest extends BeanRequest implements BatchPostExe
|
||||
*/
|
||||
public abstract int executeNow();
|
||||
|
||||
void profileBase(byte event, int offset, short beanTypeId, int beanCount) {
|
||||
transaction.profileStream().addEvent(event, offset, beanTypeId, beanCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLogSql() {
|
||||
return transaction.isLogSql();
|
||||
|
||||
@@ -12,6 +12,7 @@ import io.ebean.event.changelog.BeanChange;
|
||||
import io.ebeaninternal.api.ConcurrencyMode;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.api.SpiProfileTransactionEvent;
|
||||
import io.ebeaninternal.api.TransactionEvent;
|
||||
import io.ebeaninternal.server.cache.CacheChangeSet;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
@@ -40,7 +41,7 @@ import java.util.Set;
|
||||
/**
|
||||
* PersistRequest for insert update or delete of a bean.
|
||||
*/
|
||||
public final class PersistRequestBean<T> extends PersistRequest implements BeanPersistRequest<T>, DocStoreUpdate, PreGetterCallback {
|
||||
public final class PersistRequestBean<T> extends PersistRequest implements BeanPersistRequest<T>, DocStoreUpdate, PreGetterCallback, SpiProfileTransactionEvent {
|
||||
|
||||
private final BeanManager<T> beanManager;
|
||||
|
||||
@@ -143,6 +144,8 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
|
||||
private long now;
|
||||
|
||||
private int profileOffset;
|
||||
|
||||
/**
|
||||
* Flag set when request is added to JDBC batch registered as a "getter callback" to automatically flush batch.
|
||||
*/
|
||||
@@ -184,6 +187,14 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
initGeneratedProperties();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add to profile as batched bean insert, update or delete.
|
||||
*/
|
||||
@Override
|
||||
public void profile(int offset, int flushCount) {
|
||||
profileBase(type.profileEventId, offset, beanDescriptor.getProfileId(), flushCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the document store event that should be used for this request.
|
||||
* <p>
|
||||
@@ -753,13 +764,20 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
return control.executeOrQueue(this, true);
|
||||
|
||||
} else {
|
||||
return executeNow();
|
||||
return executeNoBatch();
|
||||
}
|
||||
} catch (BatchedSqlException e) {
|
||||
throw transaction.translate(e.getMessage(), e.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
private int executeNoBatch() {
|
||||
profileOffset = transaction.profileOffset();
|
||||
int result = executeNow();
|
||||
transaction.profileEvent(this);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the generated key back to the bean. Only used for inserts with getGeneratedKeys.
|
||||
*/
|
||||
@@ -1184,4 +1202,12 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
public boolean isStatelessUpdate() {
|
||||
return statelessUpdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add to profile as single bean insert, update or delete (not batched).
|
||||
*/
|
||||
@Override
|
||||
public void profile() {
|
||||
profileBase(type.profileEventId, profileOffset, beanDescriptor.getProfileId(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,11 @@ public final class PersistRequestCallableSql extends PersistRequest {
|
||||
this.callableSql = (SpiCallableSql) cs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void profile(int offset, int flushCount) {
|
||||
profileBase(EVT_CALLABLESQL, offset, (short)0, flushCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int executeOrQueue() {
|
||||
return executeStatement();
|
||||
|
||||
@@ -32,6 +32,11 @@ public final class PersistRequestOrmUpdate extends PersistRequest {
|
||||
this.ormUpdate = ormUpdate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void profile(int offset, int flushCount) {
|
||||
profileBase(EVT_ORMUPDATE, offset, beanDescriptor.getProfileId(), flushCount);
|
||||
}
|
||||
|
||||
public BeanDescriptor<?> getBeanDescriptor() {
|
||||
return beanDescriptor;
|
||||
}
|
||||
|
||||
@@ -38,6 +38,11 @@ public final class PersistRequestUpdateSql extends PersistRequest {
|
||||
this.updateSql = (SpiSqlUpdate) updateSql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void profile(int offset, int flushCount) {
|
||||
profileBase(EVT_UPDATESQL, offset, (short)0, flushCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int executeNow() {
|
||||
return persistExecute.executeSqlUpdate(this);
|
||||
|
||||
@@ -114,6 +114,8 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
|
||||
private final Map<String, String> namedQuery;
|
||||
|
||||
private final short profileBeanId;
|
||||
|
||||
public enum EntityType {
|
||||
ORM, EMBEDDED, VIEW, SQL, DOC
|
||||
}
|
||||
@@ -406,7 +408,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
this.name = InternString.intern(deploy.getName());
|
||||
this.baseTableAlias = "t0";
|
||||
this.fullName = InternString.intern(deploy.getFullName());
|
||||
|
||||
this.profileBeanId = deploy.getProfileId();
|
||||
this.beanType = deploy.getBeanType();
|
||||
this.rootBeanType = PersistenceContextUtil.root(beanType);
|
||||
this.prototypeEntityBean = createPrototypeEntityBean(beanType);
|
||||
@@ -528,6 +530,14 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the id used in profiling to identify the bean type.
|
||||
*/
|
||||
@Override
|
||||
public short getProfileId() {
|
||||
return profileBeanId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive an array of property positions for properties that are initialised in the constructor.
|
||||
* These properties need to be unloaded when populating beans for queries.
|
||||
|
||||
@@ -79,6 +79,7 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Creates BeanDescriptors.
|
||||
@@ -325,6 +326,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
readEntityBeanTable();
|
||||
readEntityDeploymentAssociations();
|
||||
readInheritedIdGenerators();
|
||||
setProfileIds();
|
||||
// creates the BeanDescriptors
|
||||
readEntityRelationships();
|
||||
|
||||
@@ -727,6 +729,23 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set profileIds based on descriptor full name order.
|
||||
*/
|
||||
private void setProfileIds() {
|
||||
|
||||
List<? extends DeployBeanDescriptor<?>> deployDescriptors = deployInfoMap.values().stream()
|
||||
.map(DeployBeanInfo::getDescriptor)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
deployDescriptors.sort(Comparator.comparing(DeployBeanDescriptor::getFullName));
|
||||
|
||||
short id = 0;
|
||||
for (DeployBeanDescriptor<?> desc : deployDescriptors) {
|
||||
desc.setProfileId(++id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the BeanTable from the deployment information gathered so far.
|
||||
*/
|
||||
@@ -1044,7 +1063,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
prop.setUnidirectional();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!findMappedBy(prop)) {
|
||||
makeUnidirectional(info, prop);
|
||||
return;
|
||||
|
||||
@@ -204,6 +204,8 @@ public class DeployBeanDescriptor<T> {
|
||||
|
||||
private List<DeployBeanProperty> idProperties;
|
||||
|
||||
private short profileId;
|
||||
|
||||
/**
|
||||
* Construct the BeanDescriptor.
|
||||
*/
|
||||
@@ -620,6 +622,20 @@ public class DeployBeanDescriptor<T> {
|
||||
setBaseTable(new TableName(viewName), "", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the profileId to identity this bean type.
|
||||
*/
|
||||
public void setProfileId(short profileId) {
|
||||
this.profileId = profileId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the profileId to identify this bean type.
|
||||
*/
|
||||
public short getProfileId() {
|
||||
return profileId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the base table. Only properties mapped to the base table are by default persisted.
|
||||
*/
|
||||
|
||||
@@ -30,4 +30,8 @@ public interface BatchPostExecute {
|
||||
*/
|
||||
void postExecute();
|
||||
|
||||
/**
|
||||
* Add as event to the profiling.
|
||||
*/
|
||||
void profile(int offset, int batchSize);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package io.ebeaninternal.server.persist;
|
||||
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.api.SpiProfileTransactionEvent;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
@@ -12,7 +15,7 @@ import java.util.ArrayList;
|
||||
* This can hold CallableStatements as well.
|
||||
* </p>
|
||||
*/
|
||||
public class BatchedPstmt {
|
||||
public class BatchedPstmt implements SpiProfileTransactionEvent {
|
||||
|
||||
/**
|
||||
* The underlying statement.
|
||||
@@ -31,13 +34,18 @@ public class BatchedPstmt {
|
||||
|
||||
private final String sql;
|
||||
|
||||
private final SpiTransaction transaction;
|
||||
|
||||
private int profileStart;
|
||||
|
||||
/**
|
||||
* Create with a given statement.
|
||||
*/
|
||||
public BatchedPstmt(PreparedStatement pstmt, boolean isGenKeys, String sql) {
|
||||
public BatchedPstmt(PreparedStatement pstmt, boolean isGenKeys, String sql, SpiTransaction transaction) {
|
||||
this.pstmt = pstmt;
|
||||
this.isGenKeys = isGenKeys;
|
||||
this.sql = sql;
|
||||
this.transaction = transaction;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,12 +82,20 @@ public class BatchedPstmt {
|
||||
*/
|
||||
public void executeBatch(boolean getGeneratedKeys) throws SQLException {
|
||||
|
||||
this.profileStart = transaction.profileOffset();
|
||||
executeAndCheckRowCounts();
|
||||
if (isGenKeys && getGeneratedKeys) {
|
||||
getGeneratedKeys();
|
||||
}
|
||||
postExecute();
|
||||
close();
|
||||
transaction.profileEvent(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void profile() {
|
||||
// just use the first to add the event
|
||||
list.get(0).profile(profileStart, list.size());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,8 +109,8 @@ public class BatchedPstmt {
|
||||
}
|
||||
|
||||
private void postExecute() {
|
||||
for (BatchPostExecute aList : list) {
|
||||
aList.postExecute();
|
||||
for (BatchPostExecute item : list) {
|
||||
item.postExecute();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ public class PstmtFactory {
|
||||
Connection conn = t.getInternalConnection();
|
||||
stmt = conn.prepareStatement(sql);
|
||||
|
||||
BatchedPstmt bs = new BatchedPstmt(stmt, false, sql);
|
||||
BatchedPstmt bs = new BatchedPstmt(stmt, false, sql, t);
|
||||
batch.addStmt(bs, batchExe);
|
||||
return stmt;
|
||||
}
|
||||
@@ -78,7 +78,7 @@ public class PstmtFactory {
|
||||
Connection conn = t.getInternalConnection();
|
||||
stmt = conn.prepareCall(sql);
|
||||
|
||||
BatchedPstmt bs = new BatchedPstmt(stmt, false, sql);
|
||||
BatchedPstmt bs = new BatchedPstmt(stmt, false, sql, t);
|
||||
batch.addStmt(bs, batchExe);
|
||||
return stmt;
|
||||
}
|
||||
|
||||
@@ -283,7 +283,7 @@ public abstract class DmlHandler implements PersistHandler, BindableRequest {
|
||||
|
||||
stmt = getPstmt(t, sql, genKeys);
|
||||
|
||||
BatchedPstmt bs = new BatchedPstmt(stmt, genKeys, sql);
|
||||
BatchedPstmt bs = new BatchedPstmt(stmt, genKeys, sql, t);
|
||||
batch.addStmt(bs, request);
|
||||
return stmt;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import io.ebean.event.readaudit.ReadEvent;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.SpiQuery.Mode;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.api.SpiProfileTransactionEvent;
|
||||
import io.ebeaninternal.server.autotune.ProfilingListener;
|
||||
import io.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import io.ebeaninternal.server.core.SpiOrmQueryRequest;
|
||||
@@ -50,7 +51,7 @@ import java.util.NoSuchElementException;
|
||||
* the key object used in reading the flat resultSet back into Objects.
|
||||
* </p>
|
||||
*/
|
||||
public class CQuery<T> implements DbReadContext, CancelableQuery {
|
||||
public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTransactionEvent {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(CQuery.class);
|
||||
|
||||
@@ -172,6 +173,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
|
||||
|
||||
private final Boolean readOnly;
|
||||
|
||||
private int profileOffset;
|
||||
private long startNano;
|
||||
|
||||
private long executionTimeMicros;
|
||||
@@ -318,6 +320,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
|
||||
|
||||
// prepare
|
||||
SpiTransaction t = request.getTransaction();
|
||||
profileOffset = t.profileOffset();
|
||||
Connection conn = t.getInternalConnection();
|
||||
|
||||
if (query.isRawSql()) {
|
||||
@@ -587,11 +590,19 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
|
||||
profilingListener.collectQueryInfo(objectGraphNode, loadedBeanCount, executionTimeMicros);
|
||||
}
|
||||
queryPlan.executionTime(loadedBeanCount, executionTimeMicros, objectGraphNode);
|
||||
getTransaction().profileEvent(this);
|
||||
} catch (Exception e) {
|
||||
logger.error("Error updating execution statistics", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void profile() {
|
||||
getTransaction()
|
||||
.profileStream()
|
||||
.addQueryEvent(query.profileEventId(), profileOffset, desc.getProfileId(), loadedBeanCount, query.getProfileId());
|
||||
}
|
||||
|
||||
QueryIterator<T> readIterate(int bufferSize, OrmQueryRequest<T> request) {
|
||||
|
||||
if (bufferSize > 0) {
|
||||
|
||||
@@ -253,6 +253,11 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
|
||||
private String nativeSql;
|
||||
|
||||
/**
|
||||
* Identity the query for profiling purposes (expected to be unique for a bean type).
|
||||
*/
|
||||
private short profileId;
|
||||
|
||||
public DefaultOrmQuery(BeanDescriptor<T> desc, EbeanServer server, ExpressionFactory expressionFactory) {
|
||||
this.beanDescriptor = desc;
|
||||
this.beanType = desc.getBeanType();
|
||||
@@ -280,6 +285,26 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte profileEventId() {
|
||||
switch (mode) {
|
||||
case LAZYLOAD_BEAN: return FIND_ONE_LAZY;
|
||||
case LAZYLOAD_MANY: return FIND_MANY_LAZY;
|
||||
default:
|
||||
return type.profileEventId();
|
||||
}
|
||||
}
|
||||
|
||||
public short getProfileId() {
|
||||
return profileId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> setProfileId(int profileId) {
|
||||
this.profileId = (short)profileId;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoTunable() {
|
||||
return nativeSql == null && beanDescriptor.isAutoTunable();
|
||||
|
||||
@@ -19,7 +19,7 @@ public class AutoCommitTransactionManager extends TransactionManager {
|
||||
* Create an autoCommit based Transaction.
|
||||
*/
|
||||
@Override
|
||||
protected SpiTransaction createTransaction(boolean explicit, Connection c, long id) {
|
||||
protected SpiTransaction createTransaction(int profileId, boolean explicit, Connection c, long id) {
|
||||
|
||||
return new AutoCommitJdbcTransaction(prefix + id, explicit, c, this);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
package io.ebeaninternal.server.transaction;
|
||||
|
||||
import io.ebean.config.ProfilingConfig;
|
||||
import io.ebean.plugin.BeanType;
|
||||
import io.ebean.plugin.Plugin;
|
||||
import io.ebean.plugin.SpiServer;
|
||||
import io.ebeaninternal.api.SpiProfileHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeFormatterBuilder;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static java.time.temporal.ChronoField.DAY_OF_MONTH;
|
||||
import static java.time.temporal.ChronoField.HOUR_OF_DAY;
|
||||
import static java.time.temporal.ChronoField.MINUTE_OF_HOUR;
|
||||
import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
|
||||
import static java.time.temporal.ChronoField.SECOND_OF_MINUTE;
|
||||
import static java.time.temporal.ChronoField.YEAR;
|
||||
|
||||
/**
|
||||
* Default profile handler.
|
||||
* <p>
|
||||
* Uses ConcurrentLinkedQueue to minimise contention on threads calling collectTransactionProfile().
|
||||
* </p>
|
||||
* <p>
|
||||
* Uses a sleep backoff on the single threaded consumer that reads the profiles and writes them to files.
|
||||
* </p>
|
||||
*/
|
||||
public class DefaultProfileHandler implements SpiProfileHandler, Plugin {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(DefaultProfileHandler.class);
|
||||
|
||||
private static final DateTimeFormatter DTF;
|
||||
|
||||
static {
|
||||
DTF = new DateTimeFormatterBuilder()
|
||||
.parseCaseInsensitive()
|
||||
.appendValue(YEAR, 4)
|
||||
.appendValue(MONTH_OF_YEAR, 2)
|
||||
.appendValue(DAY_OF_MONTH, 2)
|
||||
.appendLiteral('-')
|
||||
.appendValue(HOUR_OF_DAY, 2)
|
||||
.appendValue(MINUTE_OF_HOUR, 2)
|
||||
.appendValue(SECOND_OF_MINUTE, 2)
|
||||
.toFormatter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Low contention choice.
|
||||
*/
|
||||
private final Queue<TransactionProfile> queue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
private final ExecutorService executor;
|
||||
|
||||
private final File dir;
|
||||
|
||||
private final long minMicros;
|
||||
|
||||
private final int[] includeIds;
|
||||
|
||||
private final long profilesPerFile;
|
||||
|
||||
private volatile boolean shutdown;
|
||||
|
||||
private long profileCounter;
|
||||
|
||||
/**
|
||||
* Slow down polling of transaction profiling queue.
|
||||
*/
|
||||
private int sleepBackoff;
|
||||
|
||||
private FileOutputStream out;
|
||||
|
||||
public DefaultProfileHandler(ProfilingConfig config) {
|
||||
this.minMicros = config.getMinimumTransactionMicros();
|
||||
this.includeIds = config.getIncludeProfileIds();
|
||||
this.profilesPerFile = config.getProfilesPerFile();
|
||||
|
||||
// dedicated single threaded executor for consuming the
|
||||
// profiling and writing it to file(s)
|
||||
this.executor = Executors.newSingleThreadExecutor();
|
||||
this.dir = new File(config.getDirectory());
|
||||
if (!dir.mkdirs()) {
|
||||
log.error("failed to mkdirs " + dir.getAbsolutePath());
|
||||
}
|
||||
incrementFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Low contention adding the transaction profile to the queue.
|
||||
* Minimise the impact to the normal transaction processing (threads).
|
||||
*/
|
||||
@Override
|
||||
public void collectTransactionProfile(TransactionProfile transactionProfile) {
|
||||
queue.add(transactionProfile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a ProfileStream if we are profiling for the given transaction profileId.
|
||||
*/
|
||||
@Override
|
||||
public ProfileStream createProfileStream(int profileId) {
|
||||
|
||||
if (profileId < 1) {
|
||||
// not this transaction
|
||||
return null;
|
||||
}
|
||||
|
||||
if (includeIds.length == 0) {
|
||||
return new ProfileStream(profileId);
|
||||
}
|
||||
|
||||
// check if we are profiling this specific transaction profileId, just
|
||||
// perform linear search as this is expected to be a small array
|
||||
for (int includeId : includeIds) {
|
||||
if (includeId == profileId) {
|
||||
return new ProfileStream(profileId);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void flushCurrentFile() {
|
||||
if (out != null) {
|
||||
try {
|
||||
out.flush();
|
||||
out.close();
|
||||
} catch (IOException e) {
|
||||
log.error("Failed to flush and close transaction profiling file ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move to the next file to write to.
|
||||
*/
|
||||
private void incrementFile() {
|
||||
flushCurrentFile();
|
||||
try {
|
||||
String now = DTF.format(LocalDateTime.now());
|
||||
File file = new File(dir, "txprofile-" + now + ".tprofile");
|
||||
out = new FileOutputStream(file);
|
||||
} catch (FileNotFoundException e) {
|
||||
log.error("Not expected", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main loop for polling the queue and processing profiling messages.
|
||||
*/
|
||||
private void collect() {
|
||||
while (!shutdown) {
|
||||
TransactionProfile profile = queue.poll();
|
||||
if (profile == null) {
|
||||
sleep();
|
||||
|
||||
} else if (include(profile)) {
|
||||
write(profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the profile to the current file.
|
||||
*/
|
||||
private void write(TransactionProfile profile) {
|
||||
try {
|
||||
sleepBackoff = 0;
|
||||
++profileCounter;
|
||||
out.write(profile.getBytes());
|
||||
if (profileCounter % profilesPerFile == 0) {
|
||||
incrementFile();
|
||||
log.debug("profiled {} transactions", profileCounter);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.warn("Error writing transaction profiling", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the profile should be included (or false for ignored).
|
||||
*/
|
||||
private boolean include(TransactionProfile profile) {
|
||||
return profile.getTotalMicros() >= minMicros;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sleep backing off towards 250 millis when there is no activity.
|
||||
* This seems to be simple and decent for our queue consumer.
|
||||
*/
|
||||
private void sleep() {
|
||||
try {
|
||||
// backoff sleep when nothing is happening
|
||||
int sleepFor = Math.min(++sleepBackoff, 250);
|
||||
Thread.sleep(sleepFor);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(SpiServer server) {
|
||||
|
||||
StringBuilder sb = new StringBuilder(200);
|
||||
sb.append("Bean profile mapping - ");
|
||||
for (BeanType<?> type : server.getBeanTypes()) {
|
||||
sb.append("profileId:").append(type.getProfileId())
|
||||
.append(" ").append(type.getName()).append(", ");
|
||||
}
|
||||
log.info(sb.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void online(boolean online) {
|
||||
if (online) {
|
||||
executor.submit(this::collect);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
shutdown = true;
|
||||
log.trace("shutting down profiling consumer");
|
||||
flushCurrentFile();
|
||||
try {
|
||||
executor.shutdown();
|
||||
if (!executor.awaitTermination(4, TimeUnit.SECONDS)) {
|
||||
log.info("Shut down timeout exceeded. Terminating profiling consumer thread.");
|
||||
executor.shutdownNow();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.warn("Interrupt on shutdown", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,9 +22,9 @@ public class DocStoreTransactionManager extends TransactionManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiTransaction createTransaction(boolean explicit, int isolationLevel) {
|
||||
public SpiTransaction createTransaction(int profileId, boolean explicit, int isolationLevel) {
|
||||
long id = counter.incrementAndGet();
|
||||
return createTransaction(explicit, null, id);
|
||||
return createTransaction(profileId, explicit, null, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -33,7 +33,7 @@ public class DocStoreTransactionManager extends TransactionManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SpiTransaction createTransaction(boolean explicit, Connection c, long id) {
|
||||
protected SpiTransaction createTransaction(int profileId, boolean explicit, Connection c, long id) {
|
||||
return new DocStoreOnlyTransaction(prefix + id, explicit, this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ public class ExplicitTransactionManager extends TransactionManager {
|
||||
* Create a ExplicitJdbcTransaction.
|
||||
*/
|
||||
@Override
|
||||
protected SpiTransaction createTransaction(boolean explicit, Connection c, long id) {
|
||||
protected SpiTransaction createTransaction(int profileId, boolean explicit, Connection c, long id) {
|
||||
|
||||
return new ExplicitJdbcTransaction(prefix + id, explicit, c, this);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@ import io.ebean.config.dbplatform.DatabasePlatform.OnQueryOnly;
|
||||
import io.ebean.event.changelog.BeanChange;
|
||||
import io.ebean.event.changelog.ChangeSet;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.api.SpiProfileTransactionEvent;
|
||||
import io.ebeaninternal.api.TransactionEvent;
|
||||
import io.ebeaninternal.api.TxnProfileEventCodes;
|
||||
import io.ebeaninternal.server.core.PersistDeferredRelationship;
|
||||
import io.ebeaninternal.server.core.PersistRequest;
|
||||
import io.ebeaninternal.server.core.PersistRequestBean;
|
||||
@@ -34,7 +36,7 @@ import java.util.Map;
|
||||
/**
|
||||
* JDBC Connection based transaction.
|
||||
*/
|
||||
public class JdbcTransaction implements SpiTransaction {
|
||||
public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(JdbcTransaction.class);
|
||||
|
||||
@@ -169,11 +171,21 @@ public class JdbcTransaction implements SpiTransaction {
|
||||
|
||||
protected DocStoreTransaction docStoreTxn;
|
||||
|
||||
private final ProfileStream profileStream;
|
||||
|
||||
/**
|
||||
* Create without ProfileStream option (no profiling).
|
||||
*/
|
||||
public JdbcTransaction(String id, boolean explicit, Connection connection, TransactionManager manager) {
|
||||
this(null, id, explicit, connection, manager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new JdbcTransaction.
|
||||
*/
|
||||
public JdbcTransaction(String id, boolean explicit, Connection connection, TransactionManager manager) {
|
||||
public JdbcTransaction(ProfileStream profileStream, String id, boolean explicit, Connection connection, TransactionManager manager) {
|
||||
try {
|
||||
this.profileStream = profileStream;
|
||||
this.active = true;
|
||||
this.id = id;
|
||||
this.logPrefix = deriveLogPrefix(id);
|
||||
@@ -201,6 +213,23 @@ public class JdbcTransaction implements SpiTransaction {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int profileOffset() {
|
||||
return (profileStream == null) ? 0 : profileStream.offset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void profileEvent(SpiProfileTransactionEvent event) {
|
||||
if (profileStream != null) {
|
||||
event.profile();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProfileStream profileStream() {
|
||||
return profileStream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden in AutoCommitJdbcTransaction as that expects to run/operate with autocommit true.
|
||||
*/
|
||||
@@ -884,6 +913,7 @@ public class JdbcTransaction implements SpiTransaction {
|
||||
}
|
||||
connection = null;
|
||||
active = false;
|
||||
profileEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -918,14 +948,28 @@ public class JdbcTransaction implements SpiTransaction {
|
||||
* Perform the actual rollback on the connection.
|
||||
*/
|
||||
protected void performRollback() throws SQLException {
|
||||
int offset = profileOffset();
|
||||
connection.rollback();
|
||||
if (profileStream != null) {
|
||||
profileStream.addEvent(EVT_ROLLBACK, offset);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the actual commit on the connection.
|
||||
*/
|
||||
protected void performCommit() throws SQLException {
|
||||
int offset = profileOffset();
|
||||
connection.commit();
|
||||
if (profileStream != null) {
|
||||
profileStream.addEvent(EVT_COMMIT, offset);
|
||||
}
|
||||
}
|
||||
|
||||
private void profileEnd() {
|
||||
if (profileStream != null) {
|
||||
profileStream.end(manager);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package io.ebeaninternal.server.transaction;
|
||||
|
||||
import io.ebeaninternal.api.SpiProfileHandler;
|
||||
|
||||
/**
|
||||
* A do nothing SpiProfileHandler.
|
||||
*/
|
||||
public class NoopProfileHandler implements SpiProfileHandler {
|
||||
|
||||
@Override
|
||||
public void collectTransactionProfile(TransactionProfile transactionProfile) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProfileStream createProfileStream(int profileId) {
|
||||
// always return null
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package io.ebeaninternal.server.transaction;
|
||||
|
||||
import io.ebeaninternal.api.TxnProfileEventCodes;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A binary encoding of the transaction profiling events.
|
||||
*/
|
||||
public class ProfileStream implements TxnProfileEventCodes {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ProfileStream.class);
|
||||
|
||||
private final int profId;
|
||||
private final long startNanos;
|
||||
private final DataOutputStream out;
|
||||
private final ByteArrayOutputStream profileBuffer;
|
||||
|
||||
public ProfileStream(int profId) {
|
||||
this.profId = profId;
|
||||
this.startNanos = System.nanoTime();
|
||||
this.profileBuffer = new ByteArrayOutputStream(200);
|
||||
this.out = new DataOutputStream(profileBuffer);
|
||||
try {
|
||||
out.writeLong(System.currentTimeMillis());
|
||||
out.writeInt(profId);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Unexpected error starting transaction profiling", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the time offset from the beginning of the transaction.
|
||||
*/
|
||||
public int offset() {
|
||||
// int max of 2,147,483,648 as micros = 35 minutes
|
||||
// use 10_000 to get 100th of millis to max at 357 minutes (almost 6 hours)
|
||||
// not micros so we can reasonably use int rather than long
|
||||
return (int)((System.nanoTime() - startNanos) / 10_000L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the commit/rollback event.
|
||||
*/
|
||||
public void addEvent(byte event, int startOffset) {
|
||||
try {
|
||||
out.writeByte(event);
|
||||
out.writeInt(startOffset);
|
||||
out.writeInt(offset() - startOffset);
|
||||
} catch (IOException e) {
|
||||
logger.error("Error writing event to transaction profiling", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a query execution event.
|
||||
*/
|
||||
public void addQueryEvent(byte event, int offset, short beanTypeId, int beanCount, short queryId) {
|
||||
add(event, offset, beanTypeId, beanCount, queryId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a persist event.
|
||||
*/
|
||||
public void addEvent(byte event, int offset, short beanTypeId, int beanCount) {
|
||||
add(event, offset, beanTypeId, beanCount, (short)0);
|
||||
}
|
||||
|
||||
private void add(byte event, int offset, short beanTypeId, int beanCount, short queryId) {
|
||||
try {
|
||||
out.writeByte(event);
|
||||
out.writeInt(offset);
|
||||
out.writeInt(offset() - offset);
|
||||
out.writeShort(beanTypeId);
|
||||
out.writeInt(beanCount);
|
||||
out.writeShort(queryId);
|
||||
} catch (IOException e) {
|
||||
logger.error("Error writing event to transaction profiling", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* End the transaction profiling.
|
||||
*/
|
||||
public void end(TransactionManager manager) {
|
||||
try {
|
||||
long totalMicros = ((System.nanoTime() - startNanos) / 1_000L);
|
||||
out.writeByte(EVT_END);
|
||||
out.writeLong(totalMicros);
|
||||
out.flush();
|
||||
out.close();
|
||||
manager.profileCollect(new TransactionProfile(profId, totalMicros, profileBuffer.toByteArray()));
|
||||
} catch (IOException e) {
|
||||
logger.error("Error flushing and collecting profiling", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -34,7 +34,7 @@ abstract class TransactionFactory {
|
||||
/**
|
||||
* Return a new transaction.
|
||||
*/
|
||||
abstract SpiTransaction createTransaction(boolean explicit, int isolationLevel);
|
||||
abstract SpiTransaction createTransaction(int profileId, boolean explicit, int isolationLevel);
|
||||
|
||||
/**
|
||||
* Set the Transaction Isolation level if required.
|
||||
|
||||
@@ -22,20 +22,20 @@ class TransactionFactoryBasic extends TransactionFactory {
|
||||
|
||||
@Override
|
||||
public SpiTransaction createQueryTransaction(Object tenantId) {
|
||||
return create(false);
|
||||
return create(0,false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiTransaction createTransaction(boolean explicit, int isolationLevel) {
|
||||
SpiTransaction t = create(explicit);
|
||||
public SpiTransaction createTransaction(int profileId, boolean explicit, int isolationLevel) {
|
||||
SpiTransaction t = create(profileId, explicit);
|
||||
return setIsolationLevel(t, explicit, isolationLevel);
|
||||
}
|
||||
|
||||
private SpiTransaction create(boolean explicit) {
|
||||
private SpiTransaction create(int profileId, boolean explicit) {
|
||||
Connection c = null;
|
||||
try {
|
||||
c = dataSource.getConnection();
|
||||
return manager.createTransaction(explicit, c, counter.incrementAndGet());
|
||||
return manager.createTransaction(profileId, explicit, c, counter.incrementAndGet());
|
||||
|
||||
} catch (PersistenceException ex) {
|
||||
JdbcClose.close(c);
|
||||
|
||||
@@ -25,17 +25,17 @@ class TransactionFactoryTenant extends TransactionFactory {
|
||||
|
||||
@Override
|
||||
public SpiTransaction createQueryTransaction(Object tenantId) {
|
||||
return create(false, tenantId);
|
||||
return create(0,false, tenantId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiTransaction createTransaction(boolean explicit, int isolationLevel) {
|
||||
public SpiTransaction createTransaction(int profileId, boolean explicit, int isolationLevel) {
|
||||
|
||||
SpiTransaction t = create(explicit, null);
|
||||
SpiTransaction t = create(profileId, explicit, null);
|
||||
return setIsolationLevel(t, explicit, isolationLevel);
|
||||
}
|
||||
|
||||
private SpiTransaction create(boolean explicit, Object tenantId) {
|
||||
private SpiTransaction create(int profileId, boolean explicit, Object tenantId) {
|
||||
Connection c = null;
|
||||
try {
|
||||
if (tenantId == null) {
|
||||
@@ -43,7 +43,7 @@ class TransactionFactoryTenant extends TransactionFactory {
|
||||
tenantId = tenantProvider.currentId();
|
||||
}
|
||||
c = dataSourceSupplier.getConnection(tenantId);
|
||||
SpiTransaction transaction = manager.createTransaction(explicit, c, counter.incrementAndGet());
|
||||
SpiTransaction transaction = manager.createTransaction(profileId, explicit, c, counter.incrementAndGet());
|
||||
transaction.setTenantId(tenantId);
|
||||
return transaction;
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ 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.ebeaninternal.api.SpiProfileHandler;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.api.TransactionEvent;
|
||||
import io.ebeaninternal.api.TransactionEventTable;
|
||||
@@ -110,6 +111,8 @@ public class TransactionManager {
|
||||
|
||||
private final DatabasePlatform databasePlatform;
|
||||
|
||||
private final SpiProfileHandler profileHandler;
|
||||
|
||||
/**
|
||||
* Create the TransactionManager
|
||||
*/
|
||||
@@ -131,6 +134,7 @@ public class TransactionManager {
|
||||
this.dataSourceSupplier = options.dataSourceSupplier;
|
||||
this.docStoreActive = options.config.getDocStoreConfig().isActive();
|
||||
this.docStoreUpdateProcessor = options.docStoreUpdateProcessor;
|
||||
this.profileHandler = options.profileHandler;
|
||||
this.bulkEventListenerMap = new BulkEventListenerMap(options.config.getBulkTableEventListeners());
|
||||
this.prefix = "";
|
||||
this.externalTransPrefix = "e";
|
||||
@@ -248,8 +252,8 @@ public class TransactionManager {
|
||||
/**
|
||||
* Create a new Transaction.
|
||||
*/
|
||||
public SpiTransaction createTransaction(boolean explicit, int isolationLevel) {
|
||||
return transactionFactory.createTransaction(explicit, isolationLevel);
|
||||
public SpiTransaction createTransaction(int profileId, boolean explicit, int isolationLevel) {
|
||||
return transactionFactory.createTransaction(profileId, explicit, isolationLevel);
|
||||
}
|
||||
|
||||
public SpiTransaction createQueryTransaction(Object tenantId) {
|
||||
@@ -259,8 +263,10 @@ public class TransactionManager {
|
||||
/**
|
||||
* Create a new transaction.
|
||||
*/
|
||||
protected SpiTransaction createTransaction(boolean explicit, Connection c, long id) {
|
||||
return new JdbcTransaction(prefix + id, explicit, c, this);
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -412,4 +418,10 @@ public class TransactionManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the collected transaction profiling information.
|
||||
*/
|
||||
public void profileCollect(TransactionProfile transactionProfile) {
|
||||
profileHandler.collectTransactionProfile(transactionProfile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.transaction;
|
||||
|
||||
import io.ebean.BackgroundExecutor;
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebeaninternal.api.SpiProfileHandler;
|
||||
import io.ebeaninternal.server.cluster.ClusterManager;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
|
||||
import io.ebeanservice.docstore.api.DocStoreUpdateProcessor;
|
||||
@@ -19,10 +20,11 @@ public class TransactionManagerOptions {
|
||||
final DocStoreUpdateProcessor docStoreUpdateProcessor;
|
||||
final BeanDescriptorManager descMgr;
|
||||
final DataSourceSupplier dataSourceSupplier;
|
||||
final SpiProfileHandler profileHandler;
|
||||
|
||||
|
||||
public TransactionManagerOptions(boolean localL2Caching, ServerConfig config, ClusterManager clusterManager, BackgroundExecutor backgroundExecutor,
|
||||
DocStoreUpdateProcessor docStoreUpdateProcessor, BeanDescriptorManager descMgr, DataSourceSupplier dataSourceSupplier) {
|
||||
public TransactionManagerOptions(boolean localL2Caching, ServerConfig config, ClusterManager clusterManager,
|
||||
BackgroundExecutor backgroundExecutor, DocStoreUpdateProcessor docStoreUpdateProcessor,
|
||||
BeanDescriptorManager descMgr, DataSourceSupplier dataSourceSupplier, SpiProfileHandler profileHandler) {
|
||||
|
||||
this.localL2Caching = localL2Caching;
|
||||
this.config = config;
|
||||
@@ -31,6 +33,7 @@ public class TransactionManagerOptions {
|
||||
this.docStoreUpdateProcessor = docStoreUpdateProcessor;
|
||||
this.descMgr = descMgr;
|
||||
this.dataSourceSupplier = dataSourceSupplier;
|
||||
this.profileHandler = profileHandler;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package io.ebeaninternal.server.transaction;
|
||||
|
||||
/**
|
||||
* Profiling information for a single transaction that has completed.
|
||||
*/
|
||||
public class TransactionProfile {
|
||||
|
||||
/**
|
||||
* The profileId of the transaction (On @Transactional explicitly or can be automatically set by enhancement).
|
||||
*/
|
||||
private final int profileId;
|
||||
|
||||
/**
|
||||
* The total execution time of the transaction (for filtering out small/short transactions).
|
||||
*/
|
||||
private final long totalMicros;
|
||||
|
||||
/**
|
||||
* The binary encoding of the transaction profiling events.
|
||||
*/
|
||||
private final byte[] bytes;
|
||||
|
||||
/**
|
||||
* Create with profileId, total micros and encoded profile data.
|
||||
*/
|
||||
public TransactionProfile(int profileId, long totalMicros, byte[] bytes) {
|
||||
this.profileId = profileId;
|
||||
this.totalMicros = totalMicros;
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the transaction profileId.
|
||||
*/
|
||||
public int getProfileId() {
|
||||
return profileId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the total transaction execution time in micros.
|
||||
*/
|
||||
public long getTotalMicros() {
|
||||
return totalMicros;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the profiling data in encoded form.
|
||||
*/
|
||||
public byte[] getBytes() {
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
api-version: 11.3
|
||||
@@ -0,0 +1,34 @@
|
||||
package io.ebeaninternal.server.transaction;
|
||||
|
||||
import io.ebean.config.ProfilingConfig;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class DefaultProfileHandlerTest {
|
||||
@Test
|
||||
public void createProfileStream() throws Exception {
|
||||
|
||||
DefaultProfileHandler handler = new DefaultProfileHandler(new ProfilingConfig());
|
||||
|
||||
assertNotNull(handler.createProfileStream(12));
|
||||
assertNull(handler.createProfileStream(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createProfileStream_when_specificIncludeIds() throws Exception {
|
||||
|
||||
ProfilingConfig config = new ProfilingConfig();
|
||||
config.setIncludeProfileIds(new int[]{100,101});
|
||||
|
||||
DefaultProfileHandler handler = new DefaultProfileHandler(config);
|
||||
|
||||
assertNotNull(handler.createProfileStream(100));
|
||||
assertNotNull(handler.createProfileStream(101));
|
||||
|
||||
assertNull(handler.createProfileStream(0));
|
||||
assertNull(handler.createProfileStream(12));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,12 +11,12 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Helper that can collect the SQL that is logged via SLF4J.
|
||||
* Helper that can collectTransactionProfile the SQL that is logged via SLF4J.
|
||||
* <p>
|
||||
* Used {@link #start()} and {@link #stop()} to collect the logged messages that contain the
|
||||
* Used {@link #start()} and {@link #stop()} to collectTransactionProfile the logged messages that contain the
|
||||
* executed SQL statements.
|
||||
* <p>
|
||||
* Internally this uses a Logback Appender to collect messages for org.avaje.ebean.SQL.
|
||||
* Internally this uses a Logback Appender to collectTransactionProfile messages for org.avaje.ebean.SQL.
|
||||
*/
|
||||
public class LoggedSqlCollector {
|
||||
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ public class TestPersistenceContextScopeUsingOrders extends BaseTestCase {
|
||||
|
||||
assertTrue(!orders.isEmpty());
|
||||
|
||||
// collect the customer instances
|
||||
// collectTransactionProfile the customer instances
|
||||
List<Customer> customers = new ArrayList<>();
|
||||
Set<Integer> identities = new HashSet<>();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user