aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2020-02-04 11:26:51 +0100
committerChocobozzz <me@florianbigard.com>2020-02-04 11:26:51 +0100
commit0ffd6d32c13b3b59f96a212ebfd324ba06cbdf1f (patch)
treecdc6a598e1f274cae7db15e66ad52962a85ee248
parent9a11f73392928c81d4a478191e126d3ec754f781 (diff)
downloadPeerTube-0ffd6d32c13b3b59f96a212ebfd324ba06cbdf1f.tar.gz
PeerTube-0ffd6d32c13b3b59f96a212ebfd324ba06cbdf1f.tar.zst
PeerTube-0ffd6d32c13b3b59f96a212ebfd324ba06cbdf1f.zip
Use a singleton for model cache
-rw-r--r--server/models/account/account.ts67
-rw-r--r--server/models/activitypub/actor.ts78
-rw-r--r--server/models/model-cache.ts54
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
32import { AccountBlocklistModel } from './account-blocklist' 32import { AccountBlocklistModel } from './account-blocklist'
33import { ServerBlocklistModel } from '../server/server-blocklist' 33import { ServerBlocklistModel } from '../server/server-blocklist'
34import { ActorFollowModel } from '../activitypub/actor-follow' 34import { ActorFollowModel } from '../activitypub/actor-follow'
35import { MAccountActor, MAccountDefault, MAccountSummaryFormattable, MAccountFormattable, MAccountAP } from '../../typings/models' 35import { MAccountActor, MAccountAP, MAccountDefault, MAccountFormattable, MAccountSummaryFormattable } from '../../typings/models'
36import * as Bluebird from 'bluebird' 36import * as Bluebird from 'bluebird'
37import { ModelCache } from '@server/models/model-cache'
37 38
38export enum ScopeNames { 39export 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'
49import * as Bluebird from 'bluebird' 49import * as Bluebird from 'bluebird'
50import { Op, Transaction, literal } from 'sequelize' 50import { Op, Transaction, literal } from 'sequelize'
51import { ModelCache } from '@server/models/model-cache'
51 52
52enum ScopeNames { 53enum 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 @@
1import { Model } from 'sequelize-typescript'
2import * as Bluebird from 'bluebird'
3import { logger } from '@server/helpers/logger'
4
5type ModelCacheType =
6 'local-account-name'
7 | 'local-actor-name'
8 | 'local-actor-url'
9
10class 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
52export {
53 ModelCache
54}