Skip to content
This repository has been archived by the owner on Apr 3, 2024. It is now read-only.

Support GCF through a 'initialization completed promise' #341

Closed
wants to merge 15 commits into from

Conversation

gaofanmichael
Copy link
Contributor

@gaofanmichael gaofanmichael commented Oct 16, 2017

This PR is to support Google Cloud Function (GCF) through initialization stackdriver debugger through a initialization of a promise and resolve the promise after breakpointfetch being scheduled.

@googlebot googlebot added the cla: yes This human has signed the Contributor License Agreement. label Oct 16, 2017
@@ -314,6 +319,14 @@ export class Debuglet extends EventEmitter {

});
}
initializationPromise() {

This comment was marked as spam.

initializationPromise() {
this.promiseInitialized_ = true;
return new Promise<void>((resolve) => {
this.promiseResolve_ = () => {

This comment was marked as spam.

@@ -115,6 +115,8 @@ export class Debuglet extends EventEmitter {
private project_: string|null;
private controller_: Controller;
private completedBreakpointMap_: {[key: string]: boolean};
private promiseInitialized_: boolean;
private promiseResolve_: (value?: void|PromiseLike<void>|undefined) => void;

This comment was marked as spam.

This comment was marked as spam.

@gaofanmichael gaofanmichael force-pushed the gcf branch 3 times, most recently from 31e0cb2 to aac1617 Compare October 25, 2017 21:49
kjin
kjin previously requested changes Oct 25, 2017
@@ -117,6 +118,9 @@ export class Debuglet extends EventEmitter {
private controller_: Controller;
private completedBreakpointMap_: {[key: string]: boolean};

private promise_: Promise<void>|null;
private promiseResolve_: (value?: void|PromiseLike<void>|undefined) => void;

This comment was marked as spam.

This comment was marked as spam.

@@ -322,6 +329,18 @@ export class Debuglet extends EventEmitter {

});
}
checkReady() {
const diff = process.hrtime(this.promiseResolvedTimestamp_);

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

if (diff[0] < PROMISE_RESOLVE_CUT_OFF_IN_SECONDS) {
return Promise.resolve();
}
this.promise_ = new Promise<void>((resolve) => {

This comment was marked as spam.

This comment was marked as spam.

debuglet.once('registered', function reg(id: string) {
assert.equal(id, DEBUGGEE_ID);
debugPromise.then(() => {
setTimeout(function() {

This comment was marked as spam.

This comment was marked as spam.

@@ -175,6 +179,9 @@ export class Debuglet extends EventEmitter {

/** @private {Object.<string, Boolean>} */
this.completedBreakpointMap_ = {};

/** @private {Array<number>} */

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

@@ -175,6 +179,9 @@ export class Debuglet extends EventEmitter {

/** @private {Object.<string, Boolean>} */
this.completedBreakpointMap_ = {};

/** @private {Array<number>} */
this.promiseResolvedTimestamp_ = [0, 0];

This comment was marked as spam.

This comment was marked as spam.

// debug agent return a promise when checkReady is called. The value is selected
// between Stackdriver debugger hanging get duration (40s) and TCP default
// time-out (https://tools.ietf.org/html/rfc5482).
const PROMISE_RESOLVE_CUT_OFF_IN_SECONDS = 60;

This comment was marked as spam.

This comment was marked as spam.

@@ -174,6 +183,9 @@ export class Debuglet extends EventEmitter {

/** @private {Object.<string, Boolean>} */
this.completedBreakpointMap_ = {};

/** @private {Array<number>} */
this.promiseResolvedTimestamp_ = Date.now();

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

if (this.promise_) return this.promise_;
const diff = Date.now() - this.promiseResolvedTimestamp_;
if (diff < PROMISE_RESOLVE_CUT_OFF_IN_SECONDS * 1000) {
return Promise.resolve();

This comment was marked as spam.

@@ -597,6 +622,11 @@ export class Debuglet extends EventEmitter {
}
that.scheduleBreakpointFetch_(
that.config_.breakpointUpdateIntervalSec);
if (that.promise_) {

This comment was marked as spam.

This comment was marked as spam.

@@ -597,6 +622,11 @@ export class Debuglet extends EventEmitter {
}
that.scheduleBreakpointFetch_(
that.config_.breakpointUpdateIntervalSec);
if (that.promise_) {
that.promiseResolve_();
that.promise_ = null;

This comment was marked as spam.

This comment was marked as spam.

@@ -175,6 +179,9 @@ export class Debuglet extends EventEmitter {

/** @private {Object.<string, Boolean>} */
this.completedBreakpointMap_ = {};

/** @private {Array<number>} */

This comment was marked as spam.

@@ -174,6 +183,9 @@ export class Debuglet extends EventEmitter {

/** @private {Object.<string, Boolean>} */
this.completedBreakpointMap_ = {};

/** @private {Array<number>} */
this.promiseResolvedTimestamp_ = Date.now();

This comment was marked as spam.

@@ -62,6 +62,12 @@ const BREAKPOINT_ACTION_MESSAGE =
'The only currently supported breakpoint actions' +
' are CAPTURE and LOG.';

// PROMISE_RESOLVE_CUT_OFF_IN_SECONDS is a heuristic that we set to force the

This comment was marked as spam.

This comment was marked as spam.

@@ -62,6 +62,12 @@ const BREAKPOINT_ACTION_MESSAGE =
'The only currently supported breakpoint actions' +
' are CAPTURE and LOG.';

// PROMISE_RESOLVE_CUT_OFF_IN_SECONDS is a heuristic that we set to force the
// debug agent return a promise when checkReady is called. The value is selected

This comment was marked as spam.

This comment was marked as spam.

@@ -322,6 +333,17 @@ export class Debuglet extends EventEmitter {

});
}
checkReady() {
const diff = Date.now() - this.promiseResolvedTimestamp;
if (diff > PROMISE_RESOLVE_CUT_OFF_IN_MILLISECONDS) {

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

@@ -114,6 +120,9 @@ export class Debuglet extends EventEmitter {
private controller_: Controller;
private completedBreakpointMap_: {[key: string]: boolean};

private promise: Promise<void>;

This comment was marked as spam.

This comment was marked as spam.

private promiseResolve: (() => void)|null;
private promiseResolvedTimestamp: number;
constructor() {
this.promiseResolvedTimestamp = -Infinity;

This comment was marked as spam.

This comment was marked as spam.

constructor() {
this.promiseResolvedTimestamp = -Infinity;
}
isReady() {

This comment was marked as spam.

This comment was marked as spam.

return this.promise;
}

makeReady() {

This comment was marked as spam.

This comment was marked as spam.

* old one if it steals. makeReady will resolve the promise at appropriate
* code location (in our case is after listActiveBreakpoints executed).
*/
export class IsReadyManager {

This comment was marked as spam.

// isReadyManager. The value is selected between Stackdriver debugger hanging
// get duration (40s) and TCP default time-out
// (https://tools.ietf.org/html/rfc5482).
const PROMISE_RESOLVE_CUT_OFF_IN_MILLISECONDS = 60 * 1000;

This comment was marked as spam.

// PROMISE_RESOLVE_CUT_OFF_IN_MILLISECONDS is a heuristic that we set to force
// the debug agent to return a promise when isReady is called in
// isReadyManager. The value is selected between Stackdriver debugger hanging
// get duration (40s) and TCP default time-out

This comment was marked as spam.

isReady() {
const diff = Date.now() - this.promiseResolvedTimestamp;
if (diff > PROMISE_RESOLVE_CUT_OFF_IN_MILLISECONDS) {
this.promise = new Promise<void>((resolve) => {

This comment was marked as spam.

This comment was marked as spam.

* listActiveBreakpoints executed).
*/
export class CachedPromise {
private promise: Promise<void>;

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

/**
* CachedPromise class is designed to support debug agent in Google Cloud
* Function (GCF). As GCF is a serverless environment and we wanted to make
* sure debug agent could always capture the snapshots. We designed this class

This comment was marked as spam.

This comment was marked as spam.

* CachedPromise class is designed to support debug agent in Google Cloud
* Function (GCF). As GCF is a serverless environment and we wanted to make
* sure debug agent could always capture the snapshots. We designed this class
* so that GCF could wait the ListActiveBreakpoitns event completed before

This comment was marked as spam.

This comment was marked as spam.

* throttling CPU to 0. CachedPromise has two member functions.
* get() is to create or return an existing promise. resolve() will resolve
* the promise at appropriate code location (in our case is after
* listActiveBreakpoints executed).

This comment was marked as spam.

This comment was marked as spam.

this.promise = new Promise<void>((resolve) => {
this.promiseResolve = () => {
resolve();
};

This comment was marked as spam.

This comment was marked as spam.

@@ -114,6 +160,7 @@ export class Debuglet extends EventEmitter {
private controller_: Controller;
private completedBreakpointMap_: {[key: string]: boolean};

private cachedPromise: CachedPromise;

This comment was marked as spam.

This comment was marked as spam.

@@ -322,6 +372,9 @@ export class Debuglet extends EventEmitter {

});
}
isReady() {

This comment was marked as spam.

This comment was marked as spam.

@@ -74,6 +74,19 @@ function verifyBreakpointRejection(re: RegExp, body: {breakpoint: any}) {
const hasCorrectDescription = status.description.format.match(re);
return status.isError && hasCorrectDescription;
}
describe('CachedPromise', function() {

This comment was marked as spam.

This comment was marked as spam.

@@ -74,6 +74,19 @@ function verifyBreakpointRejection(re: RegExp, body: {breakpoint: any}) {
const hasCorrectDescription = status.description.format.match(re);
return status.isError && hasCorrectDescription;
}
describe('CachedPromise', function() {
it('CachedPromise can proceed execute when resolve is called', function(done) {

This comment was marked as spam.

This comment was marked as spam.

this.timeout(2000);
let cachedPromise = new CachedPromise(60 * 1000);
let promise = cachedPromise.get();
setTimeout(function(){

This comment was marked as spam.

This comment was marked as spam.

Copy link
Contributor

@jinwoo jinwoo left a comment

Choose a reason for hiding this comment

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

LGTM. Not sure about other people's opinions.

});
}
assert(this.promise);
return this.promise as Promise<void>;

This comment was marked as spam.

This comment was marked as spam.

Copy link
Contributor

@kjin kjin left a comment

Choose a reason for hiding this comment

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

Logic looks good... left a few comments on naming and comments.

private promise: Promise<void>|undefined;
private promiseResolve: (() => void)|null;
private promiseResolvedTimestamp = -Infinity;
private refresh: number;

This comment was marked as spam.

This comment was marked as spam.

private promiseResolvedTimestamp = -Infinity;
private refresh: number;
constructor(refresh: number) {
this.refresh = refresh;

This comment was marked as spam.

This comment was marked as spam.

@@ -106,6 +112,44 @@ const formatBreakpoints = function(
.join('\n');
};

/**
* CachedPromise class is designed to support debug agent in Google Cloud

This comment was marked as spam.

This comment was marked as spam.

src/debuggee.ts Outdated
private description: string;
private agentVersion?: string;
private sourceContexts?: Array<{[key: string]: any}>;
uniquifier: string;

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

describe('CachedPromise', function() {
it('CachedPromise can proceed execute when resolve is called', (done) => {
this.timeout(2000);
let cachedPromise = new CachedPromise(60 * 1000);

This comment was marked as spam.

This comment was marked as spam.

@kjin kjin dismissed their stale review November 2, 2017 17:15

request changes were made

@@ -87,6 +87,28 @@ describe('CachedPromise', function() {
done();
});
});

it('CachedPromise should return the same promise if not staled', (done) => {

This comment was marked as spam.

This comment was marked as spam.

* listActiveBreakpoints is executed).
* CachedPromise stores a promise for a limited time. Its member function get()
* will initially create a promise, or create a promise when previous promise
* stales, then return that promise. resolve() will resolve the stored promise.
*/
export class CachedPromise {

This comment was marked as spam.

This comment was marked as spam.

@@ -368,6 +365,15 @@ export class Debuglet extends EventEmitter {

});
}

/**
* isReady is designed to support debug agent on Google Cloud Function (GCF).

This comment was marked as spam.

This comment was marked as spam.

Copy link
Contributor

@kjin kjin left a comment

Choose a reason for hiding this comment

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

Looks good on my end. w/ nits

@@ -75,6 +75,53 @@ function verifyBreakpointRejection(re: RegExp, body: {breakpoint: any}) {
return status.isError && hasCorrectDescription;
}

describe('CachedPromise', function() {
it('CachedPromise can proceed execute when resolve is called', (done) => {

This comment was marked as spam.

@@ -324,6 +367,18 @@ export class Debuglet extends EventEmitter {
}

/**
* isReady is designed to support debug agent on Google Cloud Function (GCF).

This comment was marked as spam.

@gaofanmichael
Copy link
Contributor Author

Opened another PR #358
Close this one.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
cla: yes This human has signed the Contributor License Agreement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants