From e225d3e3708bccf6cd458453206d076841011630 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 24 Jan 2017 17:05:01 +0000 Subject: [PATCH] Preserve ordering of flush()es by not letting subsequent flush()es race --- src/vector/rageshake.js | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/vector/rageshake.js b/src/vector/rageshake.js index 28f901a30..086ab1552 100644 --- a/src/vector/rageshake.js +++ b/src/vector/rageshake.js @@ -104,8 +104,9 @@ class IndexedDBLogStore { this.id = "instance-" + Math.random() + Date.now(); this.index = 0; this.db = null; - // Promise is not null when a flush is IN PROGRESS 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 * contents of A's flush. * To protect against this, we set 'flushPromise' when a flush is ongoing. - * 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 - * 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. + * Subsequent calls to flush() during this period will chain another flush, + * then keep returning that same chained flush. * * This guarantees that we will always eventually do a flush when flush() is * called. @@ -178,14 +177,17 @@ class IndexedDBLogStore { flush() { // check if a flush() operation is ongoing if (this.flushPromise && this.flushPromise.isPending()) { - // chain a flush operation after this one has completed to guarantee - // that a complete flush() is done. This does mean that if there are - // 3 calls to flush() in one go, the 2nd and 3rd promises will run - // concurrently, but this is fine since they can safely race when - // collecting logs. - return this.flushPromise.then(() => { + if (this.flushAgainPromise && this.flushAgainPromise.isPending()) { + // this is the 3rd+ time we've called flush() : return the same + // promise. + return this.flushAgainPromise; + } + // queue up a flush to occur immediately after the pending one + // completes. + this.flushAgainPromise = this.flushPromise.then(() => { return this.flush(); }); + return this.flushAgainPromise; } // 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.