Closed Bug 1131412 Opened 10 years ago Closed 10 years ago

Implement a reading list engine scheduler

Categories

(Firefox Graveyard :: Reading List, defect)

defect
Not set
normal

Tracking

(firefox38 fixed, firefox39 fixed)

RESOLVED FIXED
Firefox 39
Tracking Status
firefox38 --- fixed
firefox39 --- fixed

People

(Reporter: markh, Assigned: markh)

References

(Blocks 1 open bug)

Details

Attachments

(1 file, 1 obsolete file)

We need a component so the reading-list sync-engine can be scheduled. Examples of this are: * The reading-list UI has made a change, so it wants the new state to be synced ASAP - however, we may currently be offline or behind a captive-portal etc. The scheduler should just be told to Sync ASAP and take care of the details. * We will periodically want to Sync to get new state from other devices. The scheduler will be responsible for maintaining a timer to manage this. * If the user clicks the "Sync" toolbar button, the reading-list service should also "Sync now" (which will not come automatically as the reading list engine is not a "sync engine") * The server may issue "backoff" requests - the scheduler is responsible for noticing and managing this. Note that Sync already has a scheduler, but it isn't generic enough to be reused in this context. However, it may require some changes to the sync scheduler to support this.
Flags: qe-verify-
Flags: firefox-backlog+
Assignee: nobody → mhammond
Status: NEW → ASSIGNED
Iteration: --- → 38.3 - 23 Feb
Blocks: 1132074
The scheduler. Still a bit of a WIP while we wait for the sync engine, but I think this is OK as a first cut.
Attachment #8566395 - Flags: feedback?(adw)
Comment on attachment 8566395 [details] [diff] [review] 0002-Bug-1131412-implement-a-scheduler-for-the-readinglis.patch Review of attachment 8566395 [details] [diff] [review]: ----------------------------------------------------------------- I don't see anything wrong! :-) ::: browser/components/readinglist/Scheduler.jsm @@ +182,5 @@ > + }, > + > + // checkStatus checks the current state and the environment to see when > + // we should next sync. > + _checkStatus(ignoreBlockingErrors = false) { This wants to be named _(re)setTimer I think. Something with "timer". @@ +258,5 @@ > + // Write a pref in the same format used to services/sync to indicate > + // the last success. > + prefs.set("lastSync", new Date().toString()); > + Services.obs.notifyObservers(null, "readinglist:sync:finish", null); > + this.state = this.STATE_OK; Seems like observers should see this.state == OK when they're called. @@ +265,5 @@ > + this.log.error("Sync failed", err); > + Services.obs.notifyObservers(null, "readinglist:sync:error", null); > + // XXX - how to detect an auth error? > + this.state = err == this._engine.ERROR_AUTHENTICATION ? > + this.STATE_ERROR_AUTHENTICATION : this.STATE_ERROR_OTHER; Similarly here.
Attachment #8566395 - Flags: feedback?(adw) → feedback+
Component: General → Reading List
Addressed all feedback
Attachment #8566395 - Attachment is obsolete: true
Attachment #8566957 - Flags: review?(adw)
Comment on attachment 8566957 [details] [diff] [review] 0002-Bug-1131412-implement-a-scheduler-for-the-readinglis.patch Review of attachment 8566957 [details] [diff] [review]: ----------------------------------------------------------------- Mostly nits. ::: browser/components/readinglist/Scheduler.jsm @@ +24,5 @@ > +XPCOMUtils.defineLazyModuleGetter(this, 'clearTimeout', > + 'resource://gre/modules/Timer.jsm'); > + > +XPCOMUtils.defineLazyModuleGetter(this, 'Task', > + 'resource://gre/modules/Task.jsm'); Nit: It doesn't make sense to lazy-load this since methods below are defined using Task.async. But it's not, like, wrong either, so that's why this is only a nit. @@ +52,5 @@ > + sync: Task.async(function* () { > + }), > +} > + > +let prefs = new Preferences("readinglist.scheduler."); Nit: I'm normally not a fan of prefixes in variable names, but the G prefix for globals does help me see that I shouldn't look for a random G variable in a function to be defined there somewhere. Up to you. @@ +55,5 @@ > + > +let prefs = new Preferences("readinglist.scheduler."); > + > +// A helper to manage our interval values. > +let intervals = { Same nit here I guess. @@ +91,5 @@ > + _nextScheduledSync: null, > + // The time when the most-recent "backoff request" expires - we will never > + // schedule a new timer before this. > + _backoffUntil: 0, > + _timer: null, // Our current timer. Nit: Moving the comment above the line, like the other comments here, would make this part easier to read I think. @@ +100,5 @@ > + _engine: engine, > + > + // Our state variable and constants. > + state: "ok", > + STATE_OK: "ok", Nit: Initialize `state` in init() so you can set it to STATE_OK instead of repeating STATE_OK's value. @@ +114,5 @@ > + this._nextScheduledSync = Date.now() + intervals.initial; > + this._createTimer(); > + }, > + > + // XXX - only called by tests. Nit: The XXX isn't necessary. @@ +184,5 @@ > + }, > + > + // _createTimer checks the current state and the environment to see when > + // we should next sync and creates the timer with the appropriate delay. > + _createTimer(ignoreBlockingErrors = false) { Nit: createTimer seems like not a great name either since this method can actually clear the timer, too. I think _setUpTimer would be better, but if you disagree that's fine, I won't ask you to change it. @@ +222,5 @@ > + if (!delay && !this._nextScheduledSync) { > + this.log.debug("_maybeReschedule ignoring a backoff request while running"); > + return; > + } > + // So we have a delay and a time for the next schedule - resolve them. This comment confuses me. It says that we have a time for the next schedule, but then the code immediately following it checks for !this._nextScheduledSync. And that code doesn't help me understand what "resolve" means. I think maybe this should just be moved to the other block of comments below? @@ +246,5 @@ > + this._timerRunning = true; > + // flag that there's no new schedule yet, so a request coming in while > + // we are running does the right thing. > + this._nextScheduledSync = 0; > + let onDone = (nextDelay) => { I think you can chain this to the promise chain below instead of calling it directly: this._engine.sync().then(() => { ... return intervals.schedule; }).then(null, err => { // or .catch(err => { ... return intervals.retry; }).then(nextDelay => { ... }); Maybe this would be nicer inside a Task.spawn. @@ +316,5 @@ > + get state() internalScheduler.state, > +}; > + > +// These functions are exposed purely for tests, which manage to grab them > +// via a BackstafePass. Backsta_g_ePass ::: browser/components/readinglist/test/xpcshell/head.js @@ +8,5 @@ > + > +// Setup logging prefs before importing the scheduler module. > +Services.prefs.setCharPref("readinglist.log.appender.dump", "Trace"); > + > +let {createTestableScheduler} = Cu.import("resource:///modules/readinglist/Scheduler.jsm", {}); Do Scheduler, Preferences, and setTimeout really need to be in head.js? I have an xpcshell test for the storage and it doesn't need any of that stuff. ::: browser/components/readinglist/test/xpcshell/test_scheduler.js @@ +19,5 @@ > +function createScheduler(options) { > + // avoid typos in the test and other footguns in the options. > + let allowedOptions = ["expectedDelay", "expectNewTimer", "syncFunction"]; > + for (let key of Object.keys(options)) { > + if (allowedOptions.indexOf(key) == -1) { Nit: Use array.includes(). ::: browser/components/readinglist/test/xpcshell/xpcshell.ini @@ +1,3 @@ > +[DEFAULT] > +head = head.js > +firefox-appdir = browser What's this do?
Attachment #8566957 - Flags: review?(adw) → review+
(In reply to Drew Willcoxon :adw from comment #4) > > +firefox-appdir = browser > > What's this do? Without that, xpcshell tests can't access modules in /browser/ :( See bug 810617.
Comment on attachment 8566957 [details] [diff] [review] 0002-Bug-1131412-implement-a-scheduler-for-the-readinglis.patch Review of attachment 8566957 [details] [diff] [review]: ----------------------------------------------------------------- ::: browser/components/readinglist/Scheduler.jsm @@ +27,5 @@ > +XPCOMUtils.defineLazyModuleGetter(this, 'Task', > + 'resource://gre/modules/Task.jsm'); > + > +// Note the scheduler is a singleton, so we only expose an instance. > +this.EXPORTED_SYMBOLS = ["readingListScheduler"]; Convention throughout the entire tree (well, except services...) is to upper-camel-case exported symbols. ie: ReadingListScheduler
Iteration: 38.3 - 23 Feb → 39.1 - 9 Mar
Status: ASSIGNED → RESOLVED
Closed: 10 years ago
Resolution: --- → FIXED
Target Milestone: --- → Firefox 39
Depends on: 1141505
Product: Firefox → Firefox Graveyard
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: