зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1264498 - Hide duplicate remote Sync clients that haven't synced in a week. r=markh
MozReview-Commit-ID: LaVb2pABu0X --HG-- extra : rebase_source : 1c8777ce9f461f0417f3ef6876da8f807a689600
This commit is contained in:
Родитель
6d4a2cf6c0
Коммит
b072308bf4
|
@ -14,6 +14,7 @@ Cu.import("resource://services-common/stringbundle.js");
|
|||
Cu.import("resource://services-sync/constants.js");
|
||||
Cu.import("resource://services-sync/engines.js");
|
||||
Cu.import("resource://services-sync/record.js");
|
||||
Cu.import("resource://services-sync/resource.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
|
@ -22,6 +23,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
|
|||
|
||||
const CLIENTS_TTL = 1814400; // 21 days
|
||||
const CLIENTS_TTL_REFRESH = 604800; // 7 days
|
||||
const STALE_CLIENT_REMOTE_AGE = 604800; // 7 days
|
||||
|
||||
const SUPPORTED_PROTOCOL_VERSIONS = ["1.1", "1.5"];
|
||||
|
||||
|
@ -173,7 +175,7 @@ ClientEngine.prototype = {
|
|||
_processIncoming() {
|
||||
// Fetch all records from the server.
|
||||
this.lastSync = 0;
|
||||
this._incomingClients = [];
|
||||
this._incomingClients = {};
|
||||
try {
|
||||
SyncEngine.prototype._processIncoming.call(this);
|
||||
// Since clients are synced unconditionally, any records in the local store
|
||||
|
@ -181,10 +183,28 @@ ClientEngine.prototype = {
|
|||
// them, so that we don't upload records with commands for clients that will
|
||||
// never see them. We also do this to filter out stale clients from the
|
||||
// tabs collection, since showing their list of tabs is confusing.
|
||||
let remoteClientIDs = Object.keys(this._store._remoteClients);
|
||||
let staleIDs = Utils.arraySub(remoteClientIDs, this._incomingClients);
|
||||
for (let staleID of staleIDs) {
|
||||
this._removeRemoteClient(staleID);
|
||||
for (let id in this._store._remoteClients) {
|
||||
if (!this._incomingClients[id]) {
|
||||
this._log.info(`Removing local state for deleted client ${id}`);
|
||||
this._removeRemoteClient(id);
|
||||
}
|
||||
}
|
||||
// Bug 1264498: Mobile clients don't remove themselves from the clients
|
||||
// collection when the user disconnects Sync, so we filter out clients
|
||||
// with the same name that haven't synced in over a week.
|
||||
delete this._incomingClients[this.localID];
|
||||
let names = new Set([this.localName]);
|
||||
for (let id in this._incomingClients) {
|
||||
let record = this._store._remoteClients[id];
|
||||
if (!names.has(record.name)) {
|
||||
names.add(record.name);
|
||||
continue;
|
||||
}
|
||||
let remoteAge = AsyncResource.serverTime - this._incomingClients[id];
|
||||
if (remoteAge > STALE_CLIENT_REMOTE_AGE) {
|
||||
this._log.info(`Hiding stale client ${id} with age ${remoteAge}`);
|
||||
this._removeRemoteClient(id);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this._incomingClients = null;
|
||||
|
@ -219,7 +239,7 @@ ClientEngine.prototype = {
|
|||
_reconcile: function _reconcile(item) {
|
||||
// Every incoming record is reconciled, so we use this to track the
|
||||
// contents of the collection on the server.
|
||||
this._incomingClients.push(item.id);
|
||||
this._incomingClients[item.id] = item.modified;
|
||||
|
||||
if (!this._store.itemExists(item.id)) {
|
||||
return true;
|
||||
|
|
|
@ -495,6 +495,11 @@ add_test(function test_process_incoming_commands() {
|
|||
|
||||
var handler = function() {
|
||||
Svc.Obs.remove(ev, handler);
|
||||
|
||||
Svc.Prefs.resetBranch("");
|
||||
Service.recordManager.clearCache();
|
||||
engine._resetClient();
|
||||
|
||||
run_next_test();
|
||||
};
|
||||
|
||||
|
@ -504,6 +509,114 @@ add_test(function test_process_incoming_commands() {
|
|||
do_check_false(engine.processIncomingCommands());
|
||||
});
|
||||
|
||||
add_test(function test_filter_duplicate_names() {
|
||||
_("Ensure that we exclude clients with identical names that haven't synced in a week.");
|
||||
|
||||
let now = Date.now() / 1000;
|
||||
let contents = {
|
||||
meta: {global: {engines: {clients: {version: engine.version,
|
||||
syncID: engine.syncID}}}},
|
||||
clients: {},
|
||||
crypto: {}
|
||||
};
|
||||
let server = serverForUsers({"foo": "password"}, contents);
|
||||
let user = server.user("foo");
|
||||
|
||||
new SyncTestingInfrastructure(server.server);
|
||||
generateNewKeys(Service.collectionKeys);
|
||||
|
||||
// Synced recently.
|
||||
let recentID = Utils.makeGUID();
|
||||
server.insertWBO("foo", "clients", new ServerWBO(recentID, encryptPayload({
|
||||
id: recentID,
|
||||
name: "My Phone",
|
||||
type: "mobile",
|
||||
commands: [],
|
||||
version: "48",
|
||||
protocols: ["1.5"],
|
||||
}), now - 10));
|
||||
|
||||
// Dupe of our client, synced more than 1 week ago.
|
||||
let dupeID = Utils.makeGUID();
|
||||
server.insertWBO("foo", "clients", new ServerWBO(dupeID, encryptPayload({
|
||||
id: dupeID,
|
||||
name: engine.localName,
|
||||
type: "desktop",
|
||||
commands: [],
|
||||
version: "48",
|
||||
protocols: ["1.5"],
|
||||
}), now - 604810));
|
||||
|
||||
// Synced more than 1 week ago, but not a dupe.
|
||||
let oldID = Utils.makeGUID();
|
||||
server.insertWBO("foo", "clients", new ServerWBO(oldID, encryptPayload({
|
||||
id: oldID,
|
||||
name: "My old desktop",
|
||||
type: "desktop",
|
||||
commands: [],
|
||||
version: "48",
|
||||
protocols: ["1.5"],
|
||||
}), now - 604820));
|
||||
|
||||
try {
|
||||
let store = engine._store;
|
||||
|
||||
_("First sync");
|
||||
strictEqual(engine.lastRecordUpload, 0);
|
||||
engine._sync();
|
||||
ok(engine.lastRecordUpload > 0);
|
||||
deepEqual(user.collection("clients").keys().sort(),
|
||||
[recentID, dupeID, oldID, engine.localID].sort(),
|
||||
"Our record should be uploaded on first sync");
|
||||
deepEqual(Object.keys(store.getAllIDs()).sort(),
|
||||
[recentID, oldID, engine.localID].sort(),
|
||||
"Fresh clients should be downloaded on first sync");
|
||||
|
||||
_("Broadcast logout to all clients");
|
||||
engine.sendCommand("logout", []);
|
||||
engine._sync();
|
||||
|
||||
let collection = server.getCollection("foo", "clients");
|
||||
let recentPayload = JSON.parse(JSON.parse(collection.payload(recentID)).ciphertext);
|
||||
deepEqual(recentPayload.commands, [{ command: "logout", args: [] }],
|
||||
"Should send commands to the recent client");
|
||||
|
||||
let oldPayload = JSON.parse(JSON.parse(collection.payload(oldID)).ciphertext);
|
||||
deepEqual(oldPayload.commands, [{ command: "logout", args: [] }],
|
||||
"Should send commands to the week-old client");
|
||||
|
||||
let dupePayload = JSON.parse(JSON.parse(collection.payload(dupeID)).ciphertext);
|
||||
deepEqual(dupePayload.commands, [],
|
||||
"Should not send commands to the dupe client");
|
||||
|
||||
_("Update the dupe client's modified time");
|
||||
server.insertWBO("foo", "clients", new ServerWBO(dupeID, encryptPayload({
|
||||
id: dupeID,
|
||||
name: engine.localName,
|
||||
type: "desktop",
|
||||
commands: [],
|
||||
version: "48",
|
||||
protocols: ["1.5"],
|
||||
}), now - 10));
|
||||
|
||||
_("Second sync.");
|
||||
engine._sync();
|
||||
|
||||
deepEqual(Object.keys(store.getAllIDs()).sort(),
|
||||
[recentID, oldID, dupeID, engine.localID].sort(),
|
||||
"Stale client synced, so it should no longer be marked as a dupe");
|
||||
} finally {
|
||||
Svc.Prefs.resetBranch("");
|
||||
Service.recordManager.clearCache();
|
||||
|
||||
try {
|
||||
server.deleteCollections("foo");
|
||||
} finally {
|
||||
server.stop(run_next_test);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
add_test(function test_command_sync() {
|
||||
_("Ensure that commands are synced across clients.");
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче