feat: Enable isReady for debugger for GCF#358
feat: Enable isReady for debugger for GCF#358gaofanmichael merged 10 commits intogoogleapis:masterfrom
Conversation
src/agent/debuglet.ts
Outdated
| // to force the debug agent to return a new promise for isReady. The value is | ||
| // the average of Stackdriver debugger hanging get duration (40s) and TCP | ||
| // time-out on GCF (540s). | ||
| const PROMISE_RESOLVE_CUT_OFF_IN_MILLISECONDS = 290 * 1000; |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
src/agent/debuglet.ts
Outdated
| // was successful. It is returned by isReady. | ||
| private breakpointFetched: Promise<void>|null; | ||
| // breakpointFetchedResolve is the resolve function for breakpointFetched. | ||
| private breakpointFetchedResolve: (() => void); |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
src/agent/debuglet.ts
Outdated
| // registration was successful. | ||
| private debuggeeRegistered: Promise<void>; | ||
| // debuggeeRegisteredResolve is the resolve function for debuggeeRegistered. | ||
| private debuggeeRegisteredResolve: (() => void); |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
src/agent/debuglet.ts
Outdated
| }); | ||
| this.scheduleBreakpointFetch_( | ||
| 0 /*immediately*/, true /*only fetch once*/); | ||
| return this.breakpointFetched; |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
src/index.ts
Outdated
|
|
||
| // We return the debuglet to facilitate testing. | ||
| return agentConfig.testMode_ ? debuglet : undefined; | ||
| return debuglet; |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
| debugPromise | ||
| .then(() => { | ||
| setTimeout(function() { | ||
| debuglet.stop(); |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
test/test-debuglet.ts
Outdated
| debuglet.start(); | ||
| }); | ||
|
|
||
| it('should resolve promises before exiting user functions', function(done) { |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
| * feature is mainly used in Google Cloud Function (GCF), as it is a | ||
| * serverless environment and we wanted to make sure debug agent always | ||
| * captures the snapshots. | ||
| */ |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
src/agent/debuglet.ts
Outdated
| * @private | ||
| */ | ||
| scheduleBreakpointFetch_(seconds: number): void { | ||
| scheduleBreakpointFetch_(seconds: number, onlyOnce: boolean): void { |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
src/agent/debuglet.ts
Outdated
| that.scheduleBreakpointFetch_( | ||
| that.config.breakpointUpdateIntervalSec); | ||
| if (onlyOnce) { | ||
| that.breakpointFetchedTimestamp = Date.now(); |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
src/agent/debuglet.ts
Outdated
| if (this.breakpointFetched) { | ||
| return this.breakpointFetched; | ||
| } | ||
| this.debuggeeRegistered.then(() => { |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
src/agent/debuglet.ts
Outdated
| private debuggeeRegistered: Promise<void>; | ||
| // debuggeeRegisteredResolve is the resolve function for debuggeeRegistered. | ||
| private debuggeeRegisteredResolve: (() => void); | ||
| private debuggeeRegisteredResolve: () => void; |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
test/test-debuglet.ts
Outdated
| .reply(200, {debuggee: {id: DEBUGGEE_ID}}) | ||
| .get(BPS_PATH + '?successOnTimeout=true') | ||
| .reply(200, {breakpoints: [breakpoint]}) | ||
| .get(BPS_PATH + '?successOnTimeout=true') |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
036f402 to
1cbb8d3
Compare
src/agent/debuglet.ts
Outdated
| private promise: Promise<void>|null; | ||
| private promiseResolve: () => void; | ||
|
|
||
| constructor() { |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
src/agent/debuglet.ts
Outdated
| PROMISE_RESOLVE_CUT_OFF_IN_MILLISECONDS) { | ||
| return Promise.resolve(); | ||
| } else { | ||
| if (this.breakpointFetched.get() !== null) { |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
test/test-debuglet.ts
Outdated
| it('CachedPromise.get() will be null if it has been cleared', (done) => { | ||
| this.timeout(2000); | ||
| let cachedPromise = new CachedPromise(); | ||
| assert(cachedPromise.get() === null); |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
src/agent/debuglet.ts
Outdated
| this.promiseResolve = resolve; | ||
| }); | ||
| } | ||
| get(): Promise<void>|null { |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
| this.breakpointFetched.refresh(); | ||
| (this.debuggeeRegistered.get() as Promise<void>).then(() => { | ||
| this.scheduleBreakpointFetch_( | ||
| 0 /*immediately*/, true /*only fetch once*/); |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
src/agent/debuglet.ts
Outdated
| if (this.breakpointFetched.get() !== null) { | ||
| return this.breakpointFetched.get() as Promise<void>; | ||
| } | ||
| this.breakpointFetched.refresh(); |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
| private breakpointFetched: CachedPromise; | ||
| // debuggeeRegistered is a CachedPromise only to be resolved after debuggee | ||
| // registration was successful. | ||
| private debuggeeRegistered: CachedPromise; |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
|
@ofrobots @DominicKramer I have comments addressed. PTAL :) |
src/agent/debuglet.ts
Outdated
| /** | ||
| * IsReadyManager is a wrapper class to call debuglet.isReady(). | ||
| */ | ||
| export class IsReadyManager { |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
src/agent/debuglet.ts
Outdated
| } | ||
| } | ||
| clear(): void { | ||
| this.promise = null; |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
| /** | ||
| * IsReadyManager is a wrapper class to call debuglet.isReady(). | ||
| */ | ||
| class IsReadyImpl implements IsReady { |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
src/agent/debuglet.ts
Outdated
| return result; | ||
| } | ||
| this.breakpointFetched.refresh(); | ||
| (this.debuggeeRegistered.get() as Promise<void>).then(() => { |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
src/agent/debuglet.ts
Outdated
| that.fetcherActive = false; | ||
| that.debuggeeRegistered.refresh(); | ||
| that.breakpointFetched.resolve(); | ||
| that.breakpointFetched.clear(); |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
src/agent/debuglet.ts
Outdated
| // breakpoint fetcher. | ||
| that.debuggeeRegistered.refresh(); | ||
| that.breakpointFetched.resolve(); | ||
| that.breakpointFetched.clear(); |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
6312c8a to
ee39261
Compare
This PR mainly serve for GCF. The introduced isReady will return a promise, and to be resolved either 1) time since last breakpoint fetch is within a heuristic duration. 2) breakpoint update completed.
debuggee might expire, but debuggeRegistered promise is resolved. This was a bug introduced before. This commit fixed this by refreshing debuggeeRegistered promise when debuggee expired. Since debuggeeRegistered promise is been updated so the next time user calls isReady, a new debuggeeRegistered promise will return and user will wait upon that new promise for debuggee re-registration. Also, we will resolve breakpointFetch together so that no cloud function will hang there foreever.
src/agent/debuglet.ts
Outdated
| * function resolve() and can only be resolved once. | ||
| */ | ||
| export class CachedPromise { | ||
| private promiseResolve: (() => void) | null; |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
|
|
||
| get(): Promise<void> { | ||
| return this.promise; | ||
| } |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
src/agent/debuglet.ts
Outdated
| this.completedBreakpointMap = {}; | ||
|
|
||
| this.breakpointFetchedTimestamp = -Infinity; | ||
|
|
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
src/agent/debuglet.ts
Outdated
| // again after a while. Successful registration will restart the | ||
| // breakpoint fetcher. | ||
| that.debuggeeRegistered = new CachedPromise(); | ||
| if (that.breakpointFetched) { |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
src/agent/debuglet.ts
Outdated
| that.logger.info('\t404 Registration expired.'); | ||
| that.fetcherActive = false; | ||
| that.debuggeeRegistered = new CachedPromise(); | ||
| if (that.breakpointFetched) { |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
src/agent/debuglet.ts
Outdated
|
|
||
| /** | ||
| * IsReady will return a promise to user after user starting the debug agent. | ||
| * This promise will be resolved when |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
src/agent/debuglet.ts
Outdated
| this.completedBreakpointMap = {}; | ||
|
|
||
| this.breakpointFetched = null; | ||
|
|
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
src/agent/debuglet.ts
Outdated
| this.breakpointFetched = null; | ||
|
|
||
| this.breakpointFetchedTimestamp = -Infinity; | ||
|
|
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
DominicKramer
left a comment
There was a problem hiding this comment.
LGTM after the tests pass
| * non-fetchable breakpoints. | ||
| */ | ||
| updatePromise_() { | ||
| this.debuggeeRegistered = new CachedPromise(); |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This PR mainly serve for GCF. The introduced isReady will return a
promise, and to be resolved either