googleapis / googleapis/google-cloud-node

`AsyncLocalStorage` context not exiting properly when `.run()` called in `.on('message')` handler

Open
#7,628 1 comment 5 reactions 1 assignee Claimed by @feywind View on GitHub
api: pubsub priority: p2 type: feature request
Dominant language
TypeScript
Stars
3.2k
Forks
712
Avg merge
2d 3h
Merged PRs (30d)
99

Description

### Please make sure you have searched for information in the following guides.

- [x] Search the issues already opened: https://github.com/GoogleCloudPlatform/google-cloud-node/issues
- [x] Search StackOverflow: http://stackoverflow.com/questions/tagged/google-cloud-platform+node.js
- [x] Check our Troubleshooting guide: https://github.com/googleapis/google-cloud-node/blob/main/docs/troubleshooting.md
- [x] Check our FAQ: https://github.com/googleapis/google-cloud-node/blob/main/docs/faq.md
- [x] Check our libraries HOW-TO: https://github.com/googleapis/gax-nodejs/blob/main/client-libraries.md
- [x] Check out our authentication guide: https://github.com/googleapis/google-auth-library-nodejs
- [x] Check out handwritten samples for many of our APIs: https://github.com/GoogleCloudPlatform/nodejs-docs-samples

### A screenshot that you have tested with "Try this API".

This problem applies specifically to the Node.js client in combination with AsyncLocalStorage. It's not an issue with the API itself.

### Link to the code that reproduces this issue. A link to a **public** Github Repository or gist with a minimal reproduction.

https://github.com/swarmiakimmo/pubsub-async-local-storage-repro

### A step-by-step description of how to reproduce the issue, based on the linked reproduction.

Running in Node v22.16.0.

1. Edit `projectId`, `topicName`, and `subscriptionName` in index.js. They need to refer to a GCP project you have access to, an existing topic name and subscription name.
1. `npm install`
1. Run the example script: `GOOGLE_APPLICATION_CREDENTIALS=/Users/kimmo/.gcloud/ node index.js`

### A clear and concise description of what the bug is, and what you expected to happen.

**The issue**

Once you run the example script, these see these log lines appear:

```
ERROR: operation context already has an ID: 412ebf00-3e8f-4ab1-8ae5-f001ebb97229
```

which means that the `AsyncLocalStorage` context _already_ had a value when it entered the `.on('message', ...)` callback handler.

The fact that previous context is somehow visible to the handler at that point also hints that there could be a potential memory leak when PubSub client is used together with `AsyncLocalStorage`. The contexts "stack up", so it's possible to have multiple nested `.run()` calls with their own isolated store. If the handler sees the context value at that point in the handler _(instead of it being cleared)_, something seems to retain the reference to the ALS.

**Expected result**

At the beginning of `on('message', ...)` handler, the `als.getStore()` should return `undefined` because we should be processing another message in another callback context.

Whenever `.run()` callback function exits, also the context should be exited as stated in [Node.js API docs](https://nodejs.org/docs/latest-v22.x/api/async_context.html#asynclocalstoragerunstore-callback-args):

> Runs a function synchronously within a context and returns its return value. **The store is not accessible outside of the callback function.** The store is accessible to any asynchronous operations created within the callback.

### 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. **

The expected behavior I described is how `AsyncLocalStorage` should work by standard. There's something that the PubSub Node.js internally does to break the normal expected behavior.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.