3

I want to write a small worker app in node.js.
This app would read from AWS-SQS, process the data in some way, and spit it out to another AWS-SQS.
So far I have written:

while(true){
    readFromQueue()
    .then(transform)
    .then(writeToQueue);
}

function transform(data) {
    console.log("> transforming...");
    //transformation logic
    return data;
}

//TODO: need to remove message from queue after read!
function readFromQueue() {
    // var params = {
    //   QueueUrl: 'STRING_VALUE',
    //   WaitTimeSeconds: 2
    // };

    // return new Promise(function(resolve, reject) {
    //  sqs.receiveMessage(params, function(err, data) {
    //      if (err) reject(err);
    //      else     resolve(data);
    //  });
    // });
    return new Promise(function(resolve, reject) {
        console.log("> reading from queue...");
        resolve({ data : "world" });
    });
}

function writeToQueue(data) {
    // var params = {
    //  MessageBody: data,
    //  QueueUrl: 'STRING_VALUE',
    // };
    // sqs.sendMessage(params, function(err, data) {
    //  if (err) console.log(err, err.stack);
    //  else     console.log(data);
    // });
    console.log("> writing to queue...");
    console.log(">> " + data);
}

As you can see everything is set up for AWS, but when I run it locally for the time being, I would just have some mock stuff inside, until I actually get my transformation logic tested etc...
The problems I have are:

  • AWS-SQS API is async, it takes a callback. I wrapped it with a promise because I prefer promises to callbacks. Nevertheless, I wonder if I should block it and make it sync rather than async (for the reason below, but might be unrelated).
  • When I run this, I can see only "reading from queue.." output, it's almost as if "transform" and "writeToQueue" are not executed...
  • If I, however, comment out the while loop (so the script runs only once), I can see output from all 3 steps.

So am I doing something wrong? I can understand that since promises are async, my while loop will go crazy and create thousands of them, so that concerns me... Nevertheless I want to initiate another loop once the previous read->transform->write has been finished. Is there some other pattern I should use here? Or just block and wait for readFromQueue to end...

--EDIT--
It does execute everything if not wrapped in while(true):

readFromQueue()
        .then(transform)
        .then(writeToQueue);

I also understand that since while(true) is being executed, it will essentially block the thread, and therefore the promise is not resolved. So is there a way around it?

Daniel Gruszczyk
  • 5,379
  • 8
  • 47
  • 86

4 Answers4

5

I am falling back to my setInterval way. I know you said that reading queue has to start immediately after the writing is finished, but 10ms isn't much of delay if you ask me.

function someFunc(){
    if(readingQueue)    return;
    readingQueue = true;
    return readFromQueue()
      .then(transform)
      .then(writeToQueue)
      .catch(someErrorHandler)
      .then(function(){
        readingQueue=false;
      })
} 

var readingQueue = false;
setInterval(someFunc, 10);
mido
  • 24,198
  • 15
  • 92
  • 117
  • If I set interval for whatever value, if the queue returns before this, I am wasting time on waiting for interval to kick off again, and during this time I am not reading from the queue. I want to start reading from the queue as soon as I finished writing to the output queue... – Daniel Gruszczyk Aug 20 '15 at 10:00
  • @DanielGruszczyk: What interval are you talking about? – Bergi Aug 20 '15 at 10:59
  • @Bergi mido22 suggested using setInterval, before he edited his answer in response to my comment above. – Daniel Gruszczyk Aug 20 '15 at 11:14
  • @DanielGruszczyk: Ah, didn't see that. The recursion he proposes now is the correct solution. – Bergi Aug 20 '15 at 11:16
  • Yes this works. I am just wondering what will be the memory usage or if I will eventually get some full stack errors (recursion)... This supposed to run forever unless redeployed or crashed... – Daniel Gruszczyk Aug 20 '15 at 11:19
  • Sorry it's been a while since I accepted the answer, but I have abandoned this a while ago. Now I am using it for an actual production code and it turns out it is not tail-optimised and blows out of memory quickly. Therefore this is not a good solution. – Daniel Gruszczyk Nov 30 '15 at 15:57
  • @DanielGruszczyk aah, I can understand why previous method would fail, out of curiosity could you try my updated answer – mido Dec 01 '15 at 02:10
  • 1
    Yeah this indeed would work :) And yes, small interval like that is not bad, thanks :) – Daniel Gruszczyk Dec 01 '15 at 09:22
0

After evaluating all answers in here, I tried deasync, which did the job just fine :)
Nevertheless, I decided to go with mido22 answer above, just because I didn't want a plugin that is fiddling with node's event-loop.

Community
  • 1
  • 1
Daniel Gruszczyk
  • 5,379
  • 8
  • 47
  • 86
0

Instead of this:

while(true){
    readFromQueue()
    .then(transform)
    .then(writeToQueue);
}

Can't you use a recursive function? Something like this:

function getData(){
    return readFromQueue()
        .then(transform)
        .then(function(data){
            writeToQueue(data);
            getData();
        });
}

getData(); //Just to get it starting.
Arg0n
  • 8,283
  • 2
  • 21
  • 38
  • again, this approach will eat the memory as this recursion is not tail optimised. – Daniel Gruszczyk Dec 01 '15 at 09:09
  • Acctually, i think ES6 is tail optimized for recursive functions. Correct me if i'm wrong, but see this fiddle while monitoring your memory (WITHOUT console open): http://jsfiddle.net/Arg0n/d55t2sbn/2/ (It will take a while to finish). Notice **return** in front of `readFromQueue()` also. – Arg0n Dec 01 '15 at 10:01
  • Yes, ES6 is tail optimised. But I can't rely on the user of the app to run it on a version of node and/or browser that supports it. There is too much ES5 out there. for example my dev env runs node 0.12 and this approach eats through memory quickly... – Daniel Gruszczyk Dec 01 '15 at 12:06
  • I see... Good you found another solution then :). – Arg0n Dec 01 '15 at 12:23
0

I also ran into a similar issue (see setTimeout issue trying to wait for execution of async).

I've learned the lesson of sync patterns to be avoided in javascript and so i think your code could be rewritten with something similar:

function queueLooping(loopExitConditionPromise) {

    var blockingGuard = { done: false };

    loopExitConditionPromise.then(function () {
        blockingGuard.done = true;
    });

    return loopingSequence;

    var loopingSequence = function() {
        readFromQueue()
            .then(transform)
            .then(writeToQueue);
        if(!blockingGuard.done) 
            loopingSequence();
        else
            return;
    };

    var readFromQueue = function () {
        console.log("> reading from queue...");
        // ...
    }

    var transform = function (data) {
        console.log("> transforming...");
        // ...
    }

    var writeToQueue = function (data) {
        console.log("> writing to queue...");
        // ...
    }
}
Community
  • 1
  • 1
rfb
  • 1,107
  • 1
  • 7
  • 14