All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.rapleaf.jack.transaction.TransactorImpl Maven / Gradle / Ivy

There is a newer version: 1.8
Show newest version
package com.rapleaf.jack.transaction;

import com.google.common.base.Preconditions;
import com.rapleaf.jack.IDb;
import com.rapleaf.jack.exception.SqlExecutionFailureException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.Duration;
import java.util.Optional;
import java.util.concurrent.Callable;

/**
 * If there is any exception while executing the query, throws {@link com.rapleaf.jack.exception.SqlExecutionFailureException}.
 * 

* If there is no available connections, throws {@link com.rapleaf.jack.exception.NoAvailableConnectionException}. * Users can either increase the max total connections or max wait time. *

* If the DB manager has already been closed, throws {@link IllegalStateException}. *

* If new DB connections cannot be created, throws {@link com.rapleaf.jack.exception.ConnectionCreationFailureException}. */ public class TransactorImpl implements ITransactor { private static final Logger LOG = LoggerFactory.getLogger(TransactorImpl.class); private final IDbManager dbManager; private static final int QUERY_LOG_SIZE = 30; private TransactorMetricsImpl queryMetrics = new TransactorMetricsImpl(QUERY_LOG_SIZE); private boolean metricsTrackingEnabled = DbPoolManager.DEFAULT_METRICS_TRACKING_ENABLED; TransactorImpl(IDbManager dbManager, boolean metricsTrackingEnabled) { this.dbManager = dbManager; this.metricsTrackingEnabled = metricsTrackingEnabled; } public static Builder create(Callable dbConstructor) { return new Builder<>(dbConstructor); } @Override public T query(IQuery query) { return query(query, false); } @Override public T queryAsTransaction(IQuery query) { return query(query, true); } @Override public void execute(IExecution execution) { execute(execution, false); } @Override public void executeAsTransaction(IExecution execution) { execute(execution, true); } TransactorMetrics getQueryMetrics() { if (!metricsTrackingEnabled) { return new MockTransactorMetrics(); } else { return queryMetrics; } } DbMetrics getDbMetrics() { return dbManager.getMetrics(); } private T query(IQuery query, boolean asTransaction) { DB connection = dbManager.getConnection(); try { connection.setAutoCommit(!asTransaction); long startTime = System.currentTimeMillis(); T value = query.query(connection); if (asTransaction) { connection.commit(); } long executionTime = System.currentTimeMillis() - startTime; if (metricsTrackingEnabled) { queryMetrics.update(executionTime, Thread.currentThread().getStackTrace()[3]); } // We don't return the connection in a finally block because we don't want to // do so in the case that a Throwable is thrown. In that case, the JVM is almost certainly // about to exit, so returning the connection isn't important and in some cases can actually // cause harm like committing a half-complete transaction (part of returning the connection is setting // its autocommit property to true, which would commit a half-complete transaction). dbManager.returnConnection(connection); return value; } catch (Exception e) { LOG.error("SQL execution failure", e); if (asTransaction) { connection.rollback(); } dbManager.returnConnection(connection); throw new SqlExecutionFailureException(e); } catch (Throwable t) { // We still try to explicitly rollback the transaction if a throwable is thrown. if (asTransaction) { connection.rollback(); } throw t; } } private void execute(IExecution execution, boolean asTransaction) { query( db -> { execution.execute(db); return null; }, asTransaction ); } @Override public void close() { if (metricsTrackingEnabled) { LOG.info("{}\n\n{}", dbManager.getMetrics().getSummary(), getQueryMetrics().getSummary()); } dbManager.close(); } @Override public DbPoolStatus getDbPoolStatus() { return this.dbManager.getDbPoolStatus(); } public static class Builder implements ITransactor.Builder> { final Callable dbConstructor; int maxTotalConnections = DbPoolManager.DEFAULT_MAX_TOTAL_CONNECTIONS; int minIdleConnections = DbPoolManager.DEFAULT_MIN_IDLE_CONNECTIONS; long maxWaitMillis = DbPoolManager.DEFAULT_MAX_WAIT_TIME; long keepAliveMillis = DbPoolManager.DEFAULT_KEEP_ALIVE_TIME; boolean metricsTrackingEnabled = DbPoolManager.DEFAULT_METRICS_TRACKING_ENABLED; Builder(Callable dbConstructor) { this.dbConstructor = dbConstructor; } /** * @param maxTotalConnections The maximum number of connections that can be created in the pool. Negative values * mean infinite number of connections are allowed. * @throws IllegalArgumentException if the value is zero. */ public Builder setMaxTotalConnections(int maxTotalConnections) { Preconditions.checkArgument(maxTotalConnections != 0, "Max total connections cannot be zero"); this.maxTotalConnections = maxTotalConnections; return this; } /** * Allow infinite number of connections. */ public Builder enableInfiniteConnections() { this.maxTotalConnections = -1; return this; } /** * @param minIdleConnections The minimum number of idle connections to keep in the pool. Zero or negative values * will be ignored. */ public Builder setMinIdleConnections(int minIdleConnections) { this.minIdleConnections = minIdleConnections; return this; } /** * @param maxWaitTime The maximum amount of time that the {@link DbPoolManager#getConnection} method * should block before throwing an exception when the pool is exhausted. Negative values * mean that the block can be infinite. */ public Builder setMaxWaitTime(Duration maxWaitTime) { this.maxWaitMillis = maxWaitTime.toMillis(); return this; } /** * Allow infinite block for the {@link DbPoolManager#getConnection} method. */ public Builder enableInfiniteWait() { this.maxWaitMillis = -1L; return this; } /*** * @param keepAliveTime The minimum amount of time the connection may sit idle in the pool before it is * eligible for eviction (with the extra condition that at least {@code minIdleConnections} * connections remain in the pool). */ public Builder setKeepAliveTime(Duration keepAliveTime) { this.keepAliveMillis = keepAliveTime.toMillis(); return this; } public Builder setMetricsTracking(boolean metricsTrackingEnabled) { this.metricsTrackingEnabled = metricsTrackingEnabled; return this; } @Override public TransactorImpl get() { return Builder.build(this); } private static TransactorImpl build(Builder builder) { DbPoolManager dbPoolManager = new DbPoolManager<>(builder.dbConstructor, builder.maxTotalConnections, builder.minIdleConnections, builder.maxWaitMillis, builder.keepAliveMillis, builder.metricsTrackingEnabled); return new TransactorImpl<>(dbPoolManager, builder.metricsTrackingEnabled); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy