update_engine: Reduce prefs writes during update

Currently we checkpoint after every operation during an update in order
to allow us to resume if an update is interrupted for some reason. In
addition, we write the current uptime duration every time we receive
data over the network in order to collect total uptime metrics when an
update finishes, even if it was interrupted by a restart. In practice,
this results in a massive number of writes to prefs files during an
update (~70000 calls to creat on the samus build I tested).

This change introduces rate limiting to both of these mechanisms, such
that checkpoints and changes in uptime are persisted at most once per
second. This reduces the number of calls to creat to around 650.

BUG=chromium:898648
TEST=Perform an update while running strace:
strace -p <pidof update_engine> -f -e '!read,write,sendto,recvfrom'

Change-Id: Icadc8de4efdebf480ef37c6ef69603e250212102
Reviewed-on: https://chromium-review.googlesource.com/1316467
Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Tested-by: Amin Hassani <ahassani@chromium.org>
Reviewed-by: Amin Hassani <ahassani@chromium.org>
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index 86bd6c3..50b95a0 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -70,6 +70,7 @@
 const unsigned DeltaPerformer::kProgressLogTimeoutSeconds = 30;
 const unsigned DeltaPerformer::kProgressDownloadWeight = 50;
 const unsigned DeltaPerformer::kProgressOperationsWeight = 50;
+const uint64_t DeltaPerformer::kCheckpointFrequencySeconds = 1;
 
 namespace {
 const int kUpdateStateOperationInvalid = -1;
@@ -1796,6 +1797,13 @@
 }
 
 bool DeltaPerformer::CheckpointUpdateProgress() {
+  base::Time curr_time = base::Time::Now();
+  if (curr_time > update_checkpoint_time_) {
+    update_checkpoint_time_ = curr_time + update_checkpoint_wait_;
+  } else {
+    return false;
+  }
+
   Terminator::set_exit_blocked(true);
   if (last_updated_buffer_offset_ != buffer_offset_) {
     // Resets the progress in case we die in the middle of the state update.
diff --git a/payload_consumer/delta_performer.h b/payload_consumer/delta_performer.h
index 38d2c43..e3d429b 100644
--- a/payload_consumer/delta_performer.h
+++ b/payload_consumer/delta_performer.h
@@ -60,6 +60,7 @@
   // operations. They must add up to one hundred (100).
   static const unsigned kProgressDownloadWeight;
   static const unsigned kProgressOperationsWeight;
+  static const uint64_t kCheckpointFrequencySeconds;
 
   DeltaPerformer(PrefsInterface* prefs,
                  BootControlInterface* boot_control,
@@ -396,6 +397,12 @@
       base::TimeDelta::FromSeconds(kProgressLogTimeoutSeconds)};
   base::Time forced_progress_log_time_;
 
+  // The frequency that we should write an update checkpoint (constant), and
+  // the point in time at which the next checkpoint should be written.
+  const base::TimeDelta update_checkpoint_wait_{
+      base::TimeDelta::FromSeconds(kCheckpointFrequencySeconds)};
+  base::Time update_checkpoint_time_;
+
   DISALLOW_COPY_AND_ASSIGN(DeltaPerformer);
 };
 
diff --git a/payload_state.cc b/payload_state.cc
index 4670b14..72144ef 100644
--- a/payload_state.cc
+++ b/payload_state.cc
@@ -55,6 +55,9 @@
 // We want to randomize retry attempts after the backoff by +/- 6 hours.
 static const uint32_t kMaxBackoffFuzzMinutes = 12 * 60;
 
+// Limit persisting current update duration uptime to once per second
+static const uint64_t kUptimeResolution = 1;
+
 PayloadState::PayloadState()
     : prefs_(nullptr),
       using_p2p_for_downloading_(false),
@@ -1151,9 +1154,12 @@
 void PayloadState::CalculateUpdateDurationUptime() {
   Time now = system_state_->clock()->GetMonotonicTime();
   TimeDelta uptime_since_last_update = now - update_duration_uptime_timestamp_;
-  TimeDelta new_uptime = update_duration_uptime_ + uptime_since_last_update;
-  // We're frequently called so avoid logging this write
-  SetUpdateDurationUptimeExtended(new_uptime, now, false);
+
+  if (uptime_since_last_update > TimeDelta::FromSeconds(kUptimeResolution)) {
+    TimeDelta new_uptime = update_duration_uptime_ + uptime_since_last_update;
+    // We're frequently called so avoid logging this write
+    SetUpdateDurationUptimeExtended(new_uptime, now, false);
+  }
 }
 
 string PayloadState::GetPrefsKey(const string& prefix, DownloadSource source) {