Repository: aurora
Updated Branches:
refs/heads/master 42a497438 -> 076d9177b
Ensure enum tables are complete after a snapshot restore.
In our in memory database, we model enums as two column tables. The two columns
would be `id` which corresponds to the integer value in the thrift enum and
`name` which is the all caps string name of the enum. For example to model the
`JobUpdateStatus` enum we have a table called `job_update_statuses`. In there
the `ROLLING_FORWARD` enum is modeled as a row `(0, "ROLLING_FORWARD")`. Other
tables reference the enum table via the id.
When we prepare storage on startup the `DbStorage` starts up. It does two
things:
1. Load in the schema.
2. Populate the enum tables.
This ensures that when we insert values into the database, the enum refernces
will be valid.
However, before we restore from a Snapshot with the `dbScript` field, we blow
all of that data away and restore what was in the snapshot:
````
try (Connection c = ((DataSource)
store.getUnsafeStoreAccess()).getConnection()) {
LOG.info("Dropping all tables");
try (PreparedStatement drop = c.prepareStatement("DROP ALL OBJECTS"))
drop.executeUpdate();
}
````
This means that if we add a new enum value, and then restore from a snapshot,
that enum value will not exist in the table any more. We could address this by
saying that every enum value addition requires a migration. However instead I
propose not blowing away the work done by `DbStorage` instead and re-hydrating
the enum tables.
To do this I extracted the logic into a new class `EnumBackfill`. Restoring from
a snapshot calls this after the migrations are done. The underlying SQL was
changed from `INSERT` to `MERGE` to make this work.
Testing Done:
existing tests and e2e tests
I also added a new enum value to `JobUpdateStatus` and observed it was correctly
loaded in.
Bugs closed: AURORA-1912
Reviewed at https://reviews.apache.org/r/58036/
Project: http://git-wip-us.apache.org/repos/asf/aurora/repo
Commit: http://git-wip-us.apache.org/repos/asf/aurora/commit/076d9177
Tree: http://git-wip-us.apache.org/repos/asf/aurora/tree/076d9177
Diff: http://git-wip-us.apache.org/repos/asf/aurora/diff/076d9177
Branch: refs/heads/master
Commit: 076d9177b972e10f9b2c229b21a8e195b9e9f0bb
Parents: 42a4974
Author: Zameer Manji <[email protected]>
Authored: Thu Mar 30 11:38:36 2017 -0700
Committer: Zameer Manji <[email protected]>
Committed: Thu Mar 30 11:38:36 2017 -0700
----------------------------------------------------------------------
.../storage/backup/TemporaryStorage.java | 8 ++-
.../aurora/scheduler/storage/db/DbModule.java | 4 ++
.../aurora/scheduler/storage/db/DbStorage.java | 41 ++---------
.../scheduler/storage/db/EnumBackfill.java | 75 ++++++++++++++++++++
.../storage/log/SnapshotStoreImpl.java | 10 ++-
.../scheduler/storage/db/EnumValueMapper.xml | 2 +-
.../scheduler/storage/backup/RecoveryTest.java | 5 +-
.../scheduler/storage/db/DbStorageTest.java | 2 +-
.../storage/log/SnapshotStoreImplIT.java | 20 ++++--
9 files changed, 117 insertions(+), 50 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/aurora/blob/076d9177/src/main/java/org/apache/aurora/scheduler/storage/backup/TemporaryStorage.java
----------------------------------------------------------------------
diff --git
a/src/main/java/org/apache/aurora/scheduler/storage/backup/TemporaryStorage.java
b/src/main/java/org/apache/aurora/scheduler/storage/backup/TemporaryStorage.java
index 36a1bd5..bad05f5 100644
---
a/src/main/java/org/apache/aurora/scheduler/storage/backup/TemporaryStorage.java
+++
b/src/main/java/org/apache/aurora/scheduler/storage/backup/TemporaryStorage.java
@@ -29,6 +29,7 @@ import org.apache.aurora.scheduler.storage.SnapshotStore;
import org.apache.aurora.scheduler.storage.Storage;
import org.apache.aurora.scheduler.storage.Storage.MutateWork.NoResult;
import org.apache.aurora.scheduler.storage.db.DbUtil;
+import org.apache.aurora.scheduler.storage.db.EnumBackfill;
import org.apache.aurora.scheduler.storage.entities.IScheduledTask;
import org.apache.aurora.scheduler.storage.log.SnapshotStoreImpl;
import org.apache.aurora.scheduler.storage.log.ThriftBackfill;
@@ -71,10 +72,12 @@ interface TemporaryStorage {
class TemporaryStorageFactory implements Function<Snapshot,
TemporaryStorage> {
private final ThriftBackfill thriftBackfill;
+ private final EnumBackfill enumBackfill;
@Inject
- TemporaryStorageFactory(ThriftBackfill thriftBackfill) {
+ TemporaryStorageFactory(ThriftBackfill thriftBackfill, EnumBackfill
enumBackfill) {
this.thriftBackfill = requireNonNull(thriftBackfill);
+ this.enumBackfill = requireNonNull(enumBackfill);
}
@Override
@@ -96,7 +99,8 @@ interface TemporaryStorage {
// We can just pass an empty lambda for the MigrationManager as
migration is a no-op
// when restoring from backup.
() -> { } /** migrationManager */,
- thriftBackfill);
+ thriftBackfill,
+ enumBackfill);
snapshotStore.applySnapshot(snapshot);
return new TemporaryStorage() {
http://git-wip-us.apache.org/repos/asf/aurora/blob/076d9177/src/main/java/org/apache/aurora/scheduler/storage/db/DbModule.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/storage/db/DbModule.java
b/src/main/java/org/apache/aurora/scheduler/storage/db/DbModule.java
index d1a1964..0a2516e 100644
--- a/src/main/java/org/apache/aurora/scheduler/storage/db/DbModule.java
+++ b/src/main/java/org/apache/aurora/scheduler/storage/db/DbModule.java
@@ -315,6 +315,10 @@ public final class DbModule extends PrivateModule {
bind(DbStorage.class).in(Singleton.class);
expose(storageKey);
+ bind(EnumBackfill.class).to(EnumBackfill.EnumBackfillImpl.class);
+ bind(EnumBackfill.EnumBackfillImpl.class).in(Singleton.class);
+ expose(EnumBackfill.class);
+
expose(DbStorage.class);
expose(SqlSessionFactory.class);
expose(TaskMapper.class);
http://git-wip-us.apache.org/repos/asf/aurora/blob/076d9177/src/main/java/org/apache/aurora/scheduler/storage/db/DbStorage.java
----------------------------------------------------------------------
diff --git
a/src/main/java/org/apache/aurora/scheduler/storage/db/DbStorage.java
b/src/main/java/org/apache/aurora/scheduler/storage/db/DbStorage.java
index 923e904..7904e38 100644
--- a/src/main/java/org/apache/aurora/scheduler/storage/db/DbStorage.java
+++ b/src/main/java/org/apache/aurora/scheduler/storage/db/DbStorage.java
@@ -26,16 +26,9 @@ import com.google.inject.Inject;
import org.apache.aurora.common.inject.TimedInterceptor.Timed;
import org.apache.aurora.common.stats.StatsProvider;
-import org.apache.aurora.gen.CronCollisionPolicy;
-import org.apache.aurora.gen.JobUpdateAction;
-import org.apache.aurora.gen.JobUpdateStatus;
-import org.apache.aurora.gen.MaintenanceMode;
-import org.apache.aurora.gen.Mode;
-import org.apache.aurora.gen.ScheduleStatus;
import org.apache.aurora.scheduler.async.AsyncModule.AsyncExecutor;
import org.apache.aurora.scheduler.async.GatedWorkQueue;
import org.apache.aurora.scheduler.async.GatedWorkQueue.GatedOperation;
-import org.apache.aurora.scheduler.resources.ResourceType;
import org.apache.aurora.scheduler.storage.AttributeStore;
import org.apache.aurora.scheduler.storage.CronJobStore;
import org.apache.aurora.scheduler.storage.JobUpdateStore;
@@ -67,15 +60,15 @@ import static
org.apache.ibatis.mapping.SqlCommandType.UPDATE;
class DbStorage extends AbstractIdleService implements Storage {
private final SqlSessionFactory sessionFactory;
+ private final EnumBackfill enumBackfill;
private final MutableStoreProvider storeProvider;
- private final EnumValueMapper enumValueMapper;
private final GatedWorkQueue gatedWorkQueue;
private final StatsProvider statsProvider;
@Inject
DbStorage(
SqlSessionFactory sessionFactory,
- EnumValueMapper enumValueMapper,
+ EnumBackfill enumBackfill,
@AsyncExecutor GatedWorkQueue gatedWorkQueue,
final CronJobStore.Mutable cronJobStore,
final TaskStore.Mutable taskStore,
@@ -87,7 +80,7 @@ class DbStorage extends AbstractIdleService implements
Storage {
StatsProvider statsProvider) {
this.sessionFactory = requireNonNull(sessionFactory);
- this.enumValueMapper = requireNonNull(enumValueMapper);
+ this.enumBackfill = requireNonNull(enumBackfill);
this.gatedWorkQueue = requireNonNull(gatedWorkQueue);
requireNonNull(cronJobStore);
requireNonNull(taskStore);
@@ -216,33 +209,7 @@ class DbStorage extends AbstractIdleService implements
Storage {
session.update(createStatementName);
}
- for (CronCollisionPolicy policy : CronCollisionPolicy.values()) {
- enumValueMapper.addEnumValue("cron_policies", policy.getValue(),
policy.name());
- }
-
- for (MaintenanceMode mode : MaintenanceMode.values()) {
- enumValueMapper.addEnumValue("maintenance_modes", mode.getValue(),
mode.name());
- }
-
- for (JobUpdateStatus status : JobUpdateStatus.values()) {
- enumValueMapper.addEnumValue("job_update_statuses", status.getValue(),
status.name());
- }
-
- for (JobUpdateAction action : JobUpdateAction.values()) {
- enumValueMapper.addEnumValue("job_instance_update_actions",
action.getValue(), action.name());
- }
-
- for (ScheduleStatus status : ScheduleStatus.values()) {
- enumValueMapper.addEnumValue("task_states", status.getValue(),
status.name());
- }
-
- for (ResourceType resourceType : ResourceType.values()) {
- enumValueMapper.addEnumValue("resource_types", resourceType.getValue(),
resourceType.name());
- }
-
- for (Mode mode : Mode.values()) {
- enumValueMapper.addEnumValue("volume_modes", mode.getValue(),
mode.name());
- }
+ enumBackfill.backfill();
createPoolMetrics();
}
http://git-wip-us.apache.org/repos/asf/aurora/blob/076d9177/src/main/java/org/apache/aurora/scheduler/storage/db/EnumBackfill.java
----------------------------------------------------------------------
diff --git
a/src/main/java/org/apache/aurora/scheduler/storage/db/EnumBackfill.java
b/src/main/java/org/apache/aurora/scheduler/storage/db/EnumBackfill.java
new file mode 100644
index 0000000..b4731c9
--- /dev/null
+++ b/src/main/java/org/apache/aurora/scheduler/storage/db/EnumBackfill.java
@@ -0,0 +1,75 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.aurora.scheduler.storage.db;
+
+import javax.inject.Inject;
+
+import org.apache.aurora.gen.CronCollisionPolicy;
+import org.apache.aurora.gen.JobUpdateAction;
+import org.apache.aurora.gen.JobUpdateStatus;
+import org.apache.aurora.gen.MaintenanceMode;
+import org.apache.aurora.gen.Mode;
+import org.apache.aurora.gen.ScheduleStatus;
+
+import org.apache.aurora.scheduler.resources.ResourceType;
+
+import static java.util.Objects.requireNonNull;
+
+public interface EnumBackfill {
+ /**
+ * Hydrates all of the enum tables in the database.
+ */
+ void backfill();
+
+ class EnumBackfillImpl implements EnumBackfill {
+
+ private final EnumValueMapper enumValueMapper;
+
+ @Inject
+ public EnumBackfillImpl(EnumValueMapper mapper) {
+ this.enumValueMapper = requireNonNull(mapper);
+ }
+
+ @Override
+ public void backfill() {
+ for (CronCollisionPolicy policy : CronCollisionPolicy.values()) {
+ enumValueMapper.addEnumValue("cron_policies", policy.getValue(),
policy.name());
+ }
+
+ for (MaintenanceMode mode : MaintenanceMode.values()) {
+ enumValueMapper.addEnumValue("maintenance_modes", mode.getValue(),
mode.name());
+ }
+
+ for (JobUpdateStatus status : JobUpdateStatus.values()) {
+ enumValueMapper.addEnumValue("job_update_statuses", status.getValue(),
status.name());
+ }
+
+ for (JobUpdateAction jua : JobUpdateAction.values()) {
+ enumValueMapper.addEnumValue("job_instance_update_actions",
jua.getValue(), jua.name());
+ }
+
+ for (ScheduleStatus status : ScheduleStatus.values()) {
+ enumValueMapper.addEnumValue("task_states", status.getValue(),
status.name());
+ }
+
+ for (ResourceType type : ResourceType.values()) {
+ enumValueMapper.addEnumValue("resource_types", type.getValue(),
type.name());
+ }
+
+ for (Mode mode : Mode.values()) {
+ enumValueMapper.addEnumValue("volume_modes", mode.getValue(),
mode.name());
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/076d9177/src/main/java/org/apache/aurora/scheduler/storage/log/SnapshotStoreImpl.java
----------------------------------------------------------------------
diff --git
a/src/main/java/org/apache/aurora/scheduler/storage/log/SnapshotStoreImpl.java
b/src/main/java/org/apache/aurora/scheduler/storage/log/SnapshotStoreImpl.java
index 81a8cca..9d2164f 100644
---
a/src/main/java/org/apache/aurora/scheduler/storage/log/SnapshotStoreImpl.java
+++
b/src/main/java/org/apache/aurora/scheduler/storage/log/SnapshotStoreImpl.java
@@ -62,6 +62,7 @@ import org.apache.aurora.scheduler.storage.Storage;
import org.apache.aurora.scheduler.storage.Storage.MutableStoreProvider;
import org.apache.aurora.scheduler.storage.Storage.MutateWork.NoResult;
import org.apache.aurora.scheduler.storage.Storage.Volatile;
+import org.apache.aurora.scheduler.storage.db.EnumBackfill;
import org.apache.aurora.scheduler.storage.db.MigrationManager;
import org.apache.aurora.scheduler.storage.entities.IHostAttributes;
import org.apache.aurora.scheduler.storage.entities.IJobConfiguration;
@@ -188,6 +189,10 @@ public class SnapshotStoreImpl implements
SnapshotStore<Snapshot> {
} catch (SQLException e) {
throw new RuntimeException(e);
}
+
+ // This ensures any subsequently added enum values since the last
snapshot exist in
+ // the db.
+ enumBackfill.backfill();
}
}
},
@@ -436,6 +441,7 @@ public class SnapshotStoreImpl implements
SnapshotStore<Snapshot> {
private final Set<String> hydrateSnapshotFields;
private final MigrationManager migrationManager;
private final ThriftBackfill thriftBackfill;
+ private final EnumBackfill enumBackfill;
/**
* Identifies if experimental task store is in use.
@@ -461,7 +467,8 @@ public class SnapshotStoreImpl implements
SnapshotStore<Snapshot> {
@ExperimentalTaskStore boolean useDbSnapshotForTaskStore,
@HydrateSnapshotFields Set<String> hydrateSnapshotFields,
MigrationManager migrationManager,
- ThriftBackfill thriftBackfill) {
+ ThriftBackfill thriftBackfill,
+ EnumBackfill enumBackfill) {
this.buildInfo = requireNonNull(buildInfo);
this.clock = requireNonNull(clock);
@@ -470,6 +477,7 @@ public class SnapshotStoreImpl implements
SnapshotStore<Snapshot> {
this.hydrateSnapshotFields = requireNonNull(hydrateSnapshotFields);
this.migrationManager = requireNonNull(migrationManager);
this.thriftBackfill = requireNonNull(thriftBackfill);
+ this.enumBackfill = requireNonNull(enumBackfill);
}
@Timed("snapshot_create")
http://git-wip-us.apache.org/repos/asf/aurora/blob/076d9177/src/main/resources/org/apache/aurora/scheduler/storage/db/EnumValueMapper.xml
----------------------------------------------------------------------
diff --git
a/src/main/resources/org/apache/aurora/scheduler/storage/db/EnumValueMapper.xml
b/src/main/resources/org/apache/aurora/scheduler/storage/db/EnumValueMapper.xml
index 153fd26..b1f9672 100644
---
a/src/main/resources/org/apache/aurora/scheduler/storage/db/EnumValueMapper.xml
+++
b/src/main/resources/org/apache/aurora/scheduler/storage/db/EnumValueMapper.xml
@@ -4,7 +4,7 @@
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.apache.aurora.scheduler.storage.db.EnumValueMapper">
<insert id="addEnumValue">
- INSERT INTO ${table} (
+ MERGE INTO ${table} (
id,
name
) VALUES (
http://git-wip-us.apache.org/repos/asf/aurora/blob/076d9177/src/test/java/org/apache/aurora/scheduler/storage/backup/RecoveryTest.java
----------------------------------------------------------------------
diff --git
a/src/test/java/org/apache/aurora/scheduler/storage/backup/RecoveryTest.java
b/src/test/java/org/apache/aurora/scheduler/storage/backup/RecoveryTest.java
index 42615da..04ec771 100644
--- a/src/test/java/org/apache/aurora/scheduler/storage/backup/RecoveryTest.java
+++ b/src/test/java/org/apache/aurora/scheduler/storage/backup/RecoveryTest.java
@@ -41,6 +41,7 @@ import
org.apache.aurora.scheduler.storage.backup.Recovery.RecoveryImpl;
import
org.apache.aurora.scheduler.storage.backup.StorageBackup.StorageBackupImpl;
import
org.apache.aurora.scheduler.storage.backup.StorageBackup.StorageBackupImpl.BackupConfig;
import
org.apache.aurora.scheduler.storage.backup.TemporaryStorage.TemporaryStorageFactory;
+import org.apache.aurora.scheduler.storage.db.EnumBackfill;
import org.apache.aurora.scheduler.storage.entities.IScheduledTask;
import org.apache.aurora.scheduler.testing.FakeScheduledExecutor;
import org.easymock.Capture;
@@ -81,7 +82,9 @@ public class RecoveryTest extends EasyMockTest {
shutDownNow = createMock(Command.class);
ScheduledExecutorService executor =
createMock(ScheduledExecutorService.class);
clock = FakeScheduledExecutor.scheduleExecutor(executor);
- TemporaryStorageFactory factory = new
TemporaryStorageFactory(TaskTestUtil.THRIFT_BACKFILL);
+ final EnumBackfill enumBackfill = createMock(EnumBackfill.class);
+ TemporaryStorageFactory factory =
+ new TemporaryStorageFactory(TaskTestUtil.THRIFT_BACKFILL,
enumBackfill);
storageBackup = new StorageBackupImpl(
snapshotStore,
clock,
http://git-wip-us.apache.org/repos/asf/aurora/blob/076d9177/src/test/java/org/apache/aurora/scheduler/storage/db/DbStorageTest.java
----------------------------------------------------------------------
diff --git
a/src/test/java/org/apache/aurora/scheduler/storage/db/DbStorageTest.java
b/src/test/java/org/apache/aurora/scheduler/storage/db/DbStorageTest.java
index f26529c..2229e4e 100644
--- a/src/test/java/org/apache/aurora/scheduler/storage/db/DbStorageTest.java
+++ b/src/test/java/org/apache/aurora/scheduler/storage/db/DbStorageTest.java
@@ -51,7 +51,7 @@ public class DbStorageTest extends EasyMockTest {
storage = new DbStorage(
createMock(SqlSessionFactory.class),
- createMock(EnumValueMapper.class),
+ createMock(EnumBackfill.class),
gatedWorkQueue,
createMock(CronJobStore.Mutable.class),
createMock(TaskStore.Mutable.class),
http://git-wip-us.apache.org/repos/asf/aurora/blob/076d9177/src/test/java/org/apache/aurora/scheduler/storage/log/SnapshotStoreImplIT.java
----------------------------------------------------------------------
diff --git
a/src/test/java/org/apache/aurora/scheduler/storage/log/SnapshotStoreImplIT.java
b/src/test/java/org/apache/aurora/scheduler/storage/log/SnapshotStoreImplIT.java
index ca95256..44d594e 100644
---
a/src/test/java/org/apache/aurora/scheduler/storage/log/SnapshotStoreImplIT.java
+++
b/src/test/java/org/apache/aurora/scheduler/storage/log/SnapshotStoreImplIT.java
@@ -20,6 +20,7 @@ import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+import com.google.inject.Injector;
import org.apache.aurora.common.stats.Stats;
import org.apache.aurora.common.util.testing.FakeBuildInfo;
@@ -56,6 +57,7 @@ import org.apache.aurora.scheduler.base.TaskTestUtil;
import org.apache.aurora.scheduler.resources.ResourceBag;
import org.apache.aurora.scheduler.storage.SnapshotStore;
import org.apache.aurora.scheduler.storage.Storage;
+import org.apache.aurora.scheduler.storage.db.EnumBackfill;
import org.apache.aurora.scheduler.storage.db.MigrationManager;
import org.apache.aurora.scheduler.storage.entities.IHostAttributes;
import org.apache.aurora.scheduler.storage.entities.IJobConfiguration;
@@ -74,7 +76,6 @@ import static
org.apache.aurora.common.util.testing.FakeBuildInfo.generateBuildI
import static
org.apache.aurora.scheduler.resources.ResourceManager.aggregateFromBag;
import static org.apache.aurora.scheduler.storage.Storage.MutateWork.NoResult;
import static
org.apache.aurora.scheduler.storage.db.DbModule.testModuleWithWorkQueue;
-import static org.apache.aurora.scheduler.storage.db.DbUtil.createStorage;
import static
org.apache.aurora.scheduler.storage.db.DbUtil.createStorageInjector;
import static
org.apache.aurora.scheduler.storage.log.SnapshotStoreImpl.ALL_H2_STORE_FIELDS;
import static
org.apache.aurora.scheduler.storage.log.SnapshotStoreImpl.SNAPSHOT_RESTORE;
@@ -94,11 +95,15 @@ public class SnapshotStoreImplIT {
private SnapshotStore<Snapshot> snapshotStore;
private void setUpStore(boolean dbTaskStore, Set<String> hydrateFields) {
- storage = dbTaskStore
- ? createStorage()
- : createStorageInjector(
- testModuleWithWorkQueue(PLAIN, Optional.of(new
InMemStoresModule(PLAIN))))
- .getInstance(Storage.class);
+ Injector injector;
+ if (dbTaskStore) {
+ injector = createStorageInjector(testModuleWithWorkQueue());
+ } else {
+ injector = createStorageInjector(
+ testModuleWithWorkQueue(PLAIN, Optional.of(new
InMemStoresModule(PLAIN))));
+ }
+
+ storage = injector.getInstance(Storage.class);
FakeClock clock = new FakeClock();
clock.setNowMillis(NOW);
@@ -109,7 +114,8 @@ public class SnapshotStoreImplIT {
dbTaskStore,
hydrateFields,
createStorageInjector(testModuleWithWorkQueue()).getInstance(MigrationManager.class),
- TaskTestUtil.THRIFT_BACKFILL);
+ TaskTestUtil.THRIFT_BACKFILL,
+ injector.getInstance(EnumBackfill.class));
Stats.flush();
}