Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[CONJ-297] Useless memory consumption when using Statement.setQueryTi…
…meout + checkstyle correction
  • Loading branch information
rusher committed May 18, 2016
1 parent 3ba9c73 commit 5a2a4e5
Show file tree
Hide file tree
Showing 9 changed files with 45 additions and 94 deletions.
1 change: 1 addition & 0 deletions documentation/Changelog.md
Expand Up @@ -8,6 +8,7 @@

---
## 1.4.5
* [CONJ-297] Useless memory consumption when using Statement.setQueryTimeout
* [CONJ-289] PrepareStatement on master reconnection after a failover
* [CONJ-288] using SHOW VARIABLES to replace SELECT on connection to permit connection on a galera non primary node

Expand Down
4 changes: 2 additions & 2 deletions src/main/java/org/mariadb/jdbc/MariaDbDatabaseMetaData.java
Expand Up @@ -540,8 +540,8 @@ public ResultSet getTables(String catalog, String schemaPattern, String tableNam
throws SQLException {

String sql =
"SELECT TABLE_SCHEMA TABLE_CAT, NULL TABLE_SCHEM, TABLE_NAME, IF(TABLE_TYPE='BASE TABLE', 'TABLE', TABLE_TYPE) as TABLE_TYPE, TABLE_COMMENT REMARKS,"
+ " NULL TYPE_CAT, NULL TYPE_SCHEM, NULL TYPE_NAME, NULL SELF_REFERENCING_COL_NAME, "
"SELECT TABLE_SCHEMA TABLE_CAT, NULL TABLE_SCHEM, TABLE_NAME, IF(TABLE_TYPE='BASE TABLE', 'TABLE', TABLE_TYPE) as TABLE_TYPE,"
+ " TABLE_COMMENT REMARKS, NULL TYPE_CAT, NULL TYPE_SCHEM, NULL TYPE_NAME, NULL SELF_REFERENCING_COL_NAME, "
+ " NULL REF_GENERATION"
+ " FROM INFORMATION_SCHEMA.TABLES "
+ " WHERE "
Expand Down
27 changes: 14 additions & 13 deletions src/main/java/org/mariadb/jdbc/MariaDbStatement.java
Expand Up @@ -49,33 +49,27 @@ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWIS

package org.mariadb.jdbc;

import org.mariadb.jdbc.internal.packet.dao.parameters.ParameterHolder;
import org.mariadb.jdbc.internal.queryresults.*;
import org.mariadb.jdbc.internal.protocol.Protocol;
import org.mariadb.jdbc.internal.queryresults.ExecutionResult;
import org.mariadb.jdbc.internal.queryresults.MultiIntExecutionResult;
import org.mariadb.jdbc.internal.queryresults.SingleExecutionResult;
import org.mariadb.jdbc.internal.queryresults.resultset.MariaSelectResultSet;
import org.mariadb.jdbc.internal.util.ExceptionMapper;
import org.mariadb.jdbc.internal.util.dao.QueryException;
import org.mariadb.jdbc.internal.util.scheduler.DynamicSizedSchedulerInterface;
import org.mariadb.jdbc.internal.util.scheduler.SchedulerServiceProviderHolder;

import org.mariadb.jdbc.internal.protocol.Protocol;

import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;


public class MariaDbStatement implements Statement, Cloneable {
private static final DynamicSizedSchedulerInterface timeoutScheduler =
SchedulerServiceProviderHolder.getScheduler(0);
//timeout scheduler
private static volatile ScheduledThreadPoolExecutor timeoutScheduler = null;

/**
* the protocol used to talk to the server.
*/
Expand Down Expand Up @@ -161,9 +155,16 @@ public Protocol getProtocol() {
protected void setTimerTask() {
assert (timerTaskFuture == null);

// because canceling needs to establish a new connection, we want to ensure that this
// possible blocking call can be run in parallel if multiple queries need to timeout
timerTaskFuture = timeoutScheduler.addThreadAndSchedule(new Runnable() {
if (timeoutScheduler == null) {
//Scheduler initialisation
synchronized (connection) {
if (timeoutScheduler == null) {
timeoutScheduler = SchedulerServiceProviderHolder.getSchedulerProvider().getTimeoutScheduler();
}
}
}

timerTaskFuture = timeoutScheduler.schedule(new Runnable() {
@Override
public void run() {
try {
Expand Down
Expand Up @@ -98,7 +98,6 @@ public void send(OutputStream os) throws IOException, QueryException {
"Krb5ConnectorContext {\n"
+ "com.sun.security.auth.module.Krb5LoginModule required "
+ "useTicketCache=true "
+ "debug=true "
+ "renewTGT=true "
+ "doNotPrompt=true; };"
));
Expand Down Expand Up @@ -159,14 +158,14 @@ public Void run() throws Exception {
};
Subject.doAs(mySubject, action);
} catch (PrivilegedActionException exception) {
throw new QueryException("GSS-API authentication exception", 0, "28000", exception);
throw new QueryException("GSS-API authentication exception", 1045, "28000", exception);
}
} else {
throw new QueryException("GSS-API authentication exception : no credential cache not found.", 0, "28000");
throw new QueryException("GSS-API authentication exception : no credential cache not found.", 1045, "28000");
}

} catch (LoginException le) {
throw new QueryException("GSS-API authentication exception", 0, "28000", le);
throw new QueryException("GSS-API authentication exception", 1045, "28000", le);
}

}
Expand Down
Expand Up @@ -49,10 +49,7 @@ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWIS

package org.mariadb.jdbc.internal.util.scheduler;

import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class DynamicSizedSchedulerImpl extends ScheduledThreadPoolExecutor implements DynamicSizedSchedulerInterface {
/**
Expand All @@ -70,44 +67,4 @@ public void setPoolSize(int newSize) {
}
}

@Override
public void adjustPoolSize(int delta) {
// locked to avoid check then act race condition
synchronized (this) {
super.setCorePoolSize(Math.max(0, super.getCorePoolSize() + delta));
}
}

@Override
public Future<?> addThreadAndExecute(final Runnable task) {
return addThreadAndSchedule(task, 0, TimeUnit.MILLISECONDS);
}

@Override
public Future<?> addThreadAndSchedule(final Runnable task, long delay, TimeUnit unit) {
adjustPoolSize(1);

FutureTask<?> result = new PoolSizeDecreaseFuture(task);
if (delay == 0) {
// execute is slightly better if we can, as it avoids wrapping the future in another future
super.execute(result);
} else {
super.schedule(result, delay, unit);
// can not return future from schedule above
// we must return our decreasing future to handle Future.cancel case
}
return result;
}

private class PoolSizeDecreaseFuture extends FutureTask<Object> {
public PoolSizeDecreaseFuture(Runnable runnable) {
super(runnable, null);
}

@Override
protected void done() {
// invoked when task is complete or canceled
adjustPoolSize(-1);
}
}
}
Expand Up @@ -49,9 +49,7 @@ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWIS

package org.mariadb.jdbc.internal.util.scheduler;

import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public interface DynamicSizedSchedulerInterface extends ScheduledExecutorService {
/**
Expand All @@ -60,34 +58,5 @@ public interface DynamicSizedSchedulerInterface extends ScheduledExecutorService
* @param newSize New pool size that is superior to 0
*/
public void setPoolSize(int newSize);

/**
* Adjusts the pool size by a given delta. This should atomically change the pool size in a
* thread safe way.
*
* @param delta A positive or negative number to adjust the pool size by.
*/
public void adjustPoolSize(int delta);

/**
* Used when you want to ensure an extra thread is available to execute task. This will adjust
* the pool size so that a thread can be created (if necessary), and then execute the task.
* Once the task completes the pool size will be adjusted back down.
*
* @param task Task to be executed
* @return Future which represents task execution
*/
public Future<?> addThreadAndExecute(Runnable task);

/**
* Used when you want to ensure an extra thread is available for scheduled task. This will
* adjust the pool size so that a thread can be created (if necessary), and then execute the
* task. Once the task completes the pool size will be adjusted back down.
*
* @param task Task to be executed
* @param delay delay before execution
* @param unit unit of delay
* @return Future which represents task execution
*/
public Future<?> addThreadAndSchedule(Runnable task, long delay, TimeUnit unit);
}
Expand Up @@ -70,6 +70,13 @@ public DynamicSizedSchedulerInterface getScheduler(int minimumThreads) {
public ScheduledThreadPoolExecutor getFixedSizeScheduler(int minimumThreads) {
return new FixedSizedSchedulerImpl(minimumThreads);
}

@Override
public ScheduledThreadPoolExecutor getTimeoutScheduler() {
ScheduledThreadPoolExecutor timeoutScheduler = new ScheduledThreadPoolExecutor(1, new MariaDbThreadFactory());
timeoutScheduler.setRemoveOnCancelPolicy(true);
return timeoutScheduler;
}
};

private static volatile SchedulerProvider currentProvider = null;
Expand Down Expand Up @@ -135,6 +142,17 @@ public interface SchedulerProvider {
public DynamicSizedSchedulerInterface getScheduler(int minimumThreads);

public ScheduledExecutorService getFixedSizeScheduler(int minimumThreads);

/**
* Default Timeout scheduler.
*
* This is a one Thread fixed sized scheduler.
* This specific scheduler is using java 1.7 RemoveOnCancelPolicy, so
* the task are removed from queue permitting to avoid memory consumption [CONJ-297]
*
* @return A new scheduler that is ready to accept tasks
*/
public ScheduledThreadPoolExecutor getTimeoutScheduler();
}


Expand Down
3 changes: 2 additions & 1 deletion src/test/java/org/mariadb/jdbc/DatabaseMetadataTest.java
Expand Up @@ -364,7 +364,8 @@ public void testGetTables3() throws SQLException {
assertEquals("table_type_test", tableName);

String tableType = tableSet.getString("TABLE_TYPE");
assertEquals("TABLE", tableType); // see for possible values https://docs.oracle.com/javase/7/docs/api/java/sql/DatabaseMetaData.html#getTableTypes%28%29
assertEquals("TABLE", tableType);
// see for possible values https://docs.oracle.com/javase/7/docs/api/java/sql/DatabaseMetaData.html#getTableTypes%28%29
}

@Test
Expand Down
Expand Up @@ -73,6 +73,11 @@ public DynamicSizedSchedulerInterface getScheduler(int minimumThreads) {
public ScheduledThreadPoolExecutor getFixedSizeScheduler(int minimumThreads) {
throw new UnsupportedOperationException();
}

@Override
public ScheduledThreadPoolExecutor getTimeoutScheduler() {
throw new UnsupportedOperationException();
}
};

SchedulerServiceProviderHolder.setSchedulerProvider(emptyProvider);
Expand Down

0 comments on commit 5a2a4e5

Please sign in to comment.