aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/controllers/api
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2021-11-10 14:57:09 +0100
committerChocobozzz <me@florianbigard.com>2021-11-10 14:57:09 +0100
commita2a81f5a7fe902245d9273530bbd65f522b2edfa (patch)
tree07e0f3f6d5c1ccfab6219a91a2af2f2a81ec6cfb /server/controllers/api
parent5cf027bdc46f1bf214c4cf26eee17ebda228004f (diff)
downloadPeerTube-a2a81f5a7fe902245d9273530bbd65f522b2edfa.tar.gz
PeerTube-a2a81f5a7fe902245d9273530bbd65f522b2edfa.tar.zst
PeerTube-a2a81f5a7fe902245d9273530bbd65f522b2edfa.zip
Prevent concurrent video update
Diffstat (limited to 'server/controllers/api')
-rw-r--r--server/controllers/api/videos/update.ts33
1 files changed, 18 insertions, 15 deletions
diff --git a/server/controllers/api/videos/update.ts b/server/controllers/api/videos/update.ts
index 07f9690ec..fab1c2268 100644
--- a/server/controllers/api/videos/update.ts
+++ b/server/controllers/api/videos/update.ts
@@ -52,16 +52,16 @@ export {
52// --------------------------------------------------------------------------- 52// ---------------------------------------------------------------------------
53 53
54export async function updateVideo (req: express.Request, res: express.Response) { 54export async function updateVideo (req: express.Request, res: express.Response) {
55 const videoInstance = res.locals.videoAll 55 const videoFromReq = res.locals.videoAll
56 const videoFieldsSave = videoInstance.toJSON() 56 const videoFieldsSave = videoFromReq.toJSON()
57 const oldVideoAuditView = new VideoAuditView(videoInstance.toFormattedDetailsJSON()) 57 const oldVideoAuditView = new VideoAuditView(videoFromReq.toFormattedDetailsJSON())
58 const videoInfoToUpdate: VideoUpdate = req.body 58 const videoInfoToUpdate: VideoUpdate = req.body
59 59
60 const wasConfidentialVideo = videoInstance.isConfidential() 60 const wasConfidentialVideo = videoFromReq.isConfidential()
61 const hadPrivacyForFederation = videoInstance.hasPrivacyForFederation() 61 const hadPrivacyForFederation = videoFromReq.hasPrivacyForFederation()
62 62
63 const [ thumbnailModel, previewModel ] = await buildVideoThumbnailsFromReq({ 63 const [ thumbnailModel, previewModel ] = await buildVideoThumbnailsFromReq({
64 video: videoInstance, 64 video: videoFromReq,
65 files: req.files, 65 files: req.files,
66 fallback: () => Promise.resolve(undefined), 66 fallback: () => Promise.resolve(undefined),
67 automaticallyGenerated: false 67 automaticallyGenerated: false
@@ -69,8 +69,11 @@ export async function updateVideo (req: express.Request, res: express.Response)
69 69
70 try { 70 try {
71 const videoInstanceUpdated = await sequelizeTypescript.transaction(async t => { 71 const videoInstanceUpdated = await sequelizeTypescript.transaction(async t => {
72 // Refresh video since thumbnails to prevent concurrent updates
73 const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoFromReq.id, t)
74
72 const sequelizeOptions = { transaction: t } 75 const sequelizeOptions = { transaction: t }
73 const oldVideoChannel = videoInstance.VideoChannel 76 const oldVideoChannel = video.VideoChannel
74 77
75 const keysToUpdate: (keyof VideoUpdate & FilteredModelAttributes<VideoModel>)[] = [ 78 const keysToUpdate: (keyof VideoUpdate & FilteredModelAttributes<VideoModel>)[] = [
76 'name', 79 'name',
@@ -86,25 +89,25 @@ export async function updateVideo (req: express.Request, res: express.Response)
86 ] 89 ]
87 90
88 for (const key of keysToUpdate) { 91 for (const key of keysToUpdate) {
89 if (videoInfoToUpdate[key] !== undefined) videoInstance.set(key, videoInfoToUpdate[key]) 92 if (videoInfoToUpdate[key] !== undefined) video.set(key, videoInfoToUpdate[key])
90 } 93 }
91 94
92 if (videoInfoToUpdate.originallyPublishedAt !== undefined && videoInfoToUpdate.originallyPublishedAt !== null) { 95 if (videoInfoToUpdate.originallyPublishedAt !== undefined && videoInfoToUpdate.originallyPublishedAt !== null) {
93 videoInstance.originallyPublishedAt = new Date(videoInfoToUpdate.originallyPublishedAt) 96 video.originallyPublishedAt = new Date(videoInfoToUpdate.originallyPublishedAt)
94 } 97 }
95 98
96 // Privacy update? 99 // Privacy update?
97 let isNewVideo = false 100 let isNewVideo = false
98 if (videoInfoToUpdate.privacy !== undefined) { 101 if (videoInfoToUpdate.privacy !== undefined) {
99 isNewVideo = await updateVideoPrivacy({ videoInstance, videoInfoToUpdate, hadPrivacyForFederation, transaction: t }) 102 isNewVideo = await updateVideoPrivacy({ videoInstance: video, videoInfoToUpdate, hadPrivacyForFederation, transaction: t })
100 } 103 }
101 104
102 // Force updatedAt attribute change 105 // Force updatedAt attribute change
103 if (!videoInstance.changed()) { 106 if (!video.changed()) {
104 await videoInstance.setAsRefreshed() 107 await video.setAsRefreshed()
105 } 108 }
106 109
107 const videoInstanceUpdated = await videoInstance.save(sequelizeOptions) as MVideoFullLight 110 const videoInstanceUpdated = await video.save(sequelizeOptions) as MVideoFullLight
108 111
109 // Thumbnail & preview updates? 112 // Thumbnail & preview updates?
110 if (thumbnailModel) await videoInstanceUpdated.addAndSaveThumbnail(thumbnailModel, t) 113 if (thumbnailModel) await videoInstanceUpdated.addAndSaveThumbnail(thumbnailModel, t)
@@ -141,7 +144,7 @@ export async function updateVideo (req: express.Request, res: express.Response)
141 new VideoAuditView(videoInstanceUpdated.toFormattedDetailsJSON()), 144 new VideoAuditView(videoInstanceUpdated.toFormattedDetailsJSON()),
142 oldVideoAuditView 145 oldVideoAuditView
143 ) 146 )
144 logger.info('Video with name %s and uuid %s updated.', videoInstance.name, videoInstance.uuid, lTags(videoInstance.uuid)) 147 logger.info('Video with name %s and uuid %s updated.', video.name, video.uuid, lTags(video.uuid))
145 148
146 return videoInstanceUpdated 149 return videoInstanceUpdated
147 }) 150 })
@@ -155,7 +158,7 @@ export async function updateVideo (req: express.Request, res: express.Response)
155 // Force fields we want to update 158 // Force fields we want to update
156 // If the transaction is retried, sequelize will think the object has not changed 159 // If the transaction is retried, sequelize will think the object has not changed
157 // So it will skip the SQL request, even if the last one was ROLLBACKed! 160 // So it will skip the SQL request, even if the last one was ROLLBACKed!
158 resetSequelizeInstance(videoInstance, videoFieldsSave) 161 resetSequelizeInstance(videoFromReq, videoFieldsSave)
159 162
160 throw err 163 throw err
161 } 164 }