Preserve ordering of flush()es by not letting subsequent flush()es race

pull/3000/head
Kegan Dougal 2017-01-24 17:05:01 +00:00
parent 6f3b70dbb0
commit e225d3e370
1 changed files with 13 additions and 11 deletions

View File

@ -104,8 +104,9 @@ class IndexedDBLogStore {
this.id = "instance-" + Math.random() + Date.now(); this.id = "instance-" + Math.random() + Date.now();
this.index = 0; this.index = 0;
this.db = null; this.db = null;
// Promise is not null when a flush is IN PROGRESS
this.flushPromise = null; this.flushPromise = null;
// set if flush() is called whilst one is ongoing
this.flushAgainPromise = null;
} }
/** /**
@ -165,10 +166,8 @@ class IndexedDBLogStore {
* - If B doesn't wait for A's flush to complete, B will be missing the * - If B doesn't wait for A's flush to complete, B will be missing the
* contents of A's flush. * contents of A's flush.
* To protect against this, we set 'flushPromise' when a flush is ongoing. * To protect against this, we set 'flushPromise' when a flush is ongoing.
* Subsequent calls to flush() during this period will chain another flush. * Subsequent calls to flush() during this period will chain another flush,
* This guarantees that we WILL do a brand new flush at some point in the * then keep returning that same chained flush.
* future. Once the flushes have finished, it's safe to clobber the promise
* with a new one to prevent very deep promise chains from building up.
* *
* This guarantees that we will always eventually do a flush when flush() is * This guarantees that we will always eventually do a flush when flush() is
* called. * called.
@ -178,14 +177,17 @@ class IndexedDBLogStore {
flush() { flush() {
// check if a flush() operation is ongoing // check if a flush() operation is ongoing
if (this.flushPromise && this.flushPromise.isPending()) { if (this.flushPromise && this.flushPromise.isPending()) {
// chain a flush operation after this one has completed to guarantee if (this.flushAgainPromise && this.flushAgainPromise.isPending()) {
// that a complete flush() is done. This does mean that if there are // this is the 3rd+ time we've called flush() : return the same
// 3 calls to flush() in one go, the 2nd and 3rd promises will run // promise.
// concurrently, but this is fine since they can safely race when return this.flushAgainPromise;
// collecting logs. }
return this.flushPromise.then(() => { // queue up a flush to occur immediately after the pending one
// completes.
this.flushAgainPromise = this.flushPromise.then(() => {
return this.flush(); return this.flush();
}); });
return this.flushAgainPromise;
} }
// there is no flush promise or there was but it has finished, so do // there is no flush promise or there was but it has finished, so do
// a brand new one, destroying the chain which may have been built up. // a brand new one, destroying the chain which may have been built up.