2 files changed
+119
No files matched your search
@@ -2,11 +2,14 @@ package com.dbx.agent.iris;
|
||||
|
||||
import com.dbx.agent.ConfiguredJdbcAgent;
|
||||
import com.dbx.agent.ColumnInfo;
|
||||
import com.dbx.agent.ConnectParams;
|
||||
import com.dbx.agent.JdbcAgentProfile;
|
||||
import com.dbx.agent.JdbcExecutor;
|
||||
import com.dbx.agent.MultiSessionJsonRpcServer;
|
||||
import com.dbx.agent.StandardJdbcMetadata;
|
||||
|
||||
import com.intersystems.jdbc.IRISConnection;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.PreparedStatement;
|
||||
@@ -32,6 +35,36 @@ public final class IrisAgent extends ConfiguredJdbcAgent {
|
||||
super(IRIS_PROFILE);
|
||||
}
|
||||
|
||||
// The InterSystems driver's default query prefetch block (32768 bytes) means
|
||||
// one server round trip per ~32KB of result data. Large result sets over
|
||||
// high-latency links then spend most of their wall time waiting on the
|
||||
// network instead of transferring rows, while other clients that negotiate
|
||||
// larger fetch blocks finish several times faster. Statement.setFetchSize
|
||||
// does not help here: intersystems-jdbc stores that value but never sends
|
||||
// it to the server. The connection-level query prefetch size is the only
|
||||
// effective knob, so raise it for every DBX IRIS connection. This is
|
||||
// advisory: servers that reject the value keep the driver default, and the
|
||||
// driver only round-trips when the value actually changes.
|
||||
static final int IRIS_QUERY_PREFETCH_SIZE = 262144;
|
||||
|
||||
@Override
|
||||
protected void afterConnect(ConnectParams params, Connection connection) {
|
||||
super.afterConnect(params, connection);
|
||||
applyQueryPrefetchSize(connection);
|
||||
}
|
||||
|
||||
static void applyQueryPrefetchSize(Connection connection) {
|
||||
if (!(connection instanceof IRISConnection)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
((IRISConnection) connection).setQueryPrefetchSize(IRIS_QUERY_PREFETCH_SIZE);
|
||||
} catch (Exception ignored) {
|
||||
// Performance tuning only; keep the connection usable with the
|
||||
// driver default when a server refuses the larger prefetch.
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object resultValue(ResultSet rs, int index, int sqlType) {
|
||||
if (sqlType == Types.OTHER) {
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.dbx.agent.iris;
|
||||
|
||||
import com.dbx.agent.ConnectParams;
|
||||
import com.intersystems.jdbc.IRISConnection;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class IrisAgentQueryPrefetchTest {
|
||||
/** Records setQueryPrefetchSize calls without touching driver internals. */
|
||||
static final class RecordingIrisConnection extends IRISConnection {
|
||||
final List<Integer> prefetchSizes = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void setQueryPrefetchSize(int size) {
|
||||
prefetchSizes.add(size);
|
||||
}
|
||||
}
|
||||
|
||||
static final class FailingIrisConnection extends IRISConnection {
|
||||
@Override
|
||||
public void setQueryPrefetchSize(int size) throws SQLException {
|
||||
throw new SQLException("server rejected prefetch size");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void afterConnectAppliesLargerQueryPrefetch() {
|
||||
RecordingIrisConnection connection = new RecordingIrisConnection();
|
||||
|
||||
new IrisAgent().afterConnect(new ConnectParams(), connection);
|
||||
|
||||
assertEquals(Collections.singletonList(IrisAgent.IRIS_QUERY_PREFETCH_SIZE), connection.prefetchSizes);
|
||||
}
|
||||
|
||||
@Test
|
||||
void afterConnectKeepsNonIrisConnectionsUntouched() {
|
||||
Connection connection = proxyConnection();
|
||||
|
||||
assertDoesNotThrow(() -> new IrisAgent().afterConnect(new ConnectParams(), connection));
|
||||
}
|
||||
|
||||
@Test
|
||||
void prefetchFailureDoesNotBreakConnect() {
|
||||
FailingIrisConnection connection = new FailingIrisConnection();
|
||||
|
||||
assertDoesNotThrow(() -> new IrisAgent().afterConnect(new ConnectParams(), connection));
|
||||
}
|
||||
|
||||
@Test
|
||||
void directApplyIsBoundedToIrisConnections() {
|
||||
// A plain java.sql.Connection (for example a test or wrapper instance)
|
||||
// must not be cast or fail when the prefetch tuning runs.
|
||||
assertDoesNotThrow(() -> IrisAgent.applyQueryPrefetchSize(proxyConnection()));
|
||||
}
|
||||
|
||||
private static Connection proxyConnection() {
|
||||
return (Connection) Proxy.newProxyInstance(
|
||||
IrisAgentQueryPrefetchTest.class.getClassLoader(),
|
||||
new Class<?>[] { Connection.class },
|
||||
new InvocationHandler() {
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) {
|
||||
Class<?> returnType = method.getReturnType();
|
||||
if (returnType == boolean.class) {
|
||||
return false;
|
||||
}
|
||||
if (returnType.isPrimitive() && returnType != void.class) {
|
||||
return 0;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user