0

POST /add-request - adds new request

POST /get-request - returns first (oldest) request

I have many Java apps which are called "slaves". Their job is to call POST /get-request to get request to process and after processing it save it to database. All requests are stored in mongoDB. /add-request just adds request to db. /get-request uses findAndModify mongo query to get first request from queue and set its status to "processing" (this status is required to avoid situation when same request is taken twice from DB). For now slaves just call api every 2 seconds. But I want to use some long-polling here. It should look like this:

  1. I have to keep slave requests in a queue.
  2. I have to set timeout foreach slave request to send for example 404 after 4 seconds if there is no new request.
  3. When new request is added through /add-request I have to take first slave request from queue, remove it from this queue, get request from DB and send it to this slave.

so let's say i have to handlers (i am using express):

function addRequest(req,res,next){ 
    // add request to db and check if there is waiting slave request in queue
    // if there is one remove it from queue, clear timeout on it, get request from db
    // and send it to slave
}

function getRequest(req,res,next){ 
    // get request from db - if there is no request add this slave request to queue
    // set timeout to send 404 after 4 seconds
}

The problem is with the queue - I tried many qpproaches but there is no any "synchronization" and I can't make it works... Is it possible to ensure consistency in a queue when two express handlers operate on it in the same time? The problem is with removing requests from queue after timeout. I have to search for request in queue using some uid and then removing it...

Maybe there is some library/tool to accomplish my goal?


404 is returned when there is no request for slave to process. I want to slave get the new request as fast as possible - so if I would return response instantly (new request or 404 when there is no request) then each slave would have to call api all the time non-stop and it would have impact on server performance I think. So I wanted each slave to "hold" on request for 4 seconds on server before returning any data.

user606521
  • 14,486
  • 30
  • 113
  • 204
  • What's the 4 second timeout for? Why can't you return instantly? – AndyD Jun 10 '13 at 10:36
  • I can't find the specific question, but using `findAndModify` as shown in this answer could help with multiple clients: http://stackoverflow.com/a/16182474/95190 . I don't understand the 404 either, is the resource not found? – WiredPrairie Jun 10 '13 at 11:03

1 Answers1

1

Interesting technique to ensure you process queued requests asap.

In terms of synchronising your queue, there is no need to as Node is single threaded. You don't need to worry about multithreading issues like in Java which is really nice I think.

Here's some rough code which might work. Add error handling and proper Mongo db calls.

var queue = {};

function addJob(req,res,next){ 
  var job = req.body;
  mongo.add(job, function(err){ // pseudo code to add request to db. 
    res.send(200); // request is stored, no need to keep the client waiting.
    // find a random slave in the queue:
    for(var slaveName in queue) {
      queue[slaveName].send(job);
      delete queue[slaveName];
      break;
    }      
  });
}

function getRequest(req,res,next){ 
  // get request from db - if there is no request add this slave request to queue
  mongo.get(..., function(err, job){
    if(job){
      return res.send(job);
    }
    // no job, add slave to queue:
    var slaveName = req.body;
    queue[slaveName] = res;

    // set timeout to send 404 after 4 seconds
    setTimeout(function(){
      delete queue[slaveName];
      res.send(404);
    }, 4000);
  }
}
AndyD
  • 5,252
  • 35
  • 32
  • The problem is that when there wont be any new jobs for long time then queue array will grow all the time (filled with nulls). This is the problem I have encountered - I tried to delete array index after 4 seconds but then all indexes changed and those used in setTimeout was not valid any more. For now I assumed that when timeout function is called it means that it "belongs" to the oldest slave in queue so the one in front of the queue (I use FIFO) and instead of `slave = queue[index]` I use `slave = queue.shift`. But dunno if it's good assumption... – user606521 Jun 10 '13 at 16:14
  • I thought about it and above assumption is not good because timeout func may send 404 to slave what was pushed to queue after first one in queue was shifted to send job to it. So I think it is neccessery to store some `counter` (this counter will act like id of job) in queue along with job and then in timeout function: `slave = queue[0]; if(slave && slave.counter <= counter) { queue.shift(); ... }` – user606521 Jun 10 '13 at 16:49
  • I've changed the code so that it doesn't use an index but a marker on the slave/response. – AndyD Jun 10 '13 at 17:52
  • But still if there wont be any new jobs and slaves will keep trying to get requests then queue array will grow forever (with res objects with marker set) - like this: [res,res,res,res,res,...] - I am looking to avoid this situation because for example there might be no new requests for for example 24 hours. But I guess that if u are using marker as above in your code its ok to add line `while(queue[0] && queue[0].used) queue.shift();` just after (or before) line `queue.push(res);` - if I am right please edit your code and I will accept your answer. – user606521 Jun 10 '13 at 20:51
  • 1
    I've edited the answer to use an object as a queue instead of an array. The main requirement for it to work is that the slave passes in a unique id in the request body. You could generate a unique id yourself too. – AndyD Jun 10 '13 at 21:54
  • Thanks for your answers - really tricky with this `queue[res.body]` :) – user606521 Jun 10 '13 at 21:59
  • you could always use https://github.com/broofa/node-uuid and not use res.body... Thanks for the question, nice little mind bender, but shows the slickness of node I think. Just try this in C# or Java... – AndyD Jun 10 '13 at 22:01