aurora-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ma...@apache.org
Subject git commit: Finalizing DB mapper implementation.
Date Tue, 12 Aug 2014 23:05:37 GMT
Repository: incubator-aurora
Updated Branches:
  refs/heads/master 6c28e7ee8 -> fefd73068


Finalizing DB mapper implementation.

Bugs closed: AURORA-612

Reviewed at https://reviews.apache.org/r/24521/


Project: http://git-wip-us.apache.org/repos/asf/incubator-aurora/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-aurora/commit/fefd7306
Tree: http://git-wip-us.apache.org/repos/asf/incubator-aurora/tree/fefd7306
Diff: http://git-wip-us.apache.org/repos/asf/incubator-aurora/diff/fefd7306

Branch: refs/heads/master
Commit: fefd7306870b2a0e178f8f974bd1976c6958e23f
Parents: 6c28e7e
Author: Maxim Khutornenko <maxim@apache.org>
Authored: Tue Aug 12 16:01:52 2014 -0700
Committer: Maxim Khutornenko <maxim@apache.org>
Committed: Tue Aug 12 16:01:52 2014 -0700

----------------------------------------------------------------------
 .../scheduler/storage/ForwardingStore.java      |   7 +-
 .../scheduler/storage/JobUpdateStore.java       |  20 +-
 .../scheduler/storage/db/DBJobUpdateStore.java  |  48 ++-
 .../scheduler/storage/db/InsertResult.java      |  36 ++
 .../storage/db/JobUpdateDetailsMapper.java      |  66 +++-
 .../db/typehandlers/TaskConfigTypeHandler.java  |  73 ++++
 .../storage/db/typehandlers/TypeHandlers.java   |   3 +-
 .../scheduler/storage/db/AttributeMapper.xml    |   1 -
 .../storage/db/JobUpdateDetailsMapper.xml       | 235 ++++++++++--
 .../aurora/scheduler/storage/db/schema.sql      |  24 +-
 .../thrift/org/apache/aurora/gen/api.thrift     |  69 ++--
 .../storage/db/DBJobUpdateStoreTest.java        | 358 +++++++++++++++----
 .../storage/db/DbAttributeStoreTest.java        |   7 +-
 .../scheduler/storage/db/DbLockStoreTest.java   |   9 +-
 .../scheduler/storage/db/DbQuotaStoreTest.java  |   7 +-
 .../storage/db/DbSchedulerStoreTest.java        |   7 +-
 .../aurora/scheduler/storage/db/DbUtil.java     |  34 ++
 .../scheduler/storage/log/LogStorageTest.java   |   8 +-
 .../storage/log/SnapshotStoreImplTest.java      |   4 +-
 .../org/apache/aurora/gen/api.thrift.md5        |   2 +-
 20 files changed, 842 insertions(+), 176 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/fefd7306/src/main/java/org/apache/aurora/scheduler/storage/ForwardingStore.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/storage/ForwardingStore.java b/src/main/java/org/apache/aurora/scheduler/storage/ForwardingStore.java
index a9a325b..3f083d6 100644
--- a/src/main/java/org/apache/aurora/scheduler/storage/ForwardingStore.java
+++ b/src/main/java/org/apache/aurora/scheduler/storage/ForwardingStore.java
@@ -13,18 +13,19 @@
  */
 package org.apache.aurora.scheduler.storage;
 
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
 import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableSet;
 
-import org.apache.aurora.gen.JobUpdateQuery;
 import org.apache.aurora.scheduler.base.Query;
 import org.apache.aurora.scheduler.storage.entities.IHostAttributes;
 import org.apache.aurora.scheduler.storage.entities.IJobConfiguration;
 import org.apache.aurora.scheduler.storage.entities.IJobKey;
 import org.apache.aurora.scheduler.storage.entities.IJobUpdateDetails;
+import org.apache.aurora.scheduler.storage.entities.IJobUpdateQuery;
 import org.apache.aurora.scheduler.storage.entities.IJobUpdateSummary;
 import org.apache.aurora.scheduler.storage.entities.ILock;
 import org.apache.aurora.scheduler.storage.entities.ILockKey;
@@ -139,7 +140,7 @@ public class ForwardingStore implements
   }
 
   @Override
-  public ImmutableSet<IJobUpdateSummary> fetchJobUpdateSummaries(JobUpdateQuery query) {
+  public List<IJobUpdateSummary> fetchJobUpdateSummaries(IJobUpdateQuery query) {
     return jobUpdateStore.fetchJobUpdateSummaries(query);
   }
 
@@ -149,7 +150,7 @@ public class ForwardingStore implements
   }
 
   @Override
-  public ImmutableSet<IJobUpdateDetails> fetchAllJobUpdateDetails() {
+  public List<IJobUpdateDetails> fetchAllJobUpdateDetails() {
     return jobUpdateStore.fetchAllJobUpdateDetails();
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/fefd7306/src/main/java/org/apache/aurora/scheduler/storage/JobUpdateStore.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/storage/JobUpdateStore.java b/src/main/java/org/apache/aurora/scheduler/storage/JobUpdateStore.java
index 8085958..c05833f 100644
--- a/src/main/java/org/apache/aurora/scheduler/storage/JobUpdateStore.java
+++ b/src/main/java/org/apache/aurora/scheduler/storage/JobUpdateStore.java
@@ -13,14 +13,15 @@
  */
 package org.apache.aurora.scheduler.storage;
 
+import java.util.List;
+
 import com.google.common.base.Optional;
-import com.google.common.collect.ImmutableSet;
 
-import org.apache.aurora.gen.JobUpdateQuery;
 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.IJobUpdateQuery;
 import org.apache.aurora.scheduler.storage.entities.IJobUpdateSummary;
 
 /**
@@ -34,7 +35,7 @@ public interface JobUpdateStore {
    * @param query Query to identify job update summaries with.
    * @return A read-only view of job update summaries.
    */
-  ImmutableSet<IJobUpdateSummary> fetchJobUpdateSummaries(JobUpdateQuery query);
+  List<IJobUpdateSummary> fetchJobUpdateSummaries(IJobUpdateQuery query);
 
   /**
    * Fetches a read-only view of job update details.
@@ -49,13 +50,24 @@ public interface JobUpdateStore {
    *
    * @return A read-only view of all job update details.
    */
-  ImmutableSet<IJobUpdateDetails> fetchAllJobUpdateDetails();
+  List<IJobUpdateDetails> fetchAllJobUpdateDetails();
 
   interface Mutable extends JobUpdateStore {
 
     /**
      * Saves a new job update.
      *
+     * <p>
+     * Note: This call must be followed by the {@link #saveJobUpdateEvent(IJobUpdateEvent, String)}
+     * before fetching a saved update as it does not save the following required fields:
+     * <ul>
+     *   <li>{@link org.apache.aurora.gen.JobUpdateState#status}</li>
+     *   <li>{@link org.apache.aurora.gen.JobUpdateState#createdTimestampMs}</li>
+     *   <li>{@link org.apache.aurora.gen.JobUpdateState#lastModifiedTimestampMs}</li>
+     * </ul>
+     * The above fields are auto-populated from the update events and any attempt to fetch an update
+     * without having at least one {@link IJobUpdateEvent} present in the store will return empty.
+     *
      * @param update Update to save.
      */
     void saveJobUpdate(IJobUpdate update);

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/fefd7306/src/main/java/org/apache/aurora/scheduler/storage/db/DBJobUpdateStore.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/storage/db/DBJobUpdateStore.java b/src/main/java/org/apache/aurora/scheduler/storage/db/DBJobUpdateStore.java
index 87f428b..d659aa1 100644
--- a/src/main/java/org/apache/aurora/scheduler/storage/db/DBJobUpdateStore.java
+++ b/src/main/java/org/apache/aurora/scheduler/storage/db/DBJobUpdateStore.java
@@ -13,20 +13,24 @@
  */
 package org.apache.aurora.scheduler.storage.db;
 
+import java.util.List;
+import java.util.Set;
+
 import javax.inject.Inject;
 
 import com.google.common.base.Function;
 import com.google.common.base.Optional;
-import com.google.common.collect.ImmutableSet;
 
 import org.apache.aurora.gen.JobUpdateDetails;
-import org.apache.aurora.gen.JobUpdateQuery;
 import org.apache.aurora.scheduler.storage.JobUpdateStore;
+import org.apache.aurora.scheduler.storage.entities.IInstanceTaskConfig;
 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.IJobUpdateQuery;
 import org.apache.aurora.scheduler.storage.entities.IJobUpdateSummary;
+import org.apache.aurora.scheduler.storage.entities.IRange;
 
 import static java.util.Objects.requireNonNull;
 
@@ -56,7 +60,34 @@ public class DBJobUpdateStore implements JobUpdateStore.Mutable {
   @Override
   public void saveJobUpdate(IJobUpdate update) {
     jobKeyMapper.merge(update.getSummary().getJobKey().newBuilder());
-    detailsMapper.merge(update.newBuilder());
+    detailsMapper.insert(update.newBuilder());
+
+    String updateId = update.getSummary().getUpdateId();
+
+    // Insert optional instance update overrides.
+    Set<IRange> instanceOverrides =
+        update.getConfiguration().getSettings().getUpdateOnlyTheseInstances();
+
+    if (instanceOverrides != null && !instanceOverrides.isEmpty()) {
+      detailsMapper.insertInstanceOverrides(updateId, IRange.toBuildersSet(instanceOverrides));
+    }
+
+    // Insert new task config.
+    detailsMapper.insertTaskConfig(
+        updateId,
+        update.getConfiguration().getNewTaskConfig().newBuilder(),
+        true,
+        new InsertResult());
+
+    // Insert old task configs and instance mappings.
+    for (IInstanceTaskConfig config : update.getConfiguration().getOldTaskConfigs()) {
+      InsertResult result = new InsertResult();
+      detailsMapper.insertTaskConfig(updateId, config.getTask().newBuilder(), false, result);
+
+      detailsMapper.insertTaskConfigInstances(
+          result.getId(),
+          IRange.toBuildersSet(config.getInstances()));
+    }
   }
 
   @Override
@@ -75,14 +106,12 @@ public class DBJobUpdateStore implements JobUpdateStore.Mutable {
   }
 
   @Override
-  public ImmutableSet<IJobUpdateSummary> fetchJobUpdateSummaries(JobUpdateQuery query) {
-    // TODO(maxim): implement DB mapping logic.
-    return ImmutableSet.of();
+  public List<IJobUpdateSummary> fetchJobUpdateSummaries(IJobUpdateQuery query) {
+    return IJobUpdateSummary.listFromBuilders(detailsMapper.selectSummaries(query.newBuilder()));
   }
 
   @Override
   public Optional<IJobUpdateDetails> fetchJobUpdateDetails(final String updateId) {
-    // TODO(maxim): add support for job_update_configs and update_only_these_instances.
     return Optional.fromNullable(detailsMapper.selectDetails(updateId))
         .transform(new Function<JobUpdateDetails, IJobUpdateDetails>() {
           @Override
@@ -93,8 +122,7 @@ public class DBJobUpdateStore implements JobUpdateStore.Mutable {
   }
 
   @Override
-  public ImmutableSet<IJobUpdateDetails> fetchAllJobUpdateDetails() {
-    // TODO(maxim): implement DB mapping logic.
-    return ImmutableSet.of();
+  public List<IJobUpdateDetails> fetchAllJobUpdateDetails() {
+    return IJobUpdateDetails.listFromBuilders(detailsMapper.selectAllDetails());
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/fefd7306/src/main/java/org/apache/aurora/scheduler/storage/db/InsertResult.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/storage/db/InsertResult.java b/src/main/java/org/apache/aurora/scheduler/storage/db/InsertResult.java
new file mode 100644
index 0000000..44dc8f5
--- /dev/null
+++ b/src/main/java/org/apache/aurora/scheduler/storage/db/InsertResult.java
@@ -0,0 +1,36 @@
+/**
+ * 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;
+
+/**
+ * MyBatis returns auto-generated IDs through mutable fields in parameters.
+ * This class can be used as an additional {@link org.apache.ibatis.annotations.Param Param} to
+ * retrieve the ID when the inserted object is not self-identifying.
+ */
+class InsertResult {
+  private long id = Long.MIN_VALUE;
+  private boolean isSet;
+
+  long getId() {
+    if (!isSet) {
+      throw new IllegalStateException("Missing ID value.");
+    }
+    return id;
+  }
+
+  void setId(long value) {
+    id = value;
+    isSet = true;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/fefd7306/src/main/java/org/apache/aurora/scheduler/storage/db/JobUpdateDetailsMapper.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/storage/db/JobUpdateDetailsMapper.java b/src/main/java/org/apache/aurora/scheduler/storage/db/JobUpdateDetailsMapper.java
index 5b7a3df..d590219 100644
--- a/src/main/java/org/apache/aurora/scheduler/storage/db/JobUpdateDetailsMapper.java
+++ b/src/main/java/org/apache/aurora/scheduler/storage/db/JobUpdateDetailsMapper.java
@@ -13,10 +13,18 @@
  */
 package org.apache.aurora.scheduler.storage.db;
 
+import java.util.List;
+import java.util.Set;
+
 import javax.annotation.Nullable;
 
 import org.apache.aurora.gen.JobUpdate;
 import org.apache.aurora.gen.JobUpdateDetails;
+import org.apache.aurora.gen.JobUpdateQuery;
+import org.apache.aurora.gen.JobUpdateSummary;
+import org.apache.aurora.gen.Range;
+import org.apache.aurora.gen.TaskConfig;
+import org.apache.ibatis.annotations.Param;
 
 /**
  * MyBatis mapper class for JobUpdateDetailsMapper.xml
@@ -26,11 +34,47 @@ import org.apache.aurora.gen.JobUpdateDetails;
 interface JobUpdateDetailsMapper {
 
   /**
-   * Saves the job update, modifies the existing value if one exists.
+   * Inserts new {@link JobUpdate}.
+   *
+   * @param jobUpdate Job update to insert.
+   */
+  void insert(JobUpdate jobUpdate);
+
+  /**
+   * Inserts {@link TaskConfig} entries associated with the current update.
    *
-   * @param jobUpdate Job update to save/modify.
+   * @param updateId Update ID to insert task configs for.
+   * @param taskConfig {@link TaskConfig} to insert.
+   * @param isNew Flag to identify if the task config is existing {@code false} or
+   *              desired {@code true}.
+   * @param result Container for auto-generated ID of the inserted job update row.
    */
-  void merge(JobUpdate jobUpdate);
+  void insertTaskConfig(
+      @Param("updateId") String updateId,
+      @Param("config") TaskConfig taskConfig,
+      @Param("isNew") boolean isNew,
+      @Param("result") InsertResult result);
+
+  /**
+   * Maps inserted task config with a set of associated instance ranges.
+   *
+   * @param configId ID of the {@link TaskConfig} stored.
+   * @param ranges Set of instance ID ranges.
+   */
+  void insertTaskConfigInstances(
+      @Param("configId") long configId,
+      @Param("ranges") Set<Range> ranges);
+
+  /**
+   * Maps update with an optional set of
+   * {@link org.apache.aurora.gen.JobUpdateSettings#updateOnlyTheseInstances}.
+   *
+   * @param updateId Update ID to store overrides for.
+   * @param ranges Instance ID ranges to associate with a task configuration.
+   */
+  void insertInstanceOverrides(
+      @Param("updateId") String updateId,
+      @Param("ranges") Set<Range> ranges);
 
   /**
    * Deletes all updates and events from the database.
@@ -38,6 +82,15 @@ interface JobUpdateDetailsMapper {
   void truncate();
 
   /**
+   * Gets all {@link JobUpdateSummary} matching the provided {@code query}.
+   * All {@code query} fields are ANDed together.
+   *
+   * @param query Query to filter results by.
+   * @return Job update summaries matching the query.
+   */
+  List<JobUpdateSummary> selectSummaries(JobUpdateQuery query);
+
+  /**
    * Gets {@link JobUpdateDetails} for the provided {@code updateId}.
    *
    * @param updateId Update ID to get.
@@ -45,4 +98,11 @@ interface JobUpdateDetailsMapper {
    */
   @Nullable
   JobUpdateDetails selectDetails(String updateId);
+
+  /**
+   * Gets all stored {@link JobUpdateDetails}.
+   *
+   * @return All stored job update details.
+   */
+  List<JobUpdateDetails> selectAllDetails();
 }

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/fefd7306/src/main/java/org/apache/aurora/scheduler/storage/db/typehandlers/TaskConfigTypeHandler.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/storage/db/typehandlers/TaskConfigTypeHandler.java b/src/main/java/org/apache/aurora/scheduler/storage/db/typehandlers/TaskConfigTypeHandler.java
new file mode 100644
index 0000000..2f58357
--- /dev/null
+++ b/src/main/java/org/apache/aurora/scheduler/storage/db/typehandlers/TaskConfigTypeHandler.java
@@ -0,0 +1,73 @@
+/**
+ * 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.typehandlers;
+
+import java.sql.CallableStatement;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.apache.aurora.codec.ThriftBinaryCodec;
+import org.apache.aurora.codec.ThriftBinaryCodec.CodingException;
+import org.apache.aurora.gen.TaskConfig;
+import org.apache.ibatis.type.JdbcType;
+import org.apache.ibatis.type.TypeHandler;
+
+/**
+ * Type handler for objects of type {@link TaskConfig}. Converts {@link TaskConfig} to/from byte
+ * array to be stored in SQL as BINARY type.
+ *
+ * <p/>
+ * NOTE: We don't want to store serialized thrift objects long-term, but instead plan to reference
+ * a canonical table of task configurations. This class will go away with AURORA-647.
+ */
+class TaskConfigTypeHandler implements TypeHandler<TaskConfig> {
+
+  @Override
+  public final void setParameter(
+      PreparedStatement ps,
+      int i,
+      TaskConfig parameter,
+      JdbcType jdbcType) throws SQLException {
+
+    try {
+      ps.setBytes(i, ThriftBinaryCodec.encodeNonNull(parameter));
+    } catch (CodingException e) {
+      throw new SQLException("Failed to encode thrift struct.", e);
+    }
+  }
+
+  @Override
+  public final TaskConfig getResult(ResultSet rs, String columnName) throws SQLException {
+    return decodeOrThrow(rs.getBytes(columnName));
+  }
+
+  @Override
+  public final TaskConfig getResult(ResultSet rs, int columnIndex) throws SQLException {
+    return decodeOrThrow(rs.getBytes(columnIndex));
+  }
+
+  @Override
+  public final TaskConfig getResult(CallableStatement cs, int columnIndex) throws SQLException {
+    return decodeOrThrow(cs.getBytes(columnIndex));
+  }
+
+  private TaskConfig decodeOrThrow(byte[] value) throws SQLException {
+    try {
+      return ThriftBinaryCodec.decode(TaskConfig.class, value);
+    } catch (CodingException e) {
+      throw new SQLException("Failed to decode thrift struct.", e);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/fefd7306/src/main/java/org/apache/aurora/scheduler/storage/db/typehandlers/TypeHandlers.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/storage/db/typehandlers/TypeHandlers.java b/src/main/java/org/apache/aurora/scheduler/storage/db/typehandlers/TypeHandlers.java
index c5468b1..4d0c10d 100644
--- a/src/main/java/org/apache/aurora/scheduler/storage/db/typehandlers/TypeHandlers.java
+++ b/src/main/java/org/apache/aurora/scheduler/storage/db/typehandlers/TypeHandlers.java
@@ -31,6 +31,7 @@ public final class TypeHandlers {
     return ImmutableList.<Class<? extends TypeHandler<?>>>of(
         JobUpdateActionTypeHandler.class,
         JobUpdateStatusTypeHandler.class,
-        MaintenanceModeTypeHandler.class);
+        MaintenanceModeTypeHandler.class,
+        TaskConfigTypeHandler.class);
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/fefd7306/src/main/resources/org/apache/aurora/scheduler/storage/db/AttributeMapper.xml
----------------------------------------------------------------------
diff --git a/src/main/resources/org/apache/aurora/scheduler/storage/db/AttributeMapper.xml b/src/main/resources/org/apache/aurora/scheduler/storage/db/AttributeMapper.xml
index ce4912d..53017c8 100644
--- a/src/main/resources/org/apache/aurora/scheduler/storage/db/AttributeMapper.xml
+++ b/src/main/resources/org/apache/aurora/scheduler/storage/db/AttributeMapper.xml
@@ -76,7 +76,6 @@
   </select>
 
   <delete id="truncate">
-    DELETE FROM host_attribute_values;
     DELETE FROM host_attributes
   </delete>
 </mapper>

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/fefd7306/src/main/resources/org/apache/aurora/scheduler/storage/db/JobUpdateDetailsMapper.xml
----------------------------------------------------------------------
diff --git a/src/main/resources/org/apache/aurora/scheduler/storage/db/JobUpdateDetailsMapper.xml b/src/main/resources/org/apache/aurora/scheduler/storage/db/JobUpdateDetailsMapper.xml
index 3c69bda..17c58b1 100644
--- a/src/main/resources/org/apache/aurora/scheduler/storage/db/JobUpdateDetailsMapper.xml
+++ b/src/main/resources/org/apache/aurora/scheduler/storage/db/JobUpdateDetailsMapper.xml
@@ -17,21 +17,19 @@
         PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="org.apache.aurora.scheduler.storage.db.JobUpdateDetailsMapper">
-  <insert id="merge">
-    MERGE INTO job_updates (
+  <insert id="insert">
+    INSERT INTO job_updates (
       job_key_id,
       update_id,
       user,
-      status,
-      created_timestamp_ms,
-      last_modified_timestamp_ms,
+      instance_count,
       update_group_size,
       max_per_instance_failures,
       max_failed_instances,
       max_wait_to_instance_running_ms,
       min_wait_in_instance_running_ms,
       rollback_on_failure
-    ) KEY(update_id) VALUES (
+    ) VALUES (
       (
         SELECT ID
         FROM job_keys
@@ -41,9 +39,7 @@
       ),
       #{summary.updateId},
       #{summary.user},
-      #{summary.status, typeHandler=org.apache.aurora.scheduler.storage.db.typehandlers.JobUpdateStatusTypeHandler},
-      #{summary.createdTimestampMs},
-      #{summary.lastModifiedTimestampMs},
+      #{configuration.instanceCount},
       #{configuration.settings.updateGroupSize},
       #{configuration.settings.maxPerInstanceFailures},
       #{configuration.settings.maxFailedInstances},
@@ -53,27 +49,105 @@
     )
   </insert>
 
-  <resultMap id="jobUpdateSummaryMap" type="org.apache.aurora.gen.JobUpdateSummary">
-    <id column="update_id" property="updateId"/>
+  <insert id="insertTaskConfig" useGeneratedKeys="true" keyColumn="id" keyProperty="result.id">
+    INSERT INTO job_update_configs (
+      update_id,
+      task_config,
+      is_new
+    ) VALUES (
+      (
+        SELECT id
+        FROM job_updates
+        WHERE update_id = #{updateId}
+      ),
+      #{config, typeHandler=org.apache.aurora.scheduler.storage.db.typehandlers.TaskConfigTypeHandler},
+      #{isNew}
+    )
+  </insert>
+
+  <insert id="insertTaskConfigInstances">
+    INSERT INTO job_update_configs_to_instances (
+      config_id,
+      first,
+      last
+    ) VALUES
+    <foreach item="element" collection="ranges" open="(" separator="),(" close=")">
+      #{configId},
+      #{element.first},
+      #{element.last}
+    </foreach>
+  </insert>
+
+  <insert id="insertInstanceOverrides">
+    INSERT INTO job_updates_to_instance_overrides (
+      update_id,
+      first,
+      last
+    ) VALUES
+    <foreach item="element" collection="ranges" open="(" separator="),(" close=")">
+      (
+        SELECT id
+        FROM job_updates
+        WHERE update_id = #{updateId}
+      ),
+      #{element.first},
+      #{element.last}
+    </foreach>
+  </insert>
+
+  <resultMap id="jobUpdateStateMap" type="org.apache.aurora.gen.JobUpdateState">
+    <id column="update_id" />
     <result property="status"
             column="status"
             typeHandler="org.apache.aurora.scheduler.storage.db.typehandlers.JobUpdateStatusTypeHandler" />
+  </resultMap>
+
+  <resultMap id="jobUpdateSummaryMap" type="org.apache.aurora.gen.JobUpdateSummary">
+    <id column="update_id" property="updateId"/>
     <association property="jobKey"
                  resultMap="org.apache.aurora.scheduler.storage.db.JobKeyMapper.jobKeyMap"
                  columnPrefix="jk_"/>
+    <association property="state"
+                 resultMap="jobUpdateStateMap"
+                 columnPrefix="just_" />
+  </resultMap>
+
+  <resultMap id="rangeMap" type="org.apache.aurora.gen.Range">
+    <id column="id" />
+  </resultMap>
+
+  <resultMap id="instanceConfigMap" type="org.apache.aurora.gen.InstanceTaskConfig">
+    <id column="id" />
+    <result property="task"
+            column="task"
+            typeHandler="org.apache.aurora.scheduler.storage.db.typehandlers.TaskConfigTypeHandler" />
+    <collection property="instances" resultMap="rangeMap" columnPrefix="r_" notNullColumn="id" />
   </resultMap>
 
   <resultMap id="jobUpdateSettingsMap" type="org.apache.aurora.gen.JobUpdateSettings">
-    <id column="created_timestamp_ms" />
+    <id column="id" />
+    <collection property="updateOnlyTheseInstances"
+                resultMap="rangeMap"
+                columnPrefix="r_"
+                notNullColumn="id" />
   </resultMap>
 
   <resultMap id="jobUpdateConfigurationMap" type="org.apache.aurora.gen.JobUpdateConfiguration">
+    <id column="id" />
+    <result property="newTaskConfig"
+            column="new_task_config"
+            typeHandler="org.apache.aurora.scheduler.storage.db.typehandlers.TaskConfigTypeHandler" />
     <association property="settings" resultMap="jobUpdateSettingsMap" columnPrefix="juse_"/>
+    <collection property="oldTaskConfigs"
+                resultMap="instanceConfigMap"
+                columnPrefix="itc_"
+                notNullColumn="id" />
   </resultMap>
 
   <resultMap id="jobUpdateMap" type="org.apache.aurora.gen.JobUpdate">
+    <id column="u_id" />
     <association property="summary" resultMap="jobUpdateSummaryMap" columnPrefix="jusm_"/>
-    <association property="configuration" resultMap="jobUpdateConfigurationMap" />
+    <association property="configuration" resultMap="jobUpdateConfigurationMap" columnPrefix="juc_"/>
   </resultMap>
 
   <resultMap id="jobUpdateDetailsMap" type="org.apache.aurora.gen.JobUpdateDetails">
@@ -101,20 +175,108 @@
     </collection>
   </resultMap>
 
-  <select id="selectDetails" resultMap="jobUpdateDetailsMap">
+  <sql id="timestamps_join">
+    INNER JOIN
+    (
+      SELECT
+        e_s.update_id,
+        e_s.status
+      FROM job_update_events AS e_s
+      INNER JOIN
+      (
+        SELECT
+          update_id,
+          MAX(timestamp_ms) AS timestamp_ms
+        FROM job_update_events
+        GROUP BY update_id
+      ) AS e_t ON e_t.update_id = e_s.update_id AND e_t.timestamp_ms = e_s.timestamp_ms
+    ) AS max_status ON max_status.update_id = u.id
+    INNER JOIN
+    (
+      SELECT
+        update_id,
+        MIN(timestamp_ms) AS timestamp_ms
+      FROM job_update_events
+      GROUP BY update_id
+    ) AS min_ts ON min_ts.update_id = u.id
+    INNER JOIN
+    (
+      SELECT
+        update_id,
+        MAX(timestamp_ms) AS timestamp_ms
+      FROM
+      (
+        SELECT
+          update_id,
+          timestamp_ms
+        FROM job_update_events
+        UNION ALL
+        SELECT
+          update_id,
+          timestamp_ms
+        FROM job_instance_update_events
+      )
+      GROUP BY update_id
+    ) AS max_ts ON max_ts.update_id = u.id
+  </sql>
+
+  <select id="selectSummaries" resultMap="jobUpdateSummaryMap">
     SELECT
+      u.update_id AS update_id,
+      u.user AS user,
+      max_status.status AS just_status,
+      min_ts.timestamp_ms AS just_created_timestamp_ms,
+      max_ts.timestamp_ms AS just_last_modified_timestamp_ms,
+      j.id AS jk_id,
+      j.role AS jk_role,
+      j.environment AS jk_environment,
+      j.name AS jk_name
+    FROM job_updates AS u
+    INNER JOIN job_keys AS j ON j.id = u.job_key_id
+    <include refid="timestamps_join" />
+    <if test="updateId != null || role != null || user != null || jobKey != null || updateStatuses != null || limit != 0 || offset != 0">
+    WHERE TRUE
+      <if test="updateId != null">AND u.update_id = #{updateId}</if>
+      <if test="user != null">AND u.user = #{user}</if>
+      <if test="role != null">AND j.role = #{role}</if>
+      <if test="jobKey != null">
+        AND j.role = #{jobKey.role}
+        AND j.name = #{jobKey.name}
+        AND j.environment = #{jobKey.environment}
+      </if>
+      <if test="updateStatuses != null">
+        <if test="updateStatuses.size() > 0">
+          AND (max_status.status IN
+          <foreach item="element" collection="updateStatuses" open="(" separator="," close="))">
+            #{element, typeHandler=org.apache.aurora.scheduler.storage.db.typehandlers.JobUpdateStatusTypeHandler}
+          </foreach>
+        </if>
+      </if>
+    </if>
+    ORDER BY max_ts.timestamp_ms
+    <if test="limit != 0">LIMIT #{limit}</if>
+    <if test="offset != 0">OFFSET #{offset}</if>
+  </select>
+
+  <!--Column naming convention below follows the thrift object hierarchy and columnPrefix
+      attributes used in associations.
+      For example: jusm_just_status maps to JobUpdateSummary/JobUpdateState/status field.-->
+  <sql id="unscoped_details_select">
+     SELECT
       u.id AS u_id,
+      u.id AS juc_juse_id,
       u.update_id AS jusm_update_id,
       u.user AS jusm_user,
-      u.status AS jusm_status,
-      u.created_timestamp_ms AS jusm_created_timestamp_ms,
-      u.last_modified_timestamp_ms AS jusm_last_modified_timestamp_ms,
-      u.update_group_size AS juse_update_group_size,
-      u.max_per_instance_failures AS juse_max_per_instance_failures,
-      u.max_failed_instances AS juse_max_failed_instances,
-      u.max_wait_to_instance_running_ms AS juse_max_wait_to_instance_running_ms,
-      u.min_wait_in_instance_running_ms AS juse_min_wait_in_instance_running_ms,
-      u.rollback_on_failure AS juse_rollback_on_failure,
+      max_status.status AS jusm_just_status,
+      min_ts.timestamp_ms AS jusm_just_created_timestamp_ms,
+      max_ts.timestamp_ms AS jusm_just_last_modified_timestamp_ms,
+      u.instance_count AS juc_instance_count,
+      u.update_group_size AS juc_juse_update_group_size,
+      u.max_per_instance_failures AS juc_juse_max_per_instance_failures,
+      u.max_failed_instances AS juc_juse_max_failed_instances,
+      u.max_wait_to_instance_running_ms AS juc_juse_max_wait_to_instance_running_ms,
+      u.min_wait_in_instance_running_ms AS juc_juse_min_wait_in_instance_running_ms,
+      u.rollback_on_failure AS juc_juse_rollback_on_failure,
       j.id AS jusm_jk_id,
       j.role AS jusm_jk_role,
       j.environment AS jusm_jk_environment,
@@ -125,15 +287,40 @@
       i.id AS i_id,
       i.action AS i_action,
       i.instance_id AS i_instance_id,
-      i.timestamp_ms AS i_timestamp_ms
+      i.timestamp_ms AS i_timestamp_ms,
+      cn.id AS juc_id,
+      cn.task_config AS juc_new_task_config,
+      co.id AS juc_itc_id,
+      co.task_config AS juc_itc_task,
+      ci.id AS juc_itc_r_id,
+      ci.first AS juc_itc_r_first,
+      ci.last AS juc_itc_r_last,
+      io.id AS juc_juse_r_id,
+      io.first AS juc_juse_r_first,
+      io.last AS juc_juse_r_last
     FROM job_updates AS u
     INNER JOIN job_keys AS j ON j.id = u.job_key_id
+    INNER JOIN job_update_configs AS cn ON cn.update_id = u.id AND cn.is_new = TRUE
+    INNER JOIN job_update_configs AS co ON co.update_id = u.id AND co.is_new = FALSE
+    INNER JOIN job_update_configs_to_instances AS ci ON ci.config_id = co.id
+    <include refid="timestamps_join" />
+    LEFT OUTER JOIN job_updates_to_instance_overrides AS io ON io.update_id = u.id
     LEFT OUTER JOIN job_update_events AS e ON e.update_id = u.id
     LEFT OUTER JOIN job_instance_update_events AS i ON i.update_id = u.id
+  </sql>
+
+  <select id="selectDetails" resultMap="jobUpdateDetailsMap">
+    <include refid="unscoped_details_select"/>
     WHERE u.update_id = #{id}
     ORDER BY e_timestamp_ms, i_timestamp_ms
   </select>
 
+  <!--Order by ID to facilitate proper re-insertion during recovery.-->
+  <select id="selectAllDetails" resultMap="jobUpdateDetailsMap">
+    <include refid="unscoped_details_select"/>
+    ORDER BY u_id
+  </select>
+
   <delete id="truncate">
     DELETE FROM job_updates;
   </delete>

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/fefd7306/src/main/resources/org/apache/aurora/scheduler/storage/db/schema.sql
----------------------------------------------------------------------
diff --git a/src/main/resources/org/apache/aurora/scheduler/storage/db/schema.sql b/src/main/resources/org/apache/aurora/scheduler/storage/db/schema.sql
index f0c8336..1cf803f 100644
--- a/src/main/resources/org/apache/aurora/scheduler/storage/db/schema.sql
+++ b/src/main/resources/org/apache/aurora/scheduler/storage/db/schema.sql
@@ -98,9 +98,7 @@ CREATE TABLE job_updates(
   job_key_id BIGINT NOT NULL REFERENCES job_keys(id),
   update_id VARCHAR NOT NULL,
   user VARCHAR NOT NULL,
-  status INT NOT NULL REFERENCES job_update_statuses(id),
-  created_timestamp_ms BIGINT NOT NULL,
-  last_modified_timestamp_ms BIGINT NOT NULL,
+  instance_count INT NOT NULL,
   update_group_size INT NOT NULL,
   max_per_instance_failures INT NOT NULL,
   max_failed_instances INT NOT NULL,
@@ -114,10 +112,28 @@ CREATE TABLE job_updates(
 CREATE TABLE job_update_configs(
   id IDENTITY,
   update_id BIGINT NOT NULL REFERENCES job_updates(id) ON DELETE CASCADE,
-  task_config VARCHAR NOT NULL,
+  task_config BINARY NOT NULL,
   is_new BOOLEAN NOT NULL
 );
 
+CREATE TABLE job_updates_to_instance_overrides(
+  id IDENTITY,
+  update_id BIGINT NOT NULL REFERENCES job_updates(id) ON DELETE CASCADE,
+  first INT NOT NULL,
+  last INT NOT NULL,
+
+  UNIQUE(update_id, first, last)
+);
+
+CREATE TABLE job_update_configs_to_instances(
+  id IDENTITY,
+  config_id BIGINT NOT NULL REFERENCES job_update_configs(id) ON DELETE CASCADE,
+  first INT NOT NULL,
+  last INT NOT NULL,
+
+  UNIQUE(config_id, first, last)
+);
+
 CREATE TABLE job_update_events(
   id IDENTITY,
   update_id BIGINT NOT NULL REFERENCES job_updates(id) ON DELETE CASCADE,

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/fefd7306/src/main/thrift/org/apache/aurora/gen/api.thrift
----------------------------------------------------------------------
diff --git a/src/main/thrift/org/apache/aurora/gen/api.thrift b/src/main/thrift/org/apache/aurora/gen/api.thrift
index 4ea9ec2..af9f02e 100644
--- a/src/main/thrift/org/apache/aurora/gen/api.thrift
+++ b/src/main/thrift/org/apache/aurora/gen/api.thrift
@@ -509,32 +509,29 @@ struct PendingReason {
 
 /** States that a job update may be in. */
 enum JobUpdateStatus {
-  /** Update is created but not yet started. */
-  INIT = 0,
-
   /** Update is in progress. */
-  ROLLING_FORWARD = 1,
+  ROLLING_FORWARD = 0,
 
   /** Update has failed and is being rolled back. */
-  ROLLING_BACK = 2,
+  ROLLING_BACK = 1,
 
   /** Update has been paused while in progress. */
-  ROLL_FORWARD_PAUSED = 3,
+  ROLL_FORWARD_PAUSED = 2,
 
   /** Update has been paused during rollback. */
-  ROLL_BACK_PAUSED = 4,
+  ROLL_BACK_PAUSED = 3,
 
   /** Update has completed successfully. */
-  ROLLED_FORWARD = 5,
+  ROLLED_FORWARD = 4,
 
   /** Update has failed and rolled back. */
-  ROLLED_BACK = 6,
+  ROLLED_BACK = 5,
 
   /** Update was aborted. */
-  ABORTED = 7,
+  ABORTED = 6,
 
   /** Unknown error during update. */
-  ERROR = 8
+  ERROR = 7
 }
 
 /** Job update actions that can be applied to job instances. */
@@ -563,8 +560,8 @@ struct JobUpdateSettings {
   /** If true, enables failed update rollback. */
   6: bool rollbackOnFailure
 
-  /** A set of instance IDs to act on. */
-  7: set<i32> updateOnlyTheseInstances
+  /** Instance IDs to act on. All instances will be affected if this is not set. */
+  7: set<Range> updateOnlyTheseInstances
 }
 
 /** Event marking a state transition in job update lifecycle. */
@@ -594,10 +591,22 @@ struct InstanceTaskConfig {
   1: TaskConfig task
 
   /** Instances associated with the TaskConfig. */
-  2: list<Range> instances
+  2: set<Range> instances
+}
+
+/** Current job update state including status and created/modified timestamps. */
+struct JobUpdateState {
+  /** Current status of the update. */
+  1: JobUpdateStatus status
+
+  /** Created timestamp in milliseconds. */
+  2: i64 createdTimestampMs
+
+  /** Last modified timestamp in milliseconds. */
+  3: i64 lastModifiedTimestampMs
 }
 
-/** Job update state. */
+/** Summary of the job update including job key, user and current state. */
 struct JobUpdateSummary {
   /** Update ID. */
   1: string updateId
@@ -608,14 +617,8 @@ struct JobUpdateSummary {
   /** User initiated an update. */
   3: string user
 
-  /** Current status of the update. */
-  4: JobUpdateStatus status
-
-  /** Creation timestamp in milliseconds. */
-  5: i64 createdTimestampMs
-
-  /** Last modified timestamp in milliseconds. */
-  6: i64 lastModifiedTimestampMs
+  /** Current job update state. */
+  4: JobUpdateState state
 }
 
 /** Update configuration and setting details. */
@@ -623,11 +626,14 @@ struct JobUpdateConfiguration {
   /** Actual InstanceId -> TaskConfig mapping when the update was requested. */
   1: set<InstanceTaskConfig> oldTaskConfigs
 
-  /** Desired InstanceId -> TaskConfig mapping when the update completes. */
-  2: set<InstanceTaskConfig> newTaskConfigs
+  /** Desired TaskConfig when the update completes. */
+  2: TaskConfig newTaskConfig
+
+  /** Desired instance count when the update completes. */
+  3: i32 instanceCount
 
   /** Update specific settings. */
-  3: JobUpdateSettings settings
+  4: JobUpdateSettings settings
 }
 
 /** Full definition of the job update. */
@@ -679,14 +685,17 @@ struct JobUpdateQuery {
   /** Job key. */
   3: JobKey jobKey
 
+  /** User who created the update. */
+  4: string user
+
   /** Set of update statuses. */
-  4: set<JobUpdateStatus> updateStatus
+  5: set<JobUpdateStatus> updateStatuses
 
   /** Offset to serve data from. Used by pagination. */
-  5: i32 offset
+  6: i32 offset
 
   /** Number or records to serve. Used by pagination. */
-  6: i32 limit
+  7: i32 limit
 }
 
 struct ListBackupsResult {
@@ -740,7 +749,7 @@ struct StartJobUpdateResult {
 
 /** Result of the getJobUpdateSummaries call. */
 struct GetJobUpdateSummariesResult {
-  1: set<JobUpdateSummary> updateSummaries
+  1: list<JobUpdateSummary> updateSummaries
 }
 
 /** Result of the getJobUpdateDetails call. */

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/fefd7306/src/test/java/org/apache/aurora/scheduler/storage/db/DBJobUpdateStoreTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/storage/db/DBJobUpdateStoreTest.java b/src/test/java/org/apache/aurora/scheduler/storage/db/DBJobUpdateStoreTest.java
index c76ab5c..f695b85 100644
--- a/src/test/java/org/apache/aurora/scheduler/storage/db/DBJobUpdateStoreTest.java
+++ b/src/test/java/org/apache/aurora/scheduler/storage/db/DBJobUpdateStoreTest.java
@@ -18,19 +18,24 @@ import java.util.List;
 
 import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
-import com.google.inject.Guice;
-import com.google.inject.Injector;
-import com.twitter.common.inject.Bindings;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 
+import org.apache.aurora.gen.Identity;
+import org.apache.aurora.gen.InstanceTaskConfig;
 import org.apache.aurora.gen.JobInstanceUpdateEvent;
 import org.apache.aurora.gen.JobUpdate;
 import org.apache.aurora.gen.JobUpdateAction;
 import org.apache.aurora.gen.JobUpdateConfiguration;
 import org.apache.aurora.gen.JobUpdateDetails;
 import org.apache.aurora.gen.JobUpdateEvent;
+import org.apache.aurora.gen.JobUpdateQuery;
 import org.apache.aurora.gen.JobUpdateSettings;
+import org.apache.aurora.gen.JobUpdateState;
 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.base.JobKeys;
 import org.apache.aurora.scheduler.storage.Storage;
 import org.apache.aurora.scheduler.storage.Storage.MutableStoreProvider;
@@ -42,6 +47,8 @@ import org.apache.aurora.scheduler.storage.entities.IJobKey;
 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.IJobUpdateQuery;
+import org.apache.aurora.scheduler.storage.entities.IJobUpdateSummary;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -51,14 +58,15 @@ import static org.junit.Assert.assertEquals;
 public class DBJobUpdateStoreTest {
 
   private static final IJobKey JOB = JobKeys.from("testRole", "testEnv", "job");
+  private static final long CREATED_MS = 111L;
+  private static final IJobUpdateEvent FIRST_EVENT =
+      makeJobUpdateEvent(JobUpdateStatus.ROLLING_FORWARD, CREATED_MS);
 
-  private DbStorage storage;
+  private Storage storage;
 
   @Before
   public void setUp() throws Exception {
-    Injector injector = Guice.createInjector(DbModule.testModule(Bindings.KeyFactory.PLAIN));
-    storage = injector.getInstance(DbStorage.class);
-    storage.prepare();
+    storage = DbUtil.createStorage();
   }
 
   @After
@@ -70,68 +78,105 @@ public class DBJobUpdateStoreTest {
   public void testSaveJobUpdates() {
     String updateId1 = "u1";
     String updateId2 = "u2";
-    IJobUpdate update1 = makeJobUpdate(JOB, updateId1, JobUpdateStatus.INIT);
-    IJobUpdate update2 = makeJobUpdate(JOB, updateId2, JobUpdateStatus.INIT);
+
+    IJobUpdate update1 = makeJobUpdate(JOB, updateId1);
+    IJobUpdate update2 = makeJobUpdate(JOB, updateId2);
 
     saveUpdate(update1);
-    assertEquals(update1, getUpdateDetails(updateId1).get().getUpdate());
+    assertEquals(populateExpected(update1), getUpdateDetails(updateId1).get().getUpdate());
 
     saveUpdate(update2);
-    assertEquals(update2, getUpdateDetails(updateId2).get().getUpdate());
+    assertEquals(populateExpected(update1), getUpdateDetails(updateId1).get().getUpdate());
   }
 
   @Test
-  public void testSaveJobUpdateTwice() {
+  public void testSaveJobUpdateEmptyInstanceOverrides() {
     String updateId = "u1";
-    IJobUpdate update = makeJobUpdate(JOB, updateId, JobUpdateStatus.INIT);
 
-    saveUpdate(update);
-    assertEquals(update, getUpdateDetails(updateId).get().getUpdate());
+    IJobUpdate update = makeJobUpdate(JOB, updateId);
+    JobUpdate builder = update.newBuilder();
+    builder.getConfiguration().getSettings().setUpdateOnlyTheseInstances(ImmutableSet.<Range>of());
+
+    IJobUpdate expected = IJobUpdate.build(builder);
+
+    // Save with empty overrides.
+    saveUpdate(expected);
+    assertEquals(populateExpected(expected), getUpdateDetails(updateId).get().getUpdate());
+  }
 
+  @Test
+  public void testSaveJobUpdateNullInstanceOverrides() {
+    String updateId = "u1";
+
+    IJobUpdate update = makeJobUpdate(JOB, updateId);
     JobUpdate builder = update.newBuilder();
-    builder.getSummary().setStatus(JobUpdateStatus.ABORTED);
-    update = IJobUpdate.build(builder);
+    builder.getConfiguration().getSettings().setUpdateOnlyTheseInstances(ImmutableSet.<Range>of());
+
+    IJobUpdate expected = IJobUpdate.build(builder);
+
+    // Save with null overrides.
+    builder.getConfiguration().getSettings().setUpdateOnlyTheseInstances(null);
+    saveUpdate(IJobUpdate.build(builder));
+    assertEquals(populateExpected(expected), getUpdateDetails(updateId).get().getUpdate());
+  }
+
+  @Test(expected = StorageException.class)
+  public void testSaveJobUpdateTwiceThrows() {
+    String updateId = "u1";
+    IJobUpdate update = makeJobUpdate(JOB, updateId);
+
+    saveUpdate(update);
     saveUpdate(update);
-    assertEquals(update, getUpdateDetails(updateId).get().getUpdate());
   }
 
   @Test
   public void testSaveJobEvents() {
     String updateId = "u3";
-    IJobUpdate update = makeJobUpdate(JOB, updateId, JobUpdateStatus.ROLLING_FORWARD);
-    IJobUpdateEvent event1 = makeJobUpdateEvent(JobUpdateStatus.ROLLING_FORWARD, 123L);
-    IJobUpdateEvent event2 = makeJobUpdateEvent(JobUpdateStatus.ROLL_FORWARD_PAUSED, 124L);
+    IJobUpdate update = makeJobUpdate(JOB, updateId);
+    IJobUpdateEvent event1 = makeJobUpdateEvent(JobUpdateStatus.ROLLING_FORWARD, 124L);
+    IJobUpdateEvent event2 = makeJobUpdateEvent(JobUpdateStatus.ROLL_FORWARD_PAUSED, 125L);
 
     saveUpdate(update);
-    assertEquals(update, getUpdateDetails(updateId).get().getUpdate());
-    assertEquals(0, getUpdateDetails(updateId).get().getUpdateEvents().size());
-
-    saveJobEvent(event2, updateId);
-    assertEquals(event2, getUpdateDetails(updateId).get().getUpdateEvents().get(0));
+    assertEquals(populateExpected(update), getUpdateDetails(updateId).get().getUpdate());
+    assertEquals(ImmutableList.of(FIRST_EVENT), getUpdateDetails(updateId).get().getUpdateEvents());
 
     saveJobEvent(event1, updateId);
-    assertEquals(event1, getUpdateDetails(updateId).get().getUpdateEvents().get(0));
-    assertEquals(event2, getUpdateDetails(updateId).get().getUpdateEvents().get(1));
+    assertEquals(
+        populateExpected(update, JobUpdateStatus.ROLLING_FORWARD, CREATED_MS, 124L),
+        getUpdateDetails(updateId).get().getUpdate());
+    assertEquals(event1, getUpdateDetails(updateId).get().getUpdateEvents().get(1));
+
+    saveJobEvent(event2, updateId);
+    assertEquals(
+        populateExpected(update, JobUpdateStatus.ROLL_FORWARD_PAUSED, CREATED_MS, 125L),
+        getUpdateDetails(updateId).get().getUpdate());
+    assertEquals(event1, getUpdateDetails(updateId).get().getUpdateEvents().get(1));
+    assertEquals(event2, getUpdateDetails(updateId).get().getUpdateEvents().get(2));
   }
 
   @Test
   public void testSaveInstanceEvents() {
     String updateId = "u3";
-    IJobUpdate update = makeJobUpdate(JOB, updateId, JobUpdateStatus.ROLLING_FORWARD);
+    IJobUpdate update = makeJobUpdate(JOB, updateId);
     IJobInstanceUpdateEvent event1 = makeJobInstanceEvent(0, 125L, JobUpdateAction.ADD_INSTANCE);
     IJobInstanceUpdateEvent event2 = makeJobInstanceEvent(1, 126L, JobUpdateAction.ADD_INSTANCE);
 
     saveUpdate(update);
-    assertEquals(update, getUpdateDetails(updateId).get().getUpdate());
+    assertEquals(populateExpected(update), getUpdateDetails(updateId).get().getUpdate());
     assertEquals(0, getUpdateDetails(updateId).get().getInstanceEvents().size());
 
-    saveJobInstanceEvent(event2, updateId);
-    assertEquals(1, getUpdateDetails(updateId).get().getInstanceEvents().size());
-
     saveJobInstanceEvent(event1, updateId);
-    assertEquals(2, getUpdateDetails(updateId).get().getInstanceEvents().size());
+    assertEquals(
+        populateExpected(update, JobUpdateStatus.ROLLING_FORWARD, CREATED_MS, 125L),
+        getUpdateDetails(updateId).get().getUpdate());
+    assertEquals(
+        event1,
+        Iterables.getOnlyElement(getUpdateDetails(updateId).get().getInstanceEvents()));
 
-    // Test event order.
+    saveJobInstanceEvent(event2, updateId);
+    assertEquals(
+        populateExpected(update, JobUpdateStatus.ROLLING_FORWARD, CREATED_MS, 126L),
+        getUpdateDetails(updateId).get().getUpdate());
     assertEquals(event1, getUpdateDetails(updateId).get().getInstanceEvents().get(0));
     assertEquals(event2, getUpdateDetails(updateId).get().getInstanceEvents().get(1));
   }
@@ -147,21 +192,50 @@ public class DBJobUpdateStoreTest {
   }
 
   @Test
+  public void testSaveJobUpdateStateIgnored() {
+    String updateId = "u1";
+
+    IJobUpdate update = populateExpected(
+        makeJobUpdate(JOB, updateId),
+        JobUpdateStatus.ABORTED,
+        567L,
+        567L);
+
+    saveUpdate(update);
+
+    // Assert state fields were ignored.
+    assertEquals(populateExpected(update), getUpdateDetails(updateId).get().getUpdate());
+  }
+
+  @Test
+  public void testSaveJobUpdateWithoutEventFailsSelect() {
+    final String updateId = "u3";
+    storage.write(new MutateWork.Quiet<Void>() {
+      @Override
+      public Void apply(MutableStoreProvider storeProvider) {
+        storeProvider.getJobUpdateStore().saveJobUpdate(makeJobUpdate(JOB, updateId));
+        return null;
+      }
+    });
+    assertEquals(Optional.<IJobUpdateDetails>absent(), getUpdateDetails(updateId));
+  }
+
+  @Test
   public void testMultipleJobDetails() {
     String updateId1 = "u1";
     String updateId2 = "u2";
-    IJobUpdateDetails details1 = makeJobDetails(
-        makeJobUpdate(JOB, updateId1, JobUpdateStatus.ABORTED));
-
-    IJobUpdateDetails details2 = makeJobDetails(
-        makeJobUpdate(JOB, updateId2, JobUpdateStatus.ERROR));
+    IJobUpdateDetails details1 = makeJobDetails(makeJobUpdate(JOB, updateId1));
+    IJobUpdateDetails details2 = makeJobDetails(makeJobUpdate(JOB, updateId2));
 
     saveUpdate(details1.getUpdate());
     saveUpdate(details2.getUpdate());
+
+    details1 = updateJobDetails(populateExpected(details1.getUpdate()), FIRST_EVENT);
+    details2 = updateJobDetails(populateExpected(details2.getUpdate()), FIRST_EVENT);
     assertEquals(details1, getUpdateDetails(updateId1).get());
     assertEquals(details2, getUpdateDetails(updateId2).get());
 
-    IJobUpdateEvent jEvent11 = makeJobUpdateEvent(JobUpdateStatus.ROLL_BACK_PAUSED, 456L);
+    IJobUpdateEvent jEvent11 = makeJobUpdateEvent(JobUpdateStatus.ROLLING_FORWARD, 456L);
     IJobUpdateEvent jEvent12 = makeJobUpdateEvent(JobUpdateStatus.ERROR, 457L);
     IJobInstanceUpdateEvent iEvent11 = makeJobInstanceEvent(1, 451L, JobUpdateAction.ADD_INSTANCE);
     IJobInstanceUpdateEvent iEvent12 = makeJobInstanceEvent(2, 452L, JobUpdateAction.ADD_INSTANCE);
@@ -171,14 +245,6 @@ public class DBJobUpdateStoreTest {
     IJobInstanceUpdateEvent iEvent21 = makeJobInstanceEvent(3, 561L, JobUpdateAction.ADD_INSTANCE);
     IJobInstanceUpdateEvent iEvent22 = makeJobInstanceEvent(4, 562L, JobUpdateAction.ADD_INSTANCE);
 
-    details1 = updateJobDetails(
-        details1.getUpdate(),
-        ImmutableList.of(jEvent11, jEvent12), ImmutableList.of(iEvent11, iEvent12));
-
-    details2 = updateJobDetails(
-        details2.getUpdate(),
-        ImmutableList.of(jEvent21, jEvent22), ImmutableList.of(iEvent21, iEvent22));
-
     saveJobEvent(jEvent11, updateId1);
     saveJobEvent(jEvent12, updateId1);
     saveJobInstanceEvent(iEvent11, updateId1);
@@ -189,14 +255,24 @@ public class DBJobUpdateStoreTest {
     saveJobInstanceEvent(iEvent21, updateId2);
     saveJobInstanceEvent(iEvent22, updateId2);
 
+    details1 = updateJobDetails(
+        populateExpected(details1.getUpdate(), JobUpdateStatus.ERROR, CREATED_MS, 457L),
+        ImmutableList.of(FIRST_EVENT, jEvent11, jEvent12), ImmutableList.of(iEvent11, iEvent12));
+
+    details2 = updateJobDetails(
+        populateExpected(details2.getUpdate(), JobUpdateStatus.ABORTED, CREATED_MS, 568L),
+        ImmutableList.of(FIRST_EVENT, jEvent21, jEvent22), ImmutableList.of(iEvent21, iEvent22));
+
     assertEquals(details1, getUpdateDetails(updateId1).get());
     assertEquals(details2, getUpdateDetails(updateId2).get());
+
+    assertEquals(ImmutableList.of(details1, details2), getAllUpdateDetails());
   }
 
   @Test
   public void testTruncateJobUpdates() {
     String updateId = "u5";
-    IJobUpdate update = makeJobUpdate(JOB, updateId, JobUpdateStatus.INIT);
+    IJobUpdate update = makeJobUpdate(JOB, updateId);
     IJobUpdateEvent updateEvent = IJobUpdateEvent.build(
         new JobUpdateEvent(JobUpdateStatus.ROLLING_FORWARD, 123L));
     IJobInstanceUpdateEvent instanceEvent = IJobInstanceUpdateEvent.build(
@@ -205,14 +281,77 @@ public class DBJobUpdateStoreTest {
     saveUpdate(update);
     saveJobEvent(updateEvent, updateId);
     saveJobInstanceEvent(instanceEvent, updateId);
-    assertEquals(update, getUpdateDetails(updateId).get().getUpdate());
-    assertEquals(1, getUpdateDetails(updateId).get().getUpdateEvents().size());
+    assertEquals(
+        populateExpected(update, JobUpdateStatus.ROLLING_FORWARD, CREATED_MS, 125L),
+        getUpdateDetails(updateId).get().getUpdate());
+    assertEquals(2, getUpdateDetails(updateId).get().getUpdateEvents().size());
     assertEquals(1, getUpdateDetails(updateId).get().getInstanceEvents().size());
 
     truncateUpdates();
     assertEquals(Optional.<IJobUpdateDetails>absent(), getUpdateDetails(updateId));
   }
 
+  @Test
+  public void testGetSummaries() {
+    IJobKey job2 = JobKeys.from("role", "env", "name");
+    IJobUpdateSummary s1 = saveSummary(JOB, "u1", 1230L, JobUpdateStatus.ROLLED_BACK, "user");
+    IJobUpdateSummary s2 = saveSummary(JOB, "u2", 1231L, JobUpdateStatus.ABORTED, "user");
+    IJobUpdateSummary s3 = saveSummary(JOB, "u3", 1239L, JobUpdateStatus.ERROR, "user2");
+    IJobUpdateSummary s4 = saveSummary(JOB, "u4", 1234L, JobUpdateStatus.ROLL_BACK_PAUSED, "user3");
+    IJobUpdateSummary s5 = saveSummary(job2, "u5", 1235L, JobUpdateStatus.ROLLING_FORWARD, "user4");
+
+    // Test empty query returns all.
+    assertEquals(ImmutableList.of(s1, s2, s4, s5, s3), getSummaries(new JobUpdateQuery()));
+
+    // Test query by updateId.
+    assertEquals(ImmutableList.of(s1), getSummaries(new JobUpdateQuery().setUpdateId("u1")));
+
+    // Test query by role.
+    assertEquals(
+        ImmutableList.of(s1, s2, s4, s3),
+        getSummaries(new JobUpdateQuery().setRole(JOB.getRole())));
+
+    // Test query by job key.
+    assertEquals(
+        ImmutableList.of(s5),
+        getSummaries(new JobUpdateQuery().setJobKey(job2.newBuilder())));
+
+    // Test query by user.
+    assertEquals(ImmutableList.of(s1, s2), getSummaries(new JobUpdateQuery().setUser("user")));
+
+    // Test query by one status.
+    assertEquals(ImmutableList.of(s3), getSummaries(new JobUpdateQuery().setUpdateStatuses(
+        ImmutableSet.of(JobUpdateStatus.ERROR))));
+
+    // Test query by multiple statuses.
+    assertEquals(ImmutableList.of(s1, s2, s3), getSummaries(new JobUpdateQuery().setUpdateStatuses(
+        ImmutableSet.of(
+            JobUpdateStatus.ERROR,
+            JobUpdateStatus.ABORTED,
+            JobUpdateStatus.ROLLED_BACK))));
+
+    // Test query by empty statuses.
+    assertEquals(
+        ImmutableList.of(s1, s2, s4, s5, s3),
+        getSummaries(new JobUpdateQuery().setUpdateStatuses(ImmutableSet.<JobUpdateStatus>of())));
+
+    // Test paging.
+    assertEquals(
+        ImmutableList.of(s1, s2),
+        getSummaries(new JobUpdateQuery().setLimit(2).setOffset(0)));
+    assertEquals(
+        ImmutableList.of(s4, s5),
+        getSummaries(new JobUpdateQuery().setLimit(2).setOffset(2)));
+    assertEquals(
+        ImmutableList.of(s3),
+        getSummaries(new JobUpdateQuery().setLimit(2).setOffset(4)));
+
+    // Test no match.
+    assertEquals(
+        ImmutableList.<IJobUpdateSummary>of(),
+        getSummaries(new JobUpdateQuery().setRole("no_match")));
+  }
+
   private Optional<IJobUpdateDetails> getUpdateDetails(final String updateId) {
     return storage.consistentRead(new Quiet<Optional<IJobUpdateDetails>>() {
       @Override
@@ -222,11 +361,33 @@ public class DBJobUpdateStoreTest {
     });
   }
 
+  private List<IJobUpdateDetails> getAllUpdateDetails() {
+    return storage.consistentRead(new Quiet<List<IJobUpdateDetails>>() {
+      @Override
+      public List<IJobUpdateDetails> apply(Storage.StoreProvider storeProvider) {
+        return storeProvider.getJobUpdateStore().fetchAllJobUpdateDetails();
+      }
+    });
+  }
+
+  private List<IJobUpdateSummary> getSummaries(final JobUpdateQuery query) {
+    return storage.consistentRead(new Quiet<List<IJobUpdateSummary>>() {
+      @Override
+      public List<IJobUpdateSummary> apply(Storage.StoreProvider storeProvider) {
+        return storeProvider.getJobUpdateStore().fetchJobUpdateSummaries(
+            IJobUpdateQuery.build(query));
+      }
+    });
+  }
+
   private void saveUpdate(final IJobUpdate update) {
     storage.write(new MutateWork.Quiet<Void>() {
       @Override
       public Void apply(MutableStoreProvider storeProvider) {
         storeProvider.getJobUpdateStore().saveJobUpdate(update);
+        storeProvider.getJobUpdateStore().saveJobUpdateEvent(
+            FIRST_EVENT,
+            update.getSummary().getUpdateId());
         return null;
       }
     });
@@ -262,7 +423,26 @@ public class DBJobUpdateStoreTest {
     });
   }
 
-  private IJobUpdateEvent makeJobUpdateEvent(JobUpdateStatus status, long timestampMs) {
+  private IJobUpdate populateExpected(IJobUpdate update) {
+    return populateExpected(update, JobUpdateStatus.ROLLING_FORWARD, CREATED_MS, CREATED_MS);
+  }
+
+  private IJobUpdate populateExpected(
+      IJobUpdate update,
+      JobUpdateStatus status,
+      long createdMs,
+      long lastMs) {
+
+    JobUpdateState state = new JobUpdateState()
+        .setCreatedTimestampMs(createdMs)
+        .setLastModifiedTimestampMs(lastMs)
+        .setStatus(status);
+    JobUpdate builder = update.newBuilder();
+    builder.getSummary().setState(state);
+    return IJobUpdate.build(builder);
+  }
+
+  private static IJobUpdateEvent makeJobUpdateEvent(JobUpdateStatus status, long timestampMs) {
     return IJobUpdateEvent.build(
         new JobUpdateEvent(status, timestampMs));
   }
@@ -278,7 +458,14 @@ public class DBJobUpdateStoreTest {
   private IJobUpdateDetails makeJobDetails(IJobUpdate update) {
     return updateJobDetails(
         update,
-        ImmutableList.<IJobUpdateEvent>of(),
+        ImmutableList.<IJobUpdateEvent>of(FIRST_EVENT),
+        ImmutableList.<IJobInstanceUpdateEvent>of());
+  }
+
+  private IJobUpdateDetails updateJobDetails(IJobUpdate update, IJobUpdateEvent event) {
+    return updateJobDetails(
+        update,
+        ImmutableList.of(event),
         ImmutableList.<IJobInstanceUpdateEvent>of());
   }
 
@@ -293,16 +480,61 @@ public class DBJobUpdateStoreTest {
         .setInstanceEvents(IJobInstanceUpdateEvent.toBuildersList(instanceEvents)));
   }
 
-  private IJobUpdate makeJobUpdate(IJobKey jobKey, String updateId, JobUpdateStatus status) {
+  private IJobUpdateSummary makeSummary(IJobKey jobKey, String updateId, String user) {
+    return IJobUpdateSummary.build(new JobUpdateSummary()
+        .setUpdateId(updateId)
+        .setJobKey(jobKey.newBuilder())
+        .setUser(user));
+  }
+
+  private IJobUpdateSummary saveSummary(
+      IJobKey jobKey,
+      String updateId,
+      Long modifiedTimestampMs,
+      JobUpdateStatus status,
+      String user) {
+
+    IJobUpdateSummary summary = IJobUpdateSummary.build(new JobUpdateSummary()
+        .setUpdateId(updateId)
+        .setJobKey(jobKey.newBuilder())
+        .setUser(user));
+
+    IJobUpdate update = makeJobUpdate(summary);
+    saveUpdate(update);
+    saveJobEvent(makeJobUpdateEvent(status, modifiedTimestampMs), updateId);
+    return populateExpected(update, status, CREATED_MS, modifiedTimestampMs).getSummary();
+  }
+
+  private IJobUpdate makeJobUpdate(IJobUpdateSummary summary) {
+    return IJobUpdate.build(makeJobUpdate().newBuilder().setSummary(summary.newBuilder()));
+  }
+
+  private IJobUpdate makeJobUpdate(IJobKey jobKey, String updateId) {
+    return IJobUpdate.build(makeJobUpdate().newBuilder()
+        .setSummary(makeSummary(jobKey, updateId, "user").newBuilder()));
+  }
+
+  private IJobUpdate makeJobUpdate() {
     return IJobUpdate.build(new JobUpdate()
-        .setSummary(new JobUpdateSummary()
-            .setUpdateId(updateId)
-            .setJobKey(jobKey.newBuilder())
-            .setUser("user")
-            .setStatus(status)
-            .setCreatedTimestampMs(1223L)
-            .setLastModifiedTimestampMs(1224L))
         .setConfiguration(new JobUpdateConfiguration()
-            .setSettings(new JobUpdateSettings())));
+            .setNewTaskConfig(makeTaskConfig())
+            .setInstanceCount(8)
+            .setOldTaskConfigs(ImmutableSet.of(
+                new InstanceTaskConfig()
+                    .setInstances(ImmutableSet.of(new Range(0, 1), new Range(2, 3)))
+                    .setTask(makeTaskConfig()),
+                new InstanceTaskConfig()
+                    .setInstances(ImmutableSet.of(new Range(4, 5), new Range(6, 7)))
+                    .setTask(makeTaskConfig())))
+            .setSettings(new JobUpdateSettings()
+                .setUpdateOnlyTheseInstances(ImmutableSet.of(new Range(0, 0), new Range(3, 5))))));
+  }
+
+  private TaskConfig makeTaskConfig() {
+    return new TaskConfig()
+        .setJobName(JOB.getName())
+        .setEnvironment(JOB.getEnvironment())
+        .setOwner(new Identity(JOB.getRole(), "user"))
+        .setIsService(true);
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/fefd7306/src/test/java/org/apache/aurora/scheduler/storage/db/DbAttributeStoreTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/storage/db/DbAttributeStoreTest.java b/src/test/java/org/apache/aurora/scheduler/storage/db/DbAttributeStoreTest.java
index 4c82389..4a31999 100644
--- a/src/test/java/org/apache/aurora/scheduler/storage/db/DbAttributeStoreTest.java
+++ b/src/test/java/org/apache/aurora/scheduler/storage/db/DbAttributeStoreTest.java
@@ -18,9 +18,6 @@ import java.util.Set;
 
 import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableSet;
-import com.google.inject.Guice;
-import com.google.inject.Injector;
-import com.twitter.common.inject.Bindings;
 
 import org.apache.aurora.gen.Attribute;
 import org.apache.aurora.gen.HostAttributes;
@@ -61,9 +58,7 @@ public class DbAttributeStoreTest {
 
   @Before
   public void setUp() throws IOException {
-    Injector injector = Guice.createInjector(DbModule.testModule(Bindings.KeyFactory.PLAIN));
-    storage = injector.getInstance(Storage.class);
-    storage.prepare();
+    storage = DbUtil.createStorage();
   }
 
   @Test

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/fefd7306/src/test/java/org/apache/aurora/scheduler/storage/db/DbLockStoreTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/storage/db/DbLockStoreTest.java b/src/test/java/org/apache/aurora/scheduler/storage/db/DbLockStoreTest.java
index 7ed6691..ae4cef4 100644
--- a/src/test/java/org/apache/aurora/scheduler/storage/db/DbLockStoreTest.java
+++ b/src/test/java/org/apache/aurora/scheduler/storage/db/DbLockStoreTest.java
@@ -18,9 +18,6 @@ import java.util.Set;
 
 import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableSet;
-import com.google.inject.Guice;
-import com.google.inject.Injector;
-import com.twitter.common.inject.Bindings;
 
 import org.apache.aurora.gen.JobKey;
 import org.apache.aurora.gen.Lock;
@@ -42,7 +39,7 @@ import static org.junit.Assert.fail;
 
 public class DbLockStoreTest {
 
-  private DbStorage storage;
+  private Storage storage;
 
   private void assertLocks(final ILock... expected) {
     assertEquals(
@@ -99,9 +96,7 @@ public class DbLockStoreTest {
 
   @Before
   public void setUp() throws IOException {
-    Injector injector = Guice.createInjector(DbModule.testModule(Bindings.KeyFactory.PLAIN));
-    storage = injector.getInstance(DbStorage.class);
-    storage.prepare();
+    storage = DbUtil.createStorage();
   }
 
   @Test

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/fefd7306/src/test/java/org/apache/aurora/scheduler/storage/db/DbQuotaStoreTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/storage/db/DbQuotaStoreTest.java b/src/test/java/org/apache/aurora/scheduler/storage/db/DbQuotaStoreTest.java
index 1f0af86..6d8d52a 100644
--- a/src/test/java/org/apache/aurora/scheduler/storage/db/DbQuotaStoreTest.java
+++ b/src/test/java/org/apache/aurora/scheduler/storage/db/DbQuotaStoreTest.java
@@ -18,9 +18,6 @@ import java.util.Map;
 
 import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableMap;
-import com.google.inject.Guice;
-import com.google.inject.Injector;
-import com.twitter.common.inject.Bindings;
 
 import org.apache.aurora.gen.ResourceAggregate;
 import org.apache.aurora.scheduler.storage.Storage;
@@ -47,9 +44,7 @@ public class DbQuotaStoreTest {
 
   @Before
   public void setUp() throws IOException {
-    Injector injector = Guice.createInjector(DbModule.testModule(Bindings.KeyFactory.PLAIN));
-    storage = injector.getInstance(Storage.class);
-    storage.prepare();
+    storage = DbUtil.createStorage();
   }
 
   @Test

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/fefd7306/src/test/java/org/apache/aurora/scheduler/storage/db/DbSchedulerStoreTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/storage/db/DbSchedulerStoreTest.java b/src/test/java/org/apache/aurora/scheduler/storage/db/DbSchedulerStoreTest.java
index 64e2fee..9c00c8b 100644
--- a/src/test/java/org/apache/aurora/scheduler/storage/db/DbSchedulerStoreTest.java
+++ b/src/test/java/org/apache/aurora/scheduler/storage/db/DbSchedulerStoreTest.java
@@ -16,9 +16,6 @@ package org.apache.aurora.scheduler.storage.db;
 import java.io.IOException;
 
 import com.google.common.base.Optional;
-import com.google.inject.Guice;
-import com.google.inject.Injector;
-import com.twitter.common.inject.Bindings;
 
 import org.apache.aurora.scheduler.storage.Storage;
 import org.apache.aurora.scheduler.storage.Storage.MutableStoreProvider;
@@ -36,9 +33,7 @@ public class DbSchedulerStoreTest {
 
   @Before
   public void setUp() throws IOException {
-    Injector injector = Guice.createInjector(DbModule.testModule(Bindings.KeyFactory.PLAIN));
-    storage = injector.getInstance(Storage.class);
-    storage.prepare();
+    storage = DbUtil.createStorage();
   }
 
   @Test

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/fefd7306/src/test/java/org/apache/aurora/scheduler/storage/db/DbUtil.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/storage/db/DbUtil.java b/src/test/java/org/apache/aurora/scheduler/storage/db/DbUtil.java
new file mode 100644
index 0000000..1eaf3fe
--- /dev/null
+++ b/src/test/java/org/apache/aurora/scheduler/storage/db/DbUtil.java
@@ -0,0 +1,34 @@
+/**
+ * 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 com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.twitter.common.inject.Bindings;
+
+import org.apache.aurora.scheduler.storage.Storage;
+
+final class DbUtil {
+
+  private DbUtil() {
+    // Utility class.
+  }
+
+  static Storage createStorage() {
+    Injector injector = Guice.createInjector(DbModule.testModule(Bindings.KeyFactory.PLAIN));
+    Storage storage = injector.getInstance(Storage.class);
+    storage.prepare();
+    return storage;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/fefd7306/src/test/java/org/apache/aurora/scheduler/storage/log/LogStorageTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/storage/log/LogStorageTest.java b/src/test/java/org/apache/aurora/scheduler/storage/log/LogStorageTest.java
index 548322b..ebcb910 100644
--- a/src/test/java/org/apache/aurora/scheduler/storage/log/LogStorageTest.java
+++ b/src/test/java/org/apache/aurora/scheduler/storage/log/LogStorageTest.java
@@ -755,15 +755,13 @@ public class LogStorageTest extends EasyMockTest {
         .setSummary(new JobUpdateSummary()
             .setUpdateId(UPDATE_ID)
             .setJobKey(JOB_KEY.newBuilder())
-            .setStatus(JobUpdateStatus.ROLLED_FORWARD)
             .setUser("user"))
         .setConfiguration(new JobUpdateConfiguration()
-            .setNewTaskConfigs(ImmutableSet.of(new InstanceTaskConfig()
-                .setTask(new TaskConfig())
-                .setInstances(ImmutableList.of(new Range(0, 3)))))
+            .setNewTaskConfig(new TaskConfig())
+            .setInstanceCount(4)
             .setOldTaskConfigs(ImmutableSet.of(new InstanceTaskConfig()
                 .setTask(new TaskConfig())
-                .setInstances(ImmutableList.of(new Range(0, 3)))))
+                .setInstances(ImmutableSet.of(new Range(0, 3)))))
             .setSettings(new JobUpdateSettings())));
 
     new MutationFixture() {

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/fefd7306/src/test/java/org/apache/aurora/scheduler/storage/log/SnapshotStoreImplTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/storage/log/SnapshotStoreImplTest.java b/src/test/java/org/apache/aurora/scheduler/storage/log/SnapshotStoreImplTest.java
index cbb9c46..bee9c9c 100644
--- a/src/test/java/org/apache/aurora/scheduler/storage/log/SnapshotStoreImplTest.java
+++ b/src/test/java/org/apache/aurora/scheduler/storage/log/SnapshotStoreImplTest.java
@@ -103,7 +103,7 @@ public class SnapshotStoreImplTest extends EasyMockTest {
     final String updateId = "updateId";
     IJobUpdateDetails updateDetails = IJobUpdateDetails.build(new JobUpdateDetails()
         .setUpdate(new JobUpdate().setSummary(new JobUpdateSummary().setUpdateId(updateId)))
-        .setUpdateEvents(ImmutableList.of(new JobUpdateEvent().setStatus(JobUpdateStatus.INIT)))
+        .setUpdateEvents(ImmutableList.of(new JobUpdateEvent().setStatus(JobUpdateStatus.ERROR)))
         .setInstanceEvents(ImmutableList.of(new JobInstanceUpdateEvent().setTimestampMs(123L))));
 
     storageUtil.expectOperations();
@@ -118,7 +118,7 @@ public class SnapshotStoreImplTest extends EasyMockTest {
     expect(storageUtil.schedulerStore.fetchFrameworkId()).andReturn(Optional.of(frameworkId));
     expect(storageUtil.lockStore.fetchLocks()).andReturn(ImmutableSet.of(lock));
     expect(storageUtil.updateStore.fetchAllJobUpdateDetails())
-        .andReturn(ImmutableSet.of(updateDetails));
+        .andReturn(ImmutableList.of(updateDetails));
 
     expectDataWipe();
     storageUtil.taskStore.saveTasks(tasks);

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/fefd7306/src/test/resources/org/apache/aurora/gen/api.thrift.md5
----------------------------------------------------------------------
diff --git a/src/test/resources/org/apache/aurora/gen/api.thrift.md5 b/src/test/resources/org/apache/aurora/gen/api.thrift.md5
index 6bc6ccf..21a05f6 100644
--- a/src/test/resources/org/apache/aurora/gen/api.thrift.md5
+++ b/src/test/resources/org/apache/aurora/gen/api.thrift.md5
@@ -1 +1 @@
-cea1bcc3b07f8249c10971be8c47671a
+de976ca8a260380d5473ca45e32d7f95


Mime
View raw message