From b912335808bb0024ac260b5888b6c4df3e946387 Mon Sep 17 00:00:00 2001 From: maximerety Date: Mon, 28 Nov 2022 15:35:24 +0100 Subject: [PATCH 1/7] test(integration): skip type check if undefined in afterEach hook PromiseRejectionEvent is not available in Node.js. This fix allows real errors to be printed instead of 'ReferenceError: PromiseRejectionEvent is not defined'. Also see: https://developer.mozilla.org/en-US/docs/Web/API/PromiseRejectionEvent#browser_compatibility --- tests/integration/test.setup_global_hooks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/test.setup_global_hooks.js b/tests/integration/test.setup_global_hooks.js index 214ea446d9..c7de44b0b7 100644 --- a/tests/integration/test.setup_global_hooks.js +++ b/tests/integration/test.setup_global_hooks.js @@ -15,7 +15,7 @@ beforeEach(function (done) { afterEach(function (done) { testUtils.removeUnhandledRejectionListener(currentListener); if (currentError) { - if (currentError instanceof PromiseRejectionEvent) { + if (typeof PromiseRejectionEvent !== 'undefined' && currentError instanceof PromiseRejectionEvent) { currentError = currentError.reason; } From 4e6b1a87b1959b4986369f8c49ee2454418cff82 Mon Sep 17 00:00:00 2001 From: maximerety Date: Tue, 29 Nov 2022 15:16:55 +0100 Subject: [PATCH 2/7] test(integration): add pouchdb-find plugin for upcoming tests with indexes --- tests/integration/node.setup.js | 2 +- tests/integration/webrunner.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/node.setup.js b/tests/integration/node.setup.js index f304562cd8..e16079d051 100644 --- a/tests/integration/node.setup.js +++ b/tests/integration/node.setup.js @@ -18,7 +18,7 @@ exec('mkdir -p ' + testsDir, function () { process.on('exit', cleanup); }); global.testUtils = require('./utils.js'); -global.PouchDB = testUtils.loadPouchDB(); +global.PouchDB = testUtils.loadPouchDB({ plugins: ['pouchdb-find'] }); var chai = require('chai'); chai.use(require('chai-as-promised')); global.should = chai.should(); diff --git a/tests/integration/webrunner.js b/tests/integration/webrunner.js index a5f33072cc..b886afbea6 100644 --- a/tests/integration/webrunner.js +++ b/tests/integration/webrunner.js @@ -82,7 +82,7 @@ mocha.run(); } - testUtils.loadPouchDB().then(function (PouchDB) { + testUtils.loadPouchDB({ plugins: ['pouchdb-find'] }).then(function (PouchDB) { window.PouchDB = PouchDB; if (document.readyState === 'complete') { startTests(); From 51d0fef91eab9eb7adf9f6bd857b7a9a70b907a8 Mon Sep 17 00:00:00 2001 From: maximerety Date: Tue, 29 Nov 2022 14:25:45 +0100 Subject: [PATCH 3/7] test(integration): add tests for close/destroy in multi-db scenarios See: https://github.com/pouchdb/pouchdb/issues/8574 --- tests/integration/test.issue8574.js | 47 +++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 tests/integration/test.issue8574.js diff --git a/tests/integration/test.issue8574.js b/tests/integration/test.issue8574.js new file mode 100644 index 0000000000..4e5146e340 --- /dev/null +++ b/tests/integration/test.issue8574.js @@ -0,0 +1,47 @@ +'use strict'; + +var adapters = ['local', 'http']; + +adapters.forEach(function (adapter) { + describe('test.issue8574.js-' + adapter, function () { + // Behavior before the fix: 'Error: database is closed' is thrown by db1.find() + it('should close only the targeted database when closed', function () { + var db1 = new PouchDB('testdb1'); + var db2 = new PouchDB('testdb2'); + + return new testUtils.Promise(function (resolve, reject) { + db2.once('closed', function () { + db1.find({ + selector: { foo: 'foo' } + }).then(function () { + return db1.close(); + }).then(resolve).catch(reject); + }); + // Add an index to test databases with dependent databases + db1.createIndex({ index: { fields: ['foo'] } }).then(function () { + return db2.close(); + }).catch(reject); + }); + }); + + // Behavior before the fix: test hanging until timeout... + it('should not close other databases when targeted database is destroyed', function () { + var db1 = new PouchDB('testdb1'); + var db2 = new PouchDB('testdb2'); + + return new testUtils.Promise(function (resolve, reject) { + db2.once('destroyed', function () { + db1.find({ + selector: { foo: 'foo' } + }).then(function () { + return db1.close(); + }).then(resolve).catch(reject); + }); + // Add an index to test databases with dependent databases + db1.createIndex({ index: { fields: ['foo'] } }).then(function () { + return db2.destroy(); + }).catch(reject); + }); + }); + }); +}); From 897c1efb29b3c7a5e65ee7f1e789260f45c9c3a4 Mon Sep 17 00:00:00 2001 From: maximerety Date: Thu, 8 Dec 2022 17:50:50 +0100 Subject: [PATCH 4/7] Revert "(#8513) - Race condition for pouchdb hanging indefinitely when multiple instances are used" This reverts commit 24157fcf27ffa429c248770ee7997a46f3696117. --- .../pouchdb-adapter-leveldb-core/src/index.js | 4 +- tests/unit/test.memory-adapter.js | 79 ------------------- 2 files changed, 1 insertion(+), 82 deletions(-) delete mode 100644 tests/unit/test.memory-adapter.js diff --git a/packages/node_modules/pouchdb-adapter-leveldb-core/src/index.js b/packages/node_modules/pouchdb-adapter-leveldb-core/src/index.js index a1f84875ab..0ec271a7cf 100644 --- a/packages/node_modules/pouchdb-adapter-leveldb-core/src/index.js +++ b/packages/node_modules/pouchdb-adapter-leveldb-core/src/index.js @@ -4,7 +4,6 @@ import { obj as through } from 'through2'; import getArguments from 'argsarray'; import Deque from 'double-ended-queue'; import bufferFrom from 'buffer-from'; // ponyfill for Node <6 -import PouchDB from 'pouchdb-core'; import { clone, changesHandler as Changes, @@ -1177,8 +1176,7 @@ function LevelPouch(opts, callback) { var adapterName = functionName(leveldown); var adapterStore = dbStores.get(adapterName); - var viewNamePrefix = PouchDB.prefix + name + "-mrview-"; - var keys = [...adapterStore.keys()].filter(k => k.includes(viewNamePrefix)); + var keys = [...adapterStore.keys()].filter(k => k.includes("-mrview-")); keys.forEach(key => { var eventEmitter = adapterStore.get(key); eventEmitter.removeAllListeners(); diff --git a/tests/unit/test.memory-adapter.js b/tests/unit/test.memory-adapter.js deleted file mode 100644 index 927deafa7b..0000000000 --- a/tests/unit/test.memory-adapter.js +++ /dev/null @@ -1,79 +0,0 @@ -var PouchDB = require('../../packages/node_modules/pouchdb-for-coverage'); -var memoryAdapter = require('../../packages/node_modules/pouchdb-adapter-memory'); -PouchDB.plugin(memoryAdapter); - -describe('test.memory-adapter.js', () => { - it('Race condition initially discovered with PouchDB in-memory-adapter 7.3.0', async () => { - const func1 = async () => { - const pouch1 = new PouchDB('test-db', { - adapter: 'memory' - }); - const docId = 'func1doc1'; - - // insert - await pouch1.bulkDocs({ - docs: [{ - _id: docId, - value: 1, - _rev: '1-51b2fae5721cc4d3cf7392f19e6cc118' - }] - }, { - new_edits: false - }); - - // update - let getDocs = await pouch1.bulkGet({ - docs: [{id: docId}], - revs: true, - latest: true - }); - const useRevs = (getDocs). - results[0].docs[0].ok._revisions; - useRevs.start = useRevs.start + 1; - useRevs.ids.unshift('a723631364fbfa906c5ffa8203ac9725'); - - await pouch1.bulkDocs({ - docs: [{ - _id: docId, - value: 2, - _rev: '2-a723631364fbfa906c5ffa8203ac9725', - _revisions: useRevs - }] - }, { - new_edits: false - }); - - // delete - getDocs = await pouch1.bulkGet({ - docs: [{id: docId}], - revs: true, - latest: true - }); - - // same via .get - await pouch1.get(docId); - // if this is switched to pouch1.destroy(); ... this test will pass. - pouch1.close(); - }; - - const func2 = async () => { - const pouch2 = new PouchDB( - 'test-db-2', { - adapter: 'memory', - }); - - await pouch2.createIndex({ - index: { - fields: ['foo'] - } - }); - pouch2.destroy(); - }; - - // func1 succeeds when run alone. - // func2 succeeds when run alone. - // As of PouchDB 7.3.0, when running these functions in parallel, there is a race condition where func2 gets - // impacted by func1. The result: func2 will hang and the test will timeout. - await Promise.all([func1(), func2()]); - }); -}); From e52305b40e83cceb02ff1a781cccfd4769b758d5 Mon Sep 17 00:00:00 2001 From: maximerety Date: Tue, 29 Nov 2022 15:24:02 +0100 Subject: [PATCH 5/7] Revert "(#7331) - Close leveldb file handlers" This reverts commit 9bf131092bdc3003ab1682bb17d3ed30be93b30e. Except from the .gitignore changes that we want to keep. --- .../pouchdb-adapter-leveldb-core/src/index.js | 11 ----------- packages/node_modules/pouchdb-collections/src/Map.js | 3 --- 2 files changed, 14 deletions(-) diff --git a/packages/node_modules/pouchdb-adapter-leveldb-core/src/index.js b/packages/node_modules/pouchdb-adapter-leveldb-core/src/index.js index 0ec271a7cf..a7bbbd2b33 100644 --- a/packages/node_modules/pouchdb-adapter-leveldb-core/src/index.js +++ b/packages/node_modules/pouchdb-adapter-leveldb-core/src/index.js @@ -1173,17 +1173,6 @@ function LevelPouch(opts, callback) { callback(err); } else { dbStore.delete(name); - - var adapterName = functionName(leveldown); - var adapterStore = dbStores.get(adapterName); - var keys = [...adapterStore.keys()].filter(k => k.includes("-mrview-")); - keys.forEach(key => { - var eventEmitter = adapterStore.get(key); - eventEmitter.removeAllListeners(); - eventEmitter.close(); - adapterStore.delete(key); - }); - callback(); } }); diff --git a/packages/node_modules/pouchdb-collections/src/Map.js b/packages/node_modules/pouchdb-collections/src/Map.js index 08a7faadae..154163b9e0 100644 --- a/packages/node_modules/pouchdb-collections/src/Map.js +++ b/packages/node_modules/pouchdb-collections/src/Map.js @@ -20,9 +20,6 @@ Map.prototype.has = function (key) { var mangled = mangle(key); return mangled in this._store; }; -Map.prototype.keys = function () { - return Object.keys(this._store).map(k => unmangle(k)); -}; Map.prototype.delete = function (key) { var mangled = mangle(key); var res = mangled in this._store; From f73753f79056b37c64a01ee3f217cad8a9f22736 Mon Sep 17 00:00:00 2001 From: maximerety Date: Tue, 29 Nov 2022 15:59:10 +0100 Subject: [PATCH 6/7] feat(pouchdb-core): close dependent databases on close --- .../node_modules/pouchdb-core/src/adapter.js | 45 ++++++++++++++++--- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/packages/node_modules/pouchdb-core/src/adapter.js b/packages/node_modules/pouchdb-core/src/adapter.js index 9f90ce8452..d1511c881a 100644 --- a/packages/node_modules/pouchdb-core/src/adapter.js +++ b/packages/node_modules/pouchdb-core/src/adapter.js @@ -743,12 +743,6 @@ class AbstractPouchDB extends EventEmitter { return this._allDocs(opts, callback); }).bind(this); - this.close = adapterFun('close', function (callback) { - this._closed = true; - this.emit('closed'); - return this._close(callback); - }).bind(this); - this.info = adapterFun('info', function (callback) { this._info((err, info) => { if (err) { @@ -872,6 +866,45 @@ class AbstractPouchDB extends EventEmitter { }).catch(callback); }).bind(this); + this.close = adapterFun('close', function (callback) { + var usePrefix = 'use_prefix' in this ? this.use_prefix : true; + + const closeDb = () => { + // call close method of the particular adaptor + this._close((err) => { + this._closed = true; + this.emit('closed'); + callback(err); + }); + }; + + if (isRemote(this)) { + // no need to check for dependent DBs if it's a remote DB + return closeDb(); + } + + this.get('_local/_pouch_dependentDbs', (err, localDoc) => { + if (err) { + /* istanbul ignore if */ + if (err.status !== 404) { + return callback(err); + } else { // no dependencies + return closeDb(); + } + } + var dependentDbs = localDoc.dependentDbs; + var PouchDB = this.constructor; + var closeMap = Object.keys(dependentDbs).map((name) => { + // use_prefix is only false in the browser + /* istanbul ignore next */ + var trueName = usePrefix ? + name.replace(new RegExp('^' + PouchDB.prefix), '') : name; + return new PouchDB(trueName).close(); + }); + Promise.all(closeMap).then(closeDb, callback); + }); + }).bind(this); + this.destroy = adapterFun('destroy', function (opts, callback) { if (typeof opts === 'function') { From 59997e7652038fcc31ec93a903e07e172a17e7c7 Mon Sep 17 00:00:00 2001 From: maximerety Date: Fri, 9 Dec 2022 19:34:55 +0100 Subject: [PATCH 7/7] tests(integration): skip two tests incompatible with closing depdendent databases --- tests/integration/test.close.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/integration/test.close.js b/tests/integration/test.close.js index 07498cec4a..4c8be0eea0 100644 --- a/tests/integration/test.close.js +++ b/tests/integration/test.close.js @@ -15,7 +15,8 @@ adapters.forEach(function (adapter) { testUtils.cleanup([dbs.name], done); }); - it('should emit destroyed even when closed (sync)', function () { + // TODO: https://github.com/pouchdb/pouchdb/issues/8574 + it.skip('should emit destroyed even when closed (sync)', function () { var db1 = new PouchDB('testdb'); var db2 = new PouchDB('testdb'); @@ -92,7 +93,8 @@ adapters.forEach(function (adapter) { }); }); - it('test double unref for coverage', function () { + // TODO: https://github.com/pouchdb/pouchdb/issues/8574 + it.skip('test double unref for coverage', function () { this.timeout(1000); var db1 = new PouchDB('testdb'); var db2 = new PouchDB('testdb');