Repository: aurora Updated Branches: refs/heads/master 07ab0bbf4 -> 209ab932e
Implementing benchmark for snapshot restore. Bugs closed: AURORA-1318, AURORA-1321 Reviewed at https://reviews.apache.org/r/34301/ Project: http://git-wip-us.apache.org/repos/asf/aurora/repo Commit: http://git-wip-us.apache.org/repos/asf/aurora/commit/209ab932 Tree: http://git-wip-us.apache.org/repos/asf/aurora/tree/209ab932 Diff: http://git-wip-us.apache.org/repos/asf/aurora/diff/209ab932 Branch: refs/heads/master Commit: 209ab932e791015245aebd1d0c8723f161f3e4b3 Parents: 07ab0bb Author: Maxim Khutornenko <[email protected]> Authored: Mon May 18 12:35:19 2015 -0700 Committer: Maxim Khutornenko <[email protected]> Committed: Mon May 18 12:35:19 2015 -0700 ---------------------------------------------------------------------- .../org/apache/aurora/benchmark/JobUpdates.java | 119 +++++++++++++++++ .../aurora/benchmark/SnapshotBenchmarks.java | 130 +++++++++++++++++++ .../aurora/benchmark/UpdateStoreBenchmarks.java | 103 +++++---------- .../aurora/scheduler/storage/db/DbModule.java | 21 ++- 4 files changed, 301 insertions(+), 72 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/aurora/blob/209ab932/src/jmh/java/org/apache/aurora/benchmark/JobUpdates.java ---------------------------------------------------------------------- diff --git a/src/jmh/java/org/apache/aurora/benchmark/JobUpdates.java b/src/jmh/java/org/apache/aurora/benchmark/JobUpdates.java new file mode 100644 index 0000000..3c6d4c9 --- /dev/null +++ b/src/jmh/java/org/apache/aurora/benchmark/JobUpdates.java @@ -0,0 +1,119 @@ +/** + * 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.benchmark; + +import java.util.Arrays; +import java.util.Set; +import java.util.UUID; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; + +import org.apache.aurora.gen.ExecutorConfig; +import org.apache.aurora.gen.InstanceTaskConfig; +import org.apache.aurora.gen.JobInstanceUpdateEvent; +import org.apache.aurora.gen.JobKey; +import org.apache.aurora.gen.JobUpdate; +import org.apache.aurora.gen.JobUpdateAction; +import org.apache.aurora.gen.JobUpdateDetails; +import org.apache.aurora.gen.JobUpdateEvent; +import org.apache.aurora.gen.JobUpdateInstructions; +import org.apache.aurora.gen.JobUpdateKey; +import org.apache.aurora.gen.JobUpdateSettings; +import org.apache.aurora.gen.JobUpdateStatus; +import org.apache.aurora.gen.JobUpdateSummary; +import org.apache.aurora.gen.Range; +import org.apache.aurora.gen.TaskConfig; +import org.apache.aurora.scheduler.storage.entities.IJobUpdateDetails; + +/** + * Job update factory. + */ +final class JobUpdates { + + static final class Builder { + private static final String USER = "user"; + private int numEvents = 1; + private int numInstanceEvents = 5000; + + Builder setNumEvents(int newCount) { + numEvents = newCount; + return this; + } + + Builder setNumInstanceEvents(int newCount) { + numInstanceEvents = newCount; + return this; + } + + Set<IJobUpdateDetails> build(int count) { + ImmutableSet.Builder<IJobUpdateDetails> result = ImmutableSet.builder(); + for (int i = 0; i < count; i++) { + JobKey job = new JobKey("role", "env", UUID.randomUUID().toString()); + JobUpdateKey key = new JobUpdateKey().setJob(job).setId(UUID.randomUUID().toString()); + + TaskConfig task = new TaskConfig() + .setJob(job) + .setExecutorConfig(new ExecutorConfig("cfg", string(10000))); + + JobUpdate update = new JobUpdate() + .setSummary(new JobUpdateSummary() + .setKey(key) + .setUpdateId(key.getId()) + .setJobKey(job) + .setUser(USER)) + .setInstructions(new JobUpdateInstructions() + .setSettings(new JobUpdateSettings() + .setUpdateGroupSize(100) + .setMaxFailedInstances(1) + .setMaxPerInstanceFailures(1) + .setMaxWaitToInstanceRunningMs(1) + .setMinWaitInInstanceRunningMs(1) + .setRollbackOnFailure(true) + .setWaitForBatchCompletion(false)) + .setInitialState(ImmutableSet.of(new InstanceTaskConfig() + .setTask(task) + .setInstances(ImmutableSet.of(new Range(0, 10))))) + .setDesiredState(new InstanceTaskConfig() + .setTask(task) + .setInstances(ImmutableSet.of(new Range(0, numInstanceEvents))))); + + ImmutableList.Builder<JobUpdateEvent> events = ImmutableList.builder(); + for (int j = 0; j < numEvents; j++) { + events.add(new JobUpdateEvent(JobUpdateStatus.ROLLING_FORWARD, j) + .setUser(USER) + .setMessage("message")); + } + + ImmutableList.Builder<JobInstanceUpdateEvent> instances = ImmutableList.builder(); + for (int k = 0; k < numInstanceEvents; k++) { + instances.add(new JobInstanceUpdateEvent(k, 0L, JobUpdateAction.INSTANCE_UPDATING)); + } + + result.add(IJobUpdateDetails.build(new JobUpdateDetails() + .setUpdate(update) + .setUpdateEvents(events.build()) + .setInstanceEvents(instances.build()))); + } + + return result.build(); + } + + private static String string(int numChars) { + char[] chars = new char[numChars]; + Arrays.fill(chars, 'a'); + return new String(chars); + } + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/209ab932/src/jmh/java/org/apache/aurora/benchmark/SnapshotBenchmarks.java ---------------------------------------------------------------------- diff --git a/src/jmh/java/org/apache/aurora/benchmark/SnapshotBenchmarks.java b/src/jmh/java/org/apache/aurora/benchmark/SnapshotBenchmarks.java new file mode 100644 index 0000000..03dd5d9 --- /dev/null +++ b/src/jmh/java/org/apache/aurora/benchmark/SnapshotBenchmarks.java @@ -0,0 +1,130 @@ +/** + * 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.benchmark; + +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import javax.inject.Singleton; + +import com.google.common.collect.ImmutableSet; +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Key; +import com.twitter.common.inject.Bindings; +import com.twitter.common.stats.StatsProvider; +import com.twitter.common.util.Clock; + +import org.apache.aurora.benchmark.fakes.FakeStatsProvider; +import org.apache.aurora.gen.Lock; +import org.apache.aurora.gen.LockKey; +import org.apache.aurora.gen.storage.Snapshot; +import org.apache.aurora.gen.storage.StoredJobUpdateDetails; +import org.apache.aurora.scheduler.storage.Storage; +import org.apache.aurora.scheduler.storage.db.DbModule; +import org.apache.aurora.scheduler.storage.entities.IJobUpdateDetails; +import org.apache.aurora.scheduler.storage.entities.IJobUpdateKey; +import org.apache.aurora.scheduler.storage.log.SnapshotStoreImpl; +import org.apache.thrift.TException; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; + +/** + * Performance benchmarks for snapshot related operations. + */ +public class SnapshotBenchmarks { + @BenchmarkMode(Mode.Throughput) + @OutputTimeUnit(TimeUnit.SECONDS) + @Warmup(iterations = 1, time = 10, timeUnit = TimeUnit.SECONDS) + @Measurement(iterations = 5, time = 10, timeUnit = TimeUnit.SECONDS) + @Fork(1) + @Threads(1) + @State(Scope.Thread) + public static class RestoreSnapshotWithUpdatesBenchmark { + private SnapshotStoreImpl snapshotStore; + private Snapshot snapshot; + + @Param({"1", "5", "10"}) + private int updateCount; + + @Setup(Level.Trial) + public void setUp() { + snapshotStore = getSnapshotStore(); + } + + @Setup(Level.Iteration) + public void setUpIteration() { + snapshot = createSnapshot(updateCount, 100, 10000); + } + + @Benchmark + public boolean run() throws TException { + snapshotStore.applySnapshot(snapshot); + // Return non-guessable result to satisfy "blackhole" requirement. + return System.currentTimeMillis() % 5 == 0; + } + } + + private static SnapshotStoreImpl getSnapshotStore() { + Bindings.KeyFactory keyFactory = Bindings.annotatedKeyFactory(Storage.Volatile.class); + Injector injector = Guice.createInjector( + new AbstractModule() { + @Override + protected void configure() { + bind(Clock.class).toInstance(Clock.SYSTEM_CLOCK); + bind(StatsProvider.class).toInstance(new FakeStatsProvider()); + bind(SnapshotStoreImpl.class).in(Singleton.class); + } + }, + DbModule.testModule(new DbModule.TaskStoreModule(keyFactory), keyFactory)); + + Storage storage = injector.getInstance(Key.get(Storage.class, Storage.Volatile.class)); + storage.prepare(); + return injector.getInstance(SnapshotStoreImpl.class); + } + + private static Snapshot createSnapshot(int updates, int events, int instanceEvents) { + Set<IJobUpdateDetails> updateDetails = new JobUpdates.Builder() + .setNumEvents(events) + .setNumInstanceEvents(instanceEvents) + .build(updates); + + ImmutableSet.Builder<Lock> lockBuilder = ImmutableSet.builder(); + ImmutableSet.Builder<StoredJobUpdateDetails> detailsBuilder = ImmutableSet.builder(); + for (IJobUpdateDetails details : updateDetails) { + IJobUpdateKey key = details.getUpdate().getSummary().getKey(); + String lockToken = UUID.randomUUID().toString(); + + lockBuilder.add(new Lock(LockKey.job(key.getJob().newBuilder()), lockToken, "user", 0L)); + detailsBuilder.add(new StoredJobUpdateDetails(details.newBuilder(), lockToken)); + } + + return new Snapshot() + .setLocks(lockBuilder.build()) + .setJobUpdateDetails(detailsBuilder.build()); + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/209ab932/src/jmh/java/org/apache/aurora/benchmark/UpdateStoreBenchmarks.java ---------------------------------------------------------------------- diff --git a/src/jmh/java/org/apache/aurora/benchmark/UpdateStoreBenchmarks.java b/src/jmh/java/org/apache/aurora/benchmark/UpdateStoreBenchmarks.java index e526929..f361caa 100644 --- a/src/jmh/java/org/apache/aurora/benchmark/UpdateStoreBenchmarks.java +++ b/src/jmh/java/org/apache/aurora/benchmark/UpdateStoreBenchmarks.java @@ -13,12 +13,13 @@ */ package org.apache.aurora.benchmark; -import java.util.Arrays; +import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; import com.google.common.base.Optional; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; @@ -26,27 +27,12 @@ import com.twitter.common.stats.StatsProvider; import com.twitter.common.util.Clock; import org.apache.aurora.benchmark.fakes.FakeStatsProvider; -import org.apache.aurora.gen.ExecutorConfig; -import org.apache.aurora.gen.InstanceTaskConfig; -import org.apache.aurora.gen.JobInstanceUpdateEvent; -import org.apache.aurora.gen.JobKey; -import org.apache.aurora.gen.JobUpdate; -import org.apache.aurora.gen.JobUpdateAction; -import org.apache.aurora.gen.JobUpdateEvent; -import org.apache.aurora.gen.JobUpdateInstructions; -import org.apache.aurora.gen.JobUpdateKey; -import org.apache.aurora.gen.JobUpdateSettings; -import org.apache.aurora.gen.JobUpdateStatus; -import org.apache.aurora.gen.JobUpdateSummary; import org.apache.aurora.gen.Lock; import org.apache.aurora.gen.LockKey; -import org.apache.aurora.gen.Range; -import org.apache.aurora.gen.TaskConfig; import org.apache.aurora.scheduler.storage.JobUpdateStore; import org.apache.aurora.scheduler.storage.Storage; import org.apache.aurora.scheduler.storage.db.DbModule; import org.apache.aurora.scheduler.storage.entities.IJobInstanceUpdateEvent; -import org.apache.aurora.scheduler.storage.entities.IJobUpdate; import org.apache.aurora.scheduler.storage.entities.IJobUpdateDetails; import org.apache.aurora.scheduler.storage.entities.IJobUpdateEvent; import org.apache.aurora.scheduler.storage.entities.IJobUpdateKey; @@ -63,6 +49,7 @@ import org.openjdk.jmh.annotations.Param; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; import org.openjdk.jmh.annotations.Warmup; public class UpdateStoreBenchmarks { @@ -76,7 +63,7 @@ public class UpdateStoreBenchmarks { public static class JobDetailsBenchmark { private static final String USER = "user"; private Storage storage; - private IJobUpdateKey key; + private Set<IJobUpdateKey> keys; @Param({"1000", "5000", "10000"}) private int instances; @@ -98,66 +85,45 @@ public class UpdateStoreBenchmarks { @Setup(Level.Iteration) public void setUpIteration() { - final JobKey job = new JobKey("role", "env", UUID.randomUUID().toString()); - key = IJobUpdateKey.build(new JobUpdateKey() - .setJob(job) - .setId(UUID.randomUUID().toString())); - - TaskConfig task = new TaskConfig() - .setJob(job) - .setExecutorConfig(new ExecutorConfig("cfg", string(10000))); - - final JobUpdate update = new JobUpdate() - .setSummary(new JobUpdateSummary() - .setKey(key.newBuilder()) - .setUpdateId(key.getId()) - .setJobKey(job) - .setUser(USER)) - .setInstructions(new JobUpdateInstructions() - .setSettings(new JobUpdateSettings() - .setUpdateGroupSize(100) - .setMaxFailedInstances(1) - .setMaxPerInstanceFailures(1) - .setMaxWaitToInstanceRunningMs(1) - .setMinWaitInInstanceRunningMs(1) - .setRollbackOnFailure(true) - .setWaitForBatchCompletion(false)) - .setInitialState(ImmutableSet.of(new InstanceTaskConfig() - .setTask(task) - .setInstances(ImmutableSet.of(new Range(0, 10))))) - .setDesiredState(new InstanceTaskConfig() - .setTask(task) - .setInstances(ImmutableSet.of(new Range(0, instances))))); - storage.write(new Storage.MutateWork.NoResult.Quiet() { @Override protected void execute(Storage.MutableStoreProvider storeProvider) { - String lockToken = UUID.randomUUID().toString(); - storeProvider.getLockStore().saveLock( - ILock.build(new Lock(LockKey.job(job), lockToken, USER, 0L))); - JobUpdateStore.Mutable updateStore = storeProvider.getJobUpdateStore(); - updateStore.saveJobUpdate(IJobUpdate.build(update), Optional.of(lockToken)); - updateStore.saveJobUpdateEvent( - key, - IJobUpdateEvent.build(new JobUpdateEvent(JobUpdateStatus.ROLLING_FORWARD, 0L) - .setUser(USER) - .setMessage("message"))); + Set<IJobUpdateDetails> updates = + new JobUpdates.Builder().setNumInstanceEvents(instances).build(1); + + ImmutableSet.Builder<IJobUpdateKey> keyBuilder = ImmutableSet.builder(); + for (IJobUpdateDetails details : updates) { + IJobUpdateKey key = details.getUpdate().getSummary().getKey(); + keyBuilder.add(key); + String lockToken = UUID.randomUUID().toString(); + storeProvider.getLockStore().saveLock( + ILock.build(new Lock(LockKey.job(key.getJob().newBuilder()), lockToken, USER, 0L))); + + updateStore.saveJobUpdate(details.getUpdate(), Optional.of(lockToken)); + + for (IJobUpdateEvent updateEvent : details.getUpdateEvents()) { + updateStore.saveJobUpdateEvent(key, updateEvent); + } - for (int i = 0; i < instances; i++) { - updateStore.saveJobInstanceUpdateEvent( - key, - IJobInstanceUpdateEvent.build( - new JobInstanceUpdateEvent(i, 0L, JobUpdateAction.INSTANCE_UPDATING))); + for (IJobInstanceUpdateEvent instanceEvent : details.getInstanceEvents()) { + updateStore.saveJobInstanceUpdateEvent(key, instanceEvent); + } } + keys = keyBuilder.build(); } }); } - private static String string(int numChars) { - char[] chars = new char[numChars]; - Arrays.fill(chars, 'a'); - return new String(chars); + @TearDown(Level.Iteration) + public void tearDownIteration() { + storage.write(new Storage.MutateWork.NoResult.Quiet() { + @Override + protected void execute(Storage.MutableStoreProvider storeProvider) { + storeProvider.getJobUpdateStore().deleteAllUpdatesAndEvents(); + storeProvider.getLockStore().deleteLocks(); + } + }); } @Benchmark @@ -165,7 +131,8 @@ public class UpdateStoreBenchmarks { return storage.read(new Storage.Work.Quiet<IJobUpdateDetails>() { @Override public IJobUpdateDetails apply(Storage.StoreProvider store) { - return store.getJobUpdateStore().fetchJobUpdateDetails(key).get(); + return store.getJobUpdateStore().fetchJobUpdateDetails( + Iterables.getOnlyElement(keys)).get(); } }); } http://git-wip-us.apache.org/repos/asf/aurora/blob/209ab932/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 e351588..436d384 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 @@ -109,21 +109,34 @@ public final class DbModule extends PrivateModule { /** * Creates a module that will prepare a private in-memory database, using a specific task store - * implementation bound within the provided module. + * implementation bound within the provided module and a key factory. * * @param taskStoreModule Module providing task store bindings. + * @param keyFactory Key factory to use. * @return A new database module for testing. */ @VisibleForTesting - public static Module testModule(Module taskStoreModule) { + public static Module testModule(Module taskStoreModule, KeyFactory keyFactory) { return new DbModule( - KeyFactory.PLAIN, + keyFactory, taskStoreModule, // A non-zero close delay is used here to avoid eager database cleanup in tests that // make use of multiple threads. Since all test databases are separately scoped by the // included UUID, multiple DB instances will overlap in time but they should be distinct // in content. - "testdb-" + UUID.randomUUID().toString() + ";DB_CLOSE_DELAY=5"); + "testdb-" + UUID.randomUUID().toString() + ";DB_CLOSE_DELAY=5;"); + } + + /** + * Creates a module that will prepare a private in-memory database using specific task store + * module. + * + * @param taskStoreModule Module providing task store bindings. + * @return A new database module for testing. + */ + @VisibleForTesting + public static Module testModule(Module taskStoreModule) { + return testModule(taskStoreModule, KeyFactory.PLAIN); } /**
