payload icon indicating copy to clipboard operation
payload copied to clipboard

Cloud storage plugin socket usage at capacity

Open TimLanzi opened this issue 9 months ago • 20 comments

Link to reproduction

No response

Describe the Bug

I am using the cloud storage plugin with an S3 adapter pointing to DigitalOcean Spaces. I have users making a lot of edits relating to images lately. After a significant amount of uploads and image swapping, I get reports of performance issues on the admin dashboard and uploads failing. I looked at the logs and I found these error messages:

@smithy/node-http-handler:WARN socket usage at capacity=50 and 102 additional requests are enqueued. See https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/node-configuring-maxsockets.html or increase socketAcquisitionWarningTimeout=(millis) in the NodeHttpHandler config.
@smithy/node-http-handler:WARN socket usage at capacity=50 and 114 additional requests are enqueued. See https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/node-configuring-maxsockets.html or increase socketAcquisitionWarningTimeout=(millis) in the NodeHttpHandler config.
@smithy/node-http-handler:WARN socket usage at capacity=50 and 120 additional requests are enqueued. See https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/node-configuring-maxsockets.html or increase socketAcquisitionWarningTimeout=(millis) in the NodeHttpHandler config.
@smithy/node-http-handler:WARN socket usage at capacity=50 and 121 additional requests are enqueued. See https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/node-configuring-maxsockets.html or increase socketAcquisitionWarningTimeout=(millis) in the NodeHttpHandler config.
@smithy/node-http-handler:WARN socket usage at capacity=50 and 125 additional requests are enqueued. See https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/node-configuring-maxsockets.html or increase socketAcquisitionWarningTimeout=(millis) in the NodeHttpHandler config.
@smithy/node-http-handler:WARN socket usage at capacity=50 and 135 additional requests are enqueued. See https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/node-configuring-maxsockets.html or increase socketAcquisitionWarningTimeout=(millis) in the NodeHttpHandler config.
@smithy/node-http-handler:WARN socket usage at capacity=50 and 145 additional requests are enqueued. See https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/node-configuring-maxsockets.html or increase socketAcquisitionWarningTimeout=(millis) in the NodeHttpHandler config.
@smithy/node-http-handler:WARN socket usage at capacity=50 and 156 additional requests are enqueued. See https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/node-configuring-maxsockets.html or increase socketAcquisitionWarningTimeout=(millis) in the NodeHttpHandler config.
@smithy/node-http-handler:WARN socket usage at capacity=50 and 158 additional requests are enqueued. See https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/node-configuring-maxsockets.html or increase socketAcquisitionWarningTimeout=(millis) in the NodeHttpHandler config.
@smithy/node-http-handler:WARN socket usage at capacity=50 and 161 additional requests are enqueued. See https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/node-configuring-maxsockets.html or increase socketAcquisitionWarningTimeout=(millis) in the NodeHttpHandler config.
@smithy/node-http-handler:WARN socket usage at capacity=50 and 163 additional requests are enqueued. See https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/node-configuring-maxsockets.html or increase socketAcquisitionWarningTimeout=(millis) in the NodeHttpHandler config.
@smithy/node-http-handler:WARN socket usage at capacity=50 and 166 additional requests are enqueued. See https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/node-configuring-maxsockets.html or increase socketAcquisitionWarningTimeout=(millis) in the NodeHttpHandler config.
@smithy/node-http-handler:WARN socket usage at capacity=50 and 169 additional requests are enqueued. See https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/node-configuring-maxsockets.html or increase socketAcquisitionWarningTimeout=(millis) in the NodeHttpHandler config.
@smithy/node-http-handler:WARN socket usage at capacity=50 and 170 additional requests are enqueued. See https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/node-configuring-maxsockets.html or increase socketAcquisitionWarningTimeout=(millis) in the NodeHttpHandler config.

These socket capacity warnings are sometimes preceded by error messages like this:

ERROR (payload): UnknownError
    err: {
      "type": "NoSuchKey",
      "message": "UnknownError",
      "stack":
          NoSuchKey: UnknownError
              at de_NoSuchKeyRes (/home/node/app/node_modules/@aws-sdk/client-s3/dist-cjs/index.js:4809:21)
              at de_CommandError (/home/node/app/node_modules/@aws-sdk/client-s3/dist-cjs/index.js:4747:19)
              at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
              at async /home/node/app/node_modules/@smithy/middleware-serde/dist-cjs/index.js:35:20
              at async /home/node/app/node_modules/@aws-sdk/middleware-signing/dist-cjs/index.js:225:18
              at async /home/node/app/node_modules/@smithy/middleware-retry/dist-cjs/index.js:320:38
              at async /home/node/app/node_modules/@aws-sdk/middleware-flexible-checksums/dist-cjs/index.js:173:18
              at async /home/node/app/node_modules/@aws-sdk/middleware-sdk-s3/dist-cjs/index.js:97:20
              at async /home/node/app/node_modules/@aws-sdk/middleware-sdk-s3/dist-cjs/index.js:120:14
              at async /home/node/app/node_modules/@aws-sdk/middleware-logger/dist-cjs/index.js:33:22
      "name": "NoSuchKey",
      "$fault": "client",
      "$metadata": {
        "httpStatusCode": 404,
        "requestId": "[request id]",
        "attempts": 1,
        "totalRetryDelay": 0
      },
      "Code": "NoSuchKey",
      "BucketName": "bucket-name",
      "RequestId": "[request id]",
      "HostId": "[host id]"
    }

I have my Payload app hosted on a basic DigitalOcean Droplet (1 vCPU, 1GB RAM, 25GB storage, added 1GB swap space) if that helps at all.

To Reproduce

It seems like it happens when a lot of uploading is done. I haven't seen a clear-cut way to reproduce, but the best I can do is:

  1. Set up a payload app with the cloud storage plugin
  2. Point to a DigitalOcean Spaces bucket
  3. Create an upload collection
  4. Upload images in quick succession. Upload new images into existing records. Whatever can be done to trigger the errors.

Payload Version

2.14.1

Adapters and Plugins

"@payloadcms/bundler-webpack": "^1.0.6", "@payloadcms/db-mongodb": "^1.5.1", "@payloadcms/plugin-cloud-storage": "^1.1.2", "@payloadcms/plugin-nested-docs": "^1.0.12", "@payloadcms/plugin-seo": "^2.3.1", "@payloadcms/richtext-lexical": "^0.9.1", "@aws-sdk/client-s3": "^3.556.0", "@aws-sdk/lib-storage": "^3.556.0", "aws-crt": "^1.21.2"

TimLanzi avatar May 16 '24 13:05 TimLanzi

Also having this problem, after a while(not more than hour) we stop getting images and the same socket warning start showing up... Increasing the socket number didn't help. We are hosting on render, images in S3 and DB is Mongo

Stroi avatar May 18 '24 06:05 Stroi

@TimLanzi @Stroi Can either of you give me an idea of your throughput for uploads? Seems like this might be tricky to reproduce.

denolfe avatar Jun 12 '24 18:06 denolfe

It's been a while so I can't really give exact numbers. If I had to guess, I'd say 15 upload events in 10 minutes. Image sizes were around 1MB each. I also have imageSizes set up as follows:

    imageSizes: [
      {
        name: 'thumbnail',
        width: 400,
        height: 300,
        position: 'centre',
      },
      {
        name: 'card',
        width: 768,
        height: 1024,
        position: 'centre',
      },
      {
        name: 'tablet',
        width: 1024,
        height: null,
        position: 'centre',
      },
    ],

TimLanzi avatar Jun 13 '24 17:06 TimLanzi

I have the same issue with almost the same payloadcms settings as @TimLanzi, same payloadcms and s3 errors when uploading images.

Edit: looks like the plugin is creating s3 socket connections that its not closing @denolfe

blobmold avatar Jun 26 '24 16:06 blobmold

I'm having exactly the same issue.

"name":"payload","err":{"type":"NoSuchKey","message":"The specified key does not exist.","stack":"NoSuchKey: The specified key does not exist.\n at de_NoSuchKeyRes (/home/node/backend/node_modules/@aws-sdk/client-s3/dist-cjs/index.js:4791:21)\n at de_CommandError (/home/node/backend/node_modules/@aws-sdk/client-s3/dist-cjs/index.js:4729:19)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async /home/node/backend/node_modules/@smithy/middleware-serde/dist-cjs/index.js:35:20\n at async /home/node/backend/node_modules/@aws-sdk/middleware-signing/dist-cjs/index.js:226:18\n at async /home/node/backend/node_modules/@smithy/middleware-retry/dist-cjs/index.js:320:38\n at async /home/node/backend/node_modules/@aws-sdk/middleware-flexible-checksums/dist-cjs/index.js:174:18\n at async /home/node/backend/node_modules/@aws-sdk/middleware-sdk-s3/dist-cjs/index.js:98:20\n at async /home/node/backend/node_modules/@aws-sdk/middleware-sdk-s3/dist-cjs/index.js:121:14\n at async /home/node/backend/node_modules/@aws-sdk/middleware-logger/dist-cjs/index.js:34:22","name":"NoSuchKey","$fault":"client","$metadata":{"httpStatusCode":404,"requestId":"17E092AADF93FA23","extendedRequestId":"dd9025bab4ad464b049177c95eb6ebf374d3b3fd1af9251148b658df7ac2e3e8","attempts":1,"totalRetryDelay":0},"Code":"NoSuchKey","Key":"BPRO-Spender-EN.pdf","BucketName":"production","Resource":"/production/BPRO-Spender-EN.pdf",,"},"msg":"The specified key does not exist."}

2024-07-09T15:19:53.037446982Z stdout F {"level":50,"time":1720538393036,"pid":7, "name":"payload","err":{"type":"Error","message":"Socket timed out without establishing a connection within 10000 ms","stack":"TimeoutError: Socket timed out without establishing a connection within 10000 ms\n at Timeout._onTimeout (/home/node/backend/node_modules/@smithy/node-http-handler/dist-cjs/index.js:67:21)\n at listOnTimeout (node:internal/timers:573:17)\n at process.processTimers (node:internal/timers:514:7)","name":"TimeoutError","$metadata":{"attempts":3,"totalRetryDelay":146}},"msg":"Socket timed out without establishing a connection within 10000 ms"}

creative-andrew avatar Jul 08 '24 23:07 creative-andrew

I found a thread that deals with this specific issue: https://github.com/aws/aws-sdk-js-v3/issues/3560

I would give a few of these solutions a try.

denolfe avatar Jul 12 '24 17:07 denolfe

My cloud hosting provider pointed also other issues related to this:

https://github.com/aws/aws-sdk-js-v3/issues/6263

https://github.com/aws/aws-sdk-js-v3/blob/main/supplemental-docs/CLIENTS.md#request-handler-requesthandler

I believe I have finally managed to recreate the issue with my test setup. It required me to upload a video to payload, I then ran 120 curl instances at once with a timeout shorter than the video length and that seems to trigger the problem. There is an interesting comment on the AWS SDK regarding socket exhaustion which helped me identify the problem.

creative-andrew avatar Jul 12 '24 20:07 creative-andrew

I found a thread that deals with this specific issue: aws/aws-sdk-js-v3#3560

I would give a few of these solutions a try.

Hello, Hi can I implement this solution using the adapter?

    httpsAgent: new Agent({
      maxSockets: 500,

      // keepAlive is a default from AWS SDK. We want to preserve this for
      // performance reasons.
      keepAlive: true,
      keepAliveMsecs: 1000,
    }),
    socketTimeout: 5000,
  }),

When I tried to import { NodeHttpHandler } from '@smithy/node-http-handler'; I had this error Module not found: Error: Can't resolve 'http2' in '/home/node/app/node_modules/@smithy/node-http-handler/dist-es'

LoydAndrew avatar Jul 17 '24 14:07 LoydAndrew

I found a thread that deals with this specific issue: aws/aws-sdk-js-v3#3560 I would give a few of these solutions a try.

Hello, Hi can I implement this solution using the adapter?

    httpsAgent: new Agent({
      maxSockets: 500,

      // keepAlive is a default from AWS SDK. We want to preserve this for
      // performance reasons.
      keepAlive: true,
      keepAliveMsecs: 1000,
    }),
    socketTimeout: 5000,
  }),

When I tried to import { NodeHttpHandler } from '@smithy/node-http-handler'; I had this error Module not found: Error: Can't resolve 'http2' in '/home/node/app/node_modules/@smithy/node-http-handler/dist-es'

My hosting provider gave me a similar solution to try

import https from "https"; 

              requestHandler: {
                connectionTimeout: 10 * 1000,
                httpsAgent: new Agent({
                  maxSockets: Infinity,
                  keepAlive: false,
                }),
                keepAlive: false,
                maxSockets: Infinity,
              }

However, I got stuck on the same issue you are facing. I couldn't resolve the package within Payload.

creative-andrew avatar Jul 17 '24 15:07 creative-andrew

I found a thread that deals with this specific issue: aws/aws-sdk-js-v3#3560 I would give a few of these solutions a try.

Hello, Hi can I implement this solution using the adapter?

    httpsAgent: new Agent({
      maxSockets: 500,

      // keepAlive is a default from AWS SDK. We want to preserve this for
      // performance reasons.
      keepAlive: true,
      keepAliveMsecs: 1000,
    }),
    socketTimeout: 5000,
  }),

When I tried to import { NodeHttpHandler } from '@smithy/node-http-handler'; I had this error Module not found: Error: Can't resolve 'http2' in '/home/node/app/node_modules/@smithy/node-http-handler/dist-es'

My hosting provider gave me a similar solution to try

import https from "https"; 

              requestHandler: {
                connectionTimeout: 10 * 1000,
                httpsAgent: new Agent({
                  maxSockets: Infinity,
                  keepAlive: false,
                }),
                keepAlive: false,
                maxSockets: Infinity,
              }

However, I got stuck on the same issue you are facing. I couldn't resolve the package within Payload.

@denolfe could you help with this, please?

LoydAndrew avatar Jul 17 '24 15:07 LoydAndrew

It's possible http2 was added in a later node version. Try node 20 or newer if you are not already. Pure speculation on my part as there are quite a few possible solutions in that thread.

denolfe avatar Jul 17 '24 20:07 denolfe

That's definitely a thing. I have "node": "20.10.0". NodeHttpHandler won't work on browser that's why webpack compilation will fail with Error: Can't resolve 'http2' in '/home/node/app/node_modules/@smithy/node-http-handler/dist-es'. There is no polyfil for that and this workaround http2: m, // m is emptyModule won't work too. So is there no solution to this problem? Cause increasing maxSockets through NodeHttpHandler constructor is a way to go solution according to aws-sdk docs and answers from thread

LoydAndrew avatar Jul 17 '24 21:07 LoydAndrew

Okay. So there is a dirty solution. One can implement custom NodeHttpHandler that don't rely in any way on http2 (apparently @smithy/node-http-handler use it only for some constant from there anyway). Then polyfill https and http module in webpack (using 'stream-http' and 'https-browserify'). Maybe ditching payload cloud storage and just using aws-s3-client directly in media collection hooks would be a better solution

LoydAndrew avatar Jul 18 '24 15:07 LoydAndrew

i'm facing this issue too:

@smithy/node-http-handler:WARN socket usage at capacity=50 and 102 additional requests are enqueued. See https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/node-configuring-maxsockets.html or increase socketAcquisitionWarningTimeout=(millis) in the NodeHttpHandler config.
@smithy/node-http-handle…

rebuilding the functionality via collection hooks doesn't seem to be the best use of effort, wonder if there is a way to fix this in the cloud storage plugin

JPrisk avatar Aug 02 '24 00:08 JPrisk

We have the same issue in several projects. Changing the request handler causes several webpack errors. Tried to mock some modules, but didn't work. The only workaround is to restart the payload service. So no solution. At this point the plugin is unusable :(

dkirchhof avatar Sep 02 '24 11:09 dkirchhof

To use this config


requestHandler: {
              connectionTimeout: 10 * 1000,
              httpsAgent: new https.Agent({
                maxSockets: Infinity,
                keepAlive: false,
              }),
              keepAlive: false,
              maxSockets: Infinity,
            },

We use:

"https-browserify": "^1.0.0",

and add it to the webpack config as fallback

        fallback: {
          crypto: false,
          fs: false,
          http: require.resolve("stream-http"),
          https: require.resolve("https-browserify"),
          url: require.resolve("url"),
        },
      },

creative-andrew avatar Sep 02 '24 12:09 creative-andrew

Can anyone else confirm @creative-andrew 's solution works for them?

Alternatively, here is another request handler config formed from a solution given in the linked thread

pnpm add @smithy/node-http-handler https-browserify url

import { NodeHttpHandler } from '@smithy/node-http-handler'
import { Agent as HttpAgent } from 'http'
import { Agent as HttpsAgent } from 'https'

// Pass this in your s3Adapter config
const s3AdapterConfig = {
  // .. other options
  requestHandler: new NodeHttpHandler({
    connectionTimeout: 5000,
    socketTimeout: 120000,
    httpAgent: new HttpAgent({ maxSockets: 500, keepAlive: true }),
    httpsAgent: new HttpsAgent({ maxSockets: 500, keepAlive: true }),
  }),
}

Modify webpack as needed. Might need to play around here, but this is what i got to work.

  admin: {
    webpack: (config => ({
      resolve: {
        fallback: {
          crypto: false,
          http: require.resolve('https-browserify'),
          http2: false,
          https: require.resolve('https-browserify'),
          url: require.resolve('url'),
        },
      }
    }))
  }

denolfe avatar Sep 03 '24 02:09 denolfe

Hey @denolfe - i've had @creative-andrew's solution in production since the 9th of September and the issue hasn't occurred. Usually after 3-4 days the sockets would be getting maxed out

JPrisk avatar Sep 18 '24 01:09 JPrisk

Unfortunately it doesn't work for me.

ERROR in ./node_modules/@smithy/node-http-handler/dist-es/node-http2-connection-manager.js 1:0-26 Module not found: Error: Can't resolve 'http2' in '/home/node/app/node_modules/@smithy/node-http-handler/dist-es'

payload config:

webpack: (config) => {
    return {
        ...config,
        resolve: {
            ...config.resolve,
            alias: {
                ...(config.resolve?.alias ? config.resolve.alias : {}),
                jsonwebtoken: mockModulePath,
            },
            fallback: {
                ...(config.resolve?.fallback ? config.resolve.fallback : {}),
                http: require.resolve("https-browserify"),
                http2: false,
                https: require.resolve("https-browserify"),
                url: require.resolve("url"),
            },
        },
    };
},

cloud storage adapter config:

const cloudStorageAdapter = s3Adapter({
    config: {
        region: process.env.AWS_REGION,
        credentials: {
            accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
            secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
        },
        requestHandler: new NodeHttpHandler({
            connectionTimeout: 5000,
            socketTimeout: 120000,
            httpAgent: new HttpAgent({ maxSockets: 500, keepAlive: true }),
            httpsAgent: new HttpsAgent({ maxSockets: 500, keepAlive: true }),
        }),
    },
    bucket: process.env.AWS_S3_BUCKET_NAME!,
});

dkirchhof avatar Sep 18 '24 10:09 dkirchhof

@creative-andrew where does the "stream-http" or "url" module come from? which versions of these packages do you use? @denolfe why don't you get issues with the http2 module?

would you share your package.jsons or lock-files as well? maybe the full webpack config? are there problems when using alias and fallback together? (i don't know what all these settings do - never used it before).

dkirchhof avatar Sep 25 '24 09:09 dkirchhof

@denolfe where in a payload project does the webpack config you shared go?

 webpack: (config => ({
      resolve: {
        fallback: {
          crypto: false,
          http: require.resolve('https-browserify'),
          http2: false,
          https: require.resolve('https-browserify'),
          url: require.resolve('url'),
        },
      }
    }))

there's no webpack option in payload v3 from the look of it. I see it in the docs for v2

https://github.com/payloadcms/payload/issues/6382#issuecomment-2325468104

danieloi avatar Dec 27 '24 14:12 danieloi

I have this problem in v3, as well - and also with Digital ocean spaces. I wonder if this is a coincidence, or could it be an issue with DO service specifically.

azivkovi avatar Dec 31 '24 09:12 azivkovi

I am running into this problem in v3 now and we need to launch the site next week... does anyone have or know of a hack/workaround that will work in version 3. OR another solution for handling media files in Payload.

aaronksaunders avatar Jan 03 '25 06:01 aaronksaunders

While i wasn't able to fix the issue with all these webpack aliases etc. we used a crown job, which checks if the images are still available. If not, it will restart the server.

After other problems with webpack I figured out that a plugin was overriding the config. So probably the already mentioned resolutions will work in my projects as well.

dkirchhof avatar Jan 03 '25 08:01 dkirchhof

I am getting the same error since upgrading to Payload 3.x. However I only receive this error while trying to load images. It seems like there is an unresolved socket for each image request and thats why it adds up quickly.

JulianAtTheFrontend avatar Jan 06 '25 15:01 JulianAtTheFrontend

Same error message

alsherif-khalaf avatar Jan 18 '25 11:01 alsherif-khalaf

Same for me:

@smithy/node-http-handler:WARN - socket usage at capacity=50 and 110 additional requests are enqueued.

tb-b avatar Jan 25 '25 11:01 tb-b

There is a configuration change you can make to resolve this issue, if u google it you will find it, my apologies I am not in front of my computer now. I will post it when I get home.

I still continued to get memory issues and my app crashing, I was forced to reduce the number of image sizes I was having payload create using the sharp library.

aaronksaunders avatar Jan 25 '25 19:01 aaronksaunders

We are currently testing a v3 app in a staging environment using MinIO as a s3 server and are facing similar problems. Images stop loading after a few minutes after browsing through the media collection.

As a quick fix, I've added the generateFileURL and adminThumbnail functions, as all media that is shown in our admin interface is also publicly available. This way, we are skipping payloads proxying that's currently broken.

linus-ha avatar Jan 28 '25 19:01 linus-ha

Same problem here, using payload 2.29.0 and @payloadcms/plugin-cloud-storage 1.2.0

After a few days, depending on the usage, it stops working. A payload restart solves it, but it's an inconvenience. Moreover, there's no way to configure the node handler library from payload.

joan-serra avatar Jan 30 '25 11:01 joan-serra