errorFilter: err => {
const willRetry = (err.name === 'SequelizeDatabaseError')
- logger.debug('Maybe retrying the transaction function.', { willRetry, err })
+ logger.debug('Maybe retrying the transaction function.', { willRetry, err, tags: [ 'sql', 'retry' ] })
return willRetry
}
},
})
}
+// ---------------------------------------------------------------------------
+
function updateInstanceWithAnother <M, T extends U, U extends Model<M>> (instanceToUpdate: T, baseInstance: U) {
const obj = baseInstance.toJSON()
})
}
-function afterCommitIfTransaction (t: Transaction, fn: Function) {
- if (t) return t.afterCommit(() => fn())
-
- return fn()
-}
-
function deleteNonExistingModels <T extends { hasSameUniqueKeysThan (other: T): boolean } & Pick<Model, 'destroy'>> (
fromDatabase: T[],
newModels: T[],
// ---------------------------------------------------------------------------
+function runInReadCommittedTransaction <T> (fn: (t: Transaction) => Promise<T>) {
+ return sequelizeTypescript.transaction(t => fn(t))
+}
+
+function afterCommitIfTransaction (t: Transaction, fn: Function) {
+ if (t) return t.afterCommit(() => fn())
+
+ return fn()
+}
+
+// ---------------------------------------------------------------------------
+
export {
resetSequelizeInstance,
retryTransactionWrapper,
updateInstanceWithAnother,
afterCommitIfTransaction,
deleteNonExistingModels,
- setAsUpdated
+ setAsUpdated,
+ runInReadCommittedTransaction
}
if (actor.Account) (actor as MActorAccountChannelIdActor).Account.Actor = actor
if (actor.VideoChannel) (actor as MActorAccountChannelIdActor).VideoChannel.Actor = actor
- const { actor: actorRefreshed, refreshed } = await retryTransactionWrapper(refreshActorIfNeeded, actor, fetchType)
+ const { actor: actorRefreshed, refreshed } = await refreshActorIfNeeded(actor, fetchType)
if (!actorRefreshed) throw new Error('Actor ' + actor.url + ' does not exist anymore.')
await scheduleOutboxFetchIfNeeded(actor, created, refreshed, updateCollections)
-import { resetSequelizeInstance } from '@server/helpers/database-utils'
+import { resetSequelizeInstance, runInReadCommittedTransaction } from '@server/helpers/database-utils'
import { logger } from '@server/helpers/logger'
-import { sequelizeTypescript } from '@server/initializers/database'
import { VideoChannelModel } from '@server/models/video/video-channel'
import { MAccount, MActor, MActorFull, MChannel } from '@server/types/models'
import { ActivityPubActor, ActorImageType } from '@shared/models'
const bannerInfo = getImageInfoFromObject(this.actorObject, ActorImageType.BANNER)
try {
- await sequelizeTypescript.transaction(async t => {
- await this.updateActorInstance(this.actor, this.actorObject)
+ await this.updateActorInstance(this.actor, this.actorObject)
- await updateActorImageInstance(this.actor, ActorImageType.AVATAR, avatarInfo, t)
- await updateActorImageInstance(this.actor, ActorImageType.BANNER, bannerInfo, t)
-
- await this.actor.save({ transaction: t })
-
- this.accountOrChannel.name = this.actorObject.name || this.actorObject.preferredUsername
- this.accountOrChannel.description = this.actorObject.summary
+ this.accountOrChannel.name = this.actorObject.name || this.actorObject.preferredUsername
+ this.accountOrChannel.description = this.actorObject.summary
- if (this.accountOrChannel instanceof VideoChannelModel) this.accountOrChannel.support = this.actorObject.support
+ if (this.accountOrChannel instanceof VideoChannelModel) this.accountOrChannel.support = this.actorObject.support
+ await runInReadCommittedTransaction(async t => {
+ await this.actor.save({ transaction: t })
await this.accountOrChannel.save({ transaction: t })
})
+ await runInReadCommittedTransaction(async t => {
+ await updateActorImageInstance(this.actor, ActorImageType.AVATAR, avatarInfo, t)
+ await updateActorImageInstance(this.actor, ActorImageType.BANNER, bannerInfo, t)
+ })
+
logger.info('Remote account %s updated', this.actorObject.url)
} catch (err) {
if (this.actor !== undefined && this.actorFieldsSave !== undefined) {
? activity.object
: (activity.object as ViewObject).object
- const options = {
+ const { video } = await getOrCreateAPVideo({
videoObject,
- fetchType: 'only-video' as 'only-video',
- allowRefresh: false as false
- }
- const { video } = await getOrCreateAPVideo(options)
+ fetchType: 'only-video',
+ allowRefresh: false
+ })
if (!video.isLive) {
await Redis.Instance.addVideoView(video.id)
})
}
- protected async setPreview (video: MVideoFullLight, t: Transaction) {
+ protected async setPreview (video: MVideoFullLight, t?: Transaction) {
// Don't fetch the preview that could be big, create a placeholder instead
const previewIcon = getPreviewFromIcons(this.videoObject)
if (!previewIcon) return
async function setVideoTrackers (options: {
video: MVideo
trackers: string[]
- transaction?: Transaction
+ transaction: Transaction
}) {
const { video, trackers, transaction } = options
import { Transaction } from 'sequelize/types'
-import { resetSequelizeInstance } from '@server/helpers/database-utils'
+import { resetSequelizeInstance, runInReadCommittedTransaction } from '@server/helpers/database-utils'
import { logger, loggerTagsFactory, LoggerTagsFn } from '@server/helpers/logger'
-import { sequelizeTypescript } from '@server/initializers/database'
import { Notifier } from '@server/lib/notifier'
import { PeerTubeSocket } from '@server/lib/peertube-socket'
import { autoBlacklistVideoIfNeeded } from '@server/lib/video-blacklist'
const thumbnailModel = await this.tryToGenerateThumbnail(this.video)
- const videoUpdated = await sequelizeTypescript.transaction(async t => {
- this.checkChannelUpdateOrThrow(channelActor)
+ this.checkChannelUpdateOrThrow(channelActor)
- const videoUpdated = await this.updateVideo(channelActor.VideoChannel, t, overrideTo)
+ const videoUpdated = await this.updateVideo(channelActor.VideoChannel, undefined, overrideTo)
- if (thumbnailModel) await videoUpdated.addAndSaveThumbnail(thumbnailModel, t)
+ if (thumbnailModel) await videoUpdated.addAndSaveThumbnail(thumbnailModel)
- await this.setPreview(videoUpdated, t)
+ await runInReadCommittedTransaction(async t => {
await this.setWebTorrentFiles(videoUpdated, t)
await this.setStreamingPlaylists(videoUpdated, t)
- await this.setTags(videoUpdated, t)
- await this.setTrackers(videoUpdated, t)
- await this.setCaptions(videoUpdated, t)
- await this.setOrDeleteLive(videoUpdated, t)
-
- return videoUpdated
})
+ await Promise.all([
+ runInReadCommittedTransaction(t => this.setTags(videoUpdated, t)),
+ runInReadCommittedTransaction(t => this.setTrackers(videoUpdated, t)),
+ this.setOrDeleteLive(videoUpdated),
+ this.setPreview(videoUpdated)
+ ])
+
+ await runInReadCommittedTransaction(t => this.setCaptions(videoUpdated, t))
+
await autoBlacklistVideoIfNeeded({
video: videoUpdated,
user: undefined,
}
}
- private updateVideo (channel: MChannelId, transaction: Transaction, overrideTo?: string[]) {
+ private updateVideo (channel: MChannelId, transaction?: Transaction, overrideTo?: string[]) {
const to = overrideTo || this.videoObject.to
const videoData = getVideoAttributesFromObject(channel, this.videoObject, to)
this.video.name = videoData.name
await this.insertOrReplaceCaptions(videoUpdated, t)
}
- private async setOrDeleteLive (videoUpdated: MVideoFullLight, transaction: Transaction) {
+ private async setOrDeleteLive (videoUpdated: MVideoFullLight, transaction?: Transaction) {
+ if (!this.video.isLive) return
+
if (this.video.isLive) return this.insertOrReplaceLive(videoUpdated, transaction)
// Delete existing live if it exists
return Array.isArray(this.VideoFiles) === true && this.VideoFiles.length !== 0
}
- async addAndSaveThumbnail (thumbnail: MThumbnail, transaction: Transaction) {
+ async addAndSaveThumbnail (thumbnail: MThumbnail, transaction?: Transaction) {
thumbnail.videoId = this.id
const savedThumbnail = await thumbnail.save({ transaction })