Skip to content
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

feat: initialize the state of a breaker on creation #574

Merged
merged 9 commits into from
Jul 7, 2021

Conversation

lholmquist
Copy link
Member

@lholmquist lholmquist commented May 7, 2021

This PR is a continuation of #568. That last PR only allowed a user to prime the circuit breaker with the stats of the circuit. This part adds the ability to prime the breaker with a starting state. To make it easier for users to see there state, and ultimately save it some where, the exportState function has been added.

To create a Circuit with states, there is now a state parameter in the options. Creating a new circuit might look like this:


const state = {
....
}


const breaker = new CircuitBreaker({state: state});



// to export the state

const breakerState = breaker.exportState();

I updated the README with some samples of how this would work

The only part i'm not thrilled with is but is probably ok, is that during initilization, if you prime a circuit to be open, then the code emits an "open" event so that the proper timers runs, but it also increments the status count of open, even though the protected function never ran. It probably isn't even an issue since it only will increment the status by 1.

@lholmquist lholmquist force-pushed the 504a-init-state-hard-part branch from dbc0668 to 3df0433 Compare May 24, 2021 18:26
@lholmquist lholmquist force-pushed the 504a-init-state-hard-part branch from 3df0433 to dc8b10c Compare June 14, 2021 18:54
@lholmquist lholmquist marked this pull request as ready for review June 15, 2021 00:39
@lholmquist lholmquist requested review from lance and a team June 15, 2021 00:50
@lholmquist lholmquist changed the title WIP: feat: initialize the state of a breaker on creation feat: initialize the state of a breaker on creation Jun 16, 2021
Copy link
Member

@lance lance left a comment

Choose a reason for hiding this comment

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

Overall, I like it. I do have some thoughts on field duplication and other concerns that were not driving motivators with this change, but instead historical cruft that was just highlighted as the cruft that it is with this PR. I also wonder if it makes sense to completely separate state and status, so have made a suggestion about combining these two into a single state option that contains a status.

this[CLOSED] = options.state.closed || false;
this[OPEN] = options.state.open || false;
// These should be in sync
this[HALF_OPEN] = this[PENDING_CLOSE] = options.state.halfOpen || false;
Copy link
Member

@lance lance Jun 17, 2021

Choose a reason for hiding this comment

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

I wonder if ultimately, HALF_OPEN is all that's needed. The PENDING_CLOSE state is only read once in all of circuit.js. Have you tried removing the PENDING_CLOSE state and just have getPendingClose() return this.halfOpen? If that passes the snuff test, I suggest we remove PENDING_CLOSE from the exported state altogether.

Copy link
Member Author

Choose a reason for hiding this comment

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

It seems that they are almost the same thing. I did that quick test and it looks like we can probably remove pendingClose and just go with halfOpen

lib/circuit.js Outdated
Comment on lines 174 to 175
this[CLOSED] = options.state.closed || false;
this[OPEN] = options.state.open || false;
Copy link
Member

Choose a reason for hiding this comment

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

These changes are touching a lot of areas that I think have been bothering me for a while. One is this weirdness. Why do we have both this[OPEN] and this[CLOSED]? It just opens the door for update anomolies and race conditions. I wonder if this PR is a good opportunity to revisit some of the decisions made early on in development.

If you do decide to keep this double-state, I think one should always be opposite of the other, which isn't the case here. It's worth considering if they are both needed, though.

Copy link
Member Author

@lholmquist lholmquist Jun 21, 2021

Choose a reason for hiding this comment

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

Why do we have both this[OPEN] and this[CLOSED]?

Well, you wrote it :) i would have to look a little deeper to see if both are needed. maybe that would be in a different PR though

We probably don't need to pass both in. Since if it isn't closed, then it is Open. I would opt for only using the closed parameter

@@ -168,12 +168,24 @@ class CircuitBreaker extends EventEmitter {
this[STATUS] = new Status(this.options);
}

this[STATE] = CLOSED;
Copy link
Member

Choose a reason for hiding this comment

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

I think this should stay as the default, and in the if (options.state) block, it can be updated if needed.

Copy link
Member Author

Choose a reason for hiding this comment

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

sounds good

lib/circuit.js Outdated
Comment on lines 256 to 274

// Prepopulate the State of the Breaker
if (this[CLOSED]) {
// If the state is closed, we don't do anything?
this[STATE] = CLOSED;
this.close();
} else if (this[OPEN]) {
// If the state being passed in is OPEN
// THen we need to start some timers
this.open();
} else if (this[HALF_OPEN]) {
// Not sure if anythign needs to be done here
this[STATE] = HALF_OPEN;
} else if (this[SHUTDOWN]) {
// IF the state of the circuit being passed in is SHUTDOWN
// Then i don't think we need to do anything either?
this[STATE] = SHUTDOWN;
this.shutdown();
}
Copy link
Member

Choose a reason for hiding this comment

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

This should all be in the above if (options.state) {} block, shouldn't it? And could probably be simplified to something like this.

Suggested change
// Prepopulate the State of the Breaker
if (this[CLOSED]) {
// If the state is closed, we don't do anything?
this[STATE] = CLOSED;
this.close();
} else if (this[OPEN]) {
// If the state being passed in is OPEN
// THen we need to start some timers
this.open();
} else if (this[HALF_OPEN]) {
// Not sure if anythign needs to be done here
this[STATE] = HALF_OPEN;
} else if (this[SHUTDOWN]) {
// IF the state of the circuit being passed in is SHUTDOWN
// Then i don't think we need to do anything either?
this[STATE] = SHUTDOWN;
this.shutdown();
}
if (options.state.shutdown) {
this.shutdown();
} else if (options.state.open) {
this.open();
}
if (options.state.halfOpen) {
this[STATE] = HALF_OPEN;
}

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm pretty sure there was a reason for doing this way. I think it was becuase there are event listeners and functions that need to get created first, before this code gets executed. but i'll have to go back and check that

lib/circuit.js Outdated
Comment on lines 416 to 426
exportState () {
return {
enabled: this.enabled,
closed: this.closed,
open: this.opened,
halfOpen: this.halfOpen,
warmUp: this.warmUp,
pendingClose: this.pendingClose,
shutdown: this.isShutdown
};
}
Copy link
Member

Choose a reason for hiding this comment

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

Barring my comments above about field/behavior duplication, I like this. It got me thinking that a toJSON() method might be useful. E.g.

Suggested change
exportState () {
return {
enabled: this.enabled,
closed: this.closed,
open: this.opened,
halfOpen: this.halfOpen,
warmUp: this.warmUp,
pendingClose: this.pendingClose,
shutdown: this.isShutdown
};
}
exportState () {
return this.toJSON();
}
// ensures that when a circuit is serialized as JSON, it looks like this
toJSON() {
return {
name: this.name,
group: this.group,
enabled: this.enabled,
opened: this.opened,
halfOpen: this.halfOpen,
warmUp: this.warmUp,
shutdown: this.isShutdown,
status: this.status // obviously this would affect the ctor params
};
}

This also ensures that a circuit's state and status on import/export are in sync.

@lholmquist
Copy link
Member Author

@lance i've made some updates. I added the toJSON method and removed the exportState function since they do the same thing. I also added the status.stats to the toJSON function so it now exports something like this:

{
  state: { ... },
 status: { ... }
}

Which doesn't require any changes to the constructor.

I decided not to mess around with the pendingClose/HalfOpen stuff since that can be done in a future update since it is not user facing.

@lance
Copy link
Member

lance commented Jul 6, 2021

I decided not to mess around with the pendingClose/HalfOpen stuff since that can be done in a future update since it is not user facing.

Probably a good idea.

lholmquist and others added 2 commits July 6, 2021 14:23
Co-authored-by: Lance Ball <lball@redhat.com>
@lholmquist lholmquist merged commit b3dd431 into nodeshift:main Jul 7, 2021
@lholmquist lholmquist deleted the 504a-init-state-hard-part branch July 7, 2021 16:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants