Skip to content

Commit 27929d9

Browse files
committed
[perf] introduce option to set JDBC fetch size
For some workloads and databases it can be beneficial to set a JDBC fetch size to increase performance and decrease memory usage. This can help a lot (5x and more in terms of speed) with result sets withs thousands of rows or more. The feature is disabled by default and can be enabled by adding the config option "jdbc_fetch_size", set to the desired number of rows (a value between 100 and 1000 is usually best, but depends on the workload). Since this is just a hint to the underlying JDBC driver, the option might only work in combination with other properties or not at all. While at it, always close ResultSet right after use. This shouldn't matter but there is no reason to special case anything.
1 parent 4a3a605 commit 27929d9

File tree

2 files changed

+20
-7
lines changed

2 files changed

+20
-7
lines changed

src/java/arjdbc/jdbc/RubyJdbcConnection.java

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ public class RubyJdbcConnection extends RubyObject {
128128
private boolean lazy = false; // final once set on initialize
129129
private boolean jndi; // final once set on initialize
130130
private boolean configureConnection = true; // final once initialized
131+
private int fetchSize = 0; // 0 = JDBC default
131132

132133
protected RubyJdbcConnection(Ruby runtime, RubyClass metaClass) {
133134
super(runtime, metaClass);
@@ -571,6 +572,11 @@ protected void doInitialize(final ThreadContext context, final IRubyObject confi
571572
else {
572573
this.configureConnection = value != context.runtime.getFalse();
573574
}
575+
576+
IRubyObject jdbcFetchSize = getConfigValue(context, "jdbc_fetch_size");
577+
if (jdbcFetchSize != context.nil) {
578+
this.fetchSize = RubyNumeric.fix2int(jdbcFetchSize);
579+
}
574580
}
575581

576582
@JRubyMethod(name = "adapter")
@@ -818,6 +824,7 @@ public IRubyObject call(final Connection connection) throws SQLException {
818824
// is called, so we have to process the result sets as we get them
819825
// this shouldn't be an issue in most cases since we're only getting 1 result set anyways
820826
result = mapExecuteResult(context, connection, resultSet);
827+
resultSet.close();
821828
} else {
822829
result = context.runtime.newFixnum(updateCount);
823830
}
@@ -851,6 +858,7 @@ protected Statement createStatement(final ThreadContext context, final Connectio
851858
else {
852859
statement.setEscapeProcessing(escapeProcessing.isTrue());
853860
}
861+
if (fetchSize != 0) statement.setFetchSize(fetchSize);
854862
return statement;
855863
}
856864

@@ -1045,6 +1053,7 @@ public IRubyObject call(final Connection connection) throws SQLException {
10451053
else {
10461054
final PreparedStatement prepStatement;
10471055
statement = prepStatement = connection.prepareStatement(query);
1056+
if (fetchSize != 0) statement.setFetchSize(fetchSize);
10481057
statement.setMaxRows(maxRows); // zero means there is no limit
10491058
setStatementParameters(context, connection, prepStatement, binds);
10501059
hasResult = prepStatement.execute();
@@ -1123,7 +1132,9 @@ public IRubyObject prepare_statement(final ThreadContext context, final IRubyObj
11231132
return withConnection(context, new Callable<IRubyObject>() {
11241133
public IRubyObject call(Connection connection) throws SQLException {
11251134
final String query = sql.convertToString().getUnicodeValue();
1126-
return JavaUtil.convertJavaToRuby(context.runtime, connection.prepareStatement(query));
1135+
PreparedStatement statement = connection.prepareStatement(query);
1136+
if (fetchSize != 0) statement.setFetchSize(fetchSize);
1137+
return JavaUtil.convertJavaToRuby(context.runtime, statement);
11271138
}
11281139
});
11291140
}
@@ -1158,19 +1169,15 @@ public IRubyObject call(final Connection connection) throws SQLException {
11581169
statement = (PreparedStatement) JavaEmbedUtils.rubyToJava(cachedStatement);
11591170
} else {
11601171
statement = connection.prepareStatement(query);
1172+
if (fetchSize != 0) statement.setFetchSize(fetchSize);
11611173
}
11621174

11631175
setStatementParameters(context, connection, statement, (RubyArray) binds);
11641176

11651177
if (statement.execute()) {
11661178
ResultSet resultSet = statement.getResultSet();
11671179
IRubyObject results = mapQueryResult(context, connection, resultSet);
1168-
1169-
if (cached) {
1170-
// Make sure we free the result set if we are caching the statement
1171-
// It gets closed automatically when the statement is closed if we aren't caching
1172-
resultSet.close();
1173-
}
1180+
resultSet.close();
11741181

11751182
return results;
11761183
} else {
@@ -2501,6 +2508,8 @@ protected IRubyObject arrayToRuby(final ThreadContext context,
25012508
while ( arrayResult.next() ) {
25022509
array.append( jdbcToRuby(context, runtime, 2, baseType, arrayResult) );
25032510
}
2511+
arrayResult.close();
2512+
25042513
return array;
25052514
}
25062515
finally { if ( value != null ) value.free(); }

test/rails/config.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ connections:
6262
driver: <%= 'org.mariadb.jdbc.Driver' %>
6363
<% end %>
6464
<% if defined? JRUBY_VERSION %>
65+
jdbc_fetch_size: 100
6566
properties:
6667
serverTimezone: <%= java.util.TimeZone.getDefault.getID %>
6768
<% end %>
@@ -80,6 +81,7 @@ connections:
8081
prepared_statements: <%= ENV['PREPARED_STATEMENTS'] || ENV['PS'] %> # should leave it off to its default if not set
8182
<% end %>
8283
username: <%= ENV['PGUSER'] || ENV['user'] || 'rails' %>
84+
jdbc_fetch_size: 100
8385
properties:
8486
# Postgres' JDBC driver does not prepare statements until executed 5 times by default, let's lower:
8587
prepareThreshold: <%= ENV['PREPARE_THRESHOLD'] || 1 %>
@@ -96,9 +98,11 @@ connections:
9698
arunit:
9799
database: <%= FIXTURES_ROOT %>/fixture_database.sqlite3
98100
timeout: 5000
101+
jdbc_fetch_size: 100
99102
arunit2:
100103
database: <%= FIXTURES_ROOT %>/fixture_database_2.sqlite3
101104
timeout: 5000
105+
jdbc_fetch_size: 100
102106

103107
sqlite3_mem:
104108
arunit:

0 commit comments

Comments
 (0)