Skip to content

Commit af66079

Browse files
committed
Align repairTable behavior with master
1 parent 6e127ea commit af66079

18 files changed

Lines changed: 574 additions & 499 deletions

core/src/integration-test/java/com/scalar/db/storage/cassandra/CassandraAdminRepairTableIntegrationTest.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44
import static org.assertj.core.api.Assertions.assertThatCode;
55

66
import com.scalar.db.api.DistributedStorageAdminRepairTableIntegrationTestBase;
7+
import com.scalar.db.config.DatabaseConfig;
78
import com.scalar.db.exception.storage.ExecutionException;
89
import com.scalar.db.util.AdminTestUtils;
10+
import java.util.Map;
911
import java.util.Properties;
12+
import org.junit.jupiter.api.BeforeEach;
1013
import org.junit.jupiter.api.Disabled;
1114
import org.junit.jupiter.api.Test;
1215

@@ -23,6 +26,22 @@ protected AdminTestUtils getAdminTestUtils(String testName) {
2326
return new CassandraAdminTestUtils(getProperties(testName));
2427
}
2528

29+
// Override setUp() to share the ClusterManager between admin and adminTestUtils so that schema
30+
// changes made via one are visible to the other. Without sharing, schema metadata propagates
31+
// asynchronously across driver sessions and the repair-then-tableExists test sequence becomes
32+
// flaky.
33+
@Override
34+
@BeforeEach
35+
protected void setUp() throws Exception {
36+
Properties properties = getProperties(TEST_NAME);
37+
ClusterManager clusterManager = new ClusterManager(new DatabaseConfig(properties));
38+
admin = new CassandraAdmin(clusterManager, new DatabaseConfig(properties));
39+
adminTestUtils = new CassandraAdminTestUtils(properties, clusterManager);
40+
Map<String, String> options = getCreationOptions();
41+
admin.createNamespace(getNamespace(), options);
42+
admin.createTable(getNamespace(), getTable(), getTableMetadata(), options);
43+
}
44+
2645
@Test
2746
@Disabled("there is no metadata table for Cassandra")
2847
@Override

core/src/integration-test/java/com/scalar/db/storage/cassandra/CassandraAdminTestUtils.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ public CassandraAdminTestUtils(Properties properties) {
1616
clusterManager = new ClusterManager(databaseConfig);
1717
}
1818

19+
public CassandraAdminTestUtils(Properties properties, ClusterManager clusterManager) {
20+
super(properties);
21+
this.clusterManager = clusterManager;
22+
}
23+
1924
@Override
2025
public void dropMetadataTable() {
2126
// Do nothing

core/src/integration-test/java/com/scalar/db/storage/dynamo/DynamoSchemaLoaderIntegrationTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ protected List<String> getCommandArgsForTableReparation(
3434
Path configFilePath, Path schemaFilePath) {
3535
return ImmutableList.<String>builder()
3636
.addAll(super.getCommandArgsForTableReparation(configFilePath, schemaFilePath))
37+
.add("--no-scaling")
3738
.add("--no-backup")
3839
.build();
3940
}

core/src/integration-test/java/com/scalar/db/storage/objectstorage/ConsensusCommitAdminRepairTableIntegrationTestWithObjectStorage.java

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import com.scalar.db.transaction.consensuscommit.ConsensusCommitAdminRepairTableIntegrationTestBase;
44
import com.scalar.db.util.AdminTestUtils;
55
import java.util.Properties;
6-
import org.junit.jupiter.api.Disabled;
76

87
public class ConsensusCommitAdminRepairTableIntegrationTestWithObjectStorage
98
extends ConsensusCommitAdminRepairTableIntegrationTestBase {
@@ -17,13 +16,4 @@ protected Properties getProps(String testName) {
1716
protected AdminTestUtils getAdminTestUtils(String testName) {
1817
return new ObjectStorageAdminTestUtils(getProperties(testName));
1918
}
20-
21-
@Override
22-
@Disabled("Object Storage recreates missing coordinator tables")
23-
public void
24-
repairTableAndCoordinatorTable_CoordinatorTablesDoNotExist_ShouldThrowIllegalArgumentException() {}
25-
26-
@Override
27-
@Disabled("Object Storage recreates missing coordinator tables")
28-
public void repairTable_ForNonExistingTable_ShouldThrowIllegalArgument() {}
2919
}

core/src/integration-test/java/com/scalar/db/storage/objectstorage/ObjectStorageAdminRepairTableIntegrationTest.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import com.scalar.db.api.DistributedStorageAdminRepairTableIntegrationTestBase;
44
import com.scalar.db.util.AdminTestUtils;
55
import java.util.Properties;
6-
import org.junit.jupiter.api.Disabled;
76

87
public class ObjectStorageAdminRepairTableIntegrationTest
98
extends DistributedStorageAdminRepairTableIntegrationTestBase {
@@ -17,8 +16,4 @@ protected Properties getProperties(String testName) {
1716
protected AdminTestUtils getAdminTestUtils(String testName) {
1817
return new ObjectStorageAdminTestUtils(getProperties(testName));
1918
}
20-
21-
@Override
22-
@Disabled("Object Storage recreates missing coordinator tables")
23-
public void repairTable_ForNonExistingTable_ShouldThrowIllegalArgument() {}
2419
}

core/src/main/java/com/scalar/db/api/Admin.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -363,13 +363,14 @@ default boolean tableExists(String namespace, String table) throws ExecutionExce
363363
}
364364

365365
/**
366-
* Repairs a table which may be in an unknown state.
366+
* Repairs a table that may be in an unknown state, such as the table exists in the underlying
367+
* storage but not its ScalarDB metadata or vice versa. This will re-create the table, its
368+
* secondary indexes, and their metadata if necessary.
367369
*
368-
* @param namespace an existing namespace
369-
* @param table an existing table
370+
* @param namespace a namespace
371+
* @param table a table
370372
* @param metadata the metadata associated to the table to repair
371373
* @param options options to repair
372-
* @throws IllegalArgumentException if the table does not exist
373374
* @throws ExecutionException if the operation fails
374375
*/
375376
void repairTable(

core/src/main/java/com/scalar/db/storage/cassandra/CassandraAdmin.java

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.datastax.driver.core.KeyspaceMetadata;
99
import com.datastax.driver.core.querybuilder.QueryBuilder;
1010
import com.datastax.driver.core.schemabuilder.Create;
11+
import com.datastax.driver.core.schemabuilder.CreateIndex;
1112
import com.datastax.driver.core.schemabuilder.CreateKeyspace;
1213
import com.datastax.driver.core.schemabuilder.SchemaBuilder;
1314
import com.datastax.driver.core.schemabuilder.SchemaBuilder.Direction;
@@ -92,8 +93,8 @@ public void createTable(
9293
// Create the system namespace if it does not exist
9394
createKeyspace(systemNamespace, true, options);
9495

95-
createTableInternal(namespace, table, metadata, options);
96-
createSecondaryIndexes(namespace, table, metadata.getSecondaryIndexNames(), options);
96+
createTableInternal(namespace, table, metadata, false, options);
97+
createSecondaryIndexes(namespace, table, metadata.getSecondaryIndexNames(), false);
9798
}
9899

99100
@Override
@@ -170,17 +171,29 @@ public void truncateTable(String namespace, String table) throws ExecutionExcept
170171
public void createIndex(
171172
String namespace, String table, String columnName, Map<String, String> options)
172173
throws ExecutionException {
174+
createIndexInternal(namespace, table, columnName, false);
175+
}
176+
177+
public void createIndexInternal(
178+
String namespace, String table, String columnName, boolean ifNotExists)
179+
throws ExecutionException {
173180
String indexName = getIndexName(table, columnName);
174-
SchemaStatement createIndex =
175-
SchemaBuilder.createIndex(indexName)
181+
CreateIndex createIndex = SchemaBuilder.createIndex(indexName);
182+
if (ifNotExists) {
183+
createIndex = createIndex.ifNotExists();
184+
}
185+
SchemaStatement createIndexStatement =
186+
createIndex
176187
.onTable(quoteIfNecessary(namespace), quoteIfNecessary(table))
177188
.andColumn(quoteIfNecessary(columnName));
189+
178190
try {
179-
clusterManager.getSession().execute(createIndex.getQueryString());
191+
clusterManager.getSession().execute(createIndexStatement.getQueryString());
180192
} catch (RuntimeException e) {
181193
throw new ExecutionException(
182194
String.format(
183-
"Creating the secondary index for %s.%s.%s failed", namespace, table, columnName),
195+
"Creating the secondary index on the %s column of the %s table failed",
196+
columnName, getFullTableName(namespace, table)),
184197
e);
185198
}
186199
}
@@ -300,12 +313,13 @@ public boolean namespaceExists(String namespace) throws ExecutionException {
300313
public void repairTable(
301314
String namespace, String table, TableMetadata metadata, Map<String, String> options)
302315
throws ExecutionException {
303-
// We have this check to stay consistent with the behavior of the other admins classes
304-
if (!tableExists(namespace, table)) {
305-
throw new IllegalArgumentException(
306-
"The table " + getFullTableName(namespace, table) + " does not exist");
316+
try {
317+
createTableInternal(namespace, table, metadata, true, options);
318+
createSecondaryIndexes(namespace, table, metadata.getSecondaryIndexNames(), true);
319+
} catch (ExecutionException e) {
320+
throw new ExecutionException(
321+
String.format("Repairing the %s table failed", getFullTableName(namespace, table)), e);
307322
}
308-
// The table metadata are not managed by ScalarDB, so we don't need to do anything here
309323
}
310324

311325
@Override
@@ -431,10 +445,17 @@ public Set<String> getNamespaceNames() throws ExecutionException {
431445

432446
@VisibleForTesting
433447
void createTableInternal(
434-
String keyspace, String table, TableMetadata metadata, Map<String, String> options)
448+
String keyspace,
449+
String table,
450+
TableMetadata metadata,
451+
boolean ifNotExists,
452+
Map<String, String> options)
435453
throws ExecutionException {
436454
Create createTable =
437455
SchemaBuilder.createTable(quoteIfNecessary(keyspace), quoteIfNecessary(table));
456+
if (ifNotExists) {
457+
createTable = createTable.ifNotExists();
458+
}
438459
// Add columns
439460
for (String pk : metadata.getPartitionKeyNames()) {
440461
createTable =
@@ -487,16 +508,16 @@ void createTableInternal(
487508
clusterManager.getSession().execute(createTableWithOptions.getQueryString());
488509
} catch (RuntimeException e) {
489510
throw new ExecutionException(
490-
String.format("Creating the table %s.%s failed", keyspace, table), e);
511+
String.format("Creating the %s table failed", getFullTableName(keyspace, table)), e);
491512
}
492513
}
493514

494515
@VisibleForTesting
495516
void createSecondaryIndexes(
496-
String keyspace, String table, Set<String> secondaryIndexNames, Map<String, String> options)
517+
String keyspace, String table, Set<String> secondaryIndexNames, boolean ifNotExists)
497518
throws ExecutionException {
498519
for (String name : secondaryIndexNames) {
499-
createIndex(keyspace, table, name, options);
520+
createIndexInternal(keyspace, table, name, ifNotExists);
500521
}
501522
}
502523

core/src/main/java/com/scalar/db/storage/cosmos/CosmosAdmin.java

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -95,18 +95,23 @@ public CosmosAdmin(DatabaseConfig databaseConfig) {
9595
public void createTable(
9696
String namespace, String table, TableMetadata metadata, Map<String, String> options)
9797
throws ExecutionException {
98-
checkMetadata(metadata);
9998
try {
100-
// Create the metadata database and container first if they do not exist
101-
createMetadataDatabaseAndContainerIfNotExists();
102-
103-
createContainer(namespace, table, metadata);
104-
putTableMetadata(namespace, table, metadata, false);
99+
createTableInternal(namespace, table, metadata, false);
100+
} catch (IllegalArgumentException e) {
101+
throw e;
105102
} catch (RuntimeException e) {
106103
throw new ExecutionException("Creating the container failed", e);
107104
}
108105
}
109106

107+
private void createTableInternal(
108+
String namespace, String table, TableMetadata metadata, boolean ifNotExists)
109+
throws ExecutionException {
110+
checkMetadata(metadata);
111+
createContainer(namespace, table, metadata, ifNotExists);
112+
putTableMetadata(namespace, table, metadata, true);
113+
}
114+
110115
private void checkMetadata(TableMetadata metadata) {
111116
for (String clusteringKeyName : metadata.getClusteringKeyNames()) {
112117
if (metadata.getColumnDataType(clusteringKeyName) == DataType.BLOB) {
@@ -117,19 +122,28 @@ private void checkMetadata(TableMetadata metadata) {
117122
}
118123
}
119124

120-
private void createContainer(String database, String table, TableMetadata metadata)
125+
private void createContainer(
126+
String database, String table, TableMetadata metadata, boolean ifNotExists)
121127
throws ExecutionException {
122128
CosmosDatabase cosmosDatabase = client.getDatabase(database);
123129
CosmosContainerProperties properties = computeContainerProperties(table, metadata);
124-
cosmosDatabase.createContainer(properties);
125-
126-
addStoredProcedure(database, table);
130+
if (ifNotExists) {
131+
cosmosDatabase.createContainerIfNotExists(properties);
132+
} else {
133+
cosmosDatabase.createContainer(properties);
134+
}
135+
addStoredProcedure(database, table, ifNotExists);
127136
}
128137

129-
private void addStoredProcedure(String namespace, String table) throws ExecutionException {
138+
private void addStoredProcedure(String namespace, String table, boolean ifNotExists)
139+
throws ExecutionException {
130140
CosmosDatabase cosmosDatabase = client.getDatabase(namespace);
131141
CosmosStoredProcedureProperties storedProcedureProperties =
132142
computeContainerStoredProcedureProperties();
143+
144+
if (ifNotExists && storedProcedureExists(namespace, table)) {
145+
return;
146+
}
133147
cosmosDatabase
134148
.getContainer(table)
135149
.getScripts()
@@ -510,24 +524,14 @@ public void repairTable(
510524
String namespace, String table, TableMetadata metadata, Map<String, String> options)
511525
throws ExecutionException {
512526
try {
513-
try {
514-
// Since the metadata table may be missing, we cannot use CosmosAdmin.tableExists() as it
515-
// queries the metadata table to verify if the given table exists
516-
client.getDatabase(namespace).getContainer(table).read();
517-
} catch (CosmosException e) {
518-
if (e.getStatusCode() == 404) {
519-
throw new IllegalArgumentException(
520-
"The table " + getFullTableName(namespace, table) + " does not exist");
521-
}
522-
}
523-
createMetadataDatabaseAndContainerIfNotExists();
524-
putTableMetadata(namespace, table, metadata, true);
525-
if (!storedProcedureExists(namespace, table)) {
526-
addStoredProcedure(namespace, table);
527-
}
528-
} catch (ExecutionException | CosmosException e) {
527+
createTableInternal(namespace, table, metadata, true);
528+
updateIndexingPolicy(namespace, table, metadata);
529+
} catch (IllegalArgumentException e) {
530+
throw e;
531+
} catch (Exception e) {
529532
throw new ExecutionException(
530-
String.format("Repairing the table %s.%s failed", namespace, table), e);
533+
String.format("Repairing the %s container failed", getFullTableName(namespace, table)),
534+
e);
531535
}
532536
}
533537

0 commit comments

Comments
 (0)