Bug 1140037 - Fix ping fuzzing calculations. r=dexter

This commit is contained in:
Georg Fritzsche 2015-05-06 12:54:12 +02:00
parent 133fb87ea3
commit 86d64a008e
6 changed files with 94 additions and 81 deletions

View File

@ -22,6 +22,7 @@ Cu.import("resource://gre/modules/Task.jsm", this);
Cu.import("resource://gre/modules/DeferredTask.jsm", this);
Cu.import("resource://gre/modules/Preferences.jsm");
Cu.import("resource://gre/modules/Timer.jsm");
Cu.import("resource://gre/modules/TelemetryUtils.jsm", this);
const LOGGER_NAME = "Toolkit.Telemetry";
const LOGGER_PREFIX = "TelemetryController::";
@ -55,6 +56,8 @@ const PING_SUBMIT_TIMEOUT_MS = 2 * 60 * 1000;
// We treat pings before midnight as happening "at midnight" with this tolerance.
const MIDNIGHT_TOLERANCE_MS = 15 * 60 * 1000;
// For midnight fuzzing we want to affect pings around midnight with this tolerance.
const MIDNIGHT_TOLERANCE_FUZZ_MS = 5 * 60 * 1000;
// We try to spread "midnight" pings out over this interval.
const MIDNIGHT_FUZZING_INTERVAL_MS = 60 * 60 * 1000;
const MIDNIGHT_FUZZING_DELAY_MS = Math.random() * MIDNIGHT_FUZZING_INTERVAL_MS;
@ -126,16 +129,6 @@ function isNewPingFormat(aPing) {
("version" in aPing) && (aPing.version >= 2);
}
/**
* Takes a date and returns it trunctated to a date with daily precision.
*/
function truncateToDays(date) {
return new Date(date.getFullYear(),
date.getMonth(),
date.getDate(),
0, 0, 0, 0);
}
function tomorrow(date) {
let d = new Date(date);
d.setDate(d.getDate() + 1);
@ -506,17 +499,20 @@ let Impl = {
* @return Number The next time (ms from UNIX epoch) when we can send pings.
*/
_getNextPingSendTime: function(now) {
const todayDate = truncateToDays(now);
const tomorrowDate = tomorrow(todayDate);
const nextMidnightRangeStart = tomorrowDate.getTime() - MIDNIGHT_TOLERANCE_MS;
const currentMidnightRangeEnd = todayDate.getTime() - MIDNIGHT_TOLERANCE_MS + Policy.midnightPingFuzzingDelay();
const midnight = TelemetryUtils.getNearestMidnight(now, MIDNIGHT_FUZZING_INTERVAL_MS);
if (now.getTime() < currentMidnightRangeEnd) {
return currentMidnightRangeEnd;
// Don't delay ping if we are not close to midnight.
if (!midnight) {
return now.getTime();
}
if (now.getTime() >= nextMidnightRangeStart) {
return nextMidnightRangeStart + Policy.midnightPingFuzzingDelay();
// Delay ping send if we are within the midnight fuzzing range.
// This is from: |midnight - MIDNIGHT_TOLERANCE_FUZZ_MS|
// to: |midnight + MIDNIGHT_FUZZING_INTERVAL_MS|
const midnightRangeStart = midnight.getTime() - MIDNIGHT_TOLERANCE_FUZZ_MS;
if (now.getTime() >= midnightRangeStart) {
// We spread those ping sends out between |midnight| and |midnight + midnightPingFuzzingDelay|.
return midnight.getTime() + Policy.midnightPingFuzzingDelay();
}
return now.getTime();

View File

@ -20,6 +20,7 @@ Cu.import("resource://gre/modules/DeferredTask.jsm", this);
Cu.import("resource://gre/modules/Preferences.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/Timer.jsm");
Cu.import("resource://gre/modules/TelemetryUtils.jsm", this);
const myScope = this;
@ -174,58 +175,6 @@ let Policy = {
clearSchedulerTickTimeout: id => clearTimeout(id),
};
/**
* Takes a date and returns it trunctated to a date with daily precision.
*/
function truncateToDays(date) {
return new Date(date.getFullYear(),
date.getMonth(),
date.getDate(),
0, 0, 0, 0);
}
/**
* Check if the difference between the times is within the provided tolerance.
* @param {Number} t1 A time in milliseconds.
* @param {Number} t2 A time in milliseconds.
* @param {Number} tolerance The tolerance, in milliseconds.
* @return {Boolean} True if the absolute time difference is within the tolerance, false
* otherwise.
*/
function areTimesClose(t1, t2, tolerance) {
return Math.abs(t1 - t2) <= tolerance;
}
/**
* Get the next midnight for a date.
* @param {Object} date The date object to check.
* @return {Object} The Date object representing the next midnight.
*/
function getNextMidnight(date) {
let nextMidnight = new Date(truncateToDays(date));
nextMidnight.setDate(nextMidnight.getDate() + 1);
return nextMidnight;
}
/**
* Get the midnight which is closer to the provided date.
* @param {Object} date The date object to check.
* @return {Object} The Date object representing the closes midnight, or null if midnight
* is not within the midnight tolerance.
*/
function getNearestMidnight(date) {
let lastMidnight = truncateToDays(date);
if (areTimesClose(date.getTime(), lastMidnight.getTime(), SCHEDULER_MIDNIGHT_TOLERANCE_MS)) {
return lastMidnight;
}
const nextMidnightDate = getNextMidnight(date);
if (areTimesClose(date.getTime(), nextMidnightDate.getTime(), SCHEDULER_MIDNIGHT_TOLERANCE_MS)) {
return nextMidnightDate;
}
return null;
}
/**
* Get the ping type based on the payload.
* @param {Object} aPayload The ping payload.
@ -473,7 +422,7 @@ let TelemetryScheduler = {
timeout = SCHEDULER_TICK_IDLE_INTERVAL_MS;
// We need to make sure though that we don't miss sending pings around
// midnight when we use the longer idle intervals.
const nextMidnight = getNextMidnight(now);
const nextMidnight = TelemetryUtils.getNextMidnight(now);
timeout = Math.min(timeout, nextMidnight.getTime() - now.getTime());
}
@ -484,8 +433,8 @@ let TelemetryScheduler = {
_sentDailyPingToday: function(nowDate) {
// This is today's date and also the previous midnight (0:00).
const todayDate = truncateToDays(nowDate);
const nearestMidnight = getNearestMidnight(nowDate);
const todayDate = TelemetryUtils.truncateToDays(nowDate);
const nearestMidnight = TelemetryUtils.getNearestMidnight(nowDate, SCHEDULER_MIDNIGHT_TOLERANCE_MS);
// If we are close to midnight, we check against that, otherwise against the last midnight.
const checkDate = nearestMidnight || todayDate;
// We consider a ping sent for today if it occured after midnight, or prior within the tolerance.
@ -506,7 +455,7 @@ let TelemetryScheduler = {
return false;
}
const nearestMidnight = getNearestMidnight(nowDate);
const nearestMidnight = TelemetryUtils.getNearestMidnight(nowDate, SCHEDULER_MIDNIGHT_TOLERANCE_MS);
if (!sentPingToday && !nearestMidnight) {
// Computer must have gone to sleep, the daily ping is overdue.
this._log.trace("_isDailyPingDue - daily ping is overdue... computer went to sleep?");
@ -616,7 +565,8 @@ let TelemetryScheduler = {
let nextSessionCheckpoint =
this._lastSessionCheckpointTime + ABORTED_SESSION_UPDATE_INTERVAL_MS;
let combineActions = (shouldSendDaily && isAbortedPingDue) || (shouldSendDaily &&
areTimesClose(now, nextSessionCheckpoint, SCHEDULER_COALESCE_THRESHOLD_MS));
TelemetryUtils.areTimesClose(now, nextSessionCheckpoint,
SCHEDULER_COALESCE_THRESHOLD_MS));
if (combineActions) {
this._log.trace("_schedulerTickLogic - Combining pings.");
@ -661,7 +611,7 @@ let TelemetryScheduler = {
// update the schedules.
this._saveAbortedPing(now.getTime(), competingPayload);
// If we're close to midnight, skip today's daily ping and reschedule it for tomorrow.
let nearestMidnight = getNearestMidnight(now);
let nearestMidnight = TelemetryUtils.getNearestMidnight(now, SCHEDULER_MIDNIGHT_TOLERANCE_MS);
if (nearestMidnight) {
this._lastDailyPingTime = now.getTime();
}
@ -1152,8 +1102,8 @@ let Impl = {
getMetadata: function getMetadata(reason) {
this._log.trace("getMetadata - Reason " + reason);
let sessionStartDate = toLocalTimeISOString(truncateToDays(this._sessionStartDate));
let subsessionStartDate = toLocalTimeISOString(truncateToDays(this._subsessionStartDate));
let sessionStartDate = toLocalTimeISOString(TelemetryUtils.truncateToDays(this._sessionStartDate));
let subsessionStartDate = toLocalTimeISOString(TelemetryUtils.truncateToDays(this._subsessionStartDate));
// Compute the subsession length in milliseconds, then convert to seconds.
let subsessionLength =
Math.floor((Policy.now() - this._subsessionStartDate.getTime()) / 1000);

View File

@ -0,0 +1,66 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
this.EXPORTED_SYMBOLS = [
"TelemetryUtils"
];
const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components;
this.TelemetryUtils = {
/**
* Takes a date and returns it trunctated to a date with daily precision.
*/
truncateToDays: function(date) {
return new Date(date.getFullYear(),
date.getMonth(),
date.getDate(),
0, 0, 0, 0);
},
/**
* Check if the difference between the times is within the provided tolerance.
* @param {Number} t1 A time in milliseconds.
* @param {Number} t2 A time in milliseconds.
* @param {Number} tolerance The tolerance, in milliseconds.
* @return {Boolean} True if the absolute time difference is within the tolerance, false
* otherwise.
*/
areTimesClose: function(t1, t2, tolerance) {
return Math.abs(t1 - t2) <= tolerance;
},
/**
* Get the next midnight for a date.
* @param {Object} date The date object to check.
* @return {Object} The Date object representing the next midnight.
*/
getNextMidnight: function(date) {
let nextMidnight = new Date(this.truncateToDays(date));
nextMidnight.setDate(nextMidnight.getDate() + 1);
return nextMidnight;
},
/**
* Get the midnight which is closer to the provided date.
* @param {Object} date The date object to check.
* @param {Number} tolerance The tolerance within we find the closest midnight.
* @return {Object} The Date object representing the closes midnight, or null if midnight
* is not within the midnight tolerance.
*/
getNearestMidnight: function(date, tolerance) {
let lastMidnight = this.truncateToDays(date);
if (this.areTimesClose(date.getTime(), lastMidnight.getTime(), tolerance)) {
return lastMidnight;
}
const nextMidnightDate = this.getNextMidnight(date);
if (this.areTimesClose(date.getTime(), nextMidnightDate.getTime(), tolerance)) {
return nextMidnightDate;
}
return null;
},
};

View File

@ -34,6 +34,7 @@ EXTRA_JS_MODULES += [
'TelemetryLog.jsm',
'TelemetryStopwatch.jsm',
'TelemetryStorage.jsm',
'TelemetryUtils.jsm',
'ThirdPartyCookieProbe.jsm',
'UITelemetry.jsm',
]

View File

@ -287,14 +287,14 @@ add_task(function* test_midnightPingSendFuzzing() {
yield sendPing(true, true);
Assert.ok(!!pingSendTimerCallback);
Assert.deepEqual(futureDate(now, pingSendTimeout), new Date(2030, 5, 2, 0, 45, 0));
Assert.deepEqual(futureDate(now, pingSendTimeout), new Date(2030, 5, 2, 1, 0, 0));
// A ping after midnight within the fuzzing delay should also not get sent.
now = new Date(2030, 5, 2, 0, 40, 0);
fakeNow(now);
pingSendTimeout = null;
yield sendPing(true, true);
Assert.deepEqual(futureDate(now, pingSendTimeout), new Date(2030, 5, 2, 0, 45, 0));
Assert.deepEqual(futureDate(now, pingSendTimeout), new Date(2030, 5, 2, 1, 0, 0));
// The Request constructor restores the previous ping handler.
gRequestIterator = Iterator(new Request());

View File

@ -1672,7 +1672,7 @@ add_task(function* test_sendDailyOnIdle() {
Assert.equal(ping.payload.info.reason, REASON_DAILY);
// We should also trigger a ping when going idle shortly before next midnight.
now = new Date(2040, 1, 2, 23, 55, 0);
now = new Date(2040, 1, 2, 23, 54, 0);
fakeNow(now);
yield fakeIdleNotification("idle");