diff --git a/core/src/integration-test/java/com/scalar/db/storage/cassandra/CassandraSchemaLoaderIntegrationTest.java b/core/src/integration-test/java/com/scalar/db/storage/cassandra/CassandraSchemaLoaderIntegrationTest.java index dec9e87c28..ecb5ece0b0 100644 --- a/core/src/integration-test/java/com/scalar/db/storage/cassandra/CassandraSchemaLoaderIntegrationTest.java +++ b/core/src/integration-test/java/com/scalar/db/storage/cassandra/CassandraSchemaLoaderIntegrationTest.java @@ -46,4 +46,12 @@ protected void waitForCreationIfNecessary() { // one session to the other, so we need to wait Uninterruptibles.sleepUninterruptibly(2, TimeUnit.SECONDS); } + + @Override + protected List getCommandArgsForUpgrade(Path configFilePath) { + return ImmutableList.builder() + .addAll(super.getCommandArgsForUpgrade(configFilePath)) + .add("--replication-factor=1") + .build(); + } } diff --git a/core/src/integration-test/java/com/scalar/db/storage/dynamo/DynamoSchemaLoaderIntegrationTest.java b/core/src/integration-test/java/com/scalar/db/storage/dynamo/DynamoSchemaLoaderIntegrationTest.java index 82e08fe20f..81604c58bf 100644 --- a/core/src/integration-test/java/com/scalar/db/storage/dynamo/DynamoSchemaLoaderIntegrationTest.java +++ b/core/src/integration-test/java/com/scalar/db/storage/dynamo/DynamoSchemaLoaderIntegrationTest.java @@ -37,4 +37,12 @@ protected List getCommandArgsForReparation(Path configFilePath, Path sch .add("--no-backup") .build(); } + + @Override + protected List getCommandArgsForUpgrade(Path configFilePath) { + return ImmutableList.builder() + .addAll(super.getCommandArgsForUpgrade(configFilePath)) + .add("--no-backup") + .build(); + } } diff --git a/core/src/integration-test/java/com/scalar/db/storage/multistorage/MultiStorageSchemaLoaderIntegrationTest.java b/core/src/integration-test/java/com/scalar/db/storage/multistorage/MultiStorageSchemaLoaderIntegrationTest.java index 9725f39408..ed9c1190a1 100644 --- a/core/src/integration-test/java/com/scalar/db/storage/multistorage/MultiStorageSchemaLoaderIntegrationTest.java +++ b/core/src/integration-test/java/com/scalar/db/storage/multistorage/MultiStorageSchemaLoaderIntegrationTest.java @@ -79,6 +79,14 @@ protected List getCommandArgsForReparation(Path configFilePath, Path sch .build(); } + @Override + protected List getCommandArgsForUpgrade(Path configFilePath) { + return ImmutableList.builder() + .addAll(super.getCommandArgsForUpgrade(configFilePath)) + .add("--replication-factor=1") + .build(); + } + @Override protected void waitForCreationIfNecessary() { // In some of the tests, we modify metadata in one Cassandra cluster session (via the diff --git a/integration-test/src/main/java/com/scalar/db/schemaloader/SchemaLoaderIntegrationTestBase.java b/integration-test/src/main/java/com/scalar/db/schemaloader/SchemaLoaderIntegrationTestBase.java index b642b7b7ab..4297eedd01 100644 --- a/integration-test/src/main/java/com/scalar/db/schemaloader/SchemaLoaderIntegrationTestBase.java +++ b/integration-test/src/main/java/com/scalar/db/schemaloader/SchemaLoaderIntegrationTestBase.java @@ -261,6 +261,10 @@ protected List getCommandArgsForAlteration(Path configFilePath, Path sch .build(); } + protected List getCommandArgsForUpgrade(Path configFilePath) { + return ImmutableList.of("--config", configFilePath.toString(), "--upgrade"); + } + @AfterAll public void afterAll() throws Exception { dropTablesIfExist(); @@ -407,6 +411,25 @@ public void createTableThenAlterTables_ShouldExecuteProperly() throws Exception .isEqualTo(expectedTable2Metadata); } + @Test + public void createThenDropNamespacesTableThenUpgrade_ShouldCreateNamespacesTableCorrectly() + throws Exception { + // Arrange + int exitCodeCreation = + executeWithArgs( + getCommandArgsForCreationWithCoordinator(CONFIG_FILE_PATH, SCHEMA_FILE_PATH)); + assertThat(exitCodeCreation).isZero(); + adminTestUtils.dropNamespacesTable(); + + // Act + int exitCodeUpgrade = executeWithArgs(getCommandArgsForUpgrade(CONFIG_FILE_PATH)); + + // Assert + assertThat(exitCodeUpgrade).isZero(); + waitForCreationIfNecessary(); + assertThat(transactionAdmin.getNamespaceNames()).containsOnly(namespace1, namespace2); + } + private void createTables_ShouldCreateTablesWithCoordinator() throws Exception { // Act int exitCode = diff --git a/schema-loader/src/main/java/com/scalar/db/schemaloader/SchemaLoader.java b/schema-loader/src/main/java/com/scalar/db/schemaloader/SchemaLoader.java index 44331df621..92d8c9f777 100644 --- a/schema-loader/src/main/java/com/scalar/db/schemaloader/SchemaLoader.java +++ b/schema-loader/src/main/java/com/scalar/db/schemaloader/SchemaLoader.java @@ -575,6 +575,41 @@ private static void importTables(Either config, Either options) + throws SchemaLoaderException { + Either config = new Left<>(configPath); + upgrade(config, options); + } + + /** + * Upgrades the ScalarDB environment to support the latest version of the ScalarDB API. Typically, + * you will be requested, as indicated on the release notes, to run this method after updating the + * ScalarDB version of your application environment. + * + * @param configProperties ScalarDB config properties. + * @param options specific options for upgrading. + * @throws SchemaLoaderException thrown when upgrading failed. + */ + public static void upgrade(Properties configProperties, Map options) + throws SchemaLoaderException { + Either config = new Right<>(configProperties); + upgrade(config, options); + } + + private static void upgrade(Either config, Map options) + throws SchemaLoaderException { + getSchemaOperator(config).upgrade(options); + } + @VisibleForTesting static SchemaOperator getSchemaOperator(Either config) throws SchemaLoaderException { diff --git a/schema-loader/src/main/java/com/scalar/db/schemaloader/SchemaOperator.java b/schema-loader/src/main/java/com/scalar/db/schemaloader/SchemaOperator.java index 64f8fa4300..18edd681a8 100644 --- a/schema-loader/src/main/java/com/scalar/db/schemaloader/SchemaOperator.java +++ b/schema-loader/src/main/java/com/scalar/db/schemaloader/SchemaOperator.java @@ -426,6 +426,18 @@ public void importTables(List tableSchemaList) throws SchemaL } } + public void upgrade(Map options) throws SchemaLoaderException { + try { + // As of 4.0.0, upgrade() implementation is identical for the storage or transaction admin. + // This could change in the future but for now using either admin regardless of the schema is + // fine + transactionAdmin.get().upgrade(options); + logger.info("Upgrading the environment succeeded."); + } catch (ExecutionException e) { + throw new RuntimeException("Upgrading the environment failed", e); + } + } + @Override public void close() { if (storageAdminLoaded.get()) { diff --git a/schema-loader/src/main/java/com/scalar/db/schemaloader/command/SchemaLoaderCommand.java b/schema-loader/src/main/java/com/scalar/db/schemaloader/command/SchemaLoaderCommand.java index 9897b7b882..47a2316d91 100644 --- a/schema-loader/src/main/java/com/scalar/db/schemaloader/command/SchemaLoaderCommand.java +++ b/schema-loader/src/main/java/com/scalar/db/schemaloader/command/SchemaLoaderCommand.java @@ -96,6 +96,14 @@ private static class Mode { description = "Import tables : it will import existing non-ScalarDB tables to ScalarDB.", defaultValue = "false") boolean importTables; + + @Option( + names = {"--upgrade"}, + description = + "Upgrades the ScalarDB environment to support the latest version of the ScalarDB API. Typically, you will be requested, as indicated on the release notes, to run this command after" + + " updating the ScalarDB version of your application environment.", + defaultValue = "false") + boolean upgrade; } @Override @@ -113,6 +121,8 @@ public Integer call() throws Exception { alterTables(); } else if (mode.importTables) { importTables(); + } else if (mode.upgrade) { + upgrade(); } return 0; } @@ -155,6 +165,11 @@ private void importTables() throws SchemaLoaderException { SchemaLoader.importTables(configPath, schemaFile); } + private void upgrade() throws SchemaLoaderException { + Map options = prepareAllOptions(); + SchemaLoader.upgrade(configPath, options); + } + private Map prepareAllOptions() { return prepareOptions( CassandraAdmin.REPLICATION_STRATEGY, diff --git a/schema-loader/src/test/java/com/scalar/db/schemaloader/SchemaLoaderTest.java b/schema-loader/src/test/java/com/scalar/db/schemaloader/SchemaLoaderTest.java index 6ca0ea32ac..677e29be03 100644 --- a/schema-loader/src/test/java/com/scalar/db/schemaloader/SchemaLoaderTest.java +++ b/schema-loader/src/test/java/com/scalar/db/schemaloader/SchemaLoaderTest.java @@ -774,4 +774,26 @@ public void importTable_WithConfigFilePathAndSchemaFilePath_ShouldCallParserAndO verify(importSchemaParser).parse(); verify(operator).importTables(anyList()); } + + @Test + public void upgrade_WithConfigProperties_ShouldCallOperatorProperly() throws Exception { + // Arrange + + // Act + SchemaLoader.upgrade(configProperties, options); + + // Assert + verify(operator).upgrade(options); + } + + @Test + public void upgrade_WithConfigFilePath_ShouldCallOperatorProperly() throws Exception { + // Arrange + + // Act + SchemaLoader.upgrade(configFilePath, options); + + // Assert + verify(operator).upgrade(options); + } } diff --git a/schema-loader/src/test/java/com/scalar/db/schemaloader/SchemaOperatorTest.java b/schema-loader/src/test/java/com/scalar/db/schemaloader/SchemaOperatorTest.java index d2136c2ad9..11adcf4706 100644 --- a/schema-loader/src/test/java/com/scalar/db/schemaloader/SchemaOperatorTest.java +++ b/schema-loader/src/test/java/com/scalar/db/schemaloader/SchemaOperatorTest.java @@ -590,4 +590,14 @@ private TableSchema prepareTableSchemaMock( } return schema; } + + @Test + public void upgrade_ShouldCallTransactionAdminProperly() throws Exception { + // Act + operator.upgrade(options); + + // Assert + verify(transactionAdmin).upgrade(options); + verifyNoInteractions(storageAdmin); + } } diff --git a/schema-loader/src/test/java/com/scalar/db/schemaloader/command/SchemaLoaderCommandTest.java b/schema-loader/src/test/java/com/scalar/db/schemaloader/command/SchemaLoaderCommandTest.java index 288313d22f..85c94f05ab 100644 --- a/schema-loader/src/test/java/com/scalar/db/schemaloader/command/SchemaLoaderCommandTest.java +++ b/schema-loader/src/test/java/com/scalar/db/schemaloader/command/SchemaLoaderCommandTest.java @@ -388,4 +388,41 @@ public void call_ImportOptionGivenWithCoordinatorArgument_ShouldThrowIllegalArgu Assertions.assertThat(exitCode).isEqualTo(1); schemaLoaderMockedStatic.verifyNoInteractions(); } + + @Test + public void call_WithProperArgumentsForUpgrading_ShouldCallUpgradeProperly() { + // Arrange + String configFile = "path_to_config_file"; + Map options = + ImmutableMap.builder() + .put(CassandraAdmin.REPLICATION_STRATEGY, replicationStrategy) + .put(CassandraAdmin.COMPACTION_STRATEGY, compactionStrategy) + .put(CassandraAdmin.REPLICATION_FACTOR, replicationFactor) + .put(DynamoAdmin.REQUEST_UNIT, ru) + .put(DynamoAdmin.NO_SCALING, noScaling.toString()) + .put(DynamoAdmin.NO_BACKUP, noBackup.toString()) + .build(); + + // Act + commandLine.execute( + "--config", + configFile, + "--upgrade", + "--replication-strategy", + replicationStrategy, + "--compaction-strategy", + compactionStrategy, + "--replication-factor", + replicationFactor, + "--ru", + ru, + "--no-scaling", + "--no-backup", + "-f", + schemaFile, + "--coordinator"); + + // Assert + schemaLoaderMockedStatic.verify(() -> SchemaLoader.upgrade(Paths.get(configFile), options)); + } } diff --git a/server/src/integration-test/java/com/scalar/db/server/SchemaLoaderIntegrationTestWithServer.java b/server/src/integration-test/java/com/scalar/db/server/SchemaLoaderIntegrationTestWithServer.java index 0866a5394e..38fbdbb439 100644 --- a/server/src/integration-test/java/com/scalar/db/server/SchemaLoaderIntegrationTestWithServer.java +++ b/server/src/integration-test/java/com/scalar/db/server/SchemaLoaderIntegrationTestWithServer.java @@ -54,4 +54,12 @@ public void createTablesThenDropTablesThenRepairAllWithoutCoordinator_ShouldExec @Disabled("repairAll is not supported with ScalarDB Server") @Override public void createTablesThenDropTablesThenRepairAllWithCoordinator_ShouldExecuteProperly() {} + + @Test + @Disabled("upgrade is not supported with ScalarDB Server") + @Override + public void createThenDropNamespacesTableThenUpgrade_ShouldCreateNamespacesTableCorrectly() + throws Exception { + super.createThenDropNamespacesTableThenUpgrade_ShouldCreateNamespacesTableCorrectly(); + } }