Add a pool of requests instead of making a request at each action (add
authorChocobozzz <florian.bigard@gmail.com>
Fri, 4 Dec 2015 15:13:32 +0000 (16:13 +0100)
committerChocobozzz <florian.bigard@gmail.com>
Fri, 4 Dec 2015 15:13:32 +0000 (16:13 +0100)
video/remove video) for performance in big networks

12 files changed:
middlewares/reqValidators/remote.js
package.json
routes/api/v1/remoteVideos.js
server.js
src/customValidators.js [new file with mode: 0644]
src/database.js
src/pods.js
src/poolRequests.js [new file with mode: 0644]
src/utils.js
src/videos.js
test/api/friendsAdvanced.js
test/api/multiplePods.js

index e851b49a463ccba7680e485057b4b0d17fa2d27a..642dad1c777bdee003ea2871493f97f284007495 100644 (file)
   }
 
   remote.remoteVideosAdd = function (req, res, next) {
-    req.checkBody('data.name', 'Should have a name').isLength(1, 50)
-    req.checkBody('data.description', 'Should have a description').isLength(1, 250)
-    req.checkBody('data.magnetUri', 'Should have a magnetUri').notEmpty()
-    req.checkBody('data.podUrl', 'Should have a podUrl').isURL()
+    req.checkBody('data').isArray()
+    req.checkBody('data').eachIsRemoteVideosAddValid()
 
     logger.debug('Checking remoteVideosAdd parameters', { parameters: req.body })
 
@@ -29,7 +27,8 @@
   }
 
   remote.remoteVideosRemove = function (req, res, next) {
-    req.checkBody('data.magnetUri', 'Should have a magnetUri').notEmpty()
+    req.checkBody('data').isArray()
+    req.checkBody('data').eachIsRemoteVideosRemoveValid()
 
     logger.debug('Checking remoteVideosRemove parameters', { parameters: req.body })
 
index 9b92e015268cd8c807c5c70071947d2d3a3a68e4..8f2ed9e74c8a742067595b4f81568e1558da56cf 100644 (file)
@@ -46,6 +46,7 @@
     "jquery": "^2.1.4",
     "js-yaml": "^3.3.1",
     "load-grunt-tasks": "^3.3.0",
+    "lodash-node": "3.10.1",
     "mkdirp": "^0.5.1",
     "mongoose": "^4.0.5",
     "morgan": "^1.5.3",
@@ -57,6 +58,7 @@
     "segfault-handler": "^0.2.4",
     "time-grunt": "^1.2.1",
     "ursa": "^0.9.1",
+    "validator": "^4.3.0",
     "webtorrent": "*",
     "winston": "^1.0.1",
     "ws": "^0.8.0"
index 10793b2b4cf77f87a29c9b7f0d64c61b3a2828b6..a104113b264cd4a9a17ba0262d69de4048570139 100644 (file)
@@ -3,21 +3,23 @@
 
   var express = require('express')
   var router = express.Router()
+  var pluck = require('lodash-node/compat/collection/pluck')
+
   var middleware = require('../../../middlewares')
   var miscMiddleware = middleware.misc
   var reqValidator = middleware.reqValidators.remote
   var videos = require('../../../src/videos')
 
   function addRemoteVideos (req, res, next) {
-    videos.addRemote(req.body.data, function (err, video) {
+    videos.addRemotes(req.body.data, function (err, videos) {
       if (err) return next(err)
 
-      res.json(video)
+      res.json(videos)
     })
   }
 
   function removeRemoteVideo (req, res, next) {
-    videos.removeRemote(req.body.signature.url, req.body.data.magnetUri, function (err) {
+    videos.removeRemotes(req.body.signature.url, pluck(req.body.data, 'magnetUri'), function (err) {
       if (err) return next(err)
 
       res.status(204)
index 3b899689c5544b494d579299513d0e22fa6afefc..11402ea78095387617a8c53edb1016faea37aa4d 100644 (file)
--- a/server.js
+++ b/server.js
@@ -35,7 +35,9 @@
 
   // ----------- PeerTube modules -----------
   var config = require('config')
+  var customValidators = require('./src/customValidators')
   var logger = require('./src/logger')
+  var poolRequests = require('./src/poolRequests')
   var routes = require('./routes')
   var videos = require('./src/videos')
   var webtorrent = require('./src/webTorrentNode')
@@ -56,7 +58,9 @@
   app.use(multer({ dest: uploads }))
   app.use(bodyParser.urlencoded({ extended: false }))
   // Validate some params for the API
-  app.use(expressValidator())
+  app.use(expressValidator({
+    customValidators: customValidators
+  }))
 
   // ----------- Views, routes and static files -----------
 
 
       // ----------- Make the server listening -----------
       server.listen(port, function () {
+        // Activate the pool requests
+        poolRequests.activate()
+
         videos.seedAll(function () {
           logger.info('Seeded all the videos')
           logger.info('Server listening on port %d', port)
diff --git a/src/customValidators.js b/src/customValidators.js
new file mode 100644 (file)
index 0000000..73c2f84
--- /dev/null
@@ -0,0 +1,29 @@
+;(function () {
+  'use strict'
+
+  var validator = require('validator')
+
+  var customValidators = {}
+
+  customValidators.eachIsRemoteVideosAddValid = function (values) {
+    return values.every(function (val) {
+      return validator.isLength(val.name, 1, 50) &&
+        validator.isLength(val.description, 1, 50) &&
+        validator.isLength(val.magnetUri, 10) &&
+        validator.isURL(val.podUrl)
+    })
+  }
+
+  customValidators.eachIsRemoteVideosRemoveValid = function (values) {
+    return values.every(function (val) {
+      return validator.isLength(val.magnetUri, 10)
+    })
+  }
+
+  customValidators.isArray = function (value) {
+    return Array.isArray(value)
+  }
+
+  // ----------- Export -----------
+  module.exports = customValidators
+})()
index 740e89fa4a6bb46ab67ec1f855cb26d1437708f5..514a622dc823e224a43adfe18506a0f72a35df5f 100644 (file)
 
   var PodsDB = mongoose.model('pods', podsSchema)
 
+  // ----------- PoolRequests -----------
+  var poolRequestsSchema = mongoose.Schema({
+    type: String,
+    id: String, // Special id to find duplicates (video created we want to remove...)
+    request: mongoose.Schema.Types.Mixed
+  })
+
+  var PoolRequestsDB = mongoose.model('poolRequests', poolRequestsSchema)
+
   // ----------- Connection -----------
 
   mongoose.connect('mongodb://' + host + ':' + port + '/' + dbname)
@@ -45,6 +54,7 @@
   // ----------- Export -----------
   module.exports = {
     VideosDB: VideosDB,
-    PodsDB: PodsDB
+    PodsDB: PodsDB,
+    PoolRequestsDB: PoolRequestsDB
   }
 })()
index e26b3f0ae4f66d120b35c7485abdd7cfb0ec9acd..9afc6cc96cce7d27f396d61d693293db84d98f4a 100644 (file)
@@ -8,6 +8,7 @@
 
   var logger = require('./logger')
   var PodsDB = require('./database').PodsDB
+  var poolRequests = require('./poolRequests')
   var utils = require('./utils')
 
   var pods = {}
   var host = config.get('webserver.host')
   var port = config.get('webserver.port')
 
-  // ----------- Constants -----------
-
-  var PODS_SCORE = {
-    MALUS: -10,
-    BONUS: 10
-  }
-
   // ----------- Private functions -----------
 
   function getForeignPodsList (url, callback) {
     })
   }
 
-  function updatePodsScore (good_pods, bad_pods) {
-    logger.info('Updating %d good pods and %d bad pods scores.', good_pods.length, bad_pods.length)
-
-    PodsDB.update({ _id: { $in: good_pods } }, { $inc: { score: PODS_SCORE.BONUS } }, { multi: true }).exec()
-    PodsDB.update({ _id: { $in: bad_pods } }, { $inc: { score: PODS_SCORE.MALUS } }, { multi: true }, function (err) {
-      if (err) throw err
-      removeBadPods()
-    })
-  }
-
-  function removeBadPods () {
-    PodsDB.remove({ score: 0 }, function (err, result) {
-      if (err) throw err
-
-      var number_removed = result.result.n
-      if (number_removed !== 0) logger.info('Removed %d pod.', number_removed)
-    })
-  }
-
   // ----------- Public functions -----------
 
   pods.list = function (callback) {
     })
   }
 
-  // { path, data }
-  pods.makeSecureRequest = function (data, callback) {
-    if (callback === undefined) callback = function () {}
-
-    PodsDB.find({}, { _id: 1, url: 1, publicKey: 1 }).exec(function (err, pods) {
-      if (err) {
-        logger.error('Cannot get the list of the pods.', { error: err })
-        return callback(err)
-      }
-
-      logger.debug('Make multiple requests.')
-
-      var params = {
-        encrypt: true,
-        sign: true,
-        method: data.method,
-        path: data.path,
-        data: data.data
-      }
-
-      var bad_pods = []
-      var good_pods = []
-
-      utils.makeMultipleRetryRequest(
-        params,
-
-        pods,
-
-        function callbackEachPodFinished (err, response, body, pod, callback_each_pod_finished) {
-          if (err || response.statusCode !== 200) {
-            bad_pods.push(pod._id)
-            logger.error('Error sending secure request to %s/%s pod.', pod.url, data.path, { error: err })
-          } else {
-            good_pods.push(pod._id)
-          }
-
-          return callback_each_pod_finished()
-        },
-
-        function callbackAllPodsFinished (err) {
-          if (err) {
-            logger.error('There was some errors when sending the video meta data.', { error: err })
-            return callback(err)
-          }
-
-          logger.debug('Finished')
+  pods.addVideoToFriends = function (video) {
+    // To avoid duplicates
+    var id = video.name + video.magnetUri
+    poolRequests.addToPoolRequests(id, 'add', video)
+  }
 
-          updatePodsScore(good_pods, bad_pods)
-          callback(null)
-        }
-      )
-    })
+  pods.removeVideoToFriends = function (video) {
+    // To avoid duplicates
+    var id = video.name + video.magnetUri
+    poolRequests.addToPoolRequests(id, 'remove', video)
   }
 
   pods.makeFriends = function (callback) {
 
         pods_list,
 
-        function eachRequest (err, response, body, pod, callback_each_request) {
+        function eachRequest (err, response, body, url, pod, callback_each_request) {
           // We add the pod if it responded correctly with its public certificate
           if (!err && response.statusCode === 200) {
             pods.add({ url: pod.url, publicKey: body.cert, score: global.FRIEND_BASE_SCORE }, function (err) {
diff --git a/src/poolRequests.js b/src/poolRequests.js
new file mode 100644 (file)
index 0000000..b117c99
--- /dev/null
@@ -0,0 +1,158 @@
+;(function () {
+  'use strict'
+
+  var async = require('async')
+
+  var logger = require('./logger')
+  var database = require('./database')
+  var PoolRequestsDB = database.PoolRequestsDB
+  var PodsDB = database.PodsDB
+  var utils = require('./utils')
+
+  var poolRequests = {}
+
+  // ----------- Constants -----------
+
+  // Time to wait between requests to the friends
+  var INTERVAL = utils.isTestInstance() ? 10000 : 60000
+  var PODS_SCORE = {
+    MALUS: -10,
+    BONUS: 10
+  }
+
+  // ----------- Private -----------
+  var timer = null
+
+  function makePoolRequests () {
+    logger.info('Making pool requests to friends.')
+
+    PoolRequestsDB.find({}, { type: 1, request: 1 }, function (err, pool_requests) {
+      if (err) throw err
+
+      var requests = {
+        add: [],
+        remove: []
+      }
+
+      async.each(pool_requests, function (pool_request, callback_each) {
+        if (pool_request.type === 'add') {
+          requests.add.push(pool_request.request)
+        } else if (pool_request.type === 'remove') {
+          requests.remove.push(pool_request.request)
+        } else {
+          throw new Error('Unkown pool request type.')
+        }
+
+        callback_each()
+      }, function () {
+        makePoolRequest('add', requests.add)
+        makePoolRequest('remove', requests.remove)
+        logger.info('Pool requests to friends sent.')
+      })
+    })
+  }
+
+  function updatePodsScore (good_pods, bad_pods) {
+    logger.info('Updating %d good pods and %d bad pods scores.', good_pods.length, bad_pods.length)
+
+    PodsDB.update({ _id: { $in: good_pods } }, { $inc: { score: PODS_SCORE.BONUS } }, { multi: true }).exec()
+    PodsDB.update({ _id: { $in: bad_pods } }, { $inc: { score: PODS_SCORE.MALUS } }, { multi: true }, function (err) {
+      if (err) throw err
+      removeBadPods()
+    })
+  }
+
+  function removeBadPods () {
+    PodsDB.remove({ score: 0 }, function (err, result) {
+      if (err) throw err
+
+      var number_removed = result.result.n
+      if (number_removed !== 0) logger.info('Removed %d pod.', number_removed)
+    })
+  }
+
+  function makePoolRequest (type, requests) {
+    logger.debug('Make pool requests scheduled.')
+    PodsDB.find({}, { _id: 1, url: 1, publicKey: 1 }).exec(function (err, pods) {
+      if (err) throw err
+
+      var params = {
+        encrypt: true,
+        sign: true,
+        method: 'POST',
+        path: null,
+        data: requests
+      }
+
+      if (type === 'add') {
+        params.path = '/api/' + global.API_VERSION + '/remotevideos/add'
+      } else if (type === 'remove') {
+        params.path = '/api/' + global.API_VERSION + '/remotevideos/remove'
+      } else {
+        throw new Error('Unkown pool request type.')
+      }
+
+      var bad_pods = []
+      var good_pods = []
+
+      utils.makeMultipleRetryRequest(params, pods, callbackEachPodFinished, callbackAllPodsFinished)
+
+      function callbackEachPodFinished (err, response, body, url, pod, callback_each_pod_finished) {
+        if (err || response.statusCode !== 200) {
+          bad_pods.push(pod._id)
+          logger.error('Error sending secure request to %s pod.', url, { error: err })
+        } else {
+          good_pods.push(pod._id)
+        }
+
+        return callback_each_pod_finished()
+      }
+
+      function callbackAllPodsFinished (err) {
+        if (err) {
+          logger.error('There was some errors when sending the video meta data.', { error: err })
+        }
+
+        updatePodsScore(good_pods, bad_pods)
+        PoolRequestsDB.remove().exec()
+      }
+    })
+  }
+
+  // ----------- Public -----------
+  poolRequests.activate = function () {
+    logger.info('Pool requests activated.')
+    timer = setInterval(makePoolRequests, INTERVAL)
+  }
+
+  poolRequests.addToPoolRequests = function (id, type, request) {
+    logger.debug('Add request to the pool requests.', { id: id, type: type, request: request })
+
+    PoolRequestsDB.findOne({ id: id }, function (err, entity) {
+      if (err) logger.error(err)
+
+      if (entity) {
+        if (entity.type === type) {
+          logger.error(new Error('Cannot insert two same requests.'))
+          return
+        }
+
+        // Remove the request of the other type
+        PoolRequestsDB.remove({ id: id }, function (err) {
+          if (err) logger.error(err)
+        })
+      } else {
+        PoolRequestsDB.create({ id: id, type: type, request: request }, function (err) {
+          if (err) logger.error(err)
+        })
+      }
+    })
+  }
+
+  poolRequests.deactivate = function () {
+    logger.info('Pool requests deactivated.')
+    clearInterval(timer)
+  }
+
+  module.exports = poolRequests
+})()
index dda6c7a0a7dcb4771c994d256f6cfc1fef04f0cd..4aa1fc55e1da6397c475e0d3b6ee5a38946efbf7 100644 (file)
@@ -36,7 +36,7 @@
 
     replay(
       request.post(params, function (err, response, body) {
-        callbackEach(err, response, body, to_pod)
+        callbackEach(err, response, body, params.url, to_pod)
       }),
       {
         retries: retries,
@@ -71,8 +71,8 @@
 
     // Make a request for each pod
     async.each(pods, function (pod, callback_each_async) {
-      function callbackEachRetryRequest (err, response, body, pod) {
-        callbackEach(err, response, body, pod, function () {
+      function callbackEachRetryRequest (err, response, body, url, pod) {
+        callbackEach(err, response, body, url, pod, function () {
           callback_each_async()
         })
       }
index 8c44cad950822999a448fcccd444046208ba5fab..e3a5b49f10d989d2d51a98052a8909e03b9a8dc4 100644 (file)
@@ -3,6 +3,7 @@
 
   var async = require('async')
   var config = require('config')
+  var dz = require('dezalgo')
   var fs = require('fs')
   var webtorrent = require('./webTorrentNode')
 
           return callback(err)
         }
 
-        // Now we'll send the video's meta data
+        // Now we'll add the video's meta data to our friends
         params.namePath = null
 
-        logger.info('Sending %s video to friends.', video_file.path)
-
-        var data = {
-          path: '/api/' + global.API_VERSION + '/remotevideos/add',
-          method: 'POST',
-          data: params
-        }
-
-        // Do not wait the secure requests
-        pods.makeSecureRequest(data)
+        pods.addVideoToFriends(params)
         callback(null)
       })
     })
               return callback(err)
             }
 
-            var data = {
-              path: '/api/' + global.API_VERSION + '/remotevideos/remove',
-              method: 'POST',
-              data: {
-                magnetUri: video.magnetUri
-              }
+            var params = {
+              name: video.name,
+              magnetUri: video.magnetUri
             }
 
-            // Yes this is a POST request because we add some informations in the body (signature, encrypt etc)
-            pods.makeSecureRequest(data)
+            pods.removeVideoToFriends(params)
             callback(null)
           })
         })
   }
 
   // Use the magnet Uri because the _id field is not the same on different servers
-  videos.removeRemote = function (fromUrl, magnetUri, callback) {
-    VideosDB.findOne({ magnetUri: magnetUri }, function (err, video) {
-      if (err || !video) {
-        logger.error('Cannot find the torrent URI of this remote video.')
+  videos.removeRemotes = function (fromUrl, magnetUris, callback) {
+    VideosDB.find({ magnetUri: { $in: magnetUris } }, function (err, videos) {
+      if (err || !videos) {
+        logger.error('Cannot find the torrent URI of these remote videos.')
         return callback(err)
       }
 
-      // TODO: move to reqValidators middleware ?
-      if (video.podUrl !== fromUrl) {
-        logger.error('The pod has not the rights on this video.')
-        return callback(err)
-      }
+      var to_remove = []
+      async.each(videos, function (video, callback_async) {
+        callback_async = dz(callback_async)
 
-      VideosDB.findByIdAndRemove(video._id, function (err) {
-        if (err) {
-          logger.error('Cannot remove the remote video.')
-          return callback(err)
+        if (video.podUrl !== fromUrl) {
+          logger.error('The pod %s has not the rights on the video of %s.', fromUrl, video.podUrl)
+        } else {
+          to_remove.push(video._id)
         }
 
-        callback(null)
+        callback_async()
+      }, function () {
+        VideosDB.remove({ _id: { $in: to_remove } }, function (err) {
+          if (err) {
+            logger.error('Cannot remove the remote videos.')
+            return callback(err)
+          }
+
+          callback(null)
+        })
       })
     })
   }
 
   // { name, magnetUri, podUrl }
-  videos.addRemote = function (data, callback) {
-    logger.debug('Add remote video from pod: %s', data.podUrl)
-
-    var params = {
-      name: data.name,
-      namePath: null,
-      description: data.description,
-      magnetUri: data.magnetUri,
-      podUrl: data.podUrl
-    }
+  videos.addRemotes = function (videos, callback) {
+    var to_add = []
 
-    VideosDB.create(params, function (err, video) {
-      if (err) {
-        logger.error('Cannot insert this remote video.', { error: err })
-        return callback(err)
+    async.each(videos, function (video, callback_each) {
+      callback_each = dz(callback_each)
+      logger.debug('Add remote video from pod: %s', video.podUrl)
+
+      var params = {
+        name: video.name,
+        namePath: null,
+        description: video.description,
+        magnetUri: video.magnetUri,
+        podUrl: video.podUrl
       }
 
-      return callback(null, video)
+      to_add.push(params)
+
+      callback_each()
+    }, function () {
+      VideosDB.create(to_add, function (err, videos) {
+        if (err) {
+          logger.error('Cannot insert this remote video.', { error: err })
+          return callback(err)
+        }
+
+        return callback(null, videos)
+      })
     })
   }
 
index ccddac4dc1623bdd61b9d8ff9a1a9494b339aa30..680d90aee55c8d6f42a4ad0e7fc6af1d8aa8cf89 100644 (file)
     })
 
     it('Should make friends with the pods 1, 2, 3', function (done) {
-      this.timeout(100000)
+      this.timeout(150000)
 
-      // Pods 1, 2, 3 and 4 become friends
+      // Pods 1, 2, 3 and 4 become friends (yes this is beautiful)
       makeFriend(2, function () {
         makeFriend(1, function () {
           makeFriend(4, function () {
 
             // Expulse pod 4 from pod 1 and 2
             uploadVideo(1, function () {
-              uploadVideo(1, function () {
-                uploadVideo(2, function () {
-                  uploadVideo(2, function () {
-                    // Rerun server 4
-                    utils.runServer(4, function (app, url) {
-                      apps[3] = app
-                      getFriendsList(4, function (err, res) {
-                        if (err) throw err
-                        // Pod 4 didn't know pod 1 and 2 removed it
-                        expect(res.body.length).to.equal(3)
-
-                        // Pod 6 ask pod 1, 2 and 3
-                        makeFriend(6, function () {
-                          getFriendsList(6, function (err, res) {
-                            if (err) throw err
-
-                            // Pod 4 should not be our friend
-                            var result = res.body
-                            expect(result.length).to.equal(3)
-                            for (var pod of result) {
-                              expect(pod.url).not.equal(urls[3])
-                            }
-
-                            done()
-                          })
+              setTimeout(function () {
+                uploadVideo(1, function () {
+                  setTimeout(function () {
+                    uploadVideo(2, function () {
+                      setTimeout(function () {
+                        uploadVideo(2, function () {
+                          setTimeout(function () {
+                            // Rerun server 4
+                            utils.runServer(4, function (app, url) {
+                              apps[3] = app
+                              getFriendsList(4, function (err, res) {
+                                if (err) throw err
+                                // Pod 4 didn't know pod 1 and 2 removed it
+                                expect(res.body.length).to.equal(3)
+
+                                // Pod 6 ask pod 1, 2 and 3
+                                makeFriend(6, function () {
+                                  getFriendsList(6, function (err, res) {
+                                    if (err) throw err
+
+                                    // Pod 4 should not be our friend
+                                    var result = res.body
+                                    expect(result.length).to.equal(3)
+                                    for (var pod of result) {
+                                      expect(pod.url).not.equal(urls[3])
+                                    }
+
+                                    done()
+                                  })
+                                })
+                              })
+                            })
+                          }, 11000)
                         })
-                      })
+                      }, 11000)
                     })
-                  })
+                  }, 11000)
                 })
-              })
+              }, 11000)
             })
           })
         })
index 1edfc1ce3b8c485c8900c6a991779030f5c5e79a..3ce57afa8e65ae171b27ff8732c418c7f2b6eb62 100644 (file)
@@ -14,7 +14,7 @@
     var path = '/api/v1/videos'
     var apps = []
     var urls = []
-    var video_id = -1
+    var to_remove = []
 
     function getVideosList (url, end) {
       request(url)
         .end(end)
     }
 
+    function removeVideo (url, id, end) {
+      request(url)
+        .delete(path + '/' + id)
+        .set('Accept', 'application/json')
+        .expect(204)
+        .end(end)
+    }
+
     before(function (done) {
       this.timeout(30000)
       var path_friends = '/api/v1/pods/makefriends'
@@ -89,7 +97,7 @@
 
     describe('Should upload the video and propagate on each pod', function () {
       it('Should upload the video on pod 1 and propagate on each pod', function (done) {
-        this.timeout(5000)
+        this.timeout(15000)
 
         uploadVideo(urls[0], 'my super name for pod 1', 'my super description for pod 1', 'video_short1.webm', function (err) {
           if (err) throw err
 
               done()
             })
-          }, 1000)
+          }, 11000)
         })
       })
 
       it('Should upload the video on pod 2 and propagate on each pod', function (done) {
-        this.timeout(5000)
+        this.timeout(15000)
 
         uploadVideo(urls[1], 'my super name for pod 2', 'my super description for pod 2', 'video_short2.webm', function (err) {
           if (err) throw err
 
               done()
             })
-          }, 1000)
+          }, 11000)
         })
       })
 
-      it('Should upload the video on pod 3 and propagate on each pod', function (done) {
-        this.timeout(5000)
+      it('Should upload two videos on pod 3 and propagate on each pod', function (done) {
+        this.timeout(15000)
 
         uploadVideo(urls[2], 'my super name for pod 3', 'my super description for pod 3', 'video_short3.webm', function (err) {
           if (err) throw err
+          uploadVideo(urls[2], 'my super name for pod 3-2', 'my super description for pod 3-2', 'video_short.webm', function (err) {
+            if (err) throw err
 
-          setTimeout(function () {
-            var base_magnet = null
-            // All pods should have this video
-            async.each(urls, function (url, callback) {
-              getVideosList(url, function (err, res) {
-                if (err) throw err
-
-                var videos = res.body
-                expect(videos).to.be.an('array')
-                expect(videos.length).to.equal(3)
-                var video = videos[2]
-                expect(video.name).to.equal('my super name for pod 3')
-                expect(video.description).to.equal('my super description for pod 3')
-                expect(video.podUrl).to.equal('http://localhost:9003')
-                expect(video.magnetUri).to.exist
+            setTimeout(function () {
+              var base_magnet = null
+              // All pods should have this video
+              async.each(urls, function (url, callback) {
+                getVideosList(url, function (err, res) {
+                  if (err) throw err
 
-                // All pods should have the same magnet Uri
-                if (base_magnet === null) {
-                  base_magnet = video.magnetUri
-                } else {
-                  expect(video.magnetUri).to.equal.magnetUri
-                }
+                  var videos = res.body
+                  expect(videos).to.be.an('array')
+                  expect(videos.length).to.equal(4)
+                  var video = videos[2]
+                  expect(video.name).to.equal('my super name for pod 3')
+                  expect(video.description).to.equal('my super description for pod 3')
+                  expect(video.podUrl).to.equal('http://localhost:9003')
+                  expect(video.magnetUri).to.exist
+
+                  video = videos[3]
+                  expect(video.name).to.equal('my super name for pod 3-2')
+                  expect(video.description).to.equal('my super description for pod 3-2')
+                  expect(video.podUrl).to.equal('http://localhost:9003')
+                  expect(video.magnetUri).to.exist
+
+                  // All pods should have the same magnet Uri
+                  if (base_magnet === null) {
+                    base_magnet = video.magnetUri
+                  } else {
+                    expect(video.magnetUri).to.equal.magnetUri
+                  }
+
+                  callback()
+                })
+              }, function (err) {
+                if (err) throw err
 
-                callback()
+                done()
               })
-            }, function (err) {
-              if (err) throw err
-
-              done()
-            })
-          }, 1000)
+            }, 11000)
+          })
         })
       })
     })
           if (err) throw err
 
           var video = res.body[0]
+          to_remove.push(res.body[2]._id)
+          to_remove.push(res.body[3]._id)
+
           webtorrent.add(video.magnetUri, function (torrent) {
             expect(torrent.files).to.exist
             expect(torrent.files.length).to.equal(1)
           if (err) throw err
 
           var video = res.body[2]
-          video_id = res.body[1]._id
 
           webtorrent.add(video.magnetUri, function (torrent) {
             expect(torrent.files).to.exist
         })
       })
 
-      it('Should remove the file 2 by asking pod 2', function (done) {
-        request(urls[1])
-          .delete(path + '/' + video_id)
-          .set('Accept', 'application/json')
-          .expect(204)
-          .end(function (err, res) {
+      it('Should add the file 3-2 by asking pod 1', function (done) {
+        // Yes, this could be long
+        this.timeout(200000)
+
+        getVideosList(urls[0], function (err, res) {
+          if (err) throw err
+
+          var video = res.body[3]
+
+          webtorrent.add(video.magnetUri, function (torrent) {
+            expect(torrent.files).to.exist
+            expect(torrent.files.length).to.equal(1)
+            expect(torrent.files[0].path).to.exist.and.to.not.equal('')
+
+            done()
+          })
+        })
+      })
+
+      it('Should remove the file 3 and 3-2 by asking pod 3', function (done) {
+        this.timeout(15000)
+
+        removeVideo(urls[2], to_remove[0], function (err) {
+          if (err) throw err
+          removeVideo(urls[2], to_remove[1], function (err) {
             if (err) throw err
 
             // Wait the propagation to the other pods
             setTimeout(function () {
               done()
-            }, 1000)
+            }, 11000)
           })
+        })
       })
 
       it('Should have videos 1 and 3 on each pod', function (done) {
             expect(videos).to.be.an('array')
             expect(videos.length).to.equal(2)
             expect(videos[0]._id).not.to.equal(videos[1]._id)
-            expect(videos[0]._id).not.to.equal(video_id)
-            expect(videos[1]._id).not.to.equal(video_id)
+            expect(videos[0]._id).not.to.equal(to_remove[0])
+            expect(videos[1]._id).not.to.equal(to_remove[0])
+            expect(videos[0]._id).not.to.equal(to_remove[1])
+            expect(videos[1]._id).not.to.equal(to_remove[1])
 
             callback()
           })