AWS Lambda上的ENOSPC错误

时间:2016-05-16 13:49:51

标签: javascript node.js amazon-web-services lambda aws-lambda

抱歉这个已加载的问题。

予。 TL; DR:

AWS Lambda上的/tmp目录在不应该填写的时候不断填写,并在后续请求中给出ENOSPC错误。

II。 TL 版本:

我在AWS Lambda上使用Node JS(0.10x)构建了一个微服务,可以完成两件事:

  • 给定一个网址列表,它会转到相关来源(S3,Cloudfront,thumbor等)并将物理文件下载到/tmp目录
  • 下载所有这些文件后,它会将它们压缩成tar球并上传到S3。

在发布代码之前,我只是想说我正在使用一些非标准依赖项,尽管很难说JavaScript中的标准是什么

以下是每个部分的相关代码

1。下载文件

/**
 * Function downloads a single file from S3 then write to a temporary directory to prepare for later archiving.
 *
 * @param {String} url
 * @return {Promise}
 */
function downloadSingleFile(url) {
    let options = { open_timeout: 5000 };
    let promise;

    // go to CloudFront if image to get already-resized webp image
    if (isImage(url.relative)) {
        options.headers = { 'Content-Type' : 'image/webp' };
        promise = needle
            .getAsync(url.absolute, options)
            .then(resp => [path.join(conf.TEMP_DIR, url.relative), resp.body])
            .catch(e => {
                console.log("Socket hangup for: ", url.relative, url.absolute);
                return Promise.resolve();
            });
    } else {
        // else go directly to S3
        // take care to decode url back into raw string the way s3 expects it to be
        let s3key = decodeURIComponent(url.relative).replace(/^\//, '').replace(/\+/g, ' ');
        promise = s3.getObjectAsync({
            Bucket: conf.S3_STATIC_BUCKET,
            Key: s3key
        }).then(resp => [path.join(conf.TEMP_DIR, url.relative), resp.Body])
        .catch(e => {
            console.log("No such key on S3: ", s3key);
            return Promise.resolve();
        });
    }

    return promise.then(args => fs.writeFileAsync(args[0], args[1]).return(url));
}

/**
 * Function downloads a list of urls from S3 to the local tmp dir
 *
 * @param {Array} urls list of urls to download
 * @return {Promise}
 */
function downloadAllFiles(urls) {
    return Promise.map(urls, downloadSingleFile, {concurrency: 20});
}

2。压缩下载的文件

/**
 * Function compresses downloaded files from the local temp dir
 *
 * @return {Promise}
 */
function compressFiles() {
    return new Promise((resolve, reject) => {
        exec(`tar pczf /tmp/foo.tar.gz --remove-files ${conf.TEMP_DIR}`,
            function (err, stdout, stderr) {
                if (err) reject(err);
                else resolve();
        });
    });
}

III。问题

如果要下载的文件太多,从而使/tmp目录大于AWS Lambda 500MB limit,则会出现ENOSPC错误,这是可以理解的。但是,在随后的请求中,即使下载的文件数量要少得多,它仍然会在一段时间后给我ENOSPC错误,然后才能恢复正常。

IV。可能的原因

IMO的唯一解释在于AWS Lambda的神秘文档

  

为了提高性能,AWS Lambda可能会选择保留一个实例   您的函数并重用它来提供后续请求,而不是   创建一个新副本。您的代码不应该假设这将永远   发生。

很好,问题是,当发生时,他们没有告诉你该怎么做,这导致我......

诉我试过的事情

在使用

调用函数之后,我基本上强制删除之前的目录
if (fs.existsSync(conf.TEMP_DIR))
    del.sync([conf.TEMP_DIR], {force: true});

但它似乎仍然没有从一个干净的平板开始,或者更确切地说,每次调用都是干净的/tmp。我还想到将下载文件的重点直接流式传输到支持内存流式传输的打包库,而不是缓冲到磁盘,例如: adm-zip,但是这个库在与needle结合使用时会抛出太多异常,这似乎是针对Node的最好的可流式HTTP库。 iirc,这是因为Node版本。也许如果我将lambda运行时升级到4.x,它将再次起作用。但是我想在投入任何黑客之前确保我理解这个问题:

  

为什么AWS Lambda函数不具有自己的非持久性磁盘空间   尽管我付出了最大的努力,/ tmp目录在每次调用时都会重新开始   清除它?

0 个答案:

没有答案