diff options
author | Chocobozzz <me@florianbigard.com> | 2020-02-04 11:26:51 +0100 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2020-02-04 11:26:51 +0100 |
commit | 0ffd6d32c13b3b59f96a212ebfd324ba06cbdf1f (patch) | |
tree | cdc6a598e1f274cae7db15e66ad52962a85ee248 | |
parent | 9a11f73392928c81d4a478191e126d3ec754f781 (diff) | |
download | PeerTube-0ffd6d32c13b3b59f96a212ebfd324ba06cbdf1f.tar.gz PeerTube-0ffd6d32c13b3b59f96a212ebfd324ba06cbdf1f.tar.zst PeerTube-0ffd6d32c13b3b59f96a212ebfd324ba06cbdf1f.zip |
Use a singleton for model cache
-rw-r--r-- | server/models/account/account.ts | 67 | ||||
-rw-r--r-- | server/models/activitypub/actor.ts | 78 | ||||
-rw-r--r-- | server/models/model-cache.ts | 54 |
3 files changed, 122 insertions, 77 deletions
diff --git a/server/models/account/account.ts b/server/models/account/account.ts index 0905a0fb2..a0081f259 100644 --- a/server/models/account/account.ts +++ b/server/models/account/account.ts | |||
@@ -32,8 +32,9 @@ import { FindOptions, IncludeOptions, Op, Transaction, WhereOptions } from 'sequ | |||
32 | import { AccountBlocklistModel } from './account-blocklist' | 32 | import { AccountBlocklistModel } from './account-blocklist' |
33 | import { ServerBlocklistModel } from '../server/server-blocklist' | 33 | import { ServerBlocklistModel } from '../server/server-blocklist' |
34 | import { ActorFollowModel } from '../activitypub/actor-follow' | 34 | import { ActorFollowModel } from '../activitypub/actor-follow' |
35 | import { MAccountActor, MAccountDefault, MAccountSummaryFormattable, MAccountFormattable, MAccountAP } from '../../typings/models' | 35 | import { MAccountActor, MAccountAP, MAccountDefault, MAccountFormattable, MAccountSummaryFormattable } from '../../typings/models' |
36 | import * as Bluebird from 'bluebird' | 36 | import * as Bluebird from 'bluebird' |
37 | import { ModelCache } from '@server/models/model-cache' | ||
37 | 38 | ||
38 | export enum ScopeNames { | 39 | export enum ScopeNames { |
39 | SUMMARY = 'SUMMARY' | 40 | SUMMARY = 'SUMMARY' |
@@ -218,8 +219,6 @@ export class AccountModel extends Model<AccountModel> { | |||
218 | }) | 219 | }) |
219 | BlockedAccounts: AccountBlocklistModel[] | 220 | BlockedAccounts: AccountBlocklistModel[] |
220 | 221 | ||
221 | private static cache: { [ id: string ]: any } = {} | ||
222 | |||
223 | @BeforeDestroy | 222 | @BeforeDestroy |
224 | static async sendDeleteIfOwned (instance: AccountModel, options) { | 223 | static async sendDeleteIfOwned (instance: AccountModel, options) { |
225 | if (!instance.Actor) { | 224 | if (!instance.Actor) { |
@@ -247,45 +246,43 @@ export class AccountModel extends Model<AccountModel> { | |||
247 | } | 246 | } |
248 | 247 | ||
249 | static loadLocalByName (name: string): Bluebird<MAccountDefault> { | 248 | static loadLocalByName (name: string): Bluebird<MAccountDefault> { |
250 | // The server actor never change, so we can easily cache it | 249 | const fun = () => { |
251 | if (name === SERVER_ACTOR_NAME && AccountModel.cache[name]) { | 250 | const query = { |
252 | return Bluebird.resolve(AccountModel.cache[name]) | 251 | where: { |
253 | } | 252 | [Op.or]: [ |
254 | 253 | { | |
255 | const query = { | 254 | userId: { |
256 | where: { | 255 | [Op.ne]: null |
257 | [Op.or]: [ | 256 | } |
258 | { | 257 | }, |
259 | userId: { | 258 | { |
260 | [Op.ne]: null | 259 | applicationId: { |
260 | [Op.ne]: null | ||
261 | } | ||
261 | } | 262 | } |
262 | }, | 263 | ] |
264 | }, | ||
265 | include: [ | ||
263 | { | 266 | { |
264 | applicationId: { | 267 | model: ActorModel, |
265 | [Op.ne]: null | 268 | required: true, |
269 | where: { | ||
270 | preferredUsername: name | ||
266 | } | 271 | } |
267 | } | 272 | } |
268 | ] | 273 | ] |
269 | }, | 274 | } |
270 | include: [ | ||
271 | { | ||
272 | model: ActorModel, | ||
273 | required: true, | ||
274 | where: { | ||
275 | preferredUsername: name | ||
276 | } | ||
277 | } | ||
278 | ] | ||
279 | } | ||
280 | 275 | ||
281 | return AccountModel.findOne(query) | 276 | return AccountModel.findOne(query) |
282 | .then(account => { | 277 | } |
283 | if (name === SERVER_ACTOR_NAME) { | ||
284 | AccountModel.cache[name] = account | ||
285 | } | ||
286 | 278 | ||
287 | return account | 279 | return ModelCache.Instance.doCache({ |
288 | }) | 280 | cacheType: 'local-account-name', |
281 | key: name, | ||
282 | fun, | ||
283 | // The server actor never change, so we can easily cache it | ||
284 | whitelist: () => name === SERVER_ACTOR_NAME | ||
285 | }) | ||
289 | } | 286 | } |
290 | 287 | ||
291 | static loadByNameAndHost (name: string, host: string): Bluebird<MAccountDefault> { | 288 | static loadByNameAndHost (name: string, host: string): Bluebird<MAccountDefault> { |
diff --git a/server/models/activitypub/actor.ts b/server/models/activitypub/actor.ts index 00e8dc954..9e8303a7b 100644 --- a/server/models/activitypub/actor.ts +++ b/server/models/activitypub/actor.ts | |||
@@ -48,6 +48,7 @@ import { | |||
48 | } from '../../typings/models' | 48 | } from '../../typings/models' |
49 | import * as Bluebird from 'bluebird' | 49 | import * as Bluebird from 'bluebird' |
50 | import { Op, Transaction, literal } from 'sequelize' | 50 | import { Op, Transaction, literal } from 'sequelize' |
51 | import { ModelCache } from '@server/models/model-cache' | ||
51 | 52 | ||
52 | enum ScopeNames { | 53 | enum ScopeNames { |
53 | FULL = 'FULL' | 54 | FULL = 'FULL' |
@@ -276,9 +277,6 @@ export class ActorModel extends Model<ActorModel> { | |||
276 | }) | 277 | }) |
277 | VideoChannel: VideoChannelModel | 278 | VideoChannel: VideoChannelModel |
278 | 279 | ||
279 | private static localNameCache: { [ id: string ]: any } = {} | ||
280 | private static localUrlCache: { [ id: string ]: any } = {} | ||
281 | |||
282 | static load (id: number): Bluebird<MActor> { | 280 | static load (id: number): Bluebird<MActor> { |
283 | return ActorModel.unscoped().findByPk(id) | 281 | return ActorModel.unscoped().findByPk(id) |
284 | } | 282 | } |
@@ -345,54 +343,50 @@ export class ActorModel extends Model<ActorModel> { | |||
345 | } | 343 | } |
346 | 344 | ||
347 | static loadLocalByName (preferredUsername: string, transaction?: Transaction): Bluebird<MActorFull> { | 345 | static loadLocalByName (preferredUsername: string, transaction?: Transaction): Bluebird<MActorFull> { |
348 | // The server actor never change, so we can easily cache it | 346 | const fun = () => { |
349 | if (preferredUsername === SERVER_ACTOR_NAME && ActorModel.localNameCache[preferredUsername]) { | 347 | const query = { |
350 | return Bluebird.resolve(ActorModel.localNameCache[preferredUsername]) | 348 | where: { |
351 | } | 349 | preferredUsername, |
350 | serverId: null | ||
351 | }, | ||
352 | transaction | ||
353 | } | ||
352 | 354 | ||
353 | const query = { | 355 | return ActorModel.scope(ScopeNames.FULL) |
354 | where: { | 356 | .findOne(query) |
355 | preferredUsername, | ||
356 | serverId: null | ||
357 | }, | ||
358 | transaction | ||
359 | } | 357 | } |
360 | 358 | ||
361 | return ActorModel.scope(ScopeNames.FULL) | 359 | return ModelCache.Instance.doCache({ |
362 | .findOne(query) | 360 | cacheType: 'local-actor-name', |
363 | .then(actor => { | 361 | key: preferredUsername, |
364 | if (preferredUsername === SERVER_ACTOR_NAME) { | 362 | // The server actor never change, so we can easily cache it |
365 | ActorModel.localNameCache[preferredUsername] = actor | 363 | whitelist: () => preferredUsername === SERVER_ACTOR_NAME, |
366 | } | 364 | fun |
367 | 365 | }) | |
368 | return actor | ||
369 | }) | ||
370 | } | 366 | } |
371 | 367 | ||
372 | static loadLocalUrlByName (preferredUsername: string, transaction?: Transaction): Bluebird<MActorUrl> { | 368 | static loadLocalUrlByName (preferredUsername: string, transaction?: Transaction): Bluebird<MActorUrl> { |
373 | // The server actor never change, so we can easily cache it | 369 | const fun = () => { |
374 | if (preferredUsername === SERVER_ACTOR_NAME && ActorModel.localUrlCache[preferredUsername]) { | 370 | const query = { |
375 | return Bluebird.resolve(ActorModel.localUrlCache[preferredUsername]) | 371 | attributes: [ 'url' ], |
376 | } | 372 | where: { |
373 | preferredUsername, | ||
374 | serverId: null | ||
375 | }, | ||
376 | transaction | ||
377 | } | ||
377 | 378 | ||
378 | const query = { | 379 | return ActorModel.unscoped() |
379 | attributes: [ 'url' ], | 380 | .findOne(query) |
380 | where: { | ||
381 | preferredUsername, | ||
382 | serverId: null | ||
383 | }, | ||
384 | transaction | ||
385 | } | 381 | } |
386 | 382 | ||
387 | return ActorModel.unscoped() | 383 | return ModelCache.Instance.doCache({ |
388 | .findOne(query) | 384 | cacheType: 'local-actor-name', |
389 | .then(actor => { | 385 | key: preferredUsername, |
390 | if (preferredUsername === SERVER_ACTOR_NAME) { | 386 | // The server actor never change, so we can easily cache it |
391 | ActorModel.localUrlCache[preferredUsername] = actor | 387 | whitelist: () => preferredUsername === SERVER_ACTOR_NAME, |
392 | } | 388 | fun |
393 | 389 | }) | |
394 | return actor | ||
395 | }) | ||
396 | } | 390 | } |
397 | 391 | ||
398 | static loadByNameAndHost (preferredUsername: string, host: string): Bluebird<MActorFull> { | 392 | static loadByNameAndHost (preferredUsername: string, host: string): Bluebird<MActorFull> { |
diff --git a/server/models/model-cache.ts b/server/models/model-cache.ts new file mode 100644 index 000000000..bfa163b6b --- /dev/null +++ b/server/models/model-cache.ts | |||
@@ -0,0 +1,54 @@ | |||
1 | import { Model } from 'sequelize-typescript' | ||
2 | import * as Bluebird from 'bluebird' | ||
3 | import { logger } from '@server/helpers/logger' | ||
4 | |||
5 | type ModelCacheType = | ||
6 | 'local-account-name' | ||
7 | | 'local-actor-name' | ||
8 | | 'local-actor-url' | ||
9 | |||
10 | class ModelCache { | ||
11 | |||
12 | private static instance: ModelCache | ||
13 | |||
14 | private readonly localCache: { [id in ModelCacheType]: Map<string, any> } = { | ||
15 | 'local-account-name': new Map(), | ||
16 | 'local-actor-name': new Map(), | ||
17 | 'local-actor-url': new Map() | ||
18 | } | ||
19 | |||
20 | private constructor () { | ||
21 | } | ||
22 | |||
23 | static get Instance () { | ||
24 | return this.instance || (this.instance = new this()) | ||
25 | } | ||
26 | |||
27 | doCache<T extends Model> (options: { | ||
28 | cacheType: ModelCacheType | ||
29 | key: string | ||
30 | fun: () => Bluebird<T> | ||
31 | whitelist?: () => boolean | ||
32 | }) { | ||
33 | const { cacheType, key, fun, whitelist } = options | ||
34 | |||
35 | if (whitelist && whitelist() !== true) return fun() | ||
36 | |||
37 | const cache = this.localCache[cacheType] | ||
38 | |||
39 | if (cache.has(key)) { | ||
40 | logger.debug('Model cache hit for %s -> %s.', cacheType, key) | ||
41 | return Bluebird.resolve<T>(cache.get(key)) | ||
42 | } | ||
43 | |||
44 | return fun().then(m => { | ||
45 | if (!whitelist || whitelist()) cache.set(key, m) | ||
46 | |||
47 | return m | ||
48 | }) | ||
49 | } | ||
50 | } | ||
51 | |||
52 | export { | ||
53 | ModelCache | ||
54 | } | ||