mobile/android/base/background/healthreport/prune/PrunePolicy.java

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 package org.mozilla.gecko.background.healthreport.prune;
michael@0 6
michael@0 7 import org.mozilla.gecko.background.common.log.Logger;
michael@0 8 import org.mozilla.gecko.background.healthreport.HealthReportConstants;
michael@0 9
michael@0 10 import android.content.SharedPreferences;
michael@0 11
michael@0 12 /**
michael@0 13 * Manages scheduling of the pruning of old Firefox Health Report data.
michael@0 14 *
michael@0 15 * There are three main actions that take place:
michael@0 16 * 1) Excessive storage pruning: The recorded data is taking up an unreasonable amount of space.
michael@0 17 * 2) Expired data pruning: Data that is kept around longer than is useful.
michael@0 18 * 3) Cleanup: To deal with storage maintenance (e.g. bloat and fragmentation)
michael@0 19 *
michael@0 20 * (1) and (2) are performed periodically on their own schedules. (3) will activate after a
michael@0 21 * certain duration but only after (1) or (2) is performed.
michael@0 22 */
michael@0 23 public class PrunePolicy {
michael@0 24 public static final String LOG_TAG = PrunePolicy.class.getSimpleName();
michael@0 25
michael@0 26 protected final PrunePolicyStorage storage;
michael@0 27 protected final SharedPreferences sharedPreferences;
michael@0 28 protected final Editor editor;
michael@0 29
michael@0 30 public PrunePolicy(final PrunePolicyStorage storage, final SharedPreferences sharedPrefs) {
michael@0 31 this.storage = storage;
michael@0 32 this.sharedPreferences = sharedPrefs;
michael@0 33 this.editor = new Editor(this.sharedPreferences.edit());
michael@0 34 }
michael@0 35
michael@0 36 protected SharedPreferences getSharedPreferences() {
michael@0 37 return this.sharedPreferences;
michael@0 38 }
michael@0 39
michael@0 40 public void tick(final long time) {
michael@0 41 try {
michael@0 42 try {
michael@0 43 boolean pruned = attemptPruneBySize(time);
michael@0 44 pruned = attemptExpiration(time) ? true : pruned;
michael@0 45 // We only need to cleanup after a large pruning.
michael@0 46 if (pruned) {
michael@0 47 attemptStorageCleanup(time);
michael@0 48 }
michael@0 49 } catch (Exception e) {
michael@0 50 // While catching Exception is ordinarily bad form, this Service runs in the same process
michael@0 51 // as Fennec so if we crash, it crashes. Additionally, this Service runs regularly so
michael@0 52 // these crashes could be regular. Thus, we choose to quietly fail instead.
michael@0 53 Logger.error(LOG_TAG, "Got exception pruning document.", e);
michael@0 54 } finally {
michael@0 55 editor.commit();
michael@0 56 }
michael@0 57 } catch (Exception e) {
michael@0 58 Logger.error(LOG_TAG, "Got exception committing to SharedPreferences.", e);
michael@0 59 } finally {
michael@0 60 storage.close();
michael@0 61 }
michael@0 62 }
michael@0 63
michael@0 64 protected boolean attemptPruneBySize(final long time) {
michael@0 65 final long nextPrune = getNextPruneBySizeTime();
michael@0 66 if (nextPrune < 0) {
michael@0 67 Logger.debug(LOG_TAG, "Initializing prune-by-size time.");
michael@0 68 editor.setNextPruneBySizeTime(time + getMinimumTimeBetweenPruneBySizeChecks());
michael@0 69 return false;
michael@0 70 }
michael@0 71
michael@0 72 // If the system clock is skewed into the past, making the time between prunes too long, reset
michael@0 73 // the clock.
michael@0 74 if (nextPrune > getMinimumTimeBetweenPruneBySizeChecks() + time) {
michael@0 75 Logger.debug(LOG_TAG, "Clock skew detected - resetting prune-by-size time.");
michael@0 76 editor.setNextPruneBySizeTime(time + getMinimumTimeBetweenPruneBySizeChecks());
michael@0 77 return false;
michael@0 78 }
michael@0 79
michael@0 80 if (nextPrune > time) {
michael@0 81 Logger.debug(LOG_TAG, "Skipping prune-by-size - wait period has not yet elapsed.");
michael@0 82 return false;
michael@0 83 }
michael@0 84
michael@0 85 Logger.debug(LOG_TAG, "Attempting prune-by-size.");
michael@0 86
michael@0 87 // Prune environments first because their cascading deletions may delete some events. These
michael@0 88 // environments are pruned in order of least-recently used first. Note that orphaned
michael@0 89 // environments are ignored here and should be removed elsewhere.
michael@0 90 final int environmentCount = storage.getEnvironmentCount();
michael@0 91 if (environmentCount > getMaxEnvironmentCount()) {
michael@0 92 final int environmentPruneCount = environmentCount - getEnvironmentCountAfterPrune();
michael@0 93 Logger.debug(LOG_TAG, "Pruning " + environmentPruneCount + " environments.");
michael@0 94 storage.pruneEnvironments(environmentPruneCount);
michael@0 95 }
michael@0 96
michael@0 97 final int eventCount = storage.getEventCount();
michael@0 98 if (eventCount > getMaxEventCount()) {
michael@0 99 final int eventPruneCount = eventCount - getEventCountAfterPrune();
michael@0 100 Logger.debug(LOG_TAG, "Pruning up to " + eventPruneCount + " events.");
michael@0 101 storage.pruneEvents(eventPruneCount);
michael@0 102 }
michael@0 103 editor.setNextPruneBySizeTime(time + getMinimumTimeBetweenPruneBySizeChecks());
michael@0 104 return true;
michael@0 105 }
michael@0 106
michael@0 107 protected boolean attemptExpiration(final long time) {
michael@0 108 final long nextPrune = getNextExpirationTime();
michael@0 109 if (nextPrune < 0) {
michael@0 110 Logger.debug(LOG_TAG, "Initializing expiration time.");
michael@0 111 editor.setNextExpirationTime(time + getMinimumTimeBetweenExpirationChecks());
michael@0 112 return false;
michael@0 113 }
michael@0 114
michael@0 115 // If the system clock is skewed into the past, making the time between prunes too long, reset
michael@0 116 // the clock.
michael@0 117 if (nextPrune > getMinimumTimeBetweenExpirationChecks() + time) {
michael@0 118 Logger.debug(LOG_TAG, "Clock skew detected - resetting expiration time.");
michael@0 119 editor.setNextExpirationTime(time + getMinimumTimeBetweenExpirationChecks());
michael@0 120 return false;
michael@0 121 }
michael@0 122
michael@0 123 if (nextPrune > time) {
michael@0 124 Logger.debug(LOG_TAG, "Skipping expiration - wait period has not yet elapsed.");
michael@0 125 return false;
michael@0 126 }
michael@0 127
michael@0 128 final long oldEventTime = time - getEventExistenceDuration();
michael@0 129 Logger.debug(LOG_TAG, "Pruning data older than " + oldEventTime + ".");
michael@0 130 storage.deleteDataBefore(oldEventTime);
michael@0 131 editor.setNextExpirationTime(time + getMinimumTimeBetweenExpirationChecks());
michael@0 132 return true;
michael@0 133 }
michael@0 134
michael@0 135 protected boolean attemptStorageCleanup(final long time) {
michael@0 136 // Cleanup if max duration since last cleanup is exceeded.
michael@0 137 final long nextCleanup = getNextCleanupTime();
michael@0 138 if (nextCleanup < 0) {
michael@0 139 Logger.debug(LOG_TAG, "Initializing cleanup time.");
michael@0 140 editor.setNextCleanupTime(time + getMinimumTimeBetweenCleanupChecks());
michael@0 141 return false;
michael@0 142 }
michael@0 143
michael@0 144 // If the system clock is skewed into the past, making the time between cleanups too long,
michael@0 145 // reset the clock.
michael@0 146 if (nextCleanup > getMinimumTimeBetweenCleanupChecks() + time) {
michael@0 147 Logger.debug(LOG_TAG, "Clock skew detected - resetting cleanup time.");
michael@0 148 editor.setNextCleanupTime(time + getMinimumTimeBetweenCleanupChecks());
michael@0 149 return false;
michael@0 150 }
michael@0 151
michael@0 152 if (nextCleanup > time) {
michael@0 153 Logger.debug(LOG_TAG, "Skipping cleanup - wait period has not yet elapsed.");
michael@0 154 return false;
michael@0 155 }
michael@0 156
michael@0 157 editor.setNextCleanupTime(time + getMinimumTimeBetweenCleanupChecks());
michael@0 158 Logger.debug(LOG_TAG, "Cleaning up storage.");
michael@0 159 storage.cleanup();
michael@0 160 return true;
michael@0 161 }
michael@0 162
michael@0 163 protected static class Editor {
michael@0 164 protected final SharedPreferences.Editor editor;
michael@0 165
michael@0 166 public Editor(final SharedPreferences.Editor editor) {
michael@0 167 this.editor = editor;
michael@0 168 }
michael@0 169
michael@0 170 public void commit() {
michael@0 171 editor.commit();
michael@0 172 }
michael@0 173
michael@0 174 public Editor setNextExpirationTime(final long time) {
michael@0 175 editor.putLong(HealthReportConstants.PREF_EXPIRATION_TIME, time);
michael@0 176 return this;
michael@0 177 }
michael@0 178
michael@0 179 public Editor setNextPruneBySizeTime(final long time) {
michael@0 180 editor.putLong(HealthReportConstants.PREF_PRUNE_BY_SIZE_TIME, time);
michael@0 181 return this;
michael@0 182 }
michael@0 183
michael@0 184 public Editor setNextCleanupTime(final long time) {
michael@0 185 editor.putLong(HealthReportConstants.PREF_CLEANUP_TIME, time);
michael@0 186 return this;
michael@0 187 }
michael@0 188 }
michael@0 189
michael@0 190 private long getNextExpirationTime() {
michael@0 191 return getSharedPreferences().getLong(HealthReportConstants.PREF_EXPIRATION_TIME, -1L);
michael@0 192 }
michael@0 193
michael@0 194 private long getEventExistenceDuration() {
michael@0 195 return HealthReportConstants.EVENT_EXISTENCE_DURATION;
michael@0 196 }
michael@0 197
michael@0 198 private long getMinimumTimeBetweenExpirationChecks() {
michael@0 199 return HealthReportConstants.MINIMUM_TIME_BETWEEN_EXPIRATION_CHECKS_MILLIS;
michael@0 200 }
michael@0 201
michael@0 202 private long getNextPruneBySizeTime() {
michael@0 203 return getSharedPreferences().getLong(HealthReportConstants.PREF_PRUNE_BY_SIZE_TIME, -1L);
michael@0 204 }
michael@0 205
michael@0 206 private long getMinimumTimeBetweenPruneBySizeChecks() {
michael@0 207 return HealthReportConstants.MINIMUM_TIME_BETWEEN_PRUNE_BY_SIZE_CHECKS_MILLIS;
michael@0 208 }
michael@0 209
michael@0 210 private int getMaxEnvironmentCount() {
michael@0 211 return HealthReportConstants.MAX_ENVIRONMENT_COUNT;
michael@0 212 }
michael@0 213
michael@0 214 private int getEnvironmentCountAfterPrune() {
michael@0 215 return HealthReportConstants.ENVIRONMENT_COUNT_AFTER_PRUNE;
michael@0 216 }
michael@0 217
michael@0 218 private int getMaxEventCount() {
michael@0 219 return HealthReportConstants.MAX_EVENT_COUNT;
michael@0 220 }
michael@0 221
michael@0 222 private int getEventCountAfterPrune() {
michael@0 223 return HealthReportConstants.EVENT_COUNT_AFTER_PRUNE;
michael@0 224 }
michael@0 225
michael@0 226 private long getNextCleanupTime() {
michael@0 227 return getSharedPreferences().getLong(HealthReportConstants.PREF_CLEANUP_TIME, -1L);
michael@0 228 }
michael@0 229
michael@0 230 private long getMinimumTimeBetweenCleanupChecks() {
michael@0 231 return HealthReportConstants.MINIMUM_TIME_BETWEEN_CLEANUP_CHECKS_MILLIS;
michael@0 232 }
michael@0 233 }

mercurial