--- /dev/null
+export class PromiseCache <A, R> {
+ private readonly running = new Map<string, Promise<R>>()
+
+ constructor (
+ private readonly fn: (arg: A) => Promise<R>,
+ private readonly keyBuilder: (arg: A) => string
+ ) {
+ }
+
+ run (arg: A) {
+ const key = this.keyBuilder(arg)
+
+ if (this.running.has(key)) return this.running.get(key)
+
+ const p = this.fn(arg)
+
+ this.running.set(key, p)
+
+ return p.finally(() => this.running.delete(key))
+ }
+}
if (actor.Account) (actor as MActorAccountChannelIdActor).Account.Actor = actor
if (actor.VideoChannel) (actor as MActorAccountChannelIdActor).VideoChannel.Actor = actor
- const { actor: actorRefreshed, refreshed } = await refreshActorIfNeeded(actor, fetchType)
+ const { actor: actorRefreshed, refreshed } = await refreshActorIfNeeded({ actor, fetchedType: fetchType })
if (!actorRefreshed) throw new Error('Actor ' + actor.url + ' does not exist anymore.')
await scheduleOutboxFetchIfNeeded(actor, created, refreshed, updateCollections)
import { logger, loggerTagsFactory } from '@server/helpers/logger'
+import { PromiseCache } from '@server/helpers/promise-cache'
import { PeerTubeRequestError } from '@server/helpers/requests'
import { ActorLoadByUrlType } from '@server/lib/model-loaders'
import { ActorModel } from '@server/models/actor/actor'
import { APActorUpdater } from './updater'
import { getUrlFromWebfinger } from './webfinger'
-async function refreshActorIfNeeded <T extends MActorFull | MActorAccountChannelId> (
- actorArg: T,
+type RefreshResult <T> = Promise<{ actor: T | MActorFull, refreshed: boolean }>
+
+type RefreshOptions <T> = {
+ actor: T
fetchedType: ActorLoadByUrlType
-): Promise<{ actor: T | MActorFull, refreshed: boolean }> {
- if (!actorArg.isOutdated()) return { actor: actorArg, refreshed: false }
+}
+
+const promiseCache = new PromiseCache(doRefresh, (options: RefreshOptions<MActorFull | MActorAccountChannelId>) => options.actor.url)
+
+function refreshActorIfNeeded <T extends MActorFull | MActorAccountChannelId> (options: RefreshOptions<T>): RefreshResult <T> {
+ const actorArg = options.actor
+ if (!actorArg.isOutdated()) return Promise.resolve({ actor: actorArg, refreshed: false })
+
+ return promiseCache.run(options)
+}
+
+export {
+ refreshActorIfNeeded
+}
+
+// ---------------------------------------------------------------------------
+
+async function doRefresh <T extends MActorFull | MActorAccountChannelId> (options: RefreshOptions<T>): RefreshResult <MActorFull> {
+ const { actor: actorArg, fetchedType } = options
// We need more attributes
const actor = fetchedType === 'all'
}
}
-export {
- refreshActorIfNeeded
-}
-
-// ---------------------------------------------------------------------------
-
function getActorUrl (actor: MActorFull) {
return getUrlFromWebfinger(actor.preferredUsername + '@' + actor.getHost())
.catch(err => {