aedes-cached-persistence
Version:
Abstract class to write an Aedes persistence with in-process caching of subscriptions
232 lines (199 loc) • 6.27 kB
JavaScript
'use strict'
/* This module provides a callback layer for async persistence implementations */
const { Readable } = require('node:stream')
const CachedPersistence = require('./index.js')
function toValue (obj, prop) {
if (typeof obj === 'object' && obj !== null && prop in obj) {
return obj[prop]
}
return obj
}
class CallBackPersistence extends CachedPersistence {
constructor (asyncInstanceFactory, opts = {}) {
super(opts)
this.asyncPersistence = asyncInstanceFactory(opts)
}
_setup () {
if (this.ready) {
return
}
this.asyncPersistence.broker = this.broker
this.asyncPersistence._trie = this._trie
this.asyncPersistence.setup()
.then(() => {
this.emit('ready')
})
.catch(err => {
this.emit('error', err)
})
}
storeRetained (packet, cb) {
if (!this.ready) {
this.once('ready', this.storeRetained.bind(this, packet, cb))
return
}
this.asyncPersistence.storeRetained(packet).then(() => {
cb(null)
}).catch(cb)
}
createRetainedStream (pattern) {
return Readable.from(this.asyncPersistence.createRetainedStream(pattern))
}
createRetainedStreamCombi (patterns) {
return Readable.from(this.asyncPersistence.createRetainedStreamCombi(patterns))
}
addSubscriptions (client, subs, cb) {
if (!this.ready) {
this.once('ready', this.addSubscriptions.bind(this, client, subs, cb))
return
}
this.asyncPersistence.addSubscriptions(client, subs)
.then(() => {
this._addedSubscriptions(client, subs, () => cb(null, client))
})
.catch(err => cb(err, client))
}
removeSubscriptions (client, subs, cb) {
if (!this.ready) {
this.once('ready', this.removeSubscriptions.bind(this, client, subs, cb))
return
}
this.asyncPersistence.removeSubscriptions(client, subs)
.then(() => {
const mappedSubs = subs.map(sub => { return { topic: sub } })
this._removedSubscriptions(client, mappedSubs, (err) => cb(err, client))
})
.catch(err => cb(err, client))
}
subscriptionsByClient (client, cb) {
if (!this.ready) {
this.once('ready', this.subscriptionsByClient.bind(this, client, cb))
return
}
this.asyncPersistence.subscriptionsByClient(client)
.then(results => {
// promisified shim returns an object, true async only the resubs
const resubs = results?.resubs || results
process.nextTick(cb, null, resubs.length > 0 ? resubs : null, client)
})
.catch(cb)
}
countOffline (cb) {
this.asyncPersistence.countOffline()
.then(res => process.nextTick(cb, null, res.subsCount, res.clientsCount))
.catch(cb)
}
destroy (cb = noop) {
if (!this.ready) {
this.once('ready', this.destroy.bind(this, cb))
return
}
if (this._destroyed) {
throw new Error('destroyed called twice!')
}
this._destroyed = true
this.asyncPersistence.destroy()
.finally(cb) // swallow err in case of failure
}
outgoingEnqueue (sub, packet, cb) {
if (!this.ready) {
this.once('ready', this.outgoingEnqueue.bind(this, sub, packet, cb))
return
}
this.asyncPersistence.outgoingEnqueue(sub, packet)
.then(() => process.nextTick(cb, null, packet))
.catch(cb)
}
outgoingEnqueueCombi (subs, packet, cb) {
if (!this.ready) {
this.once('ready', this.outgoingEnqueueCombi.bind(this, subs, packet, cb))
return
}
this.asyncPersistence.outgoingEnqueueCombi(subs, packet)
.then(() => process.nextTick(cb, null, packet))
.catch(cb)
}
outgoingStream (client) {
return Readable.from(this.asyncPersistence.outgoingStream(client))
}
outgoingUpdate (client, packet, cb) {
if (!this.ready) {
this.once('ready', this.outgoingUpdate.bind(this, client, packet, cb))
return
}
this.asyncPersistence.outgoingUpdate(client, packet)
.then(() => cb(null, client, packet))
.catch(cb)
}
outgoingClearMessageId (client, packet, cb) {
if (!this.ready) {
this.once('ready', this.outgoingClearMessageId.bind(this, client, packet, cb))
return
}
this.asyncPersistence.outgoingClearMessageId(client, packet)
.then((packet) => cb(null, packet))
.catch(cb)
}
incomingStorePacket (client, packet, cb) {
if (!this.ready) {
this.once('ready', this.incomingStorePacket.bind(this, client, packet, cb))
return
}
this.asyncPersistence.incomingStorePacket(client, packet)
.then(() => cb(null))
.catch(cb)
}
incomingGetPacket (client, packet, cb) {
if (!this.ready) {
this.once('ready', this.incomingGetPacket.bind(this, client, packet, cb))
return
}
this.asyncPersistence.incomingGetPacket(client, packet)
.then((packet) => cb(null, packet, client))
.catch(cb)
}
incomingDelPacket (client, packet, cb) {
if (!this.ready) {
this.once('ready', this.incomingDelPacket.bind(this, client, packet, cb))
return
}
this.asyncPersistence.incomingDelPacket(client, packet)
.then(() => cb(null))
.catch(cb)
}
putWill (client, packet, cb) {
if (!this.ready) {
this.once('ready', this.putWill.bind(this, client, packet, cb))
return
}
this.asyncPersistence.putWill(client, packet)
.then(() => cb(null, client))
.catch(cb)
}
getWill (client, cb) {
this.asyncPersistence.getWill(client)
.then((result) => {
// promisified shim returns an object, true async only the resubs
const packet = toValue(result, 'packet')
cb(null, packet, client)
})
.catch(cb)
}
delWill (client, cb) {
this.asyncPersistence.delWill(client)
.then(result => {
// promisified shim returns an object, true async only the resubs
const packet = toValue(result, 'packet')
cb(null, packet, client)
})
.catch(cb)
}
streamWill (brokers) {
return Readable.from(this.asyncPersistence.streamWill(brokers))
}
getClientList (topic) {
return Readable.from(this.asyncPersistence.getClientList(topic))
}
}
function noop () {}
module.exports = { CallBackPersistence }