Repository navigation
Description
Please make sure you have searched for information in the following guides.
- Search the issues already opened: https://github.com/GoogleCloudPlatform/google-cloud-node/issues
- Search StackOverflow: http://stackoverflow.com/questions/tagged/google-cloud-platform+node.js
- Check our Troubleshooting guide: https://github.com/googleapis/google-cloud-node/blob/main/docs/troubleshooting.md
- Check our FAQ: https://github.com/googleapis/google-cloud-node/blob/main/docs/faq.md
- Check our libraries HOW-TO: https://github.com/googleapis/gax-nodejs/blob/main/client-libraries.md
- Check out our authentication guide: https://github.com/googleapis/google-auth-library-nodejs
- Check out handwritten samples for many of our APIs: https://github.com/GoogleCloudPlatform/nodejs-docs-samples
- Check the API's issue tracker: https://cloud.google.com/support/docs/issue-trackers
Library Name
@google-cloud/storage (handwritten/storage, versions 7.0.0 through 8.4.0)
A screenshot that you have tested with "Try this API".
N/A — this is a client-side option-merging bug in handwritten/storage/src/nodejs-common/service.ts before the HTTP request is sent over the network, not a server-side Cloud Storage API issue.
Link to the code that reproduces this issue. A link to a public Github Repository or gist with a minimal reproduction.
A step-by-step description of how to reproduce the issue, based on the linked reproduction.
Scenario 1: Default 60,000 ms timeout (requestDefaults.timeout = 60000) is wiped out when new Storage() is called without { timeout }
- Instantiate
const storage = new Storage()without passing{ timeout }(storage.timeout === undefined). - Perform any request that goes through
Service.prototype.request_(e.g.,file.exists(),file.download(),file.createReadStream(),bucket.getFiles()). - In
handwritten/storage/src/nodejs-common/service.ts:222:BecausereqOpts = {...reqOpts, timeout: this.timeout};
this.timeoutisundefined, JS object spread creates an explicit own property{ timeout: undefined }onreqOpts('timeout' in reqOpts === true). - In
handwritten/storage/src/nodejs-common/util.ts:64-71andutil.ts:819,util.makeRequestpassesteenyRequest.defaults(requestDefaults)whererequestDefaults.timeout = 60000. - In
core/packages/teeny-request/src/index.ts:644:const opts = {...defaults, ...reqOpts};
reqOpts.timeout(undefined) overwritesdefaults.timeout(60000), resulting inopts.timeout === undefined(no HTTP timeout at all, even in@google-cloud/storage@7.xwithteeny-request@9/node-fetch@2, and even after teeny-request: the timeout option is ignored since 10.0.0 (node-fetch 3 has no timeout option) #9514 is resolved forteeny-request@11).
Scenario 2: Per-call options.timeout on file.createWriteStream({ resumable: false, timeout }) and bucket.upload(path, { resumable: false, timeout }) is overwritten
- Call
file.createWriteStream({ resumable: false, timeout: 15000 })(orbucket.upload(path, { resumable: false, timeout: 15000 })). - In
File.prototype.startSimpleUpload_(handwritten/storage/src/file.ts:4731-4733):if (typeof options.timeout === 'number') { reqOpts.timeout = options.timeout; }
startSimpleUpload_passesreqOptsintoutil.makeWritableStream, which invokesthis.request(reqOpts, ...)(file.ts:4755) ->Service.prototype.request_(service.ts:222):reqOpts = {...reqOpts, timeout: this.timeout};
Service.prototype.request_unconditionally overwritesreqOpts.timeout(15000) withthis.timeout(undefinedifStoragewas created without{ timeout }).
A clear and concise description of what the bug is, and what you expected to happen.
Actual behavior:
- When
new Storage()is initialized without{ timeout },Service.prototype.request_sets{ timeout: undefined }as an own property onreqOpts, which clobbersrequestDefaults.timeout = 60000inteenyRequest.defaults(requestDefaults). As a result, requests have no HTTP timeout at all (combining with [retry-request] Request hangs indefinitely when totalTimeout is exceeded before retries are exhausted #9706 to cause an infinite hang if a socket is silently blackholed). - Passing
timeouttofile.createWriteStream({ resumable: false, timeout })orbucket.upload(path, { resumable: false, timeout })has no effect becauseService.prototype.request_overwritesreqOpts.timeoutwiththis.timeout.
Expected behavior:
- If neither
reqOpts.timeoutnorthis.timeoutis defined,Service.prototype.request_should not set an explicit{ timeout: undefined }property onreqOpts(preservingrequestDefaults.timeout = 60000inteeny-request). - If a per-request
reqOpts.timeoutis already specified (e.g. bystartSimpleUpload_fromCreateWriteStreamOptions.timeoutormakeWritableStream),Service.prototype.request_should not clobber it whenthis.timeoutisundefined:const timeout = reqOpts.timeout ?? this.timeout; reqOpts = { ...reqOpts, ...(timeout !== undefined && {timeout}), };
Related issues:
- teeny-request: the timeout option is ignored since 10.0.0 (node-fetch 3 has no timeout option) #9514 (
teeny-request>= 10 ignoringtimeoutundernode-fetch@3; note that teeny-request: the timeout option is ignored since 10.0.0 (node-fetch 3 has no timeout option) #9514 mentionsnodejs-common/util.jssettingtimeout: 60000for@google-cloud/storage, which is also blocked by this bug inservice.ts:222even in@google-cloud/storage@7.xwithteeny-request@9) - [retry-request] Request hangs indefinitely when totalTimeout is exceeded before retries are exhausted #9706 (
retry-requesthanging indefinitely whentotalTimeoutis exceeded after a socket without a per-request timeout waits for the OS TCP timeout)
A clear and concise description WHY you expect this behavior, i.e., was it a recent change, there is documentation that points to this behavior, etc. **
-
Regression from removing
extendin commitff88655cf8a(Publishing a string in to PubSub topic introduces quotes to the message #2204 / common/common-grpc: update retry-request #2244):
Originally (and still today incore/common/src/service.ts:200),Service.prototype.request_usedextend:reqOpts = extend(true, {}, reqOpts, {timeout: this.timeout});
Because
extendignores properties whose value isundefined, whenthis.timeoutwasundefined:'timeout' in reqOptsremainedfalse(preservingrequestDefaults.timeout = 60000inteenyRequest.defaults), and- any existing
reqOpts.timeout(such asoptions.timeoutfromstartSimpleUpload_) was preserved.
Whenextendwas replaced with{...reqOpts, timeout: this.timeout}inff88655cf8a, the unit test athandwritten/storage/test/nodejs-common/service.ts:442-448(assert.strictEqual(reqOpts_.timeout, undefined)) continued to pass even though'timeout' in reqOpts_changed fromfalsetotrue.
-
Public JSDoc explicitly documents
timeout=60000(Default:60000):handwritten/storage/src/file.ts:2032-2034(CreateWriteStreamOptions.timeout):* @property {number} [timeout=60000] Set the HTTP request timeout in * milliseconds. This option is not available for resumable uploads. * Default: `60000`
handwritten/storage/src/bucket.ts:4325-4327andbucket.ts:4403-4405(UploadOptions.timeout):* @property {number} [timeout=60000] Set the HTTP request timeout in * milliseconds. This option is not available for resumable uploads. * Default: `60000`
handwritten/storage/src/nodejs-common/util.ts:64-71definesrequestDefaults = { timeout: 60000, ... }.
Activity