-
-
Notifications
You must be signed in to change notification settings - Fork 256
Extend createServicePolicy to support live network status #7164
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
Conversation
237b741 to
0a9dc27
Compare
In a future commit we will introduce changes to `network-controller` so that it will keep track of the status of each network as requests are made. These updates to `createServicePolicy` assist with that. See the changelog for a list of changes to the `ServicePolicy` API. Besides the changes listed there, the tests for `createServicePolicy` have been refactored slightly so that it is easier to maintain in the future.
0a9dc27 to
cca8f81
Compare
Gudahtt
left a comment
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.
Looks great! I'll continue review tomorrow, the only thing I have left is the tests.
|
|
||
| const promise = policy.execute(mockService); | ||
| // It's safe not to await this promise; adding it to the promise queue | ||
| // is enough to prevent this test from running indefinitely. |
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.
Nit: Not sure about this justification. Floating promises are problematic for reasons other than keeping the test process alive / leaking memory. They also can cause test failures in the un-awaited code to appear in later tests.
I see this comment is pre-existing in many tests though, so best not to address this in this PR anyway.
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.
Makes sense. This is an old pattern. In newer tests, I tend to use this pattern:
policy.onRetry(() => {
clock.next();
});When I rewrite this file I'll go through and make sure to use this pattern instead.
| @@ -215,6 +229,29 @@ describe('createServicePolicy', () => { | |||
|
|
|||
| expect(onDegradedListener).not.toHaveBeenCalled(); | |||
| }); | |||
|
|
|||
| it('does not call onAvailable listeners', async () => { | |||
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.
Hmm. This behavior is a bit questionable actually. If a request 'fails" but was filtered out by the policy, it means that there is no outage, i.e. that the service is working as intended, it "failed successfully", and is likely available.
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.
Similarly, it does seem fair to call service degraded if it fails really slowly.
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.
I guess we can leave this change for a separate PR, if we want to make it, since it would be more of a departure from how it works today.
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.
Sure, we can consider this for another PR.
| expect(onDegradedListener).toHaveBeenCalledWith({ error }); | ||
| }); | ||
|
|
||
| it('does not call onAvailable listeners', async () => { |
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.
Nit: These tests seem unnecessarily exhaustive, and this test case is a good example of this.
It's great that we're testing that the default number of retries works as expected, and that a custom number of retries works as expected. But why do we need to test this both with default max consecutive failures and with custom max consecutive failures? And do we really need to test all four combinations of these conditions with a "always failing" policy and a "failing repeatedly until it finally succeeds" policy - do we really expect one to fail if the other succeeds?
This unit test suite seems to be taking the extreme approach of covering every possible combination of conditions. This is certainly the only way to be absolutely certain the code works as expected, but we rarely write tests this exhaustive for good reason: it's really expensive/time-consuming.
It is enough to test expected behaviour once. We don't need to test that behaviour is identical across scenarios that don't impact that behaviour.
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.
Thank you for pointing this out. I agree that the exhaustive strategy I took with this file is hurting more than helping at this point. I think I just need to take a fresh approach and try to keep it simple. I'll think more about how I can do that.
| describe('wrapping a service that succeeds at first and then fails enough to break the circuit', () => { | ||
| describe.each([ | ||
| { | ||
| desc: `the default max number of consecutive failures (${DEFAULT_MAX_CONSECUTIVE_FAILURES})`, |
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.
Nit: as mentioned in the other comment, I don't think we need to test the effectiveness of this option in every single test scenario
Gudahtt
left a comment
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.
LGTM!
I had some suggestions for the test suite, but most of them were related to pre-existing patterns.
| { | ||
| desc: 'a custom circuit break duration', | ||
| circuitBreakDuration: DEFAULT_CIRCUIT_BREAK_DURATION, | ||
| optionsWithCircuitBreakDuration: { |
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.
Bug: Circuit Break: Default Overrides Custom
The circuitBreakDuration variable is set to DEFAULT_CIRCUIT_BREAK_DURATION instead of 5_000, which contradicts the description "a custom circuit break duration" and will cause the test to use an incorrect wait time (30 minutes instead of 5 seconds) when calling clock.tick().
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.
Ugh, I thought I fixed this already. This is true, but the test still works, so I'm inclined to go with it for now.
Explanation
In a future commit we will introduce changes to
network-controllerso that it will keep track of the status of each network as requests are made. These updates tocreateServicePolicyassist with that. See the changelog for more.Besides this, the tests for
createServicePolicyhave been refactored slightly so that they are easier to maintain in the future.References
Progresses https://consensyssoftware.atlassian.net/browse/WPC-99.
You can see how these changes will be used in the next PR: #7166
Checklist
Note
Adds
getCircuitState,onAvailable, andresettoServicePolicy, exports Cockatiel types, and updates logic/tests to support availability tracking and circuit state introspection.getCircuitState()to expose underlying circuit state.onAvailableevent for first success and post-recovery success.reset()to close the circuit and reset breaker counters.onAvailable/onDegradedappropriately.onBreakto mark unavailable; wireConsecutiveBreakerfor reset.CockatielEventEmitterandCockatielFailureReason; re-export viaindex.onAvailable,getCircuitState,reset, and timing cases; update export snapshot.CHANGELOG.mdwith new methods and exports.Written by Cursor Bugbot for commit e597d0b. This will update automatically on new commits. Configure here.