Fix missing transactions

This commit is contained in:
Chocobozzz 2021-06-15 09:17:19 +02:00
parent 51f636ad0f
commit eae0365b5c
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
14 changed files with 65 additions and 69 deletions

View File

@ -176,7 +176,7 @@ async function removeOrRejectFollower (req: express.Request, res: express.Respon
async function acceptFollower (req: express.Request, res: express.Response) {
const follow = res.locals.follow
await sendAccept(follow)
sendAccept(follow)
follow.state = 'accepted'
await follow.save()

View File

@ -105,9 +105,9 @@ function acceptOwnership (req: express.Request, res: express.Response) {
const channel = res.locals.videoChannel
// We need more attributes for federation
const targetVideo = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoChangeOwnership.Video.id)
const targetVideo = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoChangeOwnership.Video.id, t)
const oldVideoChannel = await VideoChannelModel.loadAndPopulateAccount(targetVideo.channelId)
const oldVideoChannel = await VideoChannelModel.loadAndPopulateAccount(targetVideo.channelId, t)
targetVideo.channelId = channel.id

View File

@ -1,12 +1,13 @@
import { MActorFollowActors } from '../../types/models'
import { Transaction } from 'sequelize'
import { getServerActor } from '@server/models/application/application'
import { logger } from '../../helpers/logger'
import { CONFIG } from '../../initializers/config'
import { SERVER_ACTOR_NAME } from '../../initializers/constants'
import { JobQueue } from '../job-queue'
import { logger } from '../../helpers/logger'
import { ServerModel } from '../../models/server/server'
import { getServerActor } from '@server/models/application/application'
import { MActorFollowActors } from '../../types/models'
import { JobQueue } from '../job-queue'
async function autoFollowBackIfNeeded (actorFollow: MActorFollowActors) {
async function autoFollowBackIfNeeded (actorFollow: MActorFollowActors, transaction?: Transaction) {
if (!CONFIG.FOLLOWINGS.INSTANCE.AUTO_FOLLOW_BACK.ENABLED) return
const follower = actorFollow.ActorFollower
@ -16,7 +17,7 @@ async function autoFollowBackIfNeeded (actorFollow: MActorFollowActors) {
const me = await getServerActor()
const server = await ServerModel.load(follower.serverId)
const server = await ServerModel.load(follower.serverId, transaction)
const host = server.host
const payload = {

View File

@ -16,7 +16,6 @@ import {
MChannelActor,
MCommentOwnerVideo
} from '../../../types/models'
import { markCommentAsDeleted } from '../../video-comment'
import { forwardVideoRelatedActivity } from '../send/utils'
async function processDeleteActivity (options: APProcessorOptions<ActivityDelete>) {
@ -139,11 +138,9 @@ function processDeleteVideoComment (byActor: MActorSignature, videoComment: MCom
throw new Error(`Account ${byActor.url} does not own video comment ${videoComment.url} or video ${videoComment.Video.url}`)
}
await sequelizeTypescript.transaction(async t => {
markCommentAsDeleted(videoComment)
videoComment.markAsDeleted()
await videoComment.save()
})
await videoComment.save({ transaction: t })
if (videoComment.Video.isOwned()) {
// Don't resend the activity to the sender

View File

@ -43,7 +43,7 @@ async function processFollow (byActor: MActorSignature, activityId: string, targ
if (isFollowingInstance && CONFIG.FOLLOWERS.INSTANCE.ENABLED === false) {
logger.info('Rejecting %s because instance followers are disabled.', targetActor.url)
await sendReject(activityId, byActor, targetActor)
sendReject(activityId, byActor, targetActor)
return { actorFollow: undefined as MActorFollowActors }
}
@ -84,8 +84,9 @@ async function processFollow (byActor: MActorSignature, activityId: string, targ
// Target sends to actor he accepted the follow request
if (actorFollow.state === 'accepted') {
await sendAccept(actorFollow)
await autoFollowBackIfNeeded(actorFollow)
sendAccept(actorFollow)
await autoFollowBackIfNeeded(actorFollow, t)
}
return { actorFollow, created, isFollowingInstance, targetActor }

View File

@ -106,7 +106,7 @@ async function processUndoCacheFile (byActor: MActorSignature, activity: Activit
const { video } = await getOrCreateAPVideo({ videoObject: cacheFileObject.object })
return sequelizeTypescript.transaction(async t => {
const cacheFile = await VideoRedundancyModel.loadByUrl(cacheFileObject.id)
const cacheFile = await VideoRedundancyModel.loadByUrl(cacheFileObject.id, t)
if (!cacheFile) {
logger.debug('Cannot undo unknown video cache %s.', cacheFileObject.id)
return
@ -114,7 +114,7 @@ async function processUndoCacheFile (byActor: MActorSignature, activity: Activit
if (cacheFile.actorId !== byActor.id) throw new Error('Cannot delete redundancy ' + cacheFile.url + ' of another actor.')
await cacheFile.destroy()
await cacheFile.destroy({ transaction: t })
if (video.isOwned()) {
// Don't resend the activity to the sender

View File

@ -151,35 +151,31 @@ async function onVideoFileOptimizer (
// Outside the transaction (IO on disk)
const { videoFileResolution, isPortraitMode } = await videoArg.getMaxQualityResolution()
const { videoDatabase, videoPublished } = await sequelizeTypescript.transaction(async t => {
// Maybe the video changed in database, refresh it
const videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoArg.uuid, t)
// Video does not exist anymore
if (!videoDatabase) return undefined
// Maybe the video changed in database, refresh it
const videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoArg.uuid)
// Video does not exist anymore
if (!videoDatabase) return undefined
let videoPublished = false
let videoPublished = false
// Generate HLS version of the original file
const originalFileHLSPayload = Object.assign({}, payload, {
isPortraitMode,
resolution: videoDatabase.getMaxQualityFile().resolution,
// If we quick transcoded original file, force transcoding for HLS to avoid some weird playback issues
copyCodecs: transcodeType !== 'quick-transcode',
isMaxQuality: true
})
const hasHls = await createHlsJobIfEnabled(user, originalFileHLSPayload)
const hasNewResolutions = await createLowerResolutionsJobs(videoDatabase, user, videoFileResolution, isPortraitMode, 'webtorrent')
if (!hasHls && !hasNewResolutions) {
// No transcoding to do, it's now published
videoPublished = await videoDatabase.publishIfNeededAndSave(t)
}
await federateVideoIfNeeded(videoDatabase, payload.isNewVideo, t)
return { videoDatabase, videoPublished }
// Generate HLS version of the original file
const originalFileHLSPayload = Object.assign({}, payload, {
isPortraitMode,
resolution: videoDatabase.getMaxQualityFile().resolution,
// If we quick transcoded original file, force transcoding for HLS to avoid some weird playback issues
copyCodecs: transcodeType !== 'quick-transcode',
isMaxQuality: true
})
const hasHls = await createHlsJobIfEnabled(user, originalFileHLSPayload)
const hasNewResolutions = await createLowerResolutionsJobs(videoDatabase, user, videoFileResolution, isPortraitMode, 'webtorrent')
if (!hasHls && !hasNewResolutions) {
// No transcoding to do, it's now published
videoPublished = await videoDatabase.publishIfNeededAndSave(undefined)
}
await federateVideoIfNeeded(videoDatabase, payload.isNewVideo)
if (payload.isNewVideo) Notifier.Instance.notifyOnNewVideoIfNeeded(videoDatabase)
if (videoPublished) Notifier.Instance.notifyOnVideoPublishedAfterTranscoding(videoDatabase)

View File

@ -221,7 +221,7 @@ async function createAbuse (options: {
const { isOwned } = await associateFun(abuseInstance)
if (isOwned === false) {
await sendAbuse(reporterAccount.Actor, abuseInstance, abuseInstance.FlaggedAccount, transaction)
sendAbuse(reporterAccount.Actor, abuseInstance, abuseInstance.FlaggedAccount, transaction)
}
const abuseJSON = abuseInstance.toFormattedAdminJSON()

View File

@ -44,11 +44,11 @@ async function createUserAccountAndChannelAndPlaylist (parameters: {
displayName: userDisplayName,
userId: userCreated.id,
applicationId: null,
t: t
t
})
userCreated.Account = accountCreated
const channelAttributes = await buildChannelAttributes(userCreated, channelNames)
const channelAttributes = await buildChannelAttributes(userCreated, t, channelNames)
const videoChannel = await createLocalVideoChannel(channelAttributes, accountCreated, t)
const videoPlaylist = await createWatchLaterPlaylist(accountCreated, t)
@ -203,13 +203,13 @@ function createDefaultUserNotificationSettings (user: MUserId, t: Transaction |
return UserNotificationSettingModel.create(values, { transaction: t })
}
async function buildChannelAttributes (user: MUser, channelNames?: ChannelNames) {
async function buildChannelAttributes (user: MUser, transaction?: Transaction, channelNames?: ChannelNames) {
if (channelNames) return channelNames
let channelName = user.username + '_channel'
// Conflict, generate uuid instead
const actor = await ActorModel.loadLocalByName(channelName)
const actor = await ActorModel.loadLocalByName(channelName, transaction)
if (actor) channelName = uuidv4()
const videoChannelDisplayName = `Main ${user.username} channel`

View File

@ -18,9 +18,9 @@ async function removeComment (videoCommentInstance: MCommentOwnerVideo) {
await sendDeleteVideoComment(videoCommentInstance, t)
}
markCommentAsDeleted(videoCommentInstance)
videoCommentInstance.markAsDeleted()
await videoCommentInstance.save()
await videoCommentInstance.save({ transaction: t })
})
logger.info('Video comment %d deleted.', videoCommentInstance.id)
@ -95,17 +95,10 @@ function buildFormattedCommentTree (resultList: ResultList<VideoCommentModel>):
return thread
}
function markCommentAsDeleted (comment: MComment): void {
comment.text = ''
comment.deletedAt = new Date()
comment.accountId = null
}
// ---------------------------------------------------------------------------
export {
removeComment,
createVideoComment,
buildFormattedCommentTree,
markCommentAsDeleted
buildFormattedCommentTree
}

View File

@ -1,3 +1,4 @@
import { Transaction } from 'sequelize'
import { AllowNull, Column, CreatedAt, Default, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { MServer, MServerFormattable } from '@server/types/models/server'
import { AttributesOnly } from '@shared/core-utils'
@ -51,11 +52,12 @@ export class ServerModel extends Model<Partial<AttributesOnly<ServerModel>>> {
})
BlockedByAccounts: ServerBlocklistModel[]
static load (id: number): Promise<MServer> {
static load (id: number, transaction?: Transaction): Promise<MServer> {
const query = {
where: {
id
}
},
transaction
}
return ServerModel.findOne(query)

View File

@ -91,9 +91,9 @@ export class VideoCaptionModel extends Model<Partial<AttributesOnly<VideoCaption
Video: VideoModel
@BeforeDestroy
static async removeFiles (instance: VideoCaptionModel) {
static async removeFiles (instance: VideoCaptionModel, options) {
if (!instance.Video) {
instance.Video = await instance.$get('Video')
instance.Video = await instance.$get('Video', { transaction: options.transaction })
}
if (instance.isOwned()) {
@ -113,8 +113,7 @@ export class VideoCaptionModel extends Model<Partial<AttributesOnly<VideoCaption
const videoInclude = {
model: VideoModel.unscoped(),
attributes: [ 'id', 'remote', 'uuid' ],
where: buildWhereIdOrUUID(videoId),
transaction
where: buildWhereIdOrUUID(videoId)
}
const query = {
@ -123,7 +122,8 @@ export class VideoCaptionModel extends Model<Partial<AttributesOnly<VideoCaption
},
include: [
videoInclude
]
],
transaction
}
return VideoCaptionModel.findOne(query)

View File

@ -522,10 +522,10 @@ ON "Account->Actor"."serverId" = "Account->Actor->Server"."id"`
})
}
static loadAndPopulateAccount (id: number): Promise<MChannelBannerAccountDefault> {
static loadAndPopulateAccount (id: number, transaction?: Transaction): Promise<MChannelBannerAccountDefault> {
return VideoChannelModel.unscoped()
.scope([ ScopeNames.WITH_ACTOR_BANNER, ScopeNames.WITH_ACCOUNT ])
.findByPk(id)
.findByPk(id, { transaction })
}
static loadByUrlAndPopulateAccount (url: string): Promise<MChannelBannerAccountDefault> {

View File

@ -739,6 +739,12 @@ export class VideoCommentModel extends Model<Partial<AttributesOnly<VideoComment
return this.Account.isOwned()
}
markAsDeleted () {
this.text = ''
this.deletedAt = new Date()
this.accountId = null
}
isDeleted () {
return this.deletedAt !== null
}