Skip to content

Commit c8230de

Browse files
authored
Support begin in read-only mode for JDBC transactions (#2738)
1 parent f7925f6 commit c8230de

File tree

7 files changed

+194
-105
lines changed

7 files changed

+194
-105
lines changed

core/src/integration-test/java/com/scalar/db/transaction/jdbc/JdbcTransactionIntegrationTest.java

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
import java.util.Properties;
88
import org.junit.jupiter.api.Disabled;
99
import org.junit.jupiter.api.Test;
10-
import org.junit.jupiter.params.ParameterizedTest;
11-
import org.junit.jupiter.params.provider.EnumSource;
1210

1311
public class JdbcTransactionIntegrationTest extends DistributedTransactionIntegrationTestBase {
1412

@@ -44,16 +42,4 @@ public void abort_forOngoingTransaction_ShouldAbortCorrectly() {}
4442
@Override
4543
@Test
4644
public void rollback_forOngoingTransaction_ShouldRollbackCorrectly() {}
47-
48-
@Disabled("Implement later")
49-
@Override
50-
@Test
51-
public void get_GetGivenForCommittedRecord_InReadOnlyMode_ShouldReturnRecord() {}
52-
53-
@Disabled("Implement later")
54-
@Override
55-
@ParameterizedTest
56-
@EnumSource(ScanType.class)
57-
public void scanOrGetScanner_ScanGivenForCommittedRecord_InReadOnlyMode_ShouldReturnRecords(
58-
ScanType scanType) {}
5945
}

core/src/main/java/com/scalar/db/storage/jdbc/JdbcAdmin.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ public TableMetadata getTableMetadata(String namespace, String table) throws Exe
438438
boolean tableExists = false;
439439

440440
try (Connection connection = dataSource.getConnection()) {
441-
rdbEngine.setReadOnly(connection, true);
441+
rdbEngine.setConnectionToReadOnly(connection, true);
442442

443443
try (PreparedStatement preparedStatement =
444444
connection.prepareStatement(getSelectColumnsStatement())) {
@@ -510,7 +510,7 @@ public TableMetadata getImportTableMetadata(
510510
}
511511

512512
try (Connection connection = dataSource.getConnection()) {
513-
rdbEngine.setReadOnly(connection, true);
513+
rdbEngine.setConnectionToReadOnly(connection, true);
514514

515515
String catalogName = rdbEngine.getCatalogName(namespace);
516516
String schemaName = rdbEngine.getSchemaName(namespace);
@@ -608,7 +608,7 @@ public Set<String> getNamespaceTableNames(String namespace) throws ExecutionExce
608608
+ enclose(METADATA_COL_FULL_TABLE_NAME)
609609
+ " LIKE ?";
610610
try (Connection connection = dataSource.getConnection()) {
611-
rdbEngine.setReadOnly(connection, true);
611+
rdbEngine.setConnectionToReadOnly(connection, true);
612612

613613
try (PreparedStatement preparedStatement =
614614
connection.prepareStatement(selectTablesOfNamespaceStatement)) {
@@ -644,7 +644,7 @@ public boolean namespaceExists(String namespace) throws ExecutionException {
644644
+ enclose(NAMESPACE_COL_NAMESPACE_NAME)
645645
+ " = ?";
646646
try (Connection connection = dataSource.getConnection()) {
647-
rdbEngine.setReadOnly(connection, true);
647+
rdbEngine.setConnectionToReadOnly(connection, true);
648648

649649
try (PreparedStatement statement = connection.prepareStatement(selectQuery)) {
650650
statement.setString(1, namespace);
@@ -992,7 +992,7 @@ private String encloseFullTableName(String schema, String table) {
992992
@Override
993993
public Set<String> getNamespaceNames() throws ExecutionException {
994994
try (Connection connection = dataSource.getConnection()) {
995-
rdbEngine.setReadOnly(connection, true);
995+
rdbEngine.setConnectionToReadOnly(connection, true);
996996

997997
String selectQuery =
998998
"SELECT * FROM " + encloseFullTableName(metadataSchema, NAMESPACES_TABLE);

core/src/main/java/com/scalar/db/storage/jdbc/JdbcDatabase.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ public Optional<Result> get(Get get) throws ExecutionException {
8282
Connection connection = null;
8383
try {
8484
connection = dataSource.getConnection();
85-
rdbEngine.setReadOnly(connection, true);
85+
rdbEngine.setConnectionToReadOnly(connection, true);
8686
return jdbcService.get(get, connection);
8787
} catch (SQLException e) {
8888
throw new ExecutionException(
@@ -98,7 +98,7 @@ public Scanner scan(Scan scan) throws ExecutionException {
9898
Connection connection = null;
9999
try {
100100
connection = dataSource.getConnection();
101-
rdbEngine.setReadOnly(connection, true);
101+
rdbEngine.setConnectionToReadOnly(connection, true);
102102
return jdbcService.getScanner(scan, connection);
103103
} catch (SQLException e) {
104104
close(connection);

core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineSqlite.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ public RdbEngineTimeTypeStrategy<Integer, Long, Long, Long> getTimeTypeStrategy(
340340
}
341341

342342
@Override
343-
public void setReadOnly(Connection connection, boolean readOnly) {
343+
public void setConnectionToReadOnly(Connection connection, boolean readOnly) {
344344
// Do nothing. SQLite does not support read-only mode.
345345
}
346346
}

core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineStrategy.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,8 @@ default void throwIfDuplicatedIndexWarning(SQLWarning warning) throws SQLExcepti
230230
// Do nothing
231231
}
232232

233-
default void setReadOnly(Connection connection, boolean readOnly) throws SQLException {
233+
default void setConnectionToReadOnly(Connection connection, boolean readOnly)
234+
throws SQLException {
234235
connection.setReadOnly(readOnly);
235236
}
236237
}

core/src/main/java/com/scalar/db/transaction/jdbc/JdbcTransactionManager.java

Lines changed: 56 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import com.scalar.db.api.Upsert;
1919
import com.scalar.db.common.AbstractDistributedTransactionManager;
2020
import com.scalar.db.common.AbstractTransactionManagerCrudOperableScanner;
21+
import com.scalar.db.common.ReadOnlyDistributedTransaction;
2122
import com.scalar.db.common.TableMetadataManager;
2223
import com.scalar.db.common.checker.OperationChecker;
2324
import com.scalar.db.common.error.CoreError;
@@ -36,6 +37,7 @@
3637
import com.scalar.db.storage.jdbc.RdbEngineFactory;
3738
import com.scalar.db.storage.jdbc.RdbEngineStrategy;
3839
import com.scalar.db.util.ThrowableFunction;
40+
import java.sql.Connection;
3941
import java.sql.SQLException;
4042
import java.util.List;
4143
import java.util.Optional;
@@ -90,14 +92,39 @@ public JdbcTransactionManager(DatabaseConfig databaseConfig) {
9092
@Override
9193
public DistributedTransaction begin() throws TransactionException {
9294
String txId = UUID.randomUUID().toString();
93-
return begin(txId);
95+
return begin(txId, false);
9496
}
9597

9698
@Override
9799
public DistributedTransaction begin(String txId) throws TransactionException {
100+
return begin(txId, false);
101+
}
102+
103+
@Override
104+
public DistributedTransaction beginReadOnly() throws TransactionException {
105+
String txId = UUID.randomUUID().toString();
106+
return begin(txId, true);
107+
}
108+
109+
@Override
110+
public DistributedTransaction beginReadOnly(String txId) throws TransactionException {
111+
return begin(txId, true);
112+
}
113+
114+
private DistributedTransaction begin(String txId, boolean readOnly) throws TransactionException {
98115
try {
99-
JdbcTransaction transaction =
100-
new JdbcTransaction(txId, jdbcService, dataSource.getConnection(), rdbEngine);
116+
Connection connection = dataSource.getConnection();
117+
118+
DistributedTransaction transaction;
119+
if (readOnly) {
120+
rdbEngine.setConnectionToReadOnly(connection, true);
121+
transaction =
122+
new ReadOnlyDistributedTransaction(
123+
new JdbcTransaction(txId, jdbcService, connection, rdbEngine));
124+
} else {
125+
transaction = new JdbcTransaction(txId, jdbcService, connection, rdbEngine);
126+
}
127+
101128
getNamespace().ifPresent(transaction::withNamespace);
102129
getTable().ifPresent(transaction::withTable);
103130
return transaction;
@@ -109,16 +136,6 @@ public DistributedTransaction begin(String txId) throws TransactionException {
109136
}
110137
}
111138

112-
@Override
113-
public DistributedTransaction beginReadOnly() {
114-
throw new UnsupportedOperationException("implement later");
115-
}
116-
117-
@Override
118-
public DistributedTransaction beginReadOnly(String txId) {
119-
throw new UnsupportedOperationException("implement later");
120-
}
121-
122139
/** @deprecated As of release 2.4.0. Will be removed in release 4.0.0. */
123140
@SuppressWarnings("InlineMeSuggester")
124141
@Deprecated
@@ -173,19 +190,19 @@ public DistributedTransaction start(
173190

174191
@Override
175192
public Optional<Result> get(Get get) throws CrudException, UnknownTransactionStatusException {
176-
return executeTransaction(t -> t.get(copyAndSetTargetToIfNot(get)));
193+
return executeTransaction(t -> t.get(copyAndSetTargetToIfNot(get)), true);
177194
}
178195

179196
@Override
180197
public List<Result> scan(Scan scan) throws CrudException, UnknownTransactionStatusException {
181-
return executeTransaction(t -> t.scan(copyAndSetTargetToIfNot(scan)));
198+
return executeTransaction(t -> t.scan(copyAndSetTargetToIfNot(scan)), true);
182199
}
183200

184201
@Override
185202
public Scanner getScanner(Scan scan) throws CrudException {
186203
DistributedTransaction transaction;
187204
try {
188-
transaction = begin();
205+
transaction = beginReadOnly();
189206
} catch (TransactionNotFoundException e) {
190207
throw new CrudConflictException(e.getMessage(), e, e.getTransactionId().orElse(null));
191208
} catch (TransactionException e) {
@@ -277,7 +294,8 @@ public void put(Put put) throws CrudException, UnknownTransactionStatusException
277294
t -> {
278295
t.put(copyAndSetTargetToIfNot(put));
279296
return null;
280-
});
297+
},
298+
false);
281299
}
282300

283301
/** @deprecated As of release 3.13.0. Will be removed in release 5.0.0. */
@@ -288,7 +306,8 @@ public void put(List<Put> puts) throws CrudException, UnknownTransactionStatusEx
288306
t -> {
289307
t.put(copyAndSetTargetToIfNot(puts));
290308
return null;
291-
});
309+
},
310+
false);
292311
}
293312

294313
@Override
@@ -297,7 +316,8 @@ public void insert(Insert insert) throws CrudException, UnknownTransactionStatus
297316
t -> {
298317
t.insert(copyAndSetTargetToIfNot(insert));
299318
return null;
300-
});
319+
},
320+
false);
301321
}
302322

303323
@Override
@@ -306,7 +326,8 @@ public void upsert(Upsert upsert) throws CrudException, UnknownTransactionStatus
306326
t -> {
307327
t.upsert(copyAndSetTargetToIfNot(upsert));
308328
return null;
309-
});
329+
},
330+
false);
310331
}
311332

312333
@Override
@@ -315,7 +336,8 @@ public void update(Update update) throws CrudException, UnknownTransactionStatus
315336
t -> {
316337
t.update(copyAndSetTargetToIfNot(update));
317338
return null;
318-
});
339+
},
340+
false);
319341
}
320342

321343
@Override
@@ -324,7 +346,8 @@ public void delete(Delete delete) throws CrudException, UnknownTransactionStatus
324346
t -> {
325347
t.delete(copyAndSetTargetToIfNot(delete));
326348
return null;
327-
});
349+
},
350+
false);
328351
}
329352

330353
/** @deprecated As of release 3.13.0. Will be removed in release 5.0.0. */
@@ -335,7 +358,8 @@ public void delete(List<Delete> deletes) throws CrudException, UnknownTransactio
335358
t -> {
336359
t.delete(copyAndSetTargetToIfNot(deletes));
337360
return null;
338-
});
361+
},
362+
false);
339363
}
340364

341365
@Override
@@ -345,15 +369,21 @@ public void mutate(List<? extends Mutation> mutations)
345369
t -> {
346370
t.mutate(copyAndSetTargetToIfNot(mutations));
347371
return null;
348-
});
372+
},
373+
false);
349374
}
350375

351376
private <R> R executeTransaction(
352-
ThrowableFunction<DistributedTransaction, R, TransactionException> throwableFunction)
377+
ThrowableFunction<DistributedTransaction, R, TransactionException> throwableFunction,
378+
boolean readOnly)
353379
throws CrudException, UnknownTransactionStatusException {
354380
DistributedTransaction transaction;
355381
try {
356-
transaction = begin();
382+
if (readOnly) {
383+
transaction = beginReadOnly();
384+
} else {
385+
transaction = begin();
386+
}
357387
} catch (TransactionNotFoundException e) {
358388
throw new CrudConflictException(e.getMessage(), e, e.getTransactionId().orElse(null));
359389
} catch (TransactionException e) {

0 commit comments

Comments
 (0)