Skip to content

perf: use last_statement optimization in autocommit #1911

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 21, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata;
import com.google.spanner.v1.CommitRequest;
import com.google.spanner.v1.ExecuteBatchDmlRequest;
import com.google.spanner.v1.ExecuteSqlRequest;
import com.google.spanner.v1.ResultSetMetadata;
import com.google.spanner.v1.ResultSetStats;
import com.google.spanner.v1.StructType;
Expand Down Expand Up @@ -66,6 +67,8 @@
*/
@RunWith(Parameterized.class)
public class ExecuteMockServerTest extends AbstractMockServerTest {
private static final IllegalStateException REQUEST_NOT_FOUND =
new IllegalStateException("request not found");
private static Dialect currentDialect;

@Parameters(name = "dialect = {0}")
Expand Down Expand Up @@ -219,9 +222,27 @@ public void testStatementExecuteQuery() throws SQLException {
try (ResultSet resultSet = statement.executeQuery(query)) {
verifyResultSet(resultSet);
}
ExecuteSqlRequest request =
mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).stream()
.filter(r -> r.getSql().equals(query))
.findAny()
.orElseThrow(() -> REQUEST_NOT_FOUND);
assertTrue(request.getTransaction().hasSingleUse());
assertTrue(request.getTransaction().getSingleUse().hasReadOnly());
assertFalse(request.getLastStatement());

try (ResultSet resultSet = statement.executeQuery(dmlReturning)) {
verifyResultSet(resultSet);
}
request =
mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).stream()
.filter(r -> r.getSql().equals(dmlReturning))
.findAny()
.orElseThrow(() -> REQUEST_NOT_FOUND);
assertTrue(request.getTransaction().hasBegin());
assertTrue(request.getTransaction().getBegin().hasReadWrite());
assertTrue(request.getLastStatement());

try (ResultSet resultSet = statement.executeQuery(clientSideQuery)) {
verifyClientSideResultSet(resultSet);
}
Expand All @@ -237,6 +258,15 @@ public void testStatementExecuteUpdate() throws SQLException {
try (Connection connection = createJdbcConnection();
Statement statement = connection.createStatement()) {
assertEquals(1, statement.executeUpdate(dml));
ExecuteSqlRequest request =
mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).stream()
.filter(r -> r.getSql().equals(dml))
.findAny()
.orElseThrow(() -> REQUEST_NOT_FOUND);
assertTrue(request.getTransaction().hasBegin());
assertTrue(request.getTransaction().getBegin().hasReadWrite());
assertTrue(request.getLastStatement());

assertEquals(0, statement.executeUpdate(DDL));

connection.setAutoCommit(false);
Expand All @@ -256,6 +286,14 @@ public void testStatementExecuteUpdateReturnGeneratedKeys() throws SQLException
Statement statement = connection.createStatement()) {
// TODO: Add tests for RETURN_GENERATED_KEYS when that is supported.
assertEquals(1, statement.executeUpdate(dml, Statement.NO_GENERATED_KEYS));
ExecuteSqlRequest request =
mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).stream()
.findAny()
.orElseThrow(() -> REQUEST_NOT_FOUND);
assertTrue(request.getTransaction().hasBegin());
assertTrue(request.getTransaction().getBegin().hasReadWrite());
assertTrue(request.getLastStatement());

assertEquals(0, statement.executeUpdate(DDL, Statement.NO_GENERATED_KEYS));
assertEquals(0, statement.executeUpdate(clientSideUpdate, Statement.NO_GENERATED_KEYS));

Expand All @@ -271,6 +309,14 @@ public void testStatementExecuteUpdateReturnColumnNames() throws SQLException {
try (Connection connection = createJdbcConnection();
Statement statement = connection.createStatement()) {
assertEquals(1, statement.executeUpdate(dml, new String[] {"id"}));
ExecuteSqlRequest request =
mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).stream()
.findAny()
.orElseThrow(() -> REQUEST_NOT_FOUND);
assertTrue(request.getTransaction().hasBegin());
assertTrue(request.getTransaction().getBegin().hasReadWrite());
assertTrue(request.getLastStatement());

assertEquals(0, statement.executeUpdate(DDL, new String[] {"id"}));
assertEquals(0, statement.executeUpdate(clientSideUpdate, new String[] {"id"}));

Expand All @@ -289,6 +335,14 @@ public void testStatementExecuteUpdateReturnColumnIndexes() throws SQLException
try (Connection connection = createJdbcConnection();
Statement statement = connection.createStatement()) {
assertEquals(1, statement.executeUpdate(dml, new int[] {1}));
ExecuteSqlRequest request =
mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).stream()
.findAny()
.orElseThrow(() -> REQUEST_NOT_FOUND);
assertTrue(request.getTransaction().hasBegin());
assertTrue(request.getTransaction().getBegin().hasReadWrite());
assertTrue(request.getLastStatement());

assertEquals(0, statement.executeUpdate(DDL, new int[] {1}));
assertEquals(0, statement.executeUpdate(clientSideUpdate, new int[] {1}));
verifyOverflow(() -> statement.executeUpdate(largeDml, new int[] {1}));
Expand All @@ -303,6 +357,14 @@ public void testStatementLargeExecuteUpdate() throws SQLException {
try (Connection connection = createJdbcConnection();
Statement statement = connection.createStatement()) {
assertEquals(1L, statement.executeLargeUpdate(dml));
ExecuteSqlRequest request =
mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).stream()
.findAny()
.orElseThrow(() -> REQUEST_NOT_FOUND);
assertTrue(request.getTransaction().hasBegin());
assertTrue(request.getTransaction().getBegin().hasReadWrite());
assertTrue(request.getLastStatement());

assertEquals(0L, statement.executeLargeUpdate(DDL));
assertEquals(0L, statement.executeLargeUpdate(clientSideUpdate));
assertEquals(LARGE_UPDATE_COUNT, statement.executeLargeUpdate(largeDml));
Expand All @@ -318,6 +380,14 @@ public void testStatementExecuteLargeUpdateReturnGeneratedKeys() throws SQLExcep
Statement statement = connection.createStatement()) {
// TODO: Add tests for RETURN_GENERATED_KEYS when that is supported.
assertEquals(1, statement.executeLargeUpdate(dml, Statement.NO_GENERATED_KEYS));
ExecuteSqlRequest request =
mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).stream()
.findAny()
.orElseThrow(() -> REQUEST_NOT_FOUND);
assertTrue(request.getTransaction().hasBegin());
assertTrue(request.getTransaction().getBegin().hasReadWrite());
assertTrue(request.getLastStatement());

assertEquals(0, statement.executeLargeUpdate(DDL, Statement.NO_GENERATED_KEYS));
assertEquals(0, statement.executeLargeUpdate(clientSideUpdate, Statement.NO_GENERATED_KEYS));
assertEquals(
Expand Down Expand Up @@ -870,4 +940,102 @@ public void testExecuteAutoBatchDml() throws SQLException {
assertEquals(3, request.getStatementsCount());
assertEquals(1, mockSpanner.countRequestsOfType(CommitRequest.class));
}

@Test
public void testLastStatement_AutoCommit_Query() throws SQLException {
try (Connection connection = createJdbcConnection();
Statement statement = connection.createStatement()) {
//noinspection EmptyTryBlock
try (ResultSet ignore = statement.executeQuery(query)) {}
}
assertEquals(1, mockSpanner.countRequestsOfType(ExecuteSqlRequest.class));
assertFalse(mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).get(0).getLastStatement());
}

@Test
public void testLastStatement_AutoCommit_Dml() throws SQLException {
try (Connection connection = createJdbcConnection();
Statement statement = connection.createStatement()) {
statement.executeUpdate(dml);
}
assertEquals(1, mockSpanner.countRequestsOfType(ExecuteSqlRequest.class));
assertTrue(mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).get(0).getLastStatement());
}

@Test
public void testLastStatement_AutoCommit_DmlReturning() throws SQLException {
try (Connection connection = createJdbcConnection();
Statement statement = connection.createStatement()) {
//noinspection EmptyTryBlock
try (ResultSet ignore = statement.executeQuery(dmlReturning)) {}
}
assertEquals(1, mockSpanner.countRequestsOfType(ExecuteSqlRequest.class));
assertTrue(mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).get(0).getLastStatement());
}

@Test
public void testLastStatement_AutoCommit_BatchDml() throws SQLException {
try (Connection connection = createJdbcConnection();
Statement statement = connection.createStatement()) {
statement.addBatch(dml);
statement.addBatch(dml);
statement.executeBatch();
}
assertEquals(1, mockSpanner.countRequestsOfType(ExecuteBatchDmlRequest.class));
assertTrue(
mockSpanner.getRequestsOfType(ExecuteBatchDmlRequest.class).get(0).getLastStatements());
}

@Test
public void testLastStatement_Transaction_Query() throws SQLException {
try (Connection connection = createJdbcConnection();
Statement statement = connection.createStatement()) {
connection.setAutoCommit(false);
//noinspection EmptyTryBlock
try (ResultSet ignore = statement.executeQuery(query)) {}
connection.commit();
}
assertEquals(1, mockSpanner.countRequestsOfType(ExecuteSqlRequest.class));
assertFalse(mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).get(0).getLastStatement());
}

@Test
public void testLastStatement_Transaction_Dml() throws SQLException {
try (Connection connection = createJdbcConnection();
Statement statement = connection.createStatement()) {
connection.setAutoCommit(false);
statement.executeUpdate(dml);
connection.commit();
}
assertEquals(1, mockSpanner.countRequestsOfType(ExecuteSqlRequest.class));
assertFalse(mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).get(0).getLastStatement());
}

@Test
public void testLastStatement_Transaction_DmlReturning() throws SQLException {
try (Connection connection = createJdbcConnection();
Statement statement = connection.createStatement()) {
connection.setAutoCommit(false);
//noinspection EmptyTryBlock
try (ResultSet ignore = statement.executeQuery(dmlReturning)) {}
connection.commit();
}
assertEquals(1, mockSpanner.countRequestsOfType(ExecuteSqlRequest.class));
assertFalse(mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).get(0).getLastStatement());
}

@Test
public void testLastStatement_Transaction_BatchDml() throws SQLException {
try (Connection connection = createJdbcConnection();
Statement statement = connection.createStatement()) {
connection.setAutoCommit(false);
statement.addBatch(dml);
statement.addBatch(dml);
statement.executeBatch();
connection.commit();
}
assertEquals(1, mockSpanner.countRequestsOfType(ExecuteBatchDmlRequest.class));
assertFalse(
mockSpanner.getRequestsOfType(ExecuteBatchDmlRequest.class).get(0).getLastStatements());
}
}