From 68f900096c3a76f02d234c0c77d9ce0a5e0cfc74 Mon Sep 17 00:00:00 2001 From: vinz243 Date: Tue, 24 Apr 2018 21:20:02 +0200 Subject: [PATCH] fix(ssloth-database): avoid concurrent initSetup --- src/models/SlothDatabase.ts | 122 ++++++++++++++++++++---------------- 1 file changed, 69 insertions(+), 53 deletions(-) diff --git a/src/models/SlothDatabase.ts b/src/models/SlothDatabase.ts index bc6d976..de4c71b 100644 --- a/src/models/SlothDatabase.ts +++ b/src/models/SlothDatabase.ts @@ -44,6 +44,8 @@ export default class SlothDatabase< changes: PouchDB.Core.Changes }[] = [] + private _setupPromise?: Promise + /** * Create a new database instance * @param factory the pouch factory to use @@ -57,47 +59,81 @@ export default class SlothDatabase< const { name } = getProtoData(model.prototype) + /* istanbul ignore if */ if (!name) { throw new Error('SlothEntity decorator is required') } this._name = name } - /** - * Queries and maps docs to Entity objects + * Run a query * * @param factory the pouch factory * @param view the view identifier * @param startKey the optional startkey * @param endKey the optional endkey + * @param includeDocs include_docs */ - queryDocs( + query( factory: PouchFactory, view: V, startKey = '', - endKey = join(startKey, '\uffff') - ): Promise { + endKey = join(startKey, '\uffff'), + includeDocs = false + ): Promise> { return factory(this._name) - .query(view, { + .query(view, { startkey: startKey, endkey: endKey, - include_docs: true - }) - .then(({ rows }) => { - return rows.map(({ doc }) => new this._model(factory, doc as any)) + include_docs: includeDocs }) .catch(err => { if (err.name === 'not_found') { debug(`Design document '%s' is missing, generating views...`, view) - return this.initSetup(factory).then(() => { + + /* istanbul ignore if */ + if (this._setupPromise) { + this._setupPromise.then(() => { + return this.query(factory, view, startKey, endKey, includeDocs) + }) + } + + this._setupPromise = this.initSetup(factory) + + return this._setupPromise.then(() => { debug('Created design documents') - return this.queryDocs(factory, view, startKey, endKey) + this._setupPromise = undefined + return this.query(factory, view, startKey, endKey, includeDocs) }) } throw err }) } + /** + * Queries and maps docs to Entity objects + * + * @param factory the pouch factory + * @param view the view identifier + * @param startKey the optional startkey + * @param endKey the optional endkey + */ + queryDocs( + factory: PouchFactory, + view: V, + startKey = '', + endKey = join(startKey, '\uffff') + ): Promise { + return this.query( + factory, + view, + startKey, + endKey, + true + ).then(({ rows }) => { + return rows.map(({ doc }) => new this._model(factory, doc as any)) + }) + } /** * Queries keys. Returns an array of emitted keys @@ -113,25 +149,15 @@ export default class SlothDatabase< startKey = '', endKey = join(startKey, '\uffff') ): Promise { - return factory(this._name) - .query(view, { - startkey: startKey, - endkey: endKey, - include_docs: false - }) - .then(({ rows }) => { - return rows.map(({ key }) => key) - }) - .catch(err => { - if (err.name === 'not_found') { - debug(`Design document '%s' is missing, generating views...`, view) - return this.initSetup(factory).then(() => { - debug('Created design documents') - return this.queryKeys(factory, view, startKey, endKey) - }) - } - throw err - }) + return this.query( + factory, + view, + startKey, + endKey, + false + ).then(({ rows }) => { + return rows.map(({ key }) => key) + }) } /** @@ -148,28 +174,18 @@ export default class SlothDatabase< startKey = '', endKey = join(startKey, '\uffff') ): Promise> { - return factory(this._name) - .query(view, { - startkey: startKey, - endkey: endKey, - include_docs: false - }) - .then(({ rows }) => { - return rows.reduce( - (acc, { key, id }) => ({ ...acc, [key]: id }), - {} as Dict - ) - }) - .catch(err => { - if (err.name === 'not_found') { - debug(`Design document '%s' is missing, generating views...`, view) - return this.initSetup(factory).then(() => { - debug('Created design documents') - return this.queryKeysIDs(factory, view, startKey, endKey) - }) - } - throw err - }) + return this.query( + factory, + view, + startKey, + endKey, + false + ).then(({ rows }) => { + return rows.reduce( + (acc, { key, id }) => ({ ...acc, [key]: id }), + {} as Dict + ) + }) } /**