Compare commits

...

1 Commits

Author SHA1 Message Date
Julien Fontanet
abb65325b5 tmp 2019-05-15 14:07:08 +02:00
6 changed files with 127 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
const handler = {
get(target, property) {
const value = target[property]
return value !== undefined ? value : 0
},
}
export const create = () => new Proxy({ __proto__: null }, handler)

View File

@@ -0,0 +1,14 @@
{
"name": "@xen-orchestra/multi-counter",
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@xen-orchestra/multi-counter",
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
"repository": {
"directory": "@xen-orchestra/multi-counter",
"type": "git",
"url": "https://github.com/vatesfr/xen-orchestra.git"
},
"version": "0.0.0",
"scripts": {
"postversion": "npm publish"
}
}

View File

@@ -92,6 +92,7 @@
"make-error": "^1",
"micromatch": "^3.1.4",
"minimist": "^1.2.0",
"mnemonist": "^0.27.2",
"moment-timezone": "^0.5.14",
"ms": "^2.1.1",
"multikey-hash": "^1.0.4",

View File

@@ -1,3 +1,4 @@
import * as MultiCounter from '@xen-orchestra/multi-counter'
import aclResolver from 'xo-acl-resolver'
import { forEach, includes, map } from 'lodash'
@@ -6,9 +7,11 @@ import { Acls } from '../models/acl'
// ===================================================================
// TODO add cache per user
export default class {
constructor(xo) {
this._xo = xo
this._cacheByObjectByUser = { __proto__: null }
const aclsDb = (this._acls = new Acls({
connection: xo._redis,
@@ -16,6 +19,20 @@ export default class {
indexes: ['subject', 'object'],
}))
const nSessionsByUser = MultiCounter.create()
xo.on('session.open', session => {
const userId = session.get('user_id')
if (nSessionsByUser[userId]++ === 0) {
this._cacheByObjectByUser[userId] = { __proto__: null }
}
})
xo.on('session.close', session => {
const userId = session.get('user_id')
if (--nSessionsByUser[userId] === 0) {
delete this._cacheByObjectByUser[userId]
}
})
xo.on('start', () => {
xo.addConfigManager(
'acls',

View File

@@ -0,0 +1,75 @@
import BloomFilter from 'mnemonist/bloom-filter'
import forOwn from 'lodash/forOwn'
import iteratee from 'lodash/iteratee'
const BLOOM_FILTER_CAPACITY = 50
export default class Subscriptions {
constructor(app) {
this._app = app
this._permCacheByUser = { __proto__: null }
this._objCacheBySession = { __proto__: null }
this._predicatesBySession = { __proto__: null }
}
async subscribe(sessionId, userId, filter) {
const predicatesBySession = this._predicatesBySession
const predicates =
predicatesBySession[sessionId] ??
(predicatesBySession[sessionId] = { __proto__: null })
const subscriptionId = Math.random()
.toString(36)
.slice(2)
const predicate = iteratee(filter)
predicates[subscriptionId] = predicate
const objCacheBySession = this._objCacheBySession
const objCache =
objCacheBySession[sessionId] ??
(objCacheBySession[sessionId] = { __proto__: null })
const objects = this.getObjects()
const ids = Object.keys(objects)
await Promise.all(
ids.map(async id => {
if (!(await this.hasPermissions(userId, [[id, 'view']]))) {
// user cannot see this object
return
}
const cache =
objCache[id] ??
(objCache[id] = new BloomFilter(BLOOM_FILTER_CAPACITY))
cache.add(subscriptionId)
})
)
return subscriptionId
}
unsubscribe(sessionId, userId, subscriptionId) {
const predicates = this._predicatesBySession[sessionId]
if (predicates === undefined || !(subscriptionId in predicates)) {
return
}
delete predicates[subscriptionId]
const objCache = this._objCacheBySession[sessionId]
forOwn(objCache, (cache, id) => {
if (!cache.test(subscriptionId)) {
// not handled by this subscription
return
}
const object = this.getObject(id)
cache = objCache[id] = new BloomFilter(BLOOM_FILTER_CAPACITY)
forOwn(predicates, (predicate, subscriptionId) => {
if (predicate(object)) {
cache.add(subscriptionId)
}
})
})
}
}

View File

@@ -9408,6 +9408,13 @@ mkdirp@0.5, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1:
dependencies:
minimist "0.0.8"
mnemonist@^0.27.2:
version "0.27.2"
resolved "https://registry.yarnpkg.com/mnemonist/-/mnemonist-0.27.2.tgz#c56a8ad5345b9089604a1aa0af8610480717e40a"
integrity sha512-dJOzzaEZtSR23m8e/kTg0SNvV9WdDeYwRFQwT/6yb79z2rwXnd6L2xZkUBuArmBOug7xoKfImk/In1TC/Ry8lg==
dependencies:
obliterator "^1.5.0"
modular-css-core@^12.1.3:
version "12.1.3"
resolved "https://registry.yarnpkg.com/modular-css-core/-/modular-css-core-12.1.3.tgz#801fbe5e7412ba897b40a1982a9b99134ffe4990"
@@ -10122,6 +10129,11 @@ object.values@^1.0.4, object.values@^1.1.0:
function-bind "^1.1.1"
has "^1.0.3"
obliterator@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-1.5.0.tgz#f3535e5be192473ef59efb2d30396738f7c645c6"
integrity sha512-dENe0UviDf8/auXn0bIBKwCcUr49khvSBWDLlszv/ZB2qz1VxWDmkNKFqO2nfmve7hQb/QIDY7+rc7K3LdJimQ==
on-finished@~2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"