diff options
author | Chocobozzz <florian.bigard@gmail.com> | 2017-01-06 23:24:47 +0100 |
---|---|---|
committer | Chocobozzz <florian.bigard@gmail.com> | 2017-01-06 23:46:36 +0100 |
commit | ed04d94f6d7132055f97a2f757b85c03c5f2a0b6 (patch) | |
tree | 2f1f8c6f6e46b9ab18ba009403f80b14af61af68 | |
parent | bb0b243c92577872a5f4d98f707e078082af4d2a (diff) | |
download | PeerTube-ed04d94f6d7132055f97a2f757b85c03c5f2a0b6.tar.gz PeerTube-ed04d94f6d7132055f97a2f757b85c03c5f2a0b6.tar.zst PeerTube-ed04d94f6d7132055f97a2f757b85c03c5f2a0b6.zip |
Server: try to have a better video integrity
-rw-r--r-- | server/controllers/api/remote/videos.js | 56 | ||||
-rw-r--r-- | server/controllers/api/videos.js | 114 | ||||
-rw-r--r-- | server/helpers/utils.js | 16 | ||||
-rw-r--r-- | server/lib/friends.js | 66 | ||||
-rw-r--r-- | server/models/pod.js | 9 | ||||
-rw-r--r-- | server/models/request.js | 4 |
6 files changed, 194 insertions, 71 deletions
diff --git a/server/controllers/api/remote/videos.js b/server/controllers/api/remote/videos.js index d02da4463..6d768eae8 100644 --- a/server/controllers/api/remote/videos.js +++ b/server/controllers/api/remote/videos.js | |||
@@ -10,6 +10,7 @@ const secureMiddleware = middlewares.secure | |||
10 | const videosValidators = middlewares.validators.remote.videos | 10 | const videosValidators = middlewares.validators.remote.videos |
11 | const signatureValidators = middlewares.validators.remote.signature | 11 | const signatureValidators = middlewares.validators.remote.signature |
12 | const logger = require('../../../helpers/logger') | 12 | const logger = require('../../../helpers/logger') |
13 | const utils = require('../../../helpers/utils') | ||
13 | 14 | ||
14 | const router = express.Router() | 15 | const router = express.Router() |
15 | 16 | ||
@@ -37,11 +38,11 @@ function remoteVideos (req, res, next) { | |||
37 | 38 | ||
38 | switch (request.type) { | 39 | switch (request.type) { |
39 | case 'add': | 40 | case 'add': |
40 | addRemoteVideo(data, fromPod, callbackEach) | 41 | addRemoteVideoRetryWrapper(data, fromPod, callbackEach) |
41 | break | 42 | break |
42 | 43 | ||
43 | case 'update': | 44 | case 'update': |
44 | updateRemoteVideo(data, fromPod, callbackEach) | 45 | updateRemoteVideoRetryWrapper(data, fromPod, callbackEach) |
45 | break | 46 | break |
46 | 47 | ||
47 | case 'remove': | 48 | case 'remove': |
@@ -63,13 +64,30 @@ function remoteVideos (req, res, next) { | |||
63 | return res.type('json').status(204).end() | 64 | return res.type('json').status(204).end() |
64 | } | 65 | } |
65 | 66 | ||
67 | // Handle retries on fail | ||
68 | function addRemoteVideoRetryWrapper (videoToCreateData, fromPod, finalCallback) { | ||
69 | utils.transactionRetryer( | ||
70 | function (callback) { | ||
71 | return addRemoteVideo(videoToCreateData, fromPod, callback) | ||
72 | }, | ||
73 | function (err) { | ||
74 | if (err) { | ||
75 | logger.error('Cannot insert the remote video with many retries.', { error: err }) | ||
76 | return finalCallback(err) | ||
77 | } | ||
78 | |||
79 | return finalCallback() | ||
80 | } | ||
81 | ) | ||
82 | } | ||
83 | |||
66 | function addRemoteVideo (videoToCreateData, fromPod, finalCallback) { | 84 | function addRemoteVideo (videoToCreateData, fromPod, finalCallback) { |
67 | logger.debug('Adding remote video "%s".', videoToCreateData.name) | 85 | logger.debug('Adding remote video "%s".', videoToCreateData.remoteId) |
68 | 86 | ||
69 | waterfall([ | 87 | waterfall([ |
70 | 88 | ||
71 | function startTransaction (callback) { | 89 | function startTransaction (callback) { |
72 | db.sequelize.transaction().asCallback(function (err, t) { | 90 | db.sequelize.transaction({ isolationLevel: 'SERIALIZABLE' }).asCallback(function (err, t) { |
73 | return callback(err, t) | 91 | return callback(err, t) |
74 | }) | 92 | }) |
75 | }, | 93 | }, |
@@ -103,6 +121,7 @@ function addRemoteVideo (videoToCreateData, fromPod, finalCallback) { | |||
103 | authorId: author.id, | 121 | authorId: author.id, |
104 | duration: videoToCreateData.duration, | 122 | duration: videoToCreateData.duration, |
105 | createdAt: videoToCreateData.createdAt, | 123 | createdAt: videoToCreateData.createdAt, |
124 | // FIXME: updatedAt does not seems to be considered by Sequelize | ||
106 | updatedAt: videoToCreateData.updatedAt | 125 | updatedAt: videoToCreateData.updatedAt |
107 | } | 126 | } |
108 | 127 | ||
@@ -142,7 +161,8 @@ function addRemoteVideo (videoToCreateData, fromPod, finalCallback) { | |||
142 | 161 | ||
143 | ], function (err, t) { | 162 | ], function (err, t) { |
144 | if (err) { | 163 | if (err) { |
145 | logger.error('Cannot insert the remote video.') | 164 | // This is just a debug because we will retry the insert |
165 | logger.debug('Cannot insert the remote video.', { error: err }) | ||
146 | 166 | ||
147 | // Abort transaction? | 167 | // Abort transaction? |
148 | if (t) t.rollback() | 168 | if (t) t.rollback() |
@@ -157,8 +177,25 @@ function addRemoteVideo (videoToCreateData, fromPod, finalCallback) { | |||
157 | }) | 177 | }) |
158 | } | 178 | } |
159 | 179 | ||
180 | // Handle retries on fail | ||
181 | function updateRemoteVideoRetryWrapper (videoAttributesToUpdate, fromPod, finalCallback) { | ||
182 | utils.transactionRetryer( | ||
183 | function (callback) { | ||
184 | return updateRemoteVideo(videoAttributesToUpdate, fromPod, callback) | ||
185 | }, | ||
186 | function (err) { | ||
187 | if (err) { | ||
188 | logger.error('Cannot update the remote video with many retries.', { error: err }) | ||
189 | return finalCallback(err) | ||
190 | } | ||
191 | |||
192 | return finalCallback() | ||
193 | } | ||
194 | ) | ||
195 | } | ||
196 | |||
160 | function updateRemoteVideo (videoAttributesToUpdate, fromPod, finalCallback) { | 197 | function updateRemoteVideo (videoAttributesToUpdate, fromPod, finalCallback) { |
161 | logger.debug('Updating remote video "%s".', videoAttributesToUpdate.name) | 198 | logger.debug('Updating remote video "%s".', videoAttributesToUpdate.remoteId) |
162 | 199 | ||
163 | waterfall([ | 200 | waterfall([ |
164 | 201 | ||
@@ -208,7 +245,8 @@ function updateRemoteVideo (videoAttributesToUpdate, fromPod, finalCallback) { | |||
208 | 245 | ||
209 | ], function (err, t) { | 246 | ], function (err, t) { |
210 | if (err) { | 247 | if (err) { |
211 | logger.error('Cannot update the remote video.') | 248 | // This is just a debug because we will retry the insert |
249 | logger.debug('Cannot update the remote video.', { error: err }) | ||
212 | 250 | ||
213 | // Abort transaction? | 251 | // Abort transaction? |
214 | if (t) t.rollback() | 252 | if (t) t.rollback() |
@@ -238,7 +276,7 @@ function reportAbuseRemoteVideo (reportData, fromPod, callback) { | |||
238 | if (err || !video) { | 276 | if (err || !video) { |
239 | if (!err) err = new Error('video not found') | 277 | if (!err) err = new Error('video not found') |
240 | 278 | ||
241 | logger.error('Cannot load video from host and remote id.', { error: err }) | 279 | logger.error('Cannot load video from id.', { error: err, id: reportData.videoRemoteId }) |
242 | return callback(err) | 280 | return callback(err) |
243 | } | 281 | } |
244 | 282 | ||
@@ -260,7 +298,7 @@ function fetchVideo (podHost, remoteId, callback) { | |||
260 | if (err || !video) { | 298 | if (err || !video) { |
261 | if (!err) err = new Error('video not found') | 299 | if (!err) err = new Error('video not found') |
262 | 300 | ||
263 | logger.error('Cannot load video from host and remote id.', { error: err }) | 301 | logger.error('Cannot load video from host and remote id.', { error: err, podHost, remoteId }) |
264 | return callback(err) | 302 | return callback(err) |
265 | } | 303 | } |
266 | 304 | ||
diff --git a/server/controllers/api/videos.js b/server/controllers/api/videos.js index 6829804ec..4d45c11c0 100644 --- a/server/controllers/api/videos.js +++ b/server/controllers/api/videos.js | |||
@@ -70,13 +70,13 @@ router.put('/:id', | |||
70 | oAuth.authenticate, | 70 | oAuth.authenticate, |
71 | reqFiles, | 71 | reqFiles, |
72 | validatorsVideos.videosUpdate, | 72 | validatorsVideos.videosUpdate, |
73 | updateVideo | 73 | updateVideoRetryWrapper |
74 | ) | 74 | ) |
75 | router.post('/', | 75 | router.post('/', |
76 | oAuth.authenticate, | 76 | oAuth.authenticate, |
77 | reqFiles, | 77 | reqFiles, |
78 | validatorsVideos.videosAdd, | 78 | validatorsVideos.videosAdd, |
79 | addVideo | 79 | addVideoRetryWrapper |
80 | ) | 80 | ) |
81 | router.get('/:id', | 81 | router.get('/:id', |
82 | validatorsVideos.videosGet, | 82 | validatorsVideos.videosGet, |
@@ -103,19 +103,37 @@ module.exports = router | |||
103 | 103 | ||
104 | // --------------------------------------------------------------------------- | 104 | // --------------------------------------------------------------------------- |
105 | 105 | ||
106 | function addVideo (req, res, next) { | 106 | // Wrapper to video add that retry the function if there is a database error |
107 | const videoFile = req.files.videofile[0] | 107 | // We need this because we run the transaction in SERIALIZABLE isolation that can fail |
108 | function addVideoRetryWrapper (req, res, next) { | ||
109 | utils.transactionRetryer( | ||
110 | function (callback) { | ||
111 | return addVideo(req, res, req.files.videofile[0], callback) | ||
112 | }, | ||
113 | function (err) { | ||
114 | if (err) { | ||
115 | logger.error('Cannot insert the video with many retries.', { error: err }) | ||
116 | return next(err) | ||
117 | } | ||
118 | |||
119 | // TODO : include Location of the new video -> 201 | ||
120 | return res.type('json').status(204).end() | ||
121 | } | ||
122 | ) | ||
123 | } | ||
124 | |||
125 | function addVideo (req, res, videoFile, callback) { | ||
108 | const videoInfos = req.body | 126 | const videoInfos = req.body |
109 | 127 | ||
110 | waterfall([ | 128 | waterfall([ |
111 | 129 | ||
112 | function startTransaction (callback) { | 130 | function startTransaction (callbackWaterfall) { |
113 | db.sequelize.transaction().asCallback(function (err, t) { | 131 | db.sequelize.transaction({ isolationLevel: 'SERIALIZABLE' }).asCallback(function (err, t) { |
114 | return callback(err, t) | 132 | return callbackWaterfall(err, t) |
115 | }) | 133 | }) |
116 | }, | 134 | }, |
117 | 135 | ||
118 | function findOrCreateAuthor (t, callback) { | 136 | function findOrCreateAuthor (t, callbackWaterfall) { |
119 | const user = res.locals.oauth.token.User | 137 | const user = res.locals.oauth.token.User |
120 | 138 | ||
121 | const name = user.username | 139 | const name = user.username |
@@ -124,19 +142,19 @@ function addVideo (req, res, next) { | |||
124 | const userId = user.id | 142 | const userId = user.id |
125 | 143 | ||
126 | db.Author.findOrCreateAuthor(name, podId, userId, t, function (err, authorInstance) { | 144 | db.Author.findOrCreateAuthor(name, podId, userId, t, function (err, authorInstance) { |
127 | return callback(err, t, authorInstance) | 145 | return callbackWaterfall(err, t, authorInstance) |
128 | }) | 146 | }) |
129 | }, | 147 | }, |
130 | 148 | ||
131 | function findOrCreateTags (t, author, callback) { | 149 | function findOrCreateTags (t, author, callbackWaterfall) { |
132 | const tags = videoInfos.tags | 150 | const tags = videoInfos.tags |
133 | 151 | ||
134 | db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) { | 152 | db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) { |
135 | return callback(err, t, author, tagInstances) | 153 | return callbackWaterfall(err, t, author, tagInstances) |
136 | }) | 154 | }) |
137 | }, | 155 | }, |
138 | 156 | ||
139 | function createVideoObject (t, author, tagInstances, callback) { | 157 | function createVideoObject (t, author, tagInstances, callbackWaterfall) { |
140 | const videoData = { | 158 | const videoData = { |
141 | name: videoInfos.name, | 159 | name: videoInfos.name, |
142 | remoteId: null, | 160 | remoteId: null, |
@@ -148,74 +166,97 @@ function addVideo (req, res, next) { | |||
148 | 166 | ||
149 | const video = db.Video.build(videoData) | 167 | const video = db.Video.build(videoData) |
150 | 168 | ||
151 | return callback(null, t, author, tagInstances, video) | 169 | return callbackWaterfall(null, t, author, tagInstances, video) |
152 | }, | 170 | }, |
153 | 171 | ||
154 | // Set the videoname the same as the id | 172 | // Set the videoname the same as the id |
155 | function renameVideoFile (t, author, tagInstances, video, callback) { | 173 | function renameVideoFile (t, author, tagInstances, video, callbackWaterfall) { |
156 | const videoDir = constants.CONFIG.STORAGE.VIDEOS_DIR | 174 | const videoDir = constants.CONFIG.STORAGE.VIDEOS_DIR |
157 | const source = path.join(videoDir, videoFile.filename) | 175 | const source = path.join(videoDir, videoFile.filename) |
158 | const destination = path.join(videoDir, video.getVideoFilename()) | 176 | const destination = path.join(videoDir, video.getVideoFilename()) |
159 | 177 | ||
160 | fs.rename(source, destination, function (err) { | 178 | fs.rename(source, destination, function (err) { |
161 | return callback(err, t, author, tagInstances, video) | 179 | if (err) return callbackWaterfall(err) |
180 | |||
181 | // This is important in case if there is another attempt | ||
182 | videoFile.filename = video.getVideoFilename() | ||
183 | return callbackWaterfall(null, t, author, tagInstances, video) | ||
162 | }) | 184 | }) |
163 | }, | 185 | }, |
164 | 186 | ||
165 | function insertVideoIntoDB (t, author, tagInstances, video, callback) { | 187 | function insertVideoIntoDB (t, author, tagInstances, video, callbackWaterfall) { |
166 | const options = { transaction: t } | 188 | const options = { transaction: t } |
167 | 189 | ||
168 | // Add tags association | 190 | // Add tags association |
169 | video.save(options).asCallback(function (err, videoCreated) { | 191 | video.save(options).asCallback(function (err, videoCreated) { |
170 | if (err) return callback(err) | 192 | if (err) return callbackWaterfall(err) |
171 | 193 | ||
172 | // Do not forget to add Author informations to the created video | 194 | // Do not forget to add Author informations to the created video |
173 | videoCreated.Author = author | 195 | videoCreated.Author = author |
174 | 196 | ||
175 | return callback(err, t, tagInstances, videoCreated) | 197 | return callbackWaterfall(err, t, tagInstances, videoCreated) |
176 | }) | 198 | }) |
177 | }, | 199 | }, |
178 | 200 | ||
179 | function associateTagsToVideo (t, tagInstances, video, callback) { | 201 | function associateTagsToVideo (t, tagInstances, video, callbackWaterfall) { |
180 | const options = { transaction: t } | 202 | const options = { transaction: t } |
181 | 203 | ||
182 | video.setTags(tagInstances, options).asCallback(function (err) { | 204 | video.setTags(tagInstances, options).asCallback(function (err) { |
183 | video.Tags = tagInstances | 205 | video.Tags = tagInstances |
184 | 206 | ||
185 | return callback(err, t, video) | 207 | return callbackWaterfall(err, t, video) |
186 | }) | 208 | }) |
187 | }, | 209 | }, |
188 | 210 | ||
189 | function sendToFriends (t, video, callback) { | 211 | function sendToFriends (t, video, callbackWaterfall) { |
190 | video.toAddRemoteJSON(function (err, remoteVideo) { | 212 | video.toAddRemoteJSON(function (err, remoteVideo) { |
191 | if (err) return callback(err) | 213 | if (err) return callbackWaterfall(err) |
192 | 214 | ||
193 | // Now we'll add the video's meta data to our friends | 215 | // Now we'll add the video's meta data to our friends |
194 | friends.addVideoToFriends(remoteVideo) | 216 | friends.addVideoToFriends(remoteVideo, t, function (err) { |
195 | 217 | return callbackWaterfall(err, t) | |
196 | return callback(null, t) | 218 | }) |
197 | }) | 219 | }) |
198 | } | 220 | } |
199 | 221 | ||
200 | ], function andFinally (err, t) { | 222 | ], function andFinally (err, t) { |
201 | if (err) { | 223 | if (err) { |
202 | logger.error('Cannot insert the video.') | 224 | // This is just a debug because we will retry the insert |
225 | logger.debug('Cannot insert the video.', { error: err }) | ||
203 | 226 | ||
204 | // Abort transaction? | 227 | // Abort transaction? |
205 | if (t) t.rollback() | 228 | if (t) t.rollback() |
206 | 229 | ||
207 | return next(err) | 230 | return callback(err) |
208 | } | 231 | } |
209 | 232 | ||
210 | // Commit transaction | 233 | // Commit transaction |
211 | t.commit() | 234 | t.commit() |
212 | 235 | ||
213 | // TODO : include Location of the new video -> 201 | 236 | logger.info('Video with name %s created.', videoInfos.name) |
214 | return res.type('json').status(204).end() | 237 | |
238 | return callback(null) | ||
215 | }) | 239 | }) |
216 | } | 240 | } |
217 | 241 | ||
218 | function updateVideo (req, res, next) { | 242 | function updateVideoRetryWrapper (req, res, next) { |
243 | utils.transactionRetryer( | ||
244 | function (callback) { | ||
245 | return updateVideo(req, res, callback) | ||
246 | }, | ||
247 | function (err) { | ||
248 | if (err) { | ||
249 | logger.error('Cannot update the video with many retries.', { error: err }) | ||
250 | return next(err) | ||
251 | } | ||
252 | |||
253 | // TODO : include Location of the new video -> 201 | ||
254 | return res.type('json').status(204).end() | ||
255 | } | ||
256 | ) | ||
257 | } | ||
258 | |||
259 | function updateVideo (req, res, finalCallback) { | ||
219 | const videoInstance = res.locals.video | 260 | const videoInstance = res.locals.video |
220 | const videoInfosToUpdate = req.body | 261 | const videoInfosToUpdate = req.body |
221 | 262 | ||
@@ -267,26 +308,25 @@ function updateVideo (req, res, next) { | |||
267 | const json = videoInstance.toUpdateRemoteJSON() | 308 | const json = videoInstance.toUpdateRemoteJSON() |
268 | 309 | ||
269 | // Now we'll update the video's meta data to our friends | 310 | // Now we'll update the video's meta data to our friends |
270 | friends.updateVideoToFriends(json) | 311 | friends.updateVideoToFriends(json, t, function (err) { |
271 | 312 | return callback(err, t) | |
272 | return callback(null, t) | 313 | }) |
273 | } | 314 | } |
274 | 315 | ||
275 | ], function andFinally (err, t) { | 316 | ], function andFinally (err, t) { |
276 | if (err) { | 317 | if (err) { |
277 | logger.error('Cannot insert the video.') | 318 | logger.debug('Cannot update the video.', { error: err }) |
278 | 319 | ||
279 | // Abort transaction? | 320 | // Abort transaction? |
280 | if (t) t.rollback() | 321 | if (t) t.rollback() |
281 | 322 | ||
282 | return next(err) | 323 | return finalCallback(err) |
283 | } | 324 | } |
284 | 325 | ||
285 | // Commit transaction | 326 | // Commit transaction |
286 | t.commit() | 327 | t.commit() |
287 | 328 | ||
288 | // TODO : include Location of the new video -> 201 | 329 | return finalCallback(null) |
289 | return res.type('json').status(204).end() | ||
290 | }) | 330 | }) |
291 | } | 331 | } |
292 | 332 | ||
diff --git a/server/helpers/utils.js b/server/helpers/utils.js index 9f4b14582..a902850cd 100644 --- a/server/helpers/utils.js +++ b/server/helpers/utils.js | |||
@@ -1,6 +1,7 @@ | |||
1 | 'use strict' | 1 | 'use strict' |
2 | 2 | ||
3 | const crypto = require('crypto') | 3 | const crypto = require('crypto') |
4 | const retry = require('async/retry') | ||
4 | 5 | ||
5 | const logger = require('./logger') | 6 | const logger = require('./logger') |
6 | 7 | ||
@@ -9,7 +10,8 @@ const utils = { | |||
9 | cleanForExit, | 10 | cleanForExit, |
10 | generateRandomString, | 11 | generateRandomString, |
11 | isTestInstance, | 12 | isTestInstance, |
12 | getFormatedObjects | 13 | getFormatedObjects, |
14 | transactionRetryer | ||
13 | } | 15 | } |
14 | 16 | ||
15 | function badRequest (req, res, next) { | 17 | function badRequest (req, res, next) { |
@@ -46,6 +48,18 @@ function getFormatedObjects (objects, objectsTotal) { | |||
46 | } | 48 | } |
47 | } | 49 | } |
48 | 50 | ||
51 | function transactionRetryer (func, callback) { | ||
52 | retry({ | ||
53 | times: 5, | ||
54 | |||
55 | errorFilter: function (err) { | ||
56 | const willRetry = (err.name === 'SequelizeDatabaseError') | ||
57 | logger.debug('Maybe retrying the transaction function.', { willRetry }) | ||
58 | return willRetry | ||
59 | } | ||
60 | }, func, callback) | ||
61 | } | ||
62 | |||
49 | // --------------------------------------------------------------------------- | 63 | // --------------------------------------------------------------------------- |
50 | 64 | ||
51 | module.exports = utils | 65 | module.exports = utils |
diff --git a/server/lib/friends.js b/server/lib/friends.js index 4afb91b8b..3d3d0fdee 100644 --- a/server/lib/friends.js +++ b/server/lib/friends.js | |||
@@ -24,16 +24,33 @@ const friends = { | |||
24 | sendOwnedVideosToPod | 24 | sendOwnedVideosToPod |
25 | } | 25 | } |
26 | 26 | ||
27 | function addVideoToFriends (videoData) { | 27 | function addVideoToFriends (videoData, transaction, callback) { |
28 | createRequest('add', constants.REQUEST_ENDPOINTS.VIDEOS, videoData) | 28 | const options = { |
29 | type: 'add', | ||
30 | endpoint: constants.REQUEST_ENDPOINTS.VIDEOS, | ||
31 | data: videoData, | ||
32 | transaction | ||
33 | } | ||
34 | createRequest(options, callback) | ||
29 | } | 35 | } |
30 | 36 | ||
31 | function updateVideoToFriends (videoData) { | 37 | function updateVideoToFriends (videoData, transaction, callback) { |
32 | createRequest('update', constants.REQUEST_ENDPOINTS.VIDEOS, videoData) | 38 | const options = { |
39 | type: 'update', | ||
40 | endpoint: constants.REQUEST_ENDPOINTS.VIDEOS, | ||
41 | data: videoData, | ||
42 | transaction | ||
43 | } | ||
44 | createRequest(options, callback) | ||
33 | } | 45 | } |
34 | 46 | ||
35 | function removeVideoToFriends (videoParams) { | 47 | function removeVideoToFriends (videoParams) { |
36 | createRequest('remove', constants.REQUEST_ENDPOINTS.VIDEOS, videoParams) | 48 | const options = { |
49 | type: 'remove', | ||
50 | endpoint: constants.REQUEST_ENDPOINTS.VIDEOS, | ||
51 | data: videoParams | ||
52 | } | ||
53 | createRequest(options) | ||
37 | } | 54 | } |
38 | 55 | ||
39 | function reportAbuseVideoToFriend (reportData, video) { | 56 | function reportAbuseVideoToFriend (reportData, video) { |
@@ -258,25 +275,35 @@ function makeRequestsToWinningPods (cert, podsList, callback) { | |||
258 | } | 275 | } |
259 | 276 | ||
260 | // Wrapper that populate "toIds" argument with all our friends if it is not specified | 277 | // Wrapper that populate "toIds" argument with all our friends if it is not specified |
261 | function createRequest (type, endpoint, data, toIds) { | 278 | // { type, endpoint, data, toIds, transaction } |
262 | if (toIds) return _createRequest(type, endpoint, data, toIds) | 279 | function createRequest (options, callback) { |
280 | if (!callback) callback = function () {} | ||
281 | if (options.toIds) return _createRequest(options, callback) | ||
263 | 282 | ||
264 | // If the "toIds" pods is not specified, we send the request to all our friends | 283 | // If the "toIds" pods is not specified, we send the request to all our friends |
265 | db.Pod.listAllIds(function (err, podIds) { | 284 | db.Pod.listAllIds(options.transaction, function (err, podIds) { |
266 | if (err) { | 285 | if (err) { |
267 | logger.error('Cannot get pod ids', { error: err }) | 286 | logger.error('Cannot get pod ids', { error: err }) |
268 | return | 287 | return |
269 | } | 288 | } |
270 | 289 | ||
271 | return _createRequest(type, endpoint, data, podIds) | 290 | const newOptions = Object.assign(options, { toIds: podIds }) |
291 | return _createRequest(newOptions, callback) | ||
272 | }) | 292 | }) |
273 | } | 293 | } |
274 | 294 | ||
275 | function _createRequest (type, endpoint, data, toIds) { | 295 | // { type, endpoint, data, toIds, transaction } |
296 | function _createRequest (options, callback) { | ||
297 | const type = options.type | ||
298 | const endpoint = options.endpoint | ||
299 | const data = options.data | ||
300 | const toIds = options.toIds | ||
301 | const transaction = options.transaction | ||
302 | |||
276 | const pods = [] | 303 | const pods = [] |
277 | 304 | ||
278 | // If there are no destination pods abort | 305 | // If there are no destination pods abort |
279 | if (toIds.length === 0) return | 306 | if (toIds.length === 0) return callback(null) |
280 | 307 | ||
281 | toIds.forEach(function (toPod) { | 308 | toIds.forEach(function (toPod) { |
282 | pods.push(db.Pod.build({ id: toPod })) | 309 | pods.push(db.Pod.build({ id: toPod })) |
@@ -290,17 +317,14 @@ function _createRequest (type, endpoint, data, toIds) { | |||
290 | } | 317 | } |
291 | } | 318 | } |
292 | 319 | ||
293 | // We run in transaction to keep coherency between Request and RequestToPod tables | 320 | const dbRequestOptions = { |
294 | db.sequelize.transaction(function (t) { | 321 | transaction |
295 | const dbRequestOptions = { | 322 | } |
296 | transaction: t | ||
297 | } | ||
298 | 323 | ||
299 | return db.Request.create(createQuery, dbRequestOptions).then(function (request) { | 324 | return db.Request.create(createQuery, dbRequestOptions).asCallback(function (err, request) { |
300 | return request.setPods(pods, dbRequestOptions) | 325 | if (err) return callback(err) |
301 | }) | 326 | |
302 | }).asCallback(function (err) { | 327 | return request.setPods(pods, dbRequestOptions).asCallback(callback) |
303 | if (err) logger.error('Error in createRequest transaction.', { error: err }) | ||
304 | }) | 328 | }) |
305 | } | 329 | } |
306 | 330 | ||
diff --git a/server/models/pod.js b/server/models/pod.js index 83ecd732e..8e7dd1fd8 100644 --- a/server/models/pod.js +++ b/server/models/pod.js | |||
@@ -115,11 +115,18 @@ function list (callback) { | |||
115 | return this.findAll().asCallback(callback) | 115 | return this.findAll().asCallback(callback) |
116 | } | 116 | } |
117 | 117 | ||
118 | function listAllIds (callback) { | 118 | function listAllIds (transaction, callback) { |
119 | if (!callback) { | ||
120 | callback = transaction | ||
121 | transaction = null | ||
122 | } | ||
123 | |||
119 | const query = { | 124 | const query = { |
120 | attributes: [ 'id' ] | 125 | attributes: [ 'id' ] |
121 | } | 126 | } |
122 | 127 | ||
128 | if (transaction) query.transaction = transaction | ||
129 | |||
123 | return this.findAll(query).asCallback(function (err, pods) { | 130 | return this.findAll(query).asCallback(function (err, pods) { |
124 | if (err) return callback(err) | 131 | if (err) return callback(err) |
125 | 132 | ||
diff --git a/server/models/request.js b/server/models/request.js index bae227c05..1d6038044 100644 --- a/server/models/request.js +++ b/server/models/request.js | |||
@@ -291,8 +291,8 @@ function listWithLimitAndRandom (limit, callback) { | |||
291 | order: [ | 291 | order: [ |
292 | [ 'id', 'ASC' ] | 292 | [ 'id', 'ASC' ] |
293 | ], | 293 | ], |
294 | offset: start, | 294 | // offset: start, |
295 | limit: limit, | 295 | // limit: limit, |
296 | include: [ this.sequelize.models.Pod ] | 296 | include: [ this.sequelize.models.Pod ] |
297 | } | 297 | } |
298 | 298 | ||