Prevent concurrent video update

This commit is contained in:
Chocobozzz 2021-11-10 14:57:09 +01:00
parent 5cf027bdc4
commit a2a81f5a7f
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
1 changed files with 18 additions and 15 deletions

View File

@ -52,16 +52,16 @@ export {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
export async function updateVideo (req: express.Request, res: express.Response) { export async function updateVideo (req: express.Request, res: express.Response) {
const videoInstance = res.locals.videoAll const videoFromReq = res.locals.videoAll
const videoFieldsSave = videoInstance.toJSON() const videoFieldsSave = videoFromReq.toJSON()
const oldVideoAuditView = new VideoAuditView(videoInstance.toFormattedDetailsJSON()) const oldVideoAuditView = new VideoAuditView(videoFromReq.toFormattedDetailsJSON())
const videoInfoToUpdate: VideoUpdate = req.body const videoInfoToUpdate: VideoUpdate = req.body
const wasConfidentialVideo = videoInstance.isConfidential() const wasConfidentialVideo = videoFromReq.isConfidential()
const hadPrivacyForFederation = videoInstance.hasPrivacyForFederation() const hadPrivacyForFederation = videoFromReq.hasPrivacyForFederation()
const [ thumbnailModel, previewModel ] = await buildVideoThumbnailsFromReq({ const [ thumbnailModel, previewModel ] = await buildVideoThumbnailsFromReq({
video: videoInstance, video: videoFromReq,
files: req.files, files: req.files,
fallback: () => Promise.resolve(undefined), fallback: () => Promise.resolve(undefined),
automaticallyGenerated: false automaticallyGenerated: false
@ -69,8 +69,11 @@ export async function updateVideo (req: express.Request, res: express.Response)
try { try {
const videoInstanceUpdated = await sequelizeTypescript.transaction(async t => { const videoInstanceUpdated = await sequelizeTypescript.transaction(async t => {
// Refresh video since thumbnails to prevent concurrent updates
const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoFromReq.id, t)
const sequelizeOptions = { transaction: t } const sequelizeOptions = { transaction: t }
const oldVideoChannel = videoInstance.VideoChannel const oldVideoChannel = video.VideoChannel
const keysToUpdate: (keyof VideoUpdate & FilteredModelAttributes<VideoModel>)[] = [ const keysToUpdate: (keyof VideoUpdate & FilteredModelAttributes<VideoModel>)[] = [
'name', 'name',
@ -86,25 +89,25 @@ export async function updateVideo (req: express.Request, res: express.Response)
] ]
for (const key of keysToUpdate) { for (const key of keysToUpdate) {
if (videoInfoToUpdate[key] !== undefined) videoInstance.set(key, videoInfoToUpdate[key]) if (videoInfoToUpdate[key] !== undefined) video.set(key, videoInfoToUpdate[key])
} }
if (videoInfoToUpdate.originallyPublishedAt !== undefined && videoInfoToUpdate.originallyPublishedAt !== null) { if (videoInfoToUpdate.originallyPublishedAt !== undefined && videoInfoToUpdate.originallyPublishedAt !== null) {
videoInstance.originallyPublishedAt = new Date(videoInfoToUpdate.originallyPublishedAt) video.originallyPublishedAt = new Date(videoInfoToUpdate.originallyPublishedAt)
} }
// Privacy update? // Privacy update?
let isNewVideo = false let isNewVideo = false
if (videoInfoToUpdate.privacy !== undefined) { if (videoInfoToUpdate.privacy !== undefined) {
isNewVideo = await updateVideoPrivacy({ videoInstance, videoInfoToUpdate, hadPrivacyForFederation, transaction: t }) isNewVideo = await updateVideoPrivacy({ videoInstance: video, videoInfoToUpdate, hadPrivacyForFederation, transaction: t })
} }
// Force updatedAt attribute change // Force updatedAt attribute change
if (!videoInstance.changed()) { if (!video.changed()) {
await videoInstance.setAsRefreshed() await video.setAsRefreshed()
} }
const videoInstanceUpdated = await videoInstance.save(sequelizeOptions) as MVideoFullLight const videoInstanceUpdated = await video.save(sequelizeOptions) as MVideoFullLight
// Thumbnail & preview updates? // Thumbnail & preview updates?
if (thumbnailModel) await videoInstanceUpdated.addAndSaveThumbnail(thumbnailModel, t) if (thumbnailModel) await videoInstanceUpdated.addAndSaveThumbnail(thumbnailModel, t)
@ -141,7 +144,7 @@ export async function updateVideo (req: express.Request, res: express.Response)
new VideoAuditView(videoInstanceUpdated.toFormattedDetailsJSON()), new VideoAuditView(videoInstanceUpdated.toFormattedDetailsJSON()),
oldVideoAuditView oldVideoAuditView
) )
logger.info('Video with name %s and uuid %s updated.', videoInstance.name, videoInstance.uuid, lTags(videoInstance.uuid)) logger.info('Video with name %s and uuid %s updated.', video.name, video.uuid, lTags(video.uuid))
return videoInstanceUpdated return videoInstanceUpdated
}) })
@ -155,7 +158,7 @@ export async function updateVideo (req: express.Request, res: express.Response)
// Force fields we want to update // Force fields we want to update
// If the transaction is retried, sequelize will think the object has not changed // If the transaction is retried, sequelize will think the object has not changed
// So it will skip the SQL request, even if the last one was ROLLBACKed! // So it will skip the SQL request, even if the last one was ROLLBACKed!
resetSequelizeInstance(videoInstance, videoFieldsSave) resetSequelizeInstance(videoFromReq, videoFieldsSave)
throw err throw err
} }