首页 > 解决方案 > 挂起等待 s3.listObjects

问题描述

我有一个触发 lambda 代码的事件,该代码尝试列出一个存储桶中的所有对象,然后将它们复制到另一个存储桶。

使用事件调用处理程序的代码

const cfnHandlerWrapper = async (event, handler) => {
    let resultPromise;
    try {
        handler(event).then(function() {
            console.log(`Successfully copied over all artifacts`);
            resultPromise = sendSuccess(event);
        }).catch(function(error) {
            console.log(`Failed copying over all artifacts: ${error}`);
            throw error;
        });
    } catch (e) {
        console.log('ERROR RUNNING HANDLER:');
        console.log(e);
        resultPromise = sendFailure(e.message || 'Something went wrong', event);
    }
    return resultPromise;
};

处理复制的代码

const copyHandler = async (event) => {
    console.log('Running Agent artifacts copy handler');

    const {RequestType, ResourceProperties} = event;
    const {DestBucket, SrcBucket, AdditionalArtifactsPath} = ResourceProperties;

    if (RequestType === 'Create' || RequestType === 'Update') {
        const srcLocation = [SrcBucket, AdditionalArtifactsPath].join('/');
        const batsAdditionalArtifactsParams = {
            Bucket: SrcBucket,
            Delimiter: '',
            Prefix: `${AdditionalArtifactsPath}/`
        };

        let copyPromises = [];
        console.log("about to await s3.listObjects()...");

        let listObjectsResult;
        try {
            listObjectsResult = await s3.listObjects(batsAdditionalArtifactsParams).promise();
        } catch(err) {
            console.log(`Failed to list objects for ${srcLocation}, err: ${err}`);
            throw err;
        }
        console.log(`finished waiting for listObjects. listObjectsResult: ${listObjectsResult}`);
        console.log(`Successfully listed objects in ${srcLocation}. Attempting to copy all artifacts to ${DestBucket}`);
        listObjectsResult.Contents.forEach((object) => {
            console.log(`parsing object ${object}`);
            let keyParts = object.Key.split('/');
            let fileKey = keyParts.pop();
            let destKey = fileKey.includes(agentBootstrapScript) ? `${agentBootstrapScriptsFolder}/${agentBootstrapScript}` : fileKey; // Adhere to bootstrap folder structure
            let copyParams = {
                Bucket: DestBucket,
                CopySource: `${srcLocation}/${fileKey}`,
                Key: destKey
            };
            console.log("pushing copy promise");
            copyPromises.push(s3.copyObject(copyParams).promise()
                .then(function(data) {
                    console.log(`Successfully copied ${fileKey} from ${srcLocation} to ${DestBucket}! data=${data}`);
                }).catch(function(err) {
                console.log(`Encountered error while copying ${fileKey} from ${srcLocation} to ${DestBucket}, error: ${err}`);
            }));
        });
        console.log("about to await Promise.all()....");
        await Promise.all(copyPromises);
    } else {
        console.log(`Received event type ${RequestType}, not copying anything`);
    }

    console.log("returning Promise.resolve()");
    return Promise.resolve();
};

此代码仅输出日志消息 about to await s3.listObjects()...,因此看起来它卡在等待中:

listObjectsResult = await s3.listObjects(batsAdditionalArtifactsParams).promise();

我要做的就是等待列出所有对象,承诺复制这些对象,然后使用 Promise.all 等待所有这些副本完成,然后再从该处理程序返回。我在 await 周围放了一个 try/catch,但它看起来就像它永远卡在那个 await 中。关于这个甚至如何调试这种情况的任何提示?

标签: javascriptnode.jsaws-lambdaaws-sdk-nodejs

解决方案


您的代码非常混乱。您正在将 async/await 与 Promise.then 混合使用。通常你会选择一种方式或另一种方式,但绝不会两者兼而有之。

通过查看您的代码,我猜想它会sendResult返回一个 Promise,因为您将它的结果分配给一个名为resultPromise但您从不等待它的对象。

copyHandler也返回一个 Promise 但你不等待它(你正在链接 then 调用)但是当它达到return resultPromise承诺时还没有解决。

对我来说,问题出在你的cfnHandlerWrapper功能上。我会将您发布的代码更改为:

const cfnHandlerWrapper = async (event, handler) => {
    try {
        await copyHandler(event)
        console.log(`Successfully copied over all artifacts`);
        return await sendSuccess(event);
    } catch (e) {
        console.log('ERROR RUNNING HANDLER:');
        console.log(e);
        return await sendFailure(e.message || 'Something went wrong', event);
    }
};

现在,forEach你有另一个问题,因为它里面的代码包含也将异步运行的承诺,所以到你调用的时候await Promise.all(copyPromises);copyPromises还没有填充你期望的所有承诺(很可能,它将填充没有)。

我会使用 afor of代替(下面只有相关代码):

for (const object of listObjectsResult.Contents) {
    console.log(`parsing object ${object}`);
    let keyParts = object.Key.split('/');
    let fileKey = keyParts.pop();
    let destKey = fileKey.includes(agentBootstrapScript) ? `${agentBootstrapScriptsFolder}/${agentBootstrapScript}` : fileKey; // Adhere to bootstrap folder structure
    let copyParams = {
        Bucket: DestBucket,
        CopySource: `${srcLocation}/${fileKey}`,
        Key: destKey
    };
    console.log("pushing copy promise");
    copyPromises.push(s3.copyObject(copyParams).promise())
}
console.log("about to await Promise.all()....");
await Promise.all(copyPromises)

如果不对其进行测试,很难判断此代码​​是否有效,但我不明白它为什么会失败。不管怎样,试着把你的组件分解成更小的部分,看看它到底哪里出了问题。不过,我把我所有的硬币都押在你处理承诺的方式上。


推荐阅读