89 lines
2.3 KiB
JavaScript
89 lines
2.3 KiB
JavaScript
require('bare-process/global')
|
|
const EventEmitter = require('bare-events')
|
|
const { assertNonEmpty } = require('../../_shared/lib/errors.js')
|
|
|
|
const PROTOCOL = 'sla-budget/v1'
|
|
|
|
class HyperP2PSlaBudget extends EventEmitter {
|
|
constructor (opts = {}) {
|
|
super()
|
|
this._services = new Map()
|
|
this._stats = { allocated: 0, consumed: 0, rejected: 0 }
|
|
}
|
|
|
|
allocate (service, tokens) {
|
|
assertNonEmpty(service, 'service')
|
|
const amount = Number(tokens)
|
|
if (!Number.isFinite(amount) || amount < 0) {
|
|
throw new Error('tokens must be a non-negative number')
|
|
}
|
|
const cur = this._services.get(service) || { remaining: 0, total: 0 }
|
|
cur.remaining += amount
|
|
cur.total += amount
|
|
this._services.set(service, cur)
|
|
this._stats.allocated++
|
|
this.emit('allocate', { service, tokens: amount, remaining: cur.remaining })
|
|
return cur.remaining
|
|
}
|
|
|
|
consume (service, n = 1) {
|
|
assertNonEmpty(service, 'service')
|
|
const amount = Number(n)
|
|
if (!Number.isFinite(amount) || amount < 0) {
|
|
throw new Error('n must be a non-negative number')
|
|
}
|
|
const cur = this._services.get(service)
|
|
if (!cur || cur.remaining < amount) {
|
|
this._stats.rejected++
|
|
this.emit('reject', { service, requested: amount, remaining: cur ? cur.remaining : 0 })
|
|
return false
|
|
}
|
|
cur.remaining -= amount
|
|
this._stats.consumed++
|
|
this.emit('consume', { service, n: amount, remaining: cur.remaining })
|
|
return true
|
|
}
|
|
|
|
remaining (service) {
|
|
assertNonEmpty(service, 'service')
|
|
const cur = this._services.get(service)
|
|
return cur ? cur.remaining : 0
|
|
}
|
|
|
|
services () {
|
|
return [...this._services.keys()].sort()
|
|
}
|
|
|
|
snapshot (service) {
|
|
const cur = this._services.get(service)
|
|
if (!cur) return null
|
|
return { service, remaining: cur.remaining, total: cur.total, used: cur.total - cur.remaining }
|
|
}
|
|
|
|
reset (service) {
|
|
if (service) {
|
|
this._services.delete(service)
|
|
return true
|
|
}
|
|
this._services.clear()
|
|
return true
|
|
}
|
|
|
|
getStats () {
|
|
return {
|
|
...this._stats,
|
|
services: this._services.size,
|
|
protocol: PROTOCOL
|
|
}
|
|
}
|
|
|
|
async ready () { return this }
|
|
|
|
async close () {
|
|
this._services.clear()
|
|
this.emit('closed')
|
|
}
|
|
}
|
|
|
|
module.exports = { HyperP2PSlaBudget, PROTOCOL }
|