-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
feat(node): add promise count #10149
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0d274a0
03d56bf
caf891b
0342934
644dff5
3249506
10915f6
a234d17
c1dd736
ac71c6d
4ec626f
4e45010
dd2ab2e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -50,3 +50,75 @@ export function setHooksAsyncContextStrategy(): void { | |||||||
|
|
||||||||
| setAsyncContextStrategy({ getCurrentHub, runWithAsyncContext }); | ||||||||
| } | ||||||||
| /** | ||||||||
| * Initiates the counting of created promises and settled promises. | ||||||||
| * | ||||||||
| * If `locations` is true, it will attempt to find the locations of each created promise | ||||||||
| * and return an object whose keys are the callsites as strings and whose values are the | ||||||||
| * number of promises created at that callsite. | ||||||||
| * | ||||||||
| * If `continuation` is true, promises will only be counted if there is an async | ||||||||
| * continuation chain (as determined by AsyncLocalStorage) back to the given `startCounter()` call. | ||||||||
| * This is helpful for filtering out unrelated promises, like ones from an unrelated concurrent HTTP request. | ||||||||
| * | ||||||||
| * @param {Object} opts - Options for counting promises. | ||||||||
| * @param {boolean} [opts.locations=false] - Whether to count promise locations. | ||||||||
| * @param {boolean} [opts.continuation=false] - Whether to consider async continuation chains. | ||||||||
| * @returns {function(): number | { [key: string]: number }} - A function to get the count of created and settled promises. | ||||||||
| */ | ||||||||
| /** | ||||||||
| * | ||||||||
| */ | ||||||||
|
Comment on lines
+69
to
+71
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removing of these lines gives eslint error on line 72 function
|
||||||||
| export function startCounter( | ||||||||
| opts: { locations: boolean; continuation: boolean } = { locations: false, continuation: false } | ||||||||
| ): () => { created: number; settled: number; locations: { [key: string]: number } } { | ||||||||
| const resultObject: { created: number; settled: number; locations: { [key: string]: number } } = { | ||||||||
| created: 0, | ||||||||
| settled: 0, | ||||||||
| locations: {}, | ||||||||
| }; | ||||||||
|
|
||||||||
| function getLocation(): string { | ||||||||
| const stack = new Error().stack?.split('\n'); | ||||||||
| let stackIndex = 2; | ||||||||
| let line = stack?.[stackIndex]; | ||||||||
| while ( | ||||||||
| line && | ||||||||
| (line.includes(__filename) || | ||||||||
| line.includes('node:internal/promise_hooks') || | ||||||||
| line?.endsWith('<anonymous>)')) | ||||||||
| ) { | ||||||||
| line = stack?.[++stackIndex]; | ||||||||
| } | ||||||||
| return (line || stack?.pop())?.substring(7) || ''; | ||||||||
| } | ||||||||
|
|
||||||||
| const init: () => void = () => { | ||||||||
| if (opts.locations) { | ||||||||
| const line = getLocation(); | ||||||||
| resultObject.locations[line] = 1 + (resultObject.locations[line] || 0); | ||||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
| if (opts.locations) { | ||||||||
| init(); | ||||||||
| } | ||||||||
|
|
||||||||
| return () => { | ||||||||
| return opts.locations | ||||||||
| ? resultObject | ||||||||
| : { | ||||||||
| created: resultObject.created || 0, | ||||||||
| settled: resultObject.settled || 0, | ||||||||
| locations: resultObject.locations, | ||||||||
| }; | ||||||||
| }; | ||||||||
| } | ||||||||
|
|
||||||||
| // Example usage: | ||||||||
| // 1. Call setHooksAsyncContextStrategy() in your application's entry point. | ||||||||
| // 2. Use startCounter() to get a function that tracks promises. | ||||||||
| // const trackPromises = startCounter({ locations: true, continuation: true }); | ||||||||
| // const promise1 = new Promise(resolve => setTimeout(resolve, 100)); | ||||||||
| // const promise2 = new Promise(resolve => setTimeout(resolve, 200)); | ||||||||
| // trackPromises(); // Call this to get the promise tracking results. | ||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,8 @@ | ||
| /* eslint-disable @typescript-eslint/no-unused-vars */ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we just disable in specific lines, rather than in all file? |
||
| import type { Hub } from '@sentry/core'; | ||
| import { getCurrentHub, runWithAsyncContext, setAsyncContextStrategy } from '@sentry/core'; | ||
|
|
||
| import { setHooksAsyncContextStrategy } from '../../src/async/hooks'; | ||
| import { setHooksAsyncContextStrategy , startCounter } from '../../src/async/hooks'; | ||
| import { conditionalTest } from '../utils'; | ||
|
|
||
| conditionalTest({ min: 12 })('async_hooks', () => { | ||
|
|
@@ -154,3 +155,62 @@ conditionalTest({ min: 12 })('async_hooks', () => { | |
| }); | ||
| }); | ||
| }); | ||
|
|
||
| describe('startCounter', () => { | ||
| test('should track created and settled promises when locations and continuation are true', async () => { | ||
| const trackPromises = startCounter({ locations: true, continuation: true }); | ||
|
|
||
| // Simulate promise creation and settlement | ||
| const promise1 = new Promise((resolve) => setTimeout(resolve, 100)); | ||
| const promise2 = new Promise((resolve) => setTimeout(resolve, 200)); | ||
|
|
||
| // Manually advance timers to ensure promises settle | ||
| jest.advanceTimersByTime(300); | ||
|
|
||
| // Call trackPromises to get the results | ||
| const result = trackPromises(); | ||
|
|
||
| // Assert expected results | ||
| expect(result.created).toBe(2); // Two promises were created | ||
| expect(result.settled).toBe(2); // Two promises were settled | ||
| expect(result.locations).toBeDefined(); // Locations are tracked | ||
| }); | ||
|
|
||
| test('should track created and settled promises when locations is true and continuation is false', async () => { | ||
| const trackPromises = startCounter({ locations: true, continuation: false }); | ||
|
|
||
| // Simulate promise creation and settlement | ||
| const promise1 = new Promise((resolve) => setTimeout(resolve, 100)); | ||
| const promise2 = new Promise((resolve) => setTimeout(resolve, 200)); | ||
|
|
||
| // Wait for promises to settle | ||
| await Promise.all([promise1, promise2]); | ||
|
|
||
| // Call trackPromises to get the results | ||
| const result = trackPromises(); | ||
|
|
||
| // Assert expected results | ||
| expect(result.created).toBe(2); // Two promises were created | ||
| expect(result.settled).toBe(2); // Two promises were settled | ||
| expect(result.locations).toBeDefined(); // Locations are tracked | ||
| }); | ||
|
|
||
| test('should track promises when locations and continuation are false', async () => { | ||
| const trackPromises = startCounter({ locations: false, continuation: false }); | ||
|
|
||
| // Simulate promise creation and settlement | ||
| const promise1 = new Promise((resolve) => setTimeout(resolve, 100)); | ||
| const promise2 = new Promise((resolve) => setTimeout(resolve, 200)); | ||
|
|
||
| // Wait for promises to settle | ||
| await Promise.all([promise1, promise2]); | ||
|
|
||
| // Call trackPromises to get the results | ||
| const result = trackPromises(); | ||
|
|
||
| // Assert expected results | ||
| expect(result.created).toBe(2); // Two promises were created | ||
| expect(result.settled).toBe(2); // Two promises were settled | ||
| expect(result.locations).toEqual({}); // Locations are not tracked | ||
| }); | ||
| }); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.