Skip to content

Commit

Permalink
Revisit namespaces table deletion logic
Browse files Browse the repository at this point in the history
  • Loading branch information
brfrn169 committed Dec 7, 2023
1 parent 7b053c5 commit c83c95d
Show file tree
Hide file tree
Showing 8 changed files with 391 additions and 261 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.scalar.db.config.DatabaseConfig;
import com.scalar.db.exception.storage.ExecutionException;
import com.scalar.db.io.DataType;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
Expand Down Expand Up @@ -64,6 +65,7 @@ public void createTable(
String namespace, String table, TableMetadata metadata, Map<String, String> options)
throws ExecutionException {
try {
createNamespacesTableIfNotExists();
createTableInternal(namespace, table, metadata, false, options);
createSecondaryIndexes(namespace, table, metadata.getSecondaryIndexNames(), false);
} catch (RuntimeException e) {
Expand Down Expand Up @@ -128,11 +130,13 @@ private void upsertIntoNamespacesTable(String keyspace) {

@Override
public void dropTable(String namespace, String table) throws ExecutionException {
String dropTableQuery =
SchemaBuilder.dropTable(quoteIfNecessary(namespace), quoteIfNecessary(table))
.getQueryString();
try {
String dropTableQuery =
SchemaBuilder.dropTable(quoteIfNecessary(namespace), quoteIfNecessary(table))
.getQueryString();
clusterManager.getSession().execute(dropTableQuery);

dropMetadataKeyspaceIfEmpty();
} catch (RuntimeException e) {
throw new ExecutionException(
String.format("Dropping the %s table failed", getFullTableName(namespace, table)), e);
Expand All @@ -144,7 +148,7 @@ public void dropNamespace(String namespace) throws ExecutionException {
try {
dropKeyspace(namespace);
deleteFromNamespacesTable(namespace);
dropNamespacesTableIfEmpty();
dropMetadataKeyspaceIfEmpty();
} catch (RuntimeException e) {
throw new ExecutionException(String.format("Dropping the %s keyspace failed", namespace), e);
}
Expand Down Expand Up @@ -442,17 +446,32 @@ private void createNamespacesTableIfNotExists() {
upsertIntoNamespacesTable(metadataKeyspace);
}

private void dropNamespacesTableIfEmpty() {
private void dropMetadataKeyspaceIfEmpty() {
String selectQuery =
QueryBuilder.select(NAMESPACES_NAME_COL)
.from(quoteIfNecessary(metadataKeyspace), quoteIfNecessary(NAMESPACES_TABLE))
.limit(2)
.getQueryString();

List<Row> rows = clusterManager.getSession().execute(selectQuery).all();
if (rows.isEmpty()
|| (rows.size() == 1
&& rows.get(0).getString(NAMESPACES_NAME_COL).equals(metadataKeyspace))) {

boolean onlyMetadataNamespaceLeft =
rows.size() == 1 && rows.get(0).getString(NAMESPACES_NAME_COL).equals(metadataKeyspace);
if (!onlyMetadataNamespaceLeft) {
return;
}

// Drop the metadata keyspace if there is only the namespaces table left
KeyspaceMetadata keyspace =
clusterManager
.getSession()
.getCluster()
.getMetadata()
.getKeyspace(quoteIfNecessary(metadataKeyspace));
if (keyspace == null) {
return;
}
Collection<com.datastax.driver.core.TableMetadata> tables = keyspace.getTables();
if (tables.size() == 1 && tables.iterator().next().getName().equals(NAMESPACES_TABLE)) {
dropKeyspace(metadataKeyspace);
}
}
Expand Down
32 changes: 28 additions & 4 deletions core/src/main/java/com/scalar/db/storage/cosmos/CosmosAdmin.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
Expand Down Expand Up @@ -90,6 +92,7 @@ public void createTable(
String namespace, String table, TableMetadata metadata, Map<String, String> options)
throws ExecutionException {
try {
createMetadataDatabaseAndNamespaceContainerIfNotExists();
createTableInternal(namespace, table, metadata, false);
} catch (IllegalArgumentException e) {
throw e;
Expand Down Expand Up @@ -303,6 +306,7 @@ public void dropTable(String namespace, String table) throws ExecutionException
try {
database.getContainer(table).delete();
deleteTableMetadata(namespace, table);
deleteMetadataDatabaseIfEmpty();
} catch (RuntimeException e) {
throw new ExecutionException(
String.format("Deleting the %s container failed", getFullTableName(namespace, table)), e);
Expand Down Expand Up @@ -341,13 +345,13 @@ public void dropNamespace(String namespace) throws ExecutionException {
client.getDatabase(namespace).delete();
getNamespacesContainer()
.deleteItem(new CosmosNamespace(namespace), new CosmosItemRequestOptions());
dropNamespacesTableIfEmpty();
deleteMetadataDatabaseIfEmpty();
} catch (RuntimeException e) {
throw new ExecutionException(String.format("Deleting the %s database failed", namespace), e);
}
}

private void dropNamespacesTableIfEmpty() {
private void deleteMetadataDatabaseIfEmpty() {
Set<String> namespaces =
getNamespacesContainer()
.queryItems(
Expand All @@ -357,8 +361,28 @@ private void dropNamespacesTableIfEmpty() {
.stream()
.map(CosmosNamespace::getId)
.collect(Collectors.toSet());
if (namespaces.isEmpty() || (namespaces.size() == 1 && namespaces.contains(metadataDatabase))) {
client.getDatabase(metadataDatabase).delete();

boolean onlyMetadataNamespaceLeft =
namespaces.size() == 1 && namespaces.contains(metadataDatabase);
if (!onlyMetadataNamespaceLeft) {
return;
}

// Delete the metadata database if there is only the namespaces container left
CosmosDatabase database = client.getDatabase(metadataDatabase);
Iterator<CosmosContainerProperties> iterator = database.readAllContainers().iterator();

Set<String> containers = new HashSet<>();
int count = 0;
while (iterator.hasNext()) {
containers.add(iterator.next().getId());
// Only need to fetch the first two containers
if (count++ == 2) {
break;
}
}
if (containers.size() == 1 && containers.contains(NAMESPACES_CONTAINER)) {
database.delete();
}
}

Expand Down
70 changes: 53 additions & 17 deletions core/src/main/java/com/scalar/db/storage/dynamo/DynamoAdmin.java
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,8 @@ public void createTable(
Map<String, String> options)
throws ExecutionException {
try {
boolean noBackup = Boolean.parseBoolean(options.getOrDefault(NO_BACKUP, DEFAULT_NO_BACKUP));
createNamespacesTableIfNotExists(noBackup);
createTableInternal(nonPrefixedNamespace, table, metadata, false, options);
} catch (ExecutionException e) {
throw new ExecutionException(
Expand Down Expand Up @@ -685,6 +687,7 @@ public void dropTable(String nonPrefixedNamespace, String table) throws Executio
}
waitForTableDeletion(namespace, table);
deleteTableMetadata(namespace, table);
dropNamespacesTableIfEmpty();
}

private void disableAutoScaling(Namespace namespace, String table) throws ExecutionException {
Expand Down Expand Up @@ -795,26 +798,58 @@ public void dropNamespace(String nonPrefixedNamespace) throws ExecutionException
Namespace namespace = Namespace.of(namespacePrefix, nonPrefixedNamespace);
try {
deleteFromNamespacesTable(namespace);
dropNamespacesTableIfEmpty();
} catch (Exception e) {
throw new ExecutionException("Dropping the " + namespace + " namespace failed", e);
}
dropNamespacesTableIfEmpty();
}

private void dropNamespacesTableIfEmpty() throws ExecutionException {
String namespaceTableFullName =
ScalarDbUtils.getFullTableName(metadataNamespace, NAMESPACES_TABLE);
ScanResponse scanResponse =
client.scan(ScanRequest.builder().tableName(namespaceTableFullName).limit(2).build());

ScanResponse scanResponse;
try {
scanResponse =
client.scan(ScanRequest.builder().tableName(namespaceTableFullName).limit(2).build());
} catch (Exception e) {
throw new ExecutionException("Scanning the namespaces table failed", e);
}

Set<String> namespaceNames = new HashSet<>();
for (Map<String, AttributeValue> namespace : scanResponse.items()) {
String prefixedNamespaceName = namespace.get(NAMESPACES_ATTR_NAME).s();
namespaceNames.add(prefixedNamespaceName);
String namespaceName = namespace.get(NAMESPACES_ATTR_NAME).s();
namespaceNames.add(namespaceName);
}

boolean onlyMetadataNamespaceLeft =
namespaceNames.size() == 1 && namespaceNames.contains(metadataNamespace);
if (!onlyMetadataNamespaceLeft) {
return;
}

// Delete the namespaces table if there is only the namespaces table left
Set<String> tables = new HashSet<>();
try {
String lastEvaluatedTableName = null;
do {
ListTablesRequest listTablesRequest =
ListTablesRequest.builder().exclusiveStartTableName(lastEvaluatedTableName).build();
ListTablesResponse listTablesResponse = client.listTables(listTablesRequest);
lastEvaluatedTableName = listTablesResponse.lastEvaluatedTableName();
List<String> tableNames = listTablesResponse.tableNames();
String prefix = metadataNamespace + ".";
for (String tableName : tableNames) {
if (tableName.startsWith(prefix)) {
tables.add(tableName.substring(prefix.length()));
}
}
} while (lastEvaluatedTableName != null);
} catch (Exception e) {
throw new ExecutionException("Listing tables failed", e);
}

if (namespaceNames.isEmpty()
|| (namespaceNames.size() == 1 && namespaceNames.contains(metadataNamespace))) {
if (tables.size() == 1 && tables.contains(NAMESPACES_TABLE)) {
client.deleteTable(DeleteTableRequest.builder().tableName(namespaceTableFullName).build());
waitForTableDeletion(Namespace.of(metadataNamespace), NAMESPACES_TABLE);
}
Expand Down Expand Up @@ -1409,8 +1444,8 @@ private Set<Namespace> getNamespacesOfExistingTables() throws ExecutionException
}

private void createNamespacesTableIfNotExists(boolean noBackup) throws ExecutionException {
try {
if (!namespacesTableExists()) {
if (!namespacesTableExists()) {
try {
List<AttributeDefinition> columnsToAttributeDefinitions = new ArrayList<>();
columnsToAttributeDefinitions.add(
AttributeDefinition.builder()
Expand All @@ -1432,17 +1467,18 @@ private void createNamespacesTableIfNotExists(boolean noBackup) throws Execution
.build())
.tableName(ScalarDbUtils.getFullTableName(metadataNamespace, NAMESPACES_TABLE))
.build());
waitForTableCreation(Namespace.of(metadataNamespace), NAMESPACES_TABLE);
} catch (Exception e) {
throw new ExecutionException("Creating the namespaces table failed", e);
}

// Insert the system namespace to the namespaces table
upsertIntoNamespacesTable(Namespace.of(metadataNamespace));
waitForTableCreation(Namespace.of(metadataNamespace), NAMESPACES_TABLE);

if (!noBackup) {
enableContinuousBackup(Namespace.of(metadataNamespace), NAMESPACES_TABLE);
}
} catch (Exception e) {
throw new ExecutionException("Creating the namespaces table failed", e);
}

if (!noBackup) {
enableContinuousBackup(Namespace.of(metadataNamespace), NAMESPACES_TABLE);
// Insert the system namespace to the namespaces table
upsertIntoNamespacesTable(Namespace.of(metadataNamespace));
}
}

Expand Down
38 changes: 31 additions & 7 deletions core/src/main/java/com/scalar/db/storage/jdbc/JdbcAdmin.java
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ public void createTable(
String namespace, String table, TableMetadata metadata, Map<String, String> options)
throws ExecutionException {
try (Connection connection = dataSource.getConnection()) {
createNamespacesTableIfNotExists(connection);
createTableInternal(connection, namespace, table, metadata, false);
addTableMetadata(connection, namespace, table, metadata, true, false);
} catch (SQLException e) {
Expand Down Expand Up @@ -340,6 +341,7 @@ public void dropTable(String namespace, String table) throws ExecutionException
try (Connection connection = dataSource.getConnection()) {
dropTableInternal(connection, namespace, table);
deleteTableMetadata(connection, namespace, table);
deleteNamespacesTableAndMetadataSchemaIfEmpty(connection);
} catch (SQLException e) {
throw new ExecutionException(
"Dropping the " + getFullTableName(namespace, table) + " table failed", e);
Expand Down Expand Up @@ -409,7 +411,7 @@ public void dropNamespace(String namespace) throws ExecutionException {
try (Connection connection = dataSource.getConnection()) {
execute(connection, rdbEngine.dropNamespaceSql(namespace));
deleteFromNamespacesTable(connection, namespace);
deleteNamespacesTableIfEmpty(connection);
deleteNamespacesTableAndMetadataSchemaIfEmpty(connection);
} catch (SQLException e) {
rdbEngine.dropNamespaceTranslateSQLException(e, namespace);
}
Expand Down Expand Up @@ -1008,20 +1010,22 @@ private void deleteFromNamespacesTable(Connection connection, String namespaceNa
}
}

private void deleteNamespacesTableIfEmpty(Connection connection) throws SQLException {
if (isNamespacesTableEmpty(connection)) {
private void deleteNamespacesTableAndMetadataSchemaIfEmpty(Connection connection)
throws SQLException {
if (areNamespacesTableAndMetadataSchemaEmpty(connection)) {
deleteTable(connection, encloseFullTableName(metadataSchema, NAMESPACES_TABLE));
deleteMetadataSchema(connection);
}
}

private boolean isNamespacesTableEmpty(Connection connection) throws SQLException {
private boolean areNamespacesTableAndMetadataSchemaEmpty(Connection connection)
throws SQLException {
String selectAllTables =
"SELECT * FROM " + encloseFullTableName(metadataSchema, NAMESPACES_TABLE);

Set<String> namespaces = new HashSet<>();
try (PreparedStatement preparedStatement = connection.prepareStatement(selectAllTables);
ResultSet results = preparedStatement.executeQuery()) {
try (Statement statement = connection.createStatement();
ResultSet results = statement.executeQuery(selectAllTables)) {
int count = 0;
while (results.next()) {
namespaces.add(results.getString(NAMESPACE_COL_NAMESPACE_NAME));
Expand All @@ -1032,7 +1036,27 @@ private boolean isNamespacesTableEmpty(Connection connection) throws SQLExceptio
}
}

return namespaces.isEmpty() || (namespaces.size() == 1 && namespaces.contains(metadataSchema));
boolean onlyMetadataNamespaceLeft =
namespaces.size() == 1 && namespaces.contains(metadataSchema);
if (!onlyMetadataNamespaceLeft) {
return false;
}

// Check if the metadata table exists. If it does not, the metadata schema is empty.
String sql =
rdbEngine.tableExistsInternalTableCheckSql(
encloseFullTableName(metadataSchema, METADATA_TABLE));
try {
execute(connection, sql);
return false;
} catch (SQLException e) {
// An exception will be thrown if the table does not exist when executing the select
// query
if (rdbEngine.isUndefinedTableError(e)) {
return true;
}
throw e;
}
}

@Override
Expand Down
Loading

0 comments on commit c83c95d

Please sign in to comment.