aws-sdk-js-v3: S3.send sometimes leads to application exit with code 0

Describe the bug

Executing this code…

        let s3: S3 = /*...*/;

        // Many awaits

        await s3.send(
            new PutObjectCommand({
                Bucket: bucket,
                Key: toFile,
                Body: data,
                CacheControl: cacheControl,
                ContentType: contentType ?? undefined,
            }),
        );

        // More awaits

…in the middle of a long chain of awaits sometimes leads to application exit with code 0.

I believe this may be because of the behavior described here: https://github.com/nodejs/node/issues/22088 Basically, I think, s3.send somehow drops both its handle to resolve and reject, dropping the reference count of scheduled requests in the node runtime to 0, which in turn automatically exits node with code 0.

This took a long time to figure out and is very confusing when encountering it for the first time.

The reason I think this is what happens, is because when i use the setTimeout trick, the application stays alive until setTimeout triggers and then immediately exits with code 0.

Example of this:

        const t = setTimeout(() => console.log("Timeout"), 5000);
        await s3.send(
            new PutObjectCommand({
                Bucket: bucket,
                Key: toFile,
                Body: data,
                CacheControl: cacheControl,
                ContentType: contentType ?? undefined,
            }),
        );
        clearTimeout(t);

It seems to happen more often on 4G connections than on WiFi.

Expected Behavior

An actual error to be thrown or printed.

Current Behavior

My node application exits with code 0 even though all code has not been run yet.

Reproduction Steps

Since the error is intermittent, and I have a lot of proprietary code, I can’t give a better example than this:

        let s3: S3 = /*...*/;

        // Many awaits

        await s3.send(
            new PutObjectCommand({
                Bucket: bucket,
                Key: toFile,
                Body: data,
                CacheControl: cacheControl,
                ContentType: contentType ?? undefined,
            }),
        );

        // More awaits

Possible Solution

Hopefully the code in the aws-sdk-js repository can be fixed.

Additional Information/Context

No response

SDK version used

3.245.0

Environment details (OS name and version, etc.)

macOS 13.0.1 (22A400) node v18.8.0 scripts compiled & run using ts-node

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Comments: 46 (15 by maintainers)

Most upvoted comments

Hey, we have probably the same problem.

const main = async function() {
    const dynamodb = DynamoDBDocumentClient.from(new DynamoDB({}));
    const s3 = new S3Client({});
    const config = {
        client: dynamodb,
        pageSize: 20,
    };
    const input = {
        TableName: TABLE_NAME,
    };
    const paginator = paginateScan(config, input);
    let count = 0;
    for await (const page of paginator) {
        count += page.Count!;
        //@ts-ignore
        for (const item of page.Items) {
            if (item.PK.startsWith('MY_KEY#')) {
                if (!item.S3Version) {
                    try {
                        console.log('getting object', item.PK, item.S3Key);
                        const result = await s3.send(new GetObjectCommand({
                            Bucket: BUCKET_NAME,
                            Key: item.S3Key,
                        }));
                        console.log('updating measurement object', item.PK, item.S3Key);
                        const input1 = {
                            TableName: TABLE_NAME,
                            Key: { PK: item.PK, SK: item.SK },
                            UpdateExpression: 'set S3Version = :v',
                            ExpressionAttributeValues: {
                                ':v': result.VersionId,
                            },
                        };
                        console.log(input1);
                        if (false) {
                            await dynamodb.send(new UpdateCommand(input1));
                        }
                        await sleep(500);
                    } catch (e) {
                        console.log('skipping becasue of error...');
                    }
                }
            }
        }
    }

When I iterate over the DynamoDB table without S3 interaction it just works as expected. Once S3 is involved it just silently fails in the middle of the process. That’s kind of unfortunate. node: v16.18.1

Hi @bes,

Thanks for providing me with the repo.

I refactored your code a bit and let the program run for about 5-6 minutes before stopping it. It managed to write a few thousand objects to s3 successfully without exiting:

image

I usually use unique Object bodies when testing because otherwise the Etag would be the same, and it would be harder to debug. So Something like:

Body: `hello ${Date.now()}`

The only time my application crashed was when I ran the request with an empty buffer like in your example:

Body: Buffer.from("", "utf-8")

The error I got was

node[42307]: ../src/node_http_parser.cc:589:static void node::(anonymous namespace)::Parser::Execute(const FunctionCallbackInfo<v8::Value> &): Assertion `parser->current_buffer_.IsEmpty()' failed.
 1: 0x1025c2e28 node::Abort() [/Users/rvaknin/.nvm/versions/node/v18.0.0/bin/node]
 2: 0x1025c2c68 node::AppendExceptionLine(node::Environment*, v8::Local<v8::Value>, v8::Local<v8::Message>, node::ErrorHandlingMode) [/Users/rvaknin/.nvm/versions/node/v18.0.0/bin/node]
 3: 0x1025dc004 node::(anonymous namespace)::Parser::Execute(v8::FunctionCallbackInfo<v8::Value> const&) [/Users/rvaknin/.nvm/versions/node/v18.0.0/bin/node]
 4: 0x102785274 v8::internal::FunctionCallbackArguments::Call(v8::internal::CallHandlerInfo) [/Users/rvaknin/.nvm/versions/node/v18.0.0/bin/node]
 5: 0x102784d58 v8::internal::MaybeHandle<v8::internal::Object> v8::internal::(anonymous namespace)::HandleApiCallHelper<false>(v8::internal::Isolate*, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::FunctionTemplateInfo>, v8::internal::Handle<v8::internal::Object>, v8::internal::BuiltinArguments) [/Users/rvaknin/.nvm/versions/node/v18.0.0/bin/node]
 6: 0x1027845e4 v8::internal::Builtin_HandleApiCall(int, unsigned long*, v8::internal::Isolate*) [/Users/rvaknin/.nvm/versions/node/v18.0.0/bin/node]
 7: 0x102f57a2c Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_BuiltinExit [/Users/rvaknin/.nvm/versions/node/v18.0.0/bin/node]
 8: 0x102ee1b98 Builtins_InterpreterEntryTrampoline [/Users/rvaknin/.nvm/versions/node/v18.0.0/bin/node]
 ...
 ...
 ...
 ...

And this went away when I commented out process.exit(0) which was my lead suspect to begin with. Did you try commenting that out?

Thanks, Ran~

I see from the stack trace that you are executing this with Node 18.0.0. In 18.6.0 a fix for this panic was introduced. So please make sure you use a more recent Node version.

Hi @bes,

I still have no idea what the issue is and why you are the only one experiencing this. I will discuss it again with the team and see some of the more senior peeps can take a deeper look.

Thanks, Ran~