Compare commits
1 Commits
pierre-fix
...
xo-server-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
abb65325b5 |
8
@xen-orchestra/multi-counter/index.js
Normal file
8
@xen-orchestra/multi-counter/index.js
Normal 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)
|
||||
14
@xen-orchestra/multi-counter/package.json
Normal file
14
@xen-orchestra/multi-counter/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
@@ -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',
|
||||
|
||||
75
packages/xo-server/src/xo-mixins/subscriptions.js
Normal file
75
packages/xo-server/src/xo-mixins/subscriptions.js
Normal 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)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
12
yarn.lock
12
yarn.lock
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user