Skip to content

Conversation

feywind
Copy link
Collaborator

@feywind feywind commented Jul 16, 2025

Implements a new timeout and behavior option (using Duration) for the Subscription.close() method. These options are on the SubscriberOptions/SubscriptionOptions passed in when opening a subscriber.

This provides more control over the shutdown process:

  • The subscriber stream is closed immediately.
  • If the behaviour is set to NackImmediately, then all buffered messages are nacked, and the timeout specifies how long to wait for them to go through.
  • If the behaviour is set to WaitForProcessing, then the remaining message machinery continues until the timeout is almost up, and then it switches to NackImmediately.
  • If the timeout is zero, everything closes down immediately, as quickly as possible.

(This is a re-open of #2037 to make it from the main repo.)

google-labs-jules bot and others added 16 commits May 6, 2025 22:15
Implements a new `timeout` option (using `Duration`) for the `Subscription.close()` method.

This provides more control over the shutdown process:
- If `timeout` is zero, the subscription closes as quickly as possible without nacking buffered messages.
- If `timeout` is positive, the subscription attempts to nack any buffered messages (in the lease manager) and waits up to the specified duration for pending acknowledgements and nacks to be sent to the server.
- If no timeout is provided, the behavior remains as before (waits indefinitely for pending acks/modacks, no nacking).

The core logic is implemented in `Subscriber.close()`. `PubSub.close()` documentation is updated to clarify its scope and recommend using `Subscription.close()` directly for this feature.

Includes:
- Unit tests for the new timeout behavior in `Subscriber.close()`.
- A TypeScript sample (`samples/closeSubscriptionWithTimeout.ts`) demonstrating usage.
- Updated JSDoc documentation for relevant methods.
@feywind feywind requested review from a team as code owners July 16, 2025 17:48
@product-auto-label product-auto-label bot added size: xl Pull request size is extra large. api: pubsub Issues related to the googleapis/nodejs-pubsub API. labels Jul 16, 2025
@feywind feywind added the owlbot:run Add this label to trigger the Owlbot post processor. label Jul 16, 2025
@gcf-owl-bot gcf-owl-bot bot removed the owlbot:run Add this label to trigger the Owlbot post processor. label Jul 16, 2025
sofisl
sofisl previously approved these changes Jul 16, 2025

await this._waitForFlush();
const options = this._options.closeOptions;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional chaining?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this._options is guaranteed to be non-undefined, so I don't think it's necessary here. The lines that use options do optional chaining, since closeOptions may be undefined.

// The timeout can't realistically be longer than the longest time we're willing
// to lease messages.
let timeout = durationAtMost(
options?.timeout ?? this.maxExtensionTime,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is maxExtensionTime a const? How do we know it will be available? If the former, maybe worth uppercasing?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a config option with a default. So it's set by the constructor:

this.maxExtensionTime = defaultOptions.subscription.maxExtensionTime;

And then if the user passed a value, it overwrites the default.

Copy link

@miguelvelezsa miguelvelezsa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would highly recommend to add types in all the new code :)

*
* @private
*/
dispatched(): void {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be dispatch()?

Copy link
Collaborator Author

@feywind feywind Aug 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's less of a command, more of an event. Basically there is something else doing dispatching, and this method is called to notify telemetry and such. So I don't think I'd call it dispatch, but I'm open to other ideas.

Comment on lines 981 to 982
const err = e as [unknown, boolean];
if (err[1] === false) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for my own knowledge, how err[1] === true means was timeout? probably adding type for 'e' here will help :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this has to do with implementing the time-limited Promise wait... it's super annoying to make graceful. I'll look again.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why it didn't give me a separate box for comments on the one above, but check out Sofia's comment on why I did this like I did.

I agree with you on the second one. I'll add an interface.

@feywind
Copy link
Collaborator Author

feywind commented Jul 21, 2025

I would highly recommend to add types in all the new code :)

Can you elaborate here? The only time I tend to omit them is when they're super obvious (like making a class member like foo = new Map<string, string>()). I might've missed some : void on function returns.

Edit: it didn't tag you :) @miguelvelezsa

@feywind feywind added the do not merge Indicates a pull request not ready for merge, due to either quality or timing. label Jul 22, 2025
@feywind
Copy link
Collaborator Author

feywind commented Jul 22, 2025

I need to look at a few more review comments before merging anything.

Copy link

Warning: This pull request is touching the following templated files:

@feywind feywind added owlbot:run Add this label to trigger the Owlbot post processor. and removed do not merge Indicates a pull request not ready for merge, due to either quality or timing. labels Aug 6, 2025
@gcf-owl-bot gcf-owl-bot bot removed the owlbot:run Add this label to trigger the Owlbot post processor. label Aug 6, 2025
@feywind feywind merged commit 8dee024 into main Aug 7, 2025
20 of 21 checks passed
@feywind feywind deleted the feat-close-timeout branch August 7, 2025 17:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api: pubsub Issues related to the googleapis/nodejs-pubsub API. size: xl Pull request size is extra large.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants