diff options
author | Chocobozzz <me@florianbigard.com> | 2021-11-10 14:57:09 +0100 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2021-11-10 14:57:09 +0100 |
commit | a2a81f5a7fe902245d9273530bbd65f522b2edfa (patch) | |
tree | 07e0f3f6d5c1ccfab6219a91a2af2f2a81ec6cfb | |
parent | 5cf027bdc46f1bf214c4cf26eee17ebda228004f (diff) | |
download | PeerTube-a2a81f5a7fe902245d9273530bbd65f522b2edfa.tar.gz PeerTube-a2a81f5a7fe902245d9273530bbd65f522b2edfa.tar.zst PeerTube-a2a81f5a7fe902245d9273530bbd65f522b2edfa.zip |
Prevent concurrent video update
-rw-r--r-- | server/controllers/api/videos/update.ts | 33 |
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 | ||
54 | export async function updateVideo (req: express.Request, res: express.Response) { | 54 | export 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 | } |