diff --git a/README.md b/README.md index 7f50fac..bcf2054 100644 --- a/README.md +++ b/README.md @@ -848,8 +848,6 @@ Lastly, one way or another, every implementation _must_ support `data` of type S An `abstract-level` database is an [`EventEmitter`](https://nodejs.org/api/events.html) and emits the events listed below. -The `put`, `del` and `batch` events are deprecated in favor of the `write` event and will be removed in a future version of `abstract-level`. If one or more `write` event listeners exist or if the [`prewrite`](#hook--dbhooksprewrite) hook is in use, either of which implies opting-in to the `write` event, then the deprecated events will not be emitted. - #### `opening` Emitted when database is opening. Receives 0 arguments: @@ -967,42 +965,6 @@ The same is true for `db.put()` and `db.del()`. Emitted when a `db.clear()` call completed and entries were thus successfully deleted from the database. Receives a single `options` argument, which is the verbatim `options` argument that was passed to `db.clear(options)` (or an empty object if none) before having encoded range options. -#### `put` (deprecated) - -Emitted when a `db.put()` call completed and an entry was thus successfully written to the database. Receives `key` and `value` arguments, which are the verbatim `key` and `value` that were passed to `db.put(key, value)` before having encoded them. - -```js -db.on('put', function (key, value) { - console.log('Wrote', key, value) -}) -``` - -#### `del` (deprecated) - -Emitted when a `db.del()` call completed and an entry was thus successfully deleted from the database. Receives a single `key` argument, which is the verbatim `key` that was passed to `db.del(key)` before having encoded it. - -```js -db.on('del', function (key) { - console.log('Deleted', key) -}) -``` - -#### `batch` (deprecated) - -Emitted when a `db.batch([])` or chained `db.batch().write()` call completed and the data was thus successfully written to the database. Receives a single `operations` argument, which is the verbatim `operations` array that was passed to `db.batch(operations)` before having encoded it, or the equivalent for a chained `db.batch().write()`. - -```js -db.on('batch', function (operations) { - for (const op of operations) { - if (op.type === 'put') { - console.log('Wrote', op.key, op.value) - } else { - console.log('Deleted', op.key) - } - } -}) -``` - ### Order Of Operations There is no defined order between parallel write operations. Consider: diff --git a/abstract-chained-batch.js b/abstract-chained-batch.js index e586642..3811a4e 100644 --- a/abstract-chained-batch.js +++ b/abstract-chained-batch.js @@ -14,7 +14,6 @@ class AbstractChainedBatch { #length = 0 #closePromise = null #publicOperations - #legacyOperations #prewriteRun #prewriteBatch #prewriteData @@ -33,11 +32,6 @@ class AbstractChainedBatch { // operations, which is the expensive part) if there are 0 write event listeners. this.#publicOperations = enableWriteEvent ? [] : null - // Operations for legacy batch event. If user opted-in to write event or prewrite - // hook, skip legacy batch event. We can't skip the batch event based on listener - // count, because a listener may be added between put() or del() and write(). - this.#legacyOperations = enableWriteEvent || enablePrewriteHook ? null : [] - this.#addMode = getOptions(options, emptyOptions).add === true if (enablePrewriteHook) { @@ -73,7 +67,6 @@ class AbstractChainedBatch { const delegated = options.sublevel != null const db = delegated ? options.sublevel : this.db - const original = options db._assertValidKey(key) db._assertValidValue(value) @@ -146,14 +139,6 @@ class AbstractChainedBatch { } this.#publicOperations.push(publicOperation) - } else if (this.#legacyOperations !== null && !siblings) { - const legacyOperation = { ...original } - - legacyOperation.type = 'put' - legacyOperation.key = key - legacyOperation.value = value - - this.#legacyOperations.push(legacyOperation) } // If we're forwarding the sublevel option then don't prefix the key yet @@ -182,7 +167,6 @@ class AbstractChainedBatch { const delegated = options.sublevel != null const db = delegated ? options.sublevel : this.db - const original = options db._assertValidKey(key) @@ -228,13 +212,6 @@ class AbstractChainedBatch { } this.#publicOperations.push(publicOperation) - } else if (this.#legacyOperations !== null) { - const legacyOperation = { ...original } - - legacyOperation.type = 'del' - legacyOperation.key = key - - this.#legacyOperations.push(legacyOperation) } op.key = this.db.prefixKey(encodedKey, keyFormat, true) @@ -261,7 +238,6 @@ class AbstractChainedBatch { this._clear() if (this.#publicOperations !== null) this.#publicOperations = [] - if (this.#legacyOperations !== null) this.#legacyOperations = [] if (this.#prewriteData !== null) this.#prewriteData.clear() this.#length = 0 @@ -330,8 +306,6 @@ class AbstractChainedBatch { // db close which in turn triggers (idempotently) closing this batch. if (this.#publicOperations !== null) { this.db.emit('write', this.#publicOperations) - } else if (this.#legacyOperations !== null) { - this.db.emit('batch', this.#legacyOperations) } return this.#closePromise diff --git a/abstract-level.js b/abstract-level.js index 496a9d6..7ee7ea1 100644 --- a/abstract-level.js +++ b/abstract-level.js @@ -65,21 +65,11 @@ class AbstractLevel extends EventEmitter { closing: true, closed: true, write: true, - put: true, - del: true, - batch: true, clear: true } }) - // Monitor event listeners - this.#eventMonitor = new EventMonitor(this, [ - { name: 'write' }, - { name: 'put', deprecated: true, alt: 'write' }, - { name: 'del', deprecated: true, alt: 'write' }, - { name: 'batch', deprecated: true, alt: 'write' } - ]) - + this.#eventMonitor = new EventMonitor(this) this.#transcoder = new Transcoder(formats(this)) this.#keyEncoding = this.#transcoder.encoding(keyEncoding || 'utf8') this.#valueEncoding = this.#transcoder.encoding(valueEncoding || 'utf8') @@ -579,9 +569,6 @@ class AbstractLevel extends EventEmitter { } this.emit('write', [op]) - } else { - // TODO (semver-major): remove - this.emit('put', key, value) } } @@ -630,9 +617,6 @@ class AbstractLevel extends EventEmitter { } this.emit('write', [op]) - } else { - // TODO (semver-major): remove - this.emit('del', key) } } @@ -783,9 +767,6 @@ class AbstractLevel extends EventEmitter { if (enableWriteEvent) { this.emit('write', publicOperations) - } else if (!enablePrewriteHook) { - // TODO (semver-major): remove - this.emit('batch', operations) } } diff --git a/lib/event-monitor.js b/lib/event-monitor.js index 311f6ba..829b79b 100644 --- a/lib/event-monitor.js +++ b/lib/event-monitor.js @@ -3,39 +3,28 @@ const { deprecate } = require('./common') exports.EventMonitor = class EventMonitor { - constructor (emitter, events) { - for (const event of events) { - // Track whether listeners are present - this[event.name] = false - - // Prepare deprecation message - if (event.deprecated) { - event.message = `The '${event.name}' event is deprecated in favor of '${event.alt}' and will be removed in a future version of abstract-level` + constructor (emitter) { + // Track whether listeners are present, because checking + // a boolean is faster than checking listenerCount(). + this.write = false + + const beforeAdded = (name) => { + if (name === 'write') { + this.write = true } - } - - const map = new Map(events.map(e => [e.name, e])) - const monitor = this - - emitter.on('newListener', beforeAdded) - emitter.on('removeListener', afterRemoved) - function beforeAdded (name) { - const event = map.get(name) - - if (event !== undefined) { - monitor[name] = true - - if (event.deprecated) { - deprecate(event.message) - } + if (name === 'put' || name === 'del' || name === 'batch') { + deprecate(`The '${name}' event has been removed in favor of 'write'`) } } - function afterRemoved (name) { - if (map.has(name)) { - monitor[name] = this.listenerCount(name) > 0 + const afterRemoved = (name) => { + if (name === 'write') { + this.write = emitter.listenerCount('write') > 0 } } + + emitter.on('newListener', beforeAdded) + emitter.on('removeListener', afterRemoved) } } diff --git a/test/batch-test.js b/test/batch-test.js index f687faa..c85d344 100644 --- a/test/batch-test.js +++ b/test/batch-test.js @@ -199,24 +199,6 @@ exports.atomic = function (test, testCommon) { }) } -exports.events = function (test, testCommon) { - test('batch([]) emits batch event', async function (t) { - t.plan(2) - - const db = testCommon.factory() - await db.open() - - t.ok(db.supports.events.batch) - - db.on('batch', function (ops) { - t.same(ops, [{ type: 'put', key: 456, value: 99, custom: 123 }]) - }) - - await db.batch([{ type: 'put', key: 456, value: 99, custom: 123 }]) - return db.close() - }) -} - exports.tearDown = function (test, testCommon) { test('batch([]) teardown', async function (t) { return db.close() @@ -228,6 +210,5 @@ exports.all = function (test, testCommon) { exports.args(test, testCommon) exports.batch(test, testCommon) exports.atomic(test, testCommon) - exports.events(test, testCommon) exports.tearDown(test, testCommon) } diff --git a/test/chained-batch-test.js b/test/chained-batch-test.js index 91201a0..f49bfd8 100644 --- a/test/chained-batch-test.js +++ b/test/chained-batch-test.js @@ -239,12 +239,15 @@ exports.batch = function (test, testCommon) { const db = testCommon.factory() await db.open() - db.once('batch', function (operations) { + const utf8 = db.keyEncoding('utf8') + const json = db.valueEncoding('json') + + db.once('write', function (operations) { t.same(operations, [ - { type: 'put', key: 'a', value: 'a', valueEncoding: 'json' }, - { type: 'put', key: 'b', value: 'b' }, - { type: 'put', key: '"c"', value: 'c' }, - { type: 'del', key: 'c', keyEncoding: 'json', arbitraryOption: true } + { type: 'put', key: 'a', value: 'a', keyEncoding: utf8, valueEncoding: json, encodedKey: 'a', encodedValue: '"a"' }, + { type: 'put', key: 'b', value: 'b', keyEncoding: utf8, valueEncoding: utf8, encodedKey: 'b', encodedValue: 'b' }, + { type: 'put', key: '"c"', value: 'c', keyEncoding: utf8, valueEncoding: utf8, encodedKey: '"c"', encodedValue: 'c' }, + { type: 'del', key: 'c', keyEncoding: json, encodedKey: '"c"', arbitraryOption: true } ]) }) @@ -265,32 +268,13 @@ exports.batch = function (test, testCommon) { } exports.events = function (test, testCommon) { - test('chained batch emits batch event', async function (t) { - t.plan(2) - - const db = testCommon.factory() - await db.open() - - t.ok(db.supports.events.batch) - - db.on('batch', function (ops) { - t.same(ops, [ - { type: 'put', key: 987, value: 'b', custom: 123 }, - { type: 'del', key: 216, custom: 999 } - ]) - }) - - await db.batch().put(987, 'b', { custom: 123 }).del(216, { custom: 999 }).write() - await db.close() - }) - - test('db.close() on chained batch event', async function (t) { + test('db.close() on chained batch write event', async function (t) { const db = testCommon.factory() await db.open() let promise - db.on('batch', function () { + db.on('write', function () { // Should not interfere with the current write() operation promise = db.close() }) diff --git a/test/del-test.js b/test/del-test.js index d25c2dd..b5a0a84 100644 --- a/test/del-test.js +++ b/test/del-test.js @@ -41,9 +41,9 @@ exports.del = function (test, testCommon) { traits.open('del()', testCommon, async function (t, db) { let emitted = false - db.once('del', () => { emitted = true }) - t.is(await assertPromise(db.del('foo')), undefined, 'void promise') - t.ok(emitted) + db.once('write', () => { emitted = true }) + t.is(await db.del('foo'), undefined, 'void promise') + t.ok(emitted) // Not sure what the purpose of this test is }) traits.closed('del()', testCommon, async function (t, db) { @@ -51,24 +51,6 @@ exports.del = function (test, testCommon) { }) } -exports.events = function (test, testCommon) { - test('del() emits del event', async function (t) { - t.plan(2) - - const db = testCommon.factory() - await db.open() - - t.ok(db.supports.events.del) - - db.on('del', function (key) { - t.is(key, 456) - }) - - await db.del(456) - return db.close() - }) -} - exports.tearDown = function (test, testCommon) { test('del() teardown', async function (t) { return db.close() @@ -79,6 +61,5 @@ exports.all = function (test, testCommon) { exports.setUp(test, testCommon) exports.args(test, testCommon) exports.del(test, testCommon) - exports.events(test, testCommon) exports.tearDown(test, testCommon) } diff --git a/test/events/write.js b/test/events/write.js index 054b789..96ad40b 100644 --- a/test/events/write.js +++ b/test/events/write.js @@ -128,59 +128,5 @@ module.exports = function (test, testCommon) { return db.close() }) } - - for (const method of allMethods) { - test(`db emits write event for ${method} operation in favor of deprecated events (deferred: ${deferred})`, async function (t) { - t.plan(5) - - const keys = [] - const db = testCommon.factory() - if (!deferred) await db.open() - - db.on('write', function (ops) { - keys.push(...ops.map(op => op.key)) - }) - - db.on('batch', function () { - t.fail('should not get batch event') - }) - - db.on('put', function () { - t.fail('should not get put event') - }) - - db.on('del', function () { - t.fail('should not get del event') - }) - - // Once we remove the deprecated events, this test would still pass, but we should then remove it. - t.ok(db.supports.events.batch, 'supports batch event') - t.ok(db.supports.events.put, 'supports put event') - t.ok(db.supports.events.del, 'supports del event') - - switch (method) { - case 'batch': - await db.batch([{ type: 'put', key: 'a', value: 'a' }]) - t.is(keys.pop(), 'a', 'got write event for batch put') - await db.batch([{ type: 'del', key: 'b' }]) - t.is(keys.pop(), 'b', 'got write event for batch del') - break - case 'chained batch': - await db.batch().put('c', 'c').write() - t.is(keys.pop(), 'c', 'got write event for chained batch put') - await db.batch().del('d').write() - t.is(keys.pop(), 'd', 'got write event for chained batch del') - break - case 'singular': - await db.put('e', 'e') - t.is(keys.pop(), 'e', 'got write event for put') - await db.del('f') - t.is(keys.pop(), 'f', 'got write event for del') - break - } - - return db.close() - }) - } } } diff --git a/test/put-test.js b/test/put-test.js index 27c4b6b..cbf380a 100644 --- a/test/put-test.js +++ b/test/put-test.js @@ -56,20 +56,6 @@ exports.put = function (test, testCommon) { }) } -exports.events = function (test, testCommon) { - test('put() emits put event', async function (t) { - t.plan(3) - t.ok(db.supports.events.put) - - db.on('put', function (key, value) { - t.is(key, 123) - t.is(value, 'b') - }) - - await db.put(123, 'b') - }) -} - exports.tearDown = function (test, testCommon) { test('put() teardown', async function (t) { return db.close() @@ -80,6 +66,5 @@ exports.all = function (test, testCommon) { exports.setUp(test, testCommon) exports.args(test, testCommon) exports.put(test, testCommon) - exports.events(test, testCommon) exports.tearDown(test, testCommon) } diff --git a/test/self/sublevel-test.js b/test/self/sublevel-test.js index b390542..5dbe2ef 100644 --- a/test/self/sublevel-test.js +++ b/test/self/sublevel-test.js @@ -58,9 +58,6 @@ test('sublevel is extensible', function (t) { closing: true, closed: true, write: true, - put: true, - del: true, - batch: true, clear: true })