mirror of
https://github.com/ebean-orm/ebean.git
synced 2026-10-21 05:21:01 +00:00
Merge branch 'master' of github.com:ebean-orm/ebean
This commit is contained in:
@@ -723,19 +723,6 @@ public interface DatabaseBuilder {
|
||||
@Deprecated
|
||||
DatabaseBuilder setReadAuditPrepare(ReadAuditPrepare readAuditPrepare);
|
||||
|
||||
/**
|
||||
* Set the configuration for profiling.
|
||||
*/
|
||||
default DatabaseBuilder profilingConfig(ProfilingConfig profilingConfig) {
|
||||
return setProfilingConfig(profilingConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated migrate to {@link #profilingConfig(ProfilingConfig)}.
|
||||
*/
|
||||
@Deprecated
|
||||
DatabaseBuilder setProfilingConfig(ProfilingConfig profilingConfig);
|
||||
|
||||
/**
|
||||
* Set the suffix appended to the base table to derive the view that contains the union
|
||||
* of the base table and the history table in order to support asOf queries.
|
||||
@@ -2490,11 +2477,6 @@ public interface DatabaseBuilder {
|
||||
*/
|
||||
TenantCatalogProvider getTenantCatalogProvider();
|
||||
|
||||
/**
|
||||
* Return the configuration for profiling.
|
||||
*/
|
||||
ProfilingConfig getProfilingConfig();
|
||||
|
||||
/**
|
||||
* Return the DB schema to use.
|
||||
*/
|
||||
|
||||
@@ -539,8 +539,6 @@ public class DatabaseConfig implements DatabaseBuilder.Settings {
|
||||
*/
|
||||
private SlowQueryListener slowQueryListener;
|
||||
|
||||
private ProfilingConfig profilingConfig = new ProfilingConfig();
|
||||
|
||||
/**
|
||||
* The mappingLocations for searching xml mapping.
|
||||
*/
|
||||
@@ -1028,17 +1026,6 @@ public class DatabaseConfig implements DatabaseBuilder.Settings {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProfilingConfig getProfilingConfig() {
|
||||
return profilingConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatabaseConfig setProfilingConfig(ProfilingConfig profilingConfig) {
|
||||
this.profilingConfig = profilingConfig;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDbSchema() {
|
||||
return dbSchema;
|
||||
@@ -2137,7 +2124,6 @@ public class DatabaseConfig implements DatabaseBuilder.Settings {
|
||||
*/
|
||||
protected void loadSettings(PropertiesWrapper p) {
|
||||
dbSchema = p.get("dbSchema", dbSchema);
|
||||
profilingConfig.loadSettings(p, name);
|
||||
platformConfig.loadSettings(p);
|
||||
if (platformConfig.isAllQuotedIdentifiers()) {
|
||||
adjustNamingConventionForAllQuoted();
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
package io.ebean.config;
|
||||
|
||||
/**
|
||||
* Configuration for transaction profiling.
|
||||
*/
|
||||
public class ProfilingConfig {
|
||||
|
||||
/**
|
||||
* When true transaction profiling is enabled.
|
||||
*/
|
||||
private boolean enabled;
|
||||
|
||||
/**
|
||||
* Set true for verbose mode.
|
||||
*/
|
||||
private boolean verbose;
|
||||
|
||||
/**
|
||||
* The minimum transaction execution time to be included in profiling.
|
||||
*/
|
||||
private long minimumMicros;
|
||||
|
||||
/**
|
||||
* 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 isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to enable transaction profiling.
|
||||
*/
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if verbose mode is used.
|
||||
*/
|
||||
public boolean isVerbose() {
|
||||
return verbose;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to use verbose mode.
|
||||
*/
|
||||
public void setVerbose(boolean verbose) {
|
||||
this.verbose = verbose;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the minimum transaction execution to be included in profiling.
|
||||
*/
|
||||
public long getMinimumMicros() {
|
||||
return minimumMicros;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the minimum transaction execution to be included in profiling.
|
||||
*/
|
||||
public void setMinimumMicros(long minimumMicros) {
|
||||
this.minimumMicros = minimumMicros;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
enabled = p.getBoolean("profiling", enabled);
|
||||
verbose = p.getBoolean("profiling.verbose", verbose);
|
||||
|
||||
directory = p.get("profiling.directory", directory);
|
||||
profilesPerFile = p.getLong("profiling.profilesPerFile", profilesPerFile);
|
||||
minimumMicros = p.getLong("profiling.minimumMicros", minimumMicros);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -10,18 +10,6 @@ import org.jspecify.annotations.Nullable;
|
||||
*/
|
||||
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 for this transaction, or return null to not profile this transaction.
|
||||
* <p>
|
||||
|
||||
@@ -405,12 +405,7 @@ public final class InternalConfiguration {
|
||||
if (handler != null) {
|
||||
return plugin(handler);
|
||||
}
|
||||
|
||||
ProfilingConfig profilingConfig = config.getProfilingConfig();
|
||||
if (!profilingConfig.isEnabled()) {
|
||||
return new NoopProfileHandler();
|
||||
}
|
||||
return plugin(new DefaultProfileHandler(profilingConfig));
|
||||
return new NoopProfileHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -169,7 +169,7 @@ public final class OrmQueryProperties implements Serializable {
|
||||
}
|
||||
|
||||
private static String buildImmutableQueryPlanHashSuffix(FetchConfig fetchConfig) {
|
||||
return fetchConfig == null ? "" : "/c" + fetchConfig.hashCode();
|
||||
return fetchConfig == null ? "}" : "/c" + fetchConfig.hashCode() + '}';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -468,7 +468,6 @@ public final class OrmQueryProperties implements Serializable {
|
||||
filterMany.queryPlanHash(builder);
|
||||
}
|
||||
builder.append(immutableHashSuffix);
|
||||
builder.append('}');
|
||||
}
|
||||
|
||||
private static void appendSet(StringBuilder builder, Set<String> values) {
|
||||
|
||||
-271
@@ -1,271 +0,0 @@
|
||||
package io.ebeaninternal.server.transaction;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.config.ProfilingConfig;
|
||||
import io.ebean.plugin.Plugin;
|
||||
import io.ebean.plugin.SpiServer;
|
||||
import io.ebean.util.IOUtils;
|
||||
import io.ebeaninternal.api.CoreLog;
|
||||
import io.ebeaninternal.api.SpiProfileHandler;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
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 java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import static java.lang.System.Logger.Level.*;
|
||||
import static java.time.temporal.ChronoField.*;
|
||||
|
||||
/**
|
||||
* Default profile handler.
|
||||
* <p>
|
||||
* Uses ConcurrentLinkedQueue to minimise contention on threads calling collectTransactionProfile().
|
||||
* <p>
|
||||
* Uses a sleep backoff on the single threaded consumer that reads the profiles and writes them to files.
|
||||
*/
|
||||
public final class DefaultProfileHandler implements SpiProfileHandler, Plugin {
|
||||
|
||||
private static final System.Logger log = CoreLog.internal;
|
||||
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)
|
||||
.appendLiteral('-')
|
||||
.appendValue(MILLI_OF_SECOND, 3)
|
||||
.toFormatter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Low contention choice.
|
||||
*/
|
||||
private final Queue<TransactionProfile> queue = new ConcurrentLinkedQueue<>();
|
||||
private final ExecutorService executor;
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
private final File dir;
|
||||
private final long minMicros;
|
||||
private final long profilesPerFile;
|
||||
private final boolean verbose;
|
||||
private volatile boolean shutdown;
|
||||
private long profileCounter;
|
||||
/**
|
||||
* Slow down polling of transaction profiling queue.
|
||||
*/
|
||||
private int sleepBackoff;
|
||||
private Writer out;
|
||||
|
||||
public DefaultProfileHandler(ProfilingConfig config) {
|
||||
this.verbose = config.isVerbose();
|
||||
this.minMicros = config.getMinimumMicros();
|
||||
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.exists() && !dir.mkdirs()) {
|
||||
log.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, or null if location is null (implicit transactions
|
||||
* are not profiled by the default file-based handler).
|
||||
*/
|
||||
@Override
|
||||
public ProfileStream createProfileStream(@Nullable ProfileLocation location, String label) {
|
||||
if (location == null) {
|
||||
return null;
|
||||
}
|
||||
return new DefaultProfileStream(location, verbose);
|
||||
}
|
||||
|
||||
private void flushCurrentFile() {
|
||||
lock.lock();
|
||||
try {
|
||||
if (out != null) {
|
||||
try {
|
||||
out.close();
|
||||
out = null;
|
||||
} catch (IOException e) {
|
||||
log.log(ERROR, "Failed to flush and close transaction profiling file ", e);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move to the next file to write to.
|
||||
*/
|
||||
private void incrementFile() {
|
||||
lock.lock();
|
||||
try {
|
||||
flushCurrentFile();
|
||||
try {
|
||||
String now = DTF.format(LocalDateTime.now());
|
||||
File file = new File(dir, "txprofile-" + now + ".tprofile");
|
||||
out = IOUtils.newWriter(file);
|
||||
} catch (IOException e) {
|
||||
log.log(ERROR, "Not expected", e);
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main loop for polling the queue and processing profiling messages.
|
||||
*/
|
||||
private void collect() {
|
||||
try {
|
||||
while (!shutdown) {
|
||||
TransactionProfile profile = queue.poll();
|
||||
if (profile == null) {
|
||||
sleep();
|
||||
|
||||
} else if (include(profile)) {
|
||||
write(profile);
|
||||
}
|
||||
}
|
||||
flushCurrentFile();
|
||||
} catch (Exception e) {
|
||||
log.log(WARNING, "Error on collect", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the profile to the current file.
|
||||
*/
|
||||
private void write(TransactionProfile profile) {
|
||||
try {
|
||||
sleepBackoff = 0;
|
||||
++profileCounter;
|
||||
|
||||
StringBuilder sb = new StringBuilder(80);
|
||||
|
||||
// header
|
||||
sb.append(profile.getStartTime()).append(' ')
|
||||
.append(profile.getLabel()).append(' ')
|
||||
.append(profile.getTotalMicros()).append(' ');
|
||||
|
||||
// summary
|
||||
appendSummary(profile, sb);
|
||||
|
||||
out.write(sb.toString());
|
||||
if (verbose) {
|
||||
out.write(' ');
|
||||
out.write(profile.getData());
|
||||
}
|
||||
out.write('\n');
|
||||
|
||||
if (profileCounter % profilesPerFile == 0) {
|
||||
incrementFile();
|
||||
log.log(DEBUG, "profiled {0} transactions", profileCounter);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.log(WARNING, "Error writing transaction profiling", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void appendSummary(TransactionProfile profile, StringBuilder sb) {
|
||||
|
||||
TransactionProfile.Summary summary = profile.getSummary();
|
||||
|
||||
sb.append("z:").append(rate(profile.getTotalMicros(), summary.persistCount + summary.queryCount)).append(' ');
|
||||
sb.append("p:").append(rate(summary.persistMicros, summary.persistBeans)).append(' ');
|
||||
sb.append("q:").append(rate(summary.queryMicros, summary.queryCount)).append(' ');
|
||||
|
||||
sb.append("qm:").append(summary.queryMax).append(' ');
|
||||
sb.append("qt:").append(summary.queryMicros).append(' ');
|
||||
sb.append("qc:").append(summary.queryCount).append(' ');
|
||||
sb.append("qb:").append(summary.queryBeans).append(' ');
|
||||
|
||||
sb.append("pt:").append(summary.persistMicros).append(' ');
|
||||
sb.append("pc:").append(summary.persistCount).append(' ');
|
||||
sb.append("pb:").append(summary.persistBeans).append(' ');
|
||||
sb.append("po:").append(summary.persistOneCount).append(' ');
|
||||
sb.append("pz:").append(rate(summary.persistBeans, summary.persistCount));
|
||||
}
|
||||
|
||||
private int rate(long micros, long count) {
|
||||
return count < 1 ? 0 : (int) (micros / count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void online(boolean online) {
|
||||
if (online) {
|
||||
executor.submit(this::collect);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
shutdown = true;
|
||||
log.log(TRACE, "shutting down");
|
||||
try {
|
||||
executor.shutdown();
|
||||
if (!executor.awaitTermination(4, TimeUnit.SECONDS)) {
|
||||
log.log(INFO, "Shut down timeout exceeded. Terminating profiling consumer thread.");
|
||||
executor.shutdownNow();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.log(WARNING, "Interrupt on shutdown", e);
|
||||
}
|
||||
flushCurrentFile();
|
||||
}
|
||||
}
|
||||
-94
@@ -1,94 +0,0 @@
|
||||
package io.ebeaninternal.server.transaction;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Default transaction profiling event collection.
|
||||
*/
|
||||
public final class DefaultProfileStream implements ProfileStream {
|
||||
|
||||
private final long startNanos;
|
||||
private final StringBuilder buffer;
|
||||
private final TransactionProfile profile;
|
||||
private final TransactionProfile.Summary summary;
|
||||
|
||||
DefaultProfileStream(@Nullable ProfileLocation location, boolean verbose) {
|
||||
this.startNanos = System.nanoTime();
|
||||
this.profile = new TransactionProfile(System.currentTimeMillis(), location);
|
||||
this.summary = profile.getSummary();
|
||||
this.buffer = (verbose) ? new StringBuilder(200) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the time offset from the beginning of the transaction.
|
||||
*/
|
||||
@Override
|
||||
public long offset() {
|
||||
return ((System.nanoTime() - startNanos) / 1_000L);
|
||||
}
|
||||
|
||||
private long exeMicros(long offset) {
|
||||
return offset() - offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a query execution event.
|
||||
*/
|
||||
@Override
|
||||
public void addQueryEvent(String event, long offset, String beanName, int beanCount, String queryId, String sql) {
|
||||
long micros = exeMicros(offset);
|
||||
summary.addQuery(micros, beanCount);
|
||||
if (buffer != null) {
|
||||
add(micros, event, offset, beanName, beanCount, queryId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a persist event.
|
||||
*/
|
||||
@Override
|
||||
public void addPersistEvent(String event, long offset, String beanName, int beanCount) {
|
||||
long micros = exeMicros(offset);
|
||||
summary.addPersist(micros, beanCount);
|
||||
if (buffer != null) {
|
||||
add(micros, event, offset, beanName, beanCount, "");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the commit/rollback event.
|
||||
*/
|
||||
@Override
|
||||
public void addEvent(String event, long offset) {
|
||||
long micros = exeMicros(offset);
|
||||
summary.commitMicros = micros;
|
||||
if (buffer != null) {
|
||||
buffer.append(event).append(',');
|
||||
buffer.append(offset).append(',');
|
||||
buffer.append(micros).append(';');
|
||||
}
|
||||
}
|
||||
|
||||
private void add(long micros, String event, long offset, String beanName, int beanCount, String queryId) {
|
||||
buffer.append(event).append(',');
|
||||
buffer.append(offset).append(',');
|
||||
buffer.append(micros).append(',');
|
||||
buffer.append(beanName).append(',');
|
||||
buffer.append(beanCount).append(',');
|
||||
buffer.append(queryId).append(';');
|
||||
}
|
||||
|
||||
/**
|
||||
* End the transaction profiling.
|
||||
*/
|
||||
@Override
|
||||
public void end(TransactionManager manager, String label) {
|
||||
profile.setTotalMicros(offset());
|
||||
if (buffer != null) {
|
||||
profile.setData(buffer.toString());
|
||||
}
|
||||
manager.profileCollect(profile);
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -501,7 +501,7 @@ final class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEve
|
||||
active = false;
|
||||
manager.collectMetricReadOnly((System.nanoTime() - startNanos) / 1000L);
|
||||
if (profileStream != null) {
|
||||
profileStream.end(manager, null);
|
||||
profileStream.end(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -856,7 +856,7 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
}
|
||||
manager.collectMetric(exeMicros);
|
||||
if (profileStream != null) {
|
||||
profileStream.end(manager, label);
|
||||
profileStream.end(label);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-6
@@ -9,13 +9,9 @@ import org.jspecify.annotations.Nullable;
|
||||
*/
|
||||
public final class NoopProfileHandler implements SpiProfileHandler {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public void collectTransactionProfile(TransactionProfile transactionProfile) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProfileStream createProfileStream(@Nullable ProfileLocation location, String label) {
|
||||
public ProfileStream createProfileStream(@Nullable ProfileLocation location, @Nullable String label) {
|
||||
// always return null
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -28,5 +28,5 @@ public interface ProfileStream {
|
||||
/**
|
||||
* Transaction completed collect the profiling information.
|
||||
*/
|
||||
void end(TransactionManager manager, String label);
|
||||
void end(String label);
|
||||
}
|
||||
|
||||
@@ -428,13 +428,6 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
cacheNotify.notify(new ServerCacheNotification(touchedTables));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the collected transaction profiling information.
|
||||
*/
|
||||
final void profileCollect(TransactionProfile transactionProfile) {
|
||||
profileHandler.collectTransactionProfile(transactionProfile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect execution time for an explicit transaction.
|
||||
*/
|
||||
|
||||
@@ -5,7 +5,6 @@ import io.ebean.plugin.Plugin;
|
||||
import io.ebean.plugin.SpiServer;
|
||||
import io.ebeaninternal.api.SpiProfileHandler;
|
||||
import io.ebeaninternal.server.transaction.ProfileStream;
|
||||
import io.ebeaninternal.server.transaction.TransactionProfile;
|
||||
import io.opentelemetry.api.GlobalOpenTelemetry;
|
||||
import io.opentelemetry.api.trace.Span;
|
||||
import io.opentelemetry.api.trace.SpanKind;
|
||||
@@ -39,8 +38,8 @@ public final class OtelProfileHandler implements SpiProfileHandler, Plugin {
|
||||
|
||||
@Override
|
||||
public void configure(SpiServer server) {
|
||||
if (this.tracer == null) {
|
||||
this.tracer = GlobalOpenTelemetry.getTracer(INSTRUMENTATION_NAME);
|
||||
if (tracer == null) {
|
||||
tracer = GlobalOpenTelemetry.getTracer(INSTRUMENTATION_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,11 +78,4 @@ public final class OtelProfileHandler implements SpiProfileHandler, Plugin {
|
||||
return new OtelProfileStream(tracer, txnSpan);
|
||||
}
|
||||
|
||||
/**
|
||||
* The stream handles span lifecycle inline — nothing to do here.
|
||||
*/
|
||||
@Override
|
||||
public void collectTransactionProfile(TransactionProfile transactionProfile) {
|
||||
// no-op: OtelProfileStream.end() already closed the span
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ final class OtelProfileStream implements ProfileStream {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void end(TransactionManager manager, String label) {
|
||||
public void end(String label) {
|
||||
txnSpan.setAttribute(EBEAN_TOTAL_MICROS, offset());
|
||||
if (label != null) {
|
||||
txnSpan.updateName("txn." + label);
|
||||
|
||||
+10
-10
@@ -85,7 +85,7 @@ class OtelProfileHandlerTest {
|
||||
var stream = handler.createProfileStream(null, null);
|
||||
assertNotNull(stream);
|
||||
stream.addEvent("c", 0); // commit
|
||||
stream.end(null, null);
|
||||
stream.end(null);
|
||||
} finally {
|
||||
parent.end();
|
||||
}
|
||||
@@ -98,7 +98,7 @@ class OtelProfileHandlerTest {
|
||||
var stream = handler.createProfileStream(mockLocation("OrderService.placeOrder"), null);
|
||||
assertNotNull(stream);
|
||||
stream.addEvent("c", 0);
|
||||
stream.end(null, null);
|
||||
stream.end(null);
|
||||
} finally {
|
||||
parent.end();
|
||||
}
|
||||
@@ -121,7 +121,7 @@ class OtelProfileHandlerTest {
|
||||
// First query event should update the transaction span name
|
||||
stream.addQueryEvent("fm", stream.offset(), "Customer", 5, "qplan-1", "select ...");
|
||||
stream.addEvent("c", 0);
|
||||
stream.end(null, null);
|
||||
stream.end(null);
|
||||
} finally {
|
||||
parent.end();
|
||||
}
|
||||
@@ -143,7 +143,7 @@ class OtelProfileHandlerTest {
|
||||
// Simulate some time passing then record a find_many
|
||||
stream.addQueryEvent("fm", offset, "Order", 42, "plan-abc", "select ...");
|
||||
stream.addEvent("c", 0);
|
||||
stream.end(null, null);
|
||||
stream.end(null);
|
||||
} finally {
|
||||
parent.end();
|
||||
}
|
||||
@@ -166,7 +166,7 @@ class OtelProfileHandlerTest {
|
||||
stream.addQueryEvent("fo", stream.offset(), "User", 1, "p1", "select from user");
|
||||
stream.addQueryEvent("fm", stream.offset(), "Order", 10, "p2", "select from order");
|
||||
stream.addEvent("c", 0);
|
||||
stream.end(null, null);
|
||||
stream.end(null);
|
||||
} finally {
|
||||
parent.end();
|
||||
}
|
||||
@@ -186,7 +186,7 @@ class OtelProfileHandlerTest {
|
||||
assertNotNull(stream);
|
||||
stream.addQueryEvent("fo", stream.offset(), "User", 1, "p1", "");
|
||||
stream.addEvent("c", 0);
|
||||
stream.end(null, null);
|
||||
stream.end(null);
|
||||
} finally {
|
||||
parent.end();
|
||||
}
|
||||
@@ -208,7 +208,7 @@ class OtelProfileHandlerTest {
|
||||
long offset = stream.offset();
|
||||
stream.addPersistEvent("i", offset, "Order", 1);
|
||||
stream.addEvent("c", 0);
|
||||
stream.end(null, null);
|
||||
stream.end(null);
|
||||
} finally {
|
||||
parent.end();
|
||||
}
|
||||
@@ -230,7 +230,7 @@ class OtelProfileHandlerTest {
|
||||
OtelProfileStream stream = (OtelProfileStream) handler.createProfileStream(mockLocation("Svc.ok"), null);
|
||||
assertNotNull(stream);
|
||||
stream.addEvent("c", 0);
|
||||
stream.end(null, null);
|
||||
stream.end(null);
|
||||
} finally {
|
||||
parent.end();
|
||||
}
|
||||
@@ -246,7 +246,7 @@ class OtelProfileHandlerTest {
|
||||
OtelProfileStream stream = (OtelProfileStream) handler.createProfileStream(mockLocation("Svc.fail"), null);
|
||||
assertNotNull(stream);
|
||||
stream.addEvent("r", 0);
|
||||
stream.end(null, null);
|
||||
stream.end(null);
|
||||
} finally {
|
||||
parent.end();
|
||||
}
|
||||
@@ -266,7 +266,7 @@ class OtelProfileHandlerTest {
|
||||
OtelProfileStream stream = (OtelProfileStream) handler.createProfileStream(mockLocation("Svc.timed"), null);
|
||||
assertNotNull(stream);
|
||||
stream.addEvent("c", 0);
|
||||
stream.end(null, null);
|
||||
stream.end(null);
|
||||
} finally {
|
||||
parent.end();
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package org.example.domain;
|
||||
|
||||
import io.ebean.Database;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.config.ProfilingConfig;
|
||||
import io.ebean.datasource.DataSourceBuilder;
|
||||
import io.ebean.datasource.DataSourcePool;
|
||||
import io.ebean.opentelemetry.OtelProfileHandler;
|
||||
@@ -123,9 +122,6 @@ class OtelJaegerIntegrationTest {
|
||||
}
|
||||
|
||||
private Database createDatabase(Tracer tracer) {
|
||||
ProfilingConfig profilingConfig = new ProfilingConfig();
|
||||
profilingConfig.setEnabled(true);
|
||||
|
||||
DataSourcePool ds = DataSourceBuilder.create()
|
||||
.url("jdbc:h2:mem:otelit;DB_CLOSE_DELAY=-1")
|
||||
.username("sa")
|
||||
@@ -140,7 +136,6 @@ class OtelJaegerIntegrationTest {
|
||||
.loadFromProperties()
|
||||
.ddlGenerate(true)
|
||||
.ddlRun(true)
|
||||
.profilingConfig(profilingConfig)
|
||||
.putServiceObject(SpiProfileHandler.class, new OtelProfileHandler(tracer))
|
||||
.addClass(OtelOrder.class)
|
||||
.dataSource(ds)
|
||||
|
||||
Reference in New Issue
Block a user