简体   繁体   中英

async.retry - Callback already called

I've looked around quite a bit and I am pretty sure the callback is not being called twice. Anyway, here is my code below:

async.eachSeries(saveProps,function(photo,doneMaster){
    var tries = 0;
    async.retry({times: 10, interval: 200}, 
        function(done, results){
            debug('attempt num: ' + (++tries));
            aws.getFile(photo.s3_location,function(err,s){
                if(err){
                    debug('error:' + err);
                    return done(err);
                }else{
                    done(null,s);
                 }
            })
        },
        function(err,imageStream){
            if(err){return doneMaster(err);}
            else{
                debug('appending photo to zip: %s', photo.s3_location);
                zip.append(imageStream, {name: photo.saveName});
                doneMaster();
            }
        }
    );
},function(err){
    if(err){
        reject(err);
    }else{
        zip.finalize();
    }
});

A little background for this problem, is that I am trying to iterate over some photo information, in a one-by-one fashion, and stream the data from s3 into a zip file. I am trying to make this work for many photos(a few thousand). The process trips up because one image returns an ECONNRESET. Whenever this happens I want to retry it once more. The problem I am having is that whenever an image throws an ECONNRESET and retries, the async library throws an error that the callback was already called. I've been looking at this forever and I don't call it twice.

My question, how could done() be called twice?

Some info about the objects/packages I am using:

Stack trace information:

app:worker attempt num: 1 +1ms
app:worker error:Error: read ECONNRESET +91ms
/appdir/node_modules/async/dist/async.js:803
            if (fn === null) throw new Error("Callback was already called.");
                             ^

Error: Callback was already called.
    at /appdir/node_modules/async/dist/async.js:803:36
    at /appdir/node_modules/async/dist/async.js:3481:17
    at /appdir/node_modules/async/dist/async.js:307:31
    at /appdir/node_modules/async/dist/async.js:3755:21
    at ClientRequest.<anonymous> (/appdir/modules/zip-worker.js:348:44)
    at emitOne (events.js:77:13)
    at ClientRequest.emit (events.js:169:7)
    at TLSSocket.socketErrorListener (_http_client.js:256:9)
    at emitOne (events.js:77:13)
    at TLSSocket.emit (events.js:169:7)
    at emitErrorNT (net.js:1253:8)
    at nextTickCallbackWith2Args (node.js:442:9)
    at process._tickCallback (node.js:356:17)

In this stack trace, /appdir/modules/zip-worker.js:348:44 is the line where done(null,s); is.

So, I figured out that node was having a hard time handling all the streams once the process got up to a certain amount of streams. So, each stream would be marked as done, but somewhere after the stream was appended one of the connections would reset causing that callback to refire with an error. So, I decided that instead of using getFile , I coule use get and deal with each image data individually. Instead of appending the stream to the zip i could append the image buffer data. It works beautifully now. Below is my code:

async.eachSeries(saveProps,function(photo,doneMaster){
    var tries = 0;
    async.retry({times: 10, interval: 200}, 
        function(done, results){
            var error;
            var imageData;
            debug('attempt num: ' + (++tries));
            var req = aws.get(photo.s3_location);

            req.on('response',function(res){
                if(res.statusCode !== 200){
                    error = new Error('status code ' + res.statusCode);
                }else{
                    res.on('data', function(chunk){
                        imageData += chunk;
                    });



                    res.on('end',function(){
                        if(error){
                            done(error);
                        }else{
                            done(null, imageData);
                        }
                    });
                }
            });

            req.on('error', function(err){
                error = err;
            });

            req.end();

        },
        function(err,data){
            if(err){return doneMaster(err);}
            else{
                debug('appending photo to zip: %s', photo.s3_location);
                zip.append(data, {name: photo.saveName});
                doneMaster();
            }
        }
    );
},function(err){
    if(err){
        debug('zip finished with error: ' + err);
        reject(err);
    }else{
        debug('zip finalizing');
        zip.finalize();
    }
}); 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM