nextcloud / server

☁️ Nextcloud server, a safe home for all your data
https://nextcloud.com
GNU Affero General Public License v3.0
27.53k stars 4.08k forks source link

[Bug]: S3 multipart uploads run a PutObject beforehand, causing rate limits on some providers #45501

Open elizabeth-dev opened 6 months ago

elizabeth-dev commented 6 months ago

⚠️ This issue respects the following points: ⚠️

Bug description

When using an S3-like object storage as primary storage, and uploading a big file that goes beyond the putSizeLimit setting (so it's going to be a multipart upload), an empty PutObject request is sent just before the S3 CreateMultipartUpload method, causing rate limits (HTTP 429) on some providers like Storj that limit concurrent requests.

At first glance this seems completely unnecessary, because the object will already be created by the CreateMultipartUpload method.

The following screenshot (using MITM Proxy) shows the request trace when I try to upload a big file twice (the first two requests can be ignored. The POST ones are the ones that should initiate the multipart upload, but fail with a 429 code and a Retry-After header value of 5 seconds

Pasted image 20240524213149foo

Steps to reproduce

  1. Setup a Nextcloud instance using Storj as Primary Storage
  2. Try to upload a file with a size beyond the putSizeLimit threshold (default: >100MB)
  3. The upload does not advance and an error appears in the logs regarding a 429 error when calling CreateMultipartUpload

Expected behavior

Uploads of bigger files work because no unnecessary PutObject request is made before starting the multipart upload

Installation method

Community Docker image

Nextcloud Server version

29

Operating system

Debian/Ubuntu

PHP engine version

PHP 8.2

Web server

Apache (supported)

Database engine version

PostgreSQL

Is this bug present after an update or on a fresh install?

Fresh Nextcloud Server install

Are you using the Nextcloud Server Encryption module?

Encryption is Disabled

What user-backends are you using?

Configuration report

{
    "system": {
        "htaccess.RewriteBase": "\/",
        "memcache.local": "\\OC\\Memcache\\APCu",
        "apps_paths": [
            {
                "path": "\/var\/www\/html\/apps",
                "url": "\/apps",
                "writable": false
            },
            {
                "path": "\/var\/www\/html\/custom_apps",
                "url": "\/custom_apps",
                "writable": true
            }
        ],
        "memcache.distributed": "\\OC\\Memcache\\Redis",
        "memcache.locking": "\\OC\\Memcache\\Redis",
        "redis": {
            "host": "***REMOVED SENSITIVE VALUE***",
            "port": 6379,
            "password": "***REMOVED SENSITIVE VALUE***"
        },
        "mail_smtpmode": "smtp",
        "mail_smtphost": "***REMOVED SENSITIVE VALUE***",
        "mail_smtpport": "587",
        "mail_smtpauth": true,
        "mail_smtpauthtype": "LOGIN",
        "mail_smtpname": "***REMOVED SENSITIVE VALUE***",
        "mail_from_address": "***REMOVED SENSITIVE VALUE***",
        "mail_domain": "***REMOVED SENSITIVE VALUE***",
        "mail_smtppassword": "***REMOVED SENSITIVE VALUE***",
        "upgrade.disable-web": true,
        "passwordsalt": "***REMOVED SENSITIVE VALUE***",
        "secret": "***REMOVED SENSITIVE VALUE***",
        "trusted_domains": [
            "cloud.elizabeth.sh"
        ],
        "datadirectory": "***REMOVED SENSITIVE VALUE***",
        "dbtype": "pgsql",
        "version": "29.0.0.19",
        "overwrite.cli.url": "https:\/\/cloud.elizabeth.sh",
        "dbname": "***REMOVED SENSITIVE VALUE***",
        "dbhost": "***REMOVED SENSITIVE VALUE***",
        "dbport": "",
        "dbtableprefix": "oc_",
        "dbuser": "***REMOVED SENSITIVE VALUE***",
        "dbpassword": "***REMOVED SENSITIVE VALUE***",
        "instanceid": "***REMOVED SENSITIVE VALUE***",
        "installed": true,
        "mail_sendmailmode": "smtp",
        "loglevel": 0,
        "overwritehost": "cloud.elizabeth.sh",
        "overwriteprotocol": "https",
        "overwritewebroot": "\/",
        "trusted_proxies": "***REMOVED SENSITIVE VALUE***",
        "objectstore": {
            "class": "\\OC\\Files\\ObjectStore\\S3",
            "arguments": {
                "bucket": "cloud.elizabeth.sh",
                "key": "***REMOVED SENSITIVE VALUE***",
                "secret": "***REMOVED SENSITIVE VALUE***",
                "hostname": "gateway.storjshare.io",
                "use_ssl": true,
                "use_path_style": true,
                "verify_bucket_exists": false,
                "concurrency": 1,
                "uploadPartSize": 67108864,
                "proxy": "http:\/\/localhost:8080"
            }
        },
        "mail_smtpsecure": "",
        "memories.db.triggers.fcu": true,
        "memories.exiftool": "\/var\/www\/html\/custom_apps\/memories\/bin-ext\/exiftool-amd64-glibc",
        "memories.vod.path": "\/var\/www\/html\/custom_apps\/memories\/bin-ext\/go-vod-amd64",
        "enabledPreviewProviders": [
            "OC\\Preview\\Image",
            "OC\\Preview\\HEIC",
            "OC\\Preview\\TIFF",
            "OC\\Preview\\Movie"
        ],
        "memories.gis_type": 2
    }
}

List of activated Apps

No response

Nextcloud Signing status

No errors have been found.

Nextcloud Logs

{"reqId":"VbH5zglV2xXba3eq1wNc","level":3,"time":"2024-05-24T17:52:40+00:00","remoteAddr":"2a0c:5a81:9400:9300:7463:b199:9749:b67b","user":"elizabeth","app":"webdav","method":"MKCOL","url":"/remote.php/dav/uploads/elizabeth/web-file-upload-9650657a6702d474","message":"Error executing \"CreateMultipartUpload\" on \"https://gateway.storjshare.io/cloud.elizabeth.sh/urn%3Aoid%3A2063?uploads\"; AWS HTTP error: Client error: `POST https://gateway.storjshare.io/cloud.elizabeth.sh/urn%3Aoid%3A2063?uploads` resulted in a `429 Too Many Requests` response:\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Error><Code>SlowDown</Code><Message>Please reduce your request rate.</Message><K (truncated...)\n SlowDown (client): Please reduce your request rate. - <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Error><Code>SlowDown</Code><Message>Please reduce your request rate.</Message><Key>urn:oid:2063</Key><BucketName>cloud.elizabeth.sh</BucketName><Resource>/cloud.elizabeth.sh/urn:oid:2063</Resource><RequestId>17D27E31CCA11FA1</RequestId><HostId></HostId></Error>","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36","version":"29.0.0.19","exception":{"Exception":"Aws\\S3\\Exception\\S3Exception","Message":"Error executing \"CreateMultipartUpload\" on \"https://gateway.storjshare.io/cloud.elizabeth.sh/urn%3Aoid%3A2063?uploads\"; AWS HTTP error: Client error: `POST https://gateway.storjshare.io/cloud.elizabeth.sh/urn%3Aoid%3A2063?uploads` resulted in a `429 Too Many Requests` response:\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Error><Code>SlowDown</Code><Message>Please reduce your request rate.</Message><K (truncated...)\n SlowDown (client): Please reduce your request rate. - <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Error><Code>SlowDown</Code><Message>Please reduce your request rate.</Message><Key>urn:oid:2063</Key><BucketName>cloud.elizabeth.sh</BucketName><Resource>/cloud.elizabeth.sh/urn:oid:2063</Resource><RequestId>17D27E31CCA11FA1</RequestId><HostId></HostId></Error>","Code":0,"Trace":[{"file":"/var/www/html/3rdparty/aws/aws-sdk-php/src/WrappedHttpHandler.php","line":96,"function":"parseError","class":"Aws\\WrappedHttpHandler","type":"->","args":["*** sensitive parameters replaced ***",["GuzzleHttp\\Psr7\\Request"],["Aws\\Command"],[]]},{"file":"/var/www/html/3rdparty/guzzlehttp/promises/src/Promise.php","line":204,"function":"Aws\\{closure}","class":"Aws\\WrappedHttpHandler","type":"->","args":["*** sensitive parameters replaced ***"]},{"file":"/var/www/html/3rdparty/guzzlehttp/promises/src/Promise.php","line":169,"function":"callHandler","class":"GuzzleHttp\\Promise\\Promise","type":"::","args":[2,"*** sensitive parameters replaced ***",null]},{"file":"/var/www/html/3rdparty/guzzlehttp/promises/src/RejectedPromise.php","line":42,"function":"GuzzleHttp\\Promise\\{closure}","class":"GuzzleHttp\\Promise\\Promise","type":"::","args":["*** sensitive parameters replaced ***"]},{"file":"/var/www/html/3rdparty/guzzlehttp/promises/src/TaskQueue.php","line":48,"function":"GuzzleHttp\\Promise\\{closure}","class":"GuzzleHttp\\Promise\\RejectedPromise","type":"::","args":["*** sensitive parameters replaced ***"]},{"file":"/var/www/html/3rdparty/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php","line":163,"function":"run","class":"GuzzleHttp\\Promise\\TaskQueue","type":"->","args":[]},{"file":"/var/www/html/3rdparty/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php","line":189,"function":"tick","class":"GuzzleHttp\\Handler\\CurlMultiHandler","type":"->","args":[]},{"file":"/var/www/html/3rdparty/guzzlehttp/promises/src/Promise.php","line":248,"function":"execute","class":"GuzzleHttp\\Handler\\CurlMultiHandler","type":"->","args":[true]},{"file":"/var/www/html/3rdparty/guzzlehttp/promises/src/Promise.php","line":224,"function":"invokeWaitFn","class":"GuzzleHttp\\Promise\\Promise","type":"->","args":[]},{"file":"/var/www/html/3rdparty/guzzlehttp/promises/src/Promise.php","line":269,"function":"waitIfPending","class":"GuzzleHttp\\Promise\\Promise","type":"->","args":[]},{"file":"/var/www/html/3rdparty/guzzlehttp/promises/src/Promise.php","line":226,"function":"invokeWaitList","class":"GuzzleHttp\\Promise\\Promise","type":"->","args":[]},{"file":"/var/www/html/3rdparty/guzzlehttp/promises/src/Promise.php","line":269,"function":"waitIfPending","class":"GuzzleHttp\\Promise\\Promise","type":"->","args":[]},{"file":"/var/www/html/3rdparty/guzzlehttp/promises/src/Promise.php","line":226,"function":"invokeWaitList","class":"GuzzleHttp\\Promise\\Promise","type":"->","args":[]},{"file":"/var/www/html/3rdparty/guzzlehttp/promises/src/Promise.php","line":62,"function":"waitIfPending","class":"GuzzleHttp\\Promise\\Promise","type":"->","args":[]},{"file":"/var/www/html/3rdparty/aws/aws-sdk-php/src/AwsClientTrait.php","line":58,"function":"wait","class":"GuzzleHttp\\Promise\\Promise","type":"->","args":[]},{"file":"/var/www/html/3rdparty/aws/aws-sdk-php/src/AwsClientTrait.php","line":86,"function":"execute","class":"Aws\\AwsClient","type":"->","args":[["Aws\\Command"]]},{"file":"/var/www/html/lib/private/Files/ObjectStore/S3.php","line":49,"function":"__call","class":"Aws\\AwsClient","type":"->","args":["createMultipartUpload",[["cloud.elizabeth.sh","urn:oid:2063"]]]},{"file":"/var/www/html/lib/private/Files/ObjectStore/ObjectStoreStorage.php","line":684,"function":"initiateMultipartUpload","class":"OC\\Files\\ObjectStore\\S3","type":"->","args":["urn:oid:2063"]},{"function":"startChunkedWrite","class":"OC\\Files\\ObjectStore\\ObjectStoreStorage","type":"->","args":["uploads/web-file-upload-9650657a6702d474/.target"]},{"file":"/var/www/html/lib/private/Files/Storage/Wrapper/Wrapper.php","line":533,"function":"call_user_func_array","args":[[["OC\\Files\\ObjectStore\\HomeObjectStoreStorage"],"startChunkedWrite"],["uploads/web-file-upload-9650657a6702d474/.target"]]},{"function":"__call","class":"OC\\Files\\Storage\\Wrapper\\Wrapper","type":"->","args":["startChunkedWrite",["uploads/web-file-upload-9650657a6702d474/.target"]]},{"file":"/var/www/html/lib/private/Files/Storage/Wrapper/Wrapper.php","line":533,"function":"call_user_func_array","args":[[["OC\\Files\\Storage\\Wrapper\\Quota",null,null,null,null,null],"startChunkedWrite"],["uploads/web-file-upload-9650657a6702d474/.target"]]},{"file":"/var/www/html/apps/dav/lib/Upload/ChunkingV2Plugin.php","line":136,"function":"__call","class":"OC\\Files\\Storage\\Wrapper\\Wrapper","type":"->","args":["startChunkedWrite",["uploads/web-file-upload-9650657a6702d474/.target"]]},{"file":"/var/www/html/3rdparty/sabre/event/lib/WildcardEmitterTrait.php","line":89,"function":"afterMkcol","class":"OCA\\DAV\\Upload\\ChunkingV2Plugin","type":"->","args":[["Sabre\\HTTP\\Request"],["Sabre\\HTTP\\Response"]]},{"file":"/var/www/html/3rdparty/sabre/dav/lib/DAV/Server.php","line":482,"function":"emit","class":"Sabre\\DAV\\Server","type":"->","args":["afterMethod:MKCOL",[["Sabre\\HTTP\\Request"],["Sabre\\HTTP\\Response"]]]},{"file":"/var/www/html/3rdparty/sabre/dav/lib/DAV/Server.php","line":253,"function":"invokeMethod","class":"Sabre\\DAV\\Server","type":"->","args":[["Sabre\\HTTP\\Request"],["Sabre\\HTTP\\Response"]]},{"file":"/var/www/html/3rdparty/sabre/dav/lib/DAV/Server.php","line":321,"function":"start","class":"Sabre\\DAV\\Server","type":"->","args":[]},{"file":"/var/www/html/apps/dav/lib/Server.php","line":374,"function":"exec","class":"Sabre\\DAV\\Server","type":"->","args":[]},{"file":"/var/www/html/apps/dav/appinfo/v2/remote.php","line":35,"function":"exec","class":"OCA\\DAV\\Server","type":"->","args":[]},{"file":"/var/www/html/remote.php","line":172,"args":["/var/www/html/apps/dav/appinfo/v2/remote.php"],"function":"require_once"}],"File":"/var/www/html/3rdparty/aws/aws-sdk-php/src/WrappedHttpHandler.php","Line":195,"Previous":{"Exception":"GuzzleHttp\\Exception\\ClientException","Message":"Client error: `POST https://gateway.storjshare.io/cloud.elizabeth.sh/urn%3Aoid%3A2063?uploads` resulted in a `429 Too Many Requests` response:\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Error><Code>SlowDown</Code><Message>Please reduce your request rate.</Message><K (truncated...)\n","Code":429,"Trace":[{"file":"/var/www/html/3rdparty/guzzlehttp/guzzle/src/Middleware.php","line":72,"function":"create","class":"GuzzleHttp\\Exception\\RequestException","type":"::","args":["*** sensitive parameters replaced ***"]},{"file":"/var/www/html/3rdparty/guzzlehttp/promises/src/Promise.php","line":204,"function":"GuzzleHttp\\{closure}","class":"GuzzleHttp\\Middleware","type":"::","args":["*** sensitive parameters replaced ***"]},{"file":"/var/www/html/3rdparty/guzzlehttp/promises/src/Promise.php","line":153,"function":"callHandler","class":"GuzzleHttp\\Promise\\Promise","type":"::","args":[1,"*** sensitive parameters replaced ***","*** sensitive parameters replaced ***"]},{"file":"/var/www/html/3rdparty/guzzlehttp/promises/src/TaskQueue.php","line":48,"function":"GuzzleHttp\\Promise\\{closure}","class":"GuzzleHttp\\Promise\\Promise","type":"::","args":["*** sensitive parameters replaced ***"]},{"file":"/var/www/html/3rdparty/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php","line":163,"function":"run","class":"GuzzleHttp\\Promise\\TaskQueue","type":"->","args":[]},{"file":"/var/www/html/3rdparty/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php","line":189,"function":"tick","class":"GuzzleHttp\\Handler\\CurlMultiHandler","type":"->","args":[]},{"file":"/var/www/html/3rdparty/guzzlehttp/promises/src/Promise.php","line":248,"function":"execute","class":"GuzzleHttp\\Handler\\CurlMultiHandler","type":"->","args":[true]},{"file":"/var/www/html/3rdparty/guzzlehttp/promises/src/Promise.php","line":224,"function":"invokeWaitFn","class":"GuzzleHttp\\Promise\\Promise","type":"->","args":[]},{"file":"/var/www/html/3rdparty/guzzlehttp/promises/src/Promise.php","line":269,"function":"waitIfPending","class":"GuzzleHttp\\Promise\\Promise","type":"->","args":[]},{"file":"/var/www/html/3rdparty/guzzlehttp/promises/src/Promise.php","line":226,"function":"invokeWaitList","class":"GuzzleHttp\\Promise\\Promise","type":"->","args":[]},{"file":"/var/www/html/3rdparty/guzzlehttp/promises/src/Promise.php","line":269,"function":"waitIfPending","class":"GuzzleHttp\\Promise\\Promise","type":"->","args":[]},{"file":"/var/www/html/3rdparty/guzzlehttp/promises/src/Promise.php","line":226,"function":"invokeWaitList","class":"GuzzleHttp\\Promise\\Promise","type":"->","args":[]},{"file":"/var/www/html/3rdparty/guzzlehttp/promises/src/Promise.php","line":62,"function":"waitIfPending","class":"GuzzleHttp\\Promise\\Promise","type":"->","args":[]},{"file":"/var/www/html/3rdparty/aws/aws-sdk-php/src/AwsClientTrait.php","line":58,"function":"wait","class":"GuzzleHttp\\Promise\\Promise","type":"->","args":[]},{"file":"/var/www/html/3rdparty/aws/aws-sdk-php/src/AwsClientTrait.php","line":86,"function":"execute","class":"Aws\\AwsClient","type":"->","args":[["Aws\\Command"]]},{"file":"/var/www/html/lib/private/Files/ObjectStore/S3.php","line":49,"function":"__call","class":"Aws\\AwsClient","type":"->","args":["createMultipartUpload",[["cloud.elizabeth.sh","urn:oid:2063"]]]},{"file":"/var/www/html/lib/private/Files/ObjectStore/ObjectStoreStorage.php","line":684,"function":"initiateMultipartUpload","class":"OC\\Files\\ObjectStore\\S3","type":"->","args":["urn:oid:2063"]},{"function":"startChunkedWrite","class":"OC\\Files\\ObjectStore\\ObjectStoreStorage","type":"->","args":["uploads/web-file-upload-9650657a6702d474/.target"]},{"file":"/var/www/html/lib/private/Files/Storage/Wrapper/Wrapper.php","line":533,"function":"call_user_func_array","args":[[["OC\\Files\\ObjectStore\\HomeObjectStoreStorage"],"startChunkedWrite"],["uploads/web-file-upload-9650657a6702d474/.target"]]},{"function":"__call","class":"OC\\Files\\Storage\\Wrapper\\Wrapper","type":"->","args":["startChunkedWrite",["uploads/web-file-upload-9650657a6702d474/.target"]]},{"file":"/var/www/html/lib/private/Files/Storage/Wrapper/Wrapper.php","line":533,"function":"call_user_func_array","args":[[["OC\\Files\\Storage\\Wrapper\\Quota","*** sensitive parameters replaced ***","*** sensitive parameters replaced ***","*** sensitive parameters replaced ***","*** sensitive parameters replaced ***","*** sensitive parameters replaced ***"],"startChunkedWrite"],["uploads/web-file-upload-9650657a6702d474/.target"]]},{"file":"/var/www/html/apps/dav/lib/Upload/ChunkingV2Plugin.php","line":136,"function":"__call","class":"OC\\Files\\Storage\\Wrapper\\Wrapper","type":"->","args":["startChunkedWrite",["uploads/web-file-upload-9650657a6702d474/.target"]]},{"file":"/var/www/html/3rdparty/sabre/event/lib/WildcardEmitterTrait.php","line":89,"function":"afterMkcol","class":"OCA\\DAV\\Upload\\ChunkingV2Plugin","type":"->","args":[["Sabre\\HTTP\\Request"],["Sabre\\HTTP\\Response"]]},{"file":"/var/www/html/3rdparty/sabre/dav/lib/DAV/Server.php","line":482,"function":"emit","class":"Sabre\\DAV\\Server","type":"->","args":["afterMethod:MKCOL",[["Sabre\\HTTP\\Request"],["Sabre\\HTTP\\Response"]]]},{"file":"/var/www/html/3rdparty/sabre/dav/lib/DAV/Server.php","line":253,"function":"invokeMethod","class":"Sabre\\DAV\\Server","type":"->","args":[["Sabre\\HTTP\\Request"],["Sabre\\HTTP\\Response"]]},{"file":"/var/www/html/3rdparty/sabre/dav/lib/DAV/Server.php","line":321,"function":"start","class":"Sabre\\DAV\\Server","type":"->","args":[]},{"file":"/var/www/html/apps/dav/lib/Server.php","line":374,"function":"exec","class":"Sabre\\DAV\\Server","type":"->","args":[]},{"file":"/var/www/html/apps/dav/appinfo/v2/remote.php","line":35,"function":"exec","class":"OCA\\DAV\\Server","type":"->","args":[]},{"file":"/var/www/html/remote.php","line":172,"args":["/var/www/html/apps/dav/appinfo/v2/remote.php"],"function":"require_once"}],"File":"/var/www/html/3rdparty/guzzlehttp/guzzle/src/Exception/RequestException.php","Line":113},"message":"Error executing \"CreateMultipartUpload\" on \"https://gateway.storjshare.io/cloud.elizabeth.sh/urn%3Aoid%3A2063?uploads\"; AWS HTTP error: Client error: `POST https://gateway.storjshare.io/cloud.elizabeth.sh/urn%3Aoid%3A2063?uploads` resulted in a `429 Too Many Requests` response:\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Error><Code>SlowDown</Code><Message>Please reduce your request rate.</Message><K (truncated...)\n SlowDown (client): Please reduce your request rate. - <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Error><Code>SlowDown</Code><Message>Please reduce your request rate.</Message><Key>urn:oid:2063</Key><BucketName>cloud.elizabeth.sh</BucketName><Resource>/cloud.elizabeth.sh/urn:oid:2063</Resource><RequestId>17D27E31CCA11FA1</RequestId><HostId></HostId></Error>","exception":[],"CustomMessage":"Error executing \"CreateMultipartUpload\" on \"https://gateway.storjshare.io/cloud.elizabeth.sh/urn%3Aoid%3A2063?uploads\"; AWS HTTP error: Client error: `POST https://gateway.storjshare.io/cloud.elizabeth.sh/urn%3Aoid%3A2063?uploads` resulted in a `429 Too Many Requests` response:\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Error><Code>SlowDown</Code><Message>Please reduce your request rate.</Message><K (truncated...)\n SlowDown (client): Please reduce your request rate. - <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Error><Code>SlowDown</Code><Message>Please reduce your request rate.</Message><Key>urn:oid:2063</Key><BucketName>cloud.elizabeth.sh</BucketName><Resource>/cloud.elizabeth.sh/urn:oid:2063</Resource><RequestId>17D27E31CCA11FA1</RequestId><HostId></HostId></Error>"},"id":"6650f3a016815"}

Additional info

No response

kesselb commented 6 months ago

I tried to reproduce, but couldn't figure out where this put request was coming from.

How do you upload the file?

elizabeth-dev commented 6 months ago

I tried to reproduce, but couldn't figure out where this put request was coming from.

How do you upload the file?

just using the web interface

I tried to find out myself where the call was being made on the code, PHP isn't my thing, but I think it might have to do with a function called stat or fopen or something causing that call? but at the end I couldn't figure it out. I might give it another try. any tips on how I could debug that? or anything else I could do?

kesselb commented 6 months ago

just using the web interface

Did you adjust the chunk size / turned chunking off? The default is 10mb and parts should always use putObject until you tweak the value to be bigger than 100mb (the default putSizeLimit).

any tips on how I could debug that?

Not really. In my tests, I saw some weird behavior with mitmproxy. I think that was caused because we use request_fulluri when a proxy is defined.

elizabeth-dev commented 6 months ago

the only setting I changed from the default is uploadPartSize with a value of 67108864 as per Storj recommendation

kesselb commented 6 months ago

The empty put request is coming from: https://github.com/nextcloud/server/blob/ddb840c36babd02abedce7da41b0c04849009edb/apps/dav/lib/Upload/ChunkingV2Plugin.php#L99

As I understand it, it's a placeholder for the file to be uploaded.

elizabeth-dev commented 6 months ago

I see... CreateMultipartUpload already creates the file, though

Senwere commented 5 months ago

Make use of this instead

<?php
$CONFIG = array (
    'system' => array (
        'htaccess.RewriteBase' => '/',
        'memcache.local' => '\OC\Memcache\APCu',  // Local caching with APCu
        'memcache.distributed' => '\OC\Memcache\Redis', // Distributed caching with Redis
        'memcache.locking' => '\OC\Memcache\Redis', // Locking with Redis

        'apps_paths' => array (
            array (
                'path' => '/var/www/html/apps',
                'url' => '/apps',
                'writable' => false
            ),
            array (
                'path' => '/var/www/html/custom_apps',
                'url' => '/custom_apps',
                'writable' => true
            )
        ),

        // Redis Configuration (Moved to a separate file for security)
        'redis' => array(
            'host' => $_ENV['REDIS_HOST'], // Load host from environment variable
            'port' => $_ENV['REDIS_PORT'] ?: 6379, // Load port, default to 6379
            'password' => $_ENV['REDIS_PASSWORD']  // Load password
        ),

        'mail_smtpmode' => 'smtp',
        'mail_from_address' => $_ENV['MAIL_FROM_ADDRESS'], // Load from environment variable
        'mail_domain' => $_ENV['MAIL_DOMAIN'],
        'mail_smtphost' => $_ENV['MAIL_SMTP_HOST'],
        'mail_smtpport' => $_ENV['MAIL_SMTP_PORT'] ?: 587, // Default to 587 if not set
        'mail_smtpauth' => true,
        'mail_smtpauthtype' => 'LOGIN',
        'mail_smtpname' => $_ENV['MAIL_SMTP_NAME'],
        'mail_smtppassword' => $_ENV['MAIL_SMTP_PASSWORD'],

        'mail_smtpsecure' => $_ENV['MAIL_SMTP_SECURE'] ?: 'tls', // Default to TLS if not set

        'upgrade.disable-web' => true,
        'passwordsalt' => $_ENV['PASSWORD_SALT'],  // Load from environment variable
        'secret' => $_ENV['SECRET'],

        'trusted_domains' => array (
            'cloud.elizabeth.sh'
        ),

        // Sensitive database configuration moved to separate file
        'dbtype' => 'pgsql',
        'dbname' => $_ENV['DB_NAME'], // Load database name from environment variable
        'dbhost' => $_ENV['DB_HOST'], // Load database host from environment variable
        'dbuser' => $_ENV['DB_USER'], // Load database user from environment variable
        'dbpassword' => $_ENV['DB_PASSWORD'], // Load database password from environment variable

        'version' => '29.0.0.19',
        'overwrite.cli.url' => 'https://cloud.elizabeth.sh',
        'instanceid' => $_ENV['INSTANCE_ID'],

        'installed' => true,

        'loglevel' => $_ENV['LOG_LEVEL'] ?: 0, // Load log level, default to 0
        'overwritehost' => 'cloud.elizabeth.sh',
        'overwriteprotocol' => 'https',
        'overwritewebroot' => '/',
        'trusted_proxies' => $_ENV['TRUSTED_PROXIES'], // Load from environment variable

        // ... (The rest of the configuration remains the same)

    )
);
Zetanova commented 2 months ago

Issue persist in nextcloud 29.0.9

I worked around the issue by adding a 1sec sleep after file touch operation in /lib/private/Files/View.php

The dual operation touch+upload for S3 makes little sense. S3 does create or overwrite files on upload and it needs extra features to handle lock and concurrency.

EchedelleLR commented 2 weeks ago

I am also affected by this issue. I am using StorJ storage as backend. The situation seems to be mitigated just a bit by increasing the chunking size, but the background issue is this one.

EDIT: I also tried to configure concurrency limit to 1 (to make every upload sequential), but the issue is still triggered. Is like if that value was overriden.

Zetanova commented 2 weeks ago

@EchedelleLR add a sleep under the line 519 in /lib/private/Files/View.php

$result = $this->basicOperation('touch', $path, $hooks, $mtime);
sleep(1);
EchedelleLR commented 18 hours ago

@Zetanova I tried your solution but the issue is still happening from time to time. I don't think the approach of creating the files first to then update the content is a good idea and should be progressively.

Zetanova commented 17 hours ago

@EchedelleLR Strange this should resolve the issue for sure, maybe increate the sleep to 2sec. storj has a more unique rate limit on touching/uploading the same object/file: "1 write per second to the same object name" see: https://storj.dev/learn/concepts/limits

The nextcloud approach to first create a file by touch-operation and after writing to it, makes for regular file-system sense but not for S3. S3 files are objects and and cant be overwritten fully or partially. A object-path can only be replaced by a new/other object.