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

Added polling-controller #1703

Merged
merged 27 commits into from
Sep 26, 2023
Merged

Added polling-controller #1703

merged 27 commits into from
Sep 26, 2023

Conversation

shanejonas
Copy link
Contributor

Explanation

Originally was in #1673 but pulled out to get this in on its own.

Adds an abstract class (currently named PollingController. The start and stop methods are parameterized by networkClientId's and pollingToken's and polling intervals are stored in class variables by chainId so that multiple networkClients/chainIds can poll simultaneously. executePoll is an abstract method to be implemented by the controller itself so that this pattern could be generalized and be agnostic to what is being executed on the polling interval.

References

Related to #1673
Related to https://github.com/MetaMask/MetaMask-planning/issues/1314

@socket-security
Copy link

socket-security bot commented Sep 22, 2023

No top level dependency changes detected. Learn more about Socket for GitHub ↗︎

@adonesky1
Copy link
Contributor

A few small nits but otherwise lets gooo

if (this.intervalIds[networkClientId]) {
clearTimeout(this.intervalIds[networkClientId]);
delete this.intervalIds[networkClientId];
}
Copy link
Contributor

Choose a reason for hiding this comment

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

i think this logic should be DRY'ed into it's own private method since we use it in the stop() method as well

}
let found = false;
this.networkClientIdTokensMap.forEach((tokens, networkClientId) => {
if (tokens.has(pollingToken)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit. feel free to ignore. I feel that this would be slightly cleaner if this was turned negated / turned into a guard.

Gudahtt
Gudahtt previously approved these changes Sep 22, 2023
Copy link
Member

@Gudahtt Gudahtt left a comment

Choose a reason for hiding this comment

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

Looks great!

@Gudahtt Gudahtt dismissed their stale review September 22, 2023 20:58

On second thought, it does seem important for the interval to be configurable

packages/polling-controller/package.json Outdated Show resolved Hide resolved
* @param networkClientId - The networkClientId to start polling for
* @returns void
*/
start(networkClientId: NetworkClientId) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we support polling controllers which may only have 1 polling loop active per networkClientId? Perhaps we could have an option passed into the constructor to indicate this? for example, if the blocktracker was a controller, we would only want to have 1 active block polling per network.

Copy link
Member

Choose a reason for hiding this comment

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

Good question. I can't think of any controllers where we'd want this offhand though.

/**
* Stops polling for all networkClientIds
*/
stopAll() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we have this optionally take a networkClientId, allowing all polling to be stopped for a given network?

Copy link
Contributor

Choose a reason for hiding this comment

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

We could do this but I'm not sure I can think of a situation where we'd want this behavior. In general PollingTokens should be disconnected individually so we can be sure that there are no lingering listeners depending on active polling. TBH I'm not even sure where we'll want to call stopAll?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

stopAll is useful in tests but I don't think many other places will call it

try {
await this.executePoll(networkClientId);
} catch (error) {
console.error(error);
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we be eating the errors? I suppose even if we let the error bubble up, since this is happening inside an interval, nothing else would be able to catch the error anyways. In this case, does it make sense to add an interface to subscribe to errors? IE how would a controller using this abstract controller be able to detect an error in its executePoll call, and react to it.

Should we keep polling when there are errors? Maybe we can have an option to configure an linear/exponential backoff when there are errors.

Copy link
Member

Choose a reason for hiding this comment

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

I had the same thought, this is definitely not ideal for error handling. But it's strictly better than the status quo, where typically the error is completely swallowed and not even logged. Something to consider later as an enhancement perhaps

} from '@metamask/base-controller';
import type { NetworkClientId } from '@metamask/network-controller';
import type { Json } from '@metamask/utils';
import { v1 as random } from 'uuid';
Copy link
Member

Choose a reason for hiding this comment

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

Hmm, why not use v4? It's my understanding that v1 is not quite as random, and that v4 is the better choice in most situations

Gudahtt
Gudahtt previously approved these changes Sep 26, 2023
Copy link
Member

@Gudahtt Gudahtt left a comment

Choose a reason for hiding this comment

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

LGTM! Just one pending suggestion

@socket-security
Copy link

socket-security bot commented Sep 26, 2023

👍 Dependency issues cleared. Learn more about Socket for GitHub ↗︎

This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored.

Copy link
Member

@Gudahtt Gudahtt left a comment

Choose a reason for hiding this comment

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

LGTM!

@Gudahtt Gudahtt dismissed legobeat’s stale review September 26, 2023 14:34

Unused dependencies have been removed

@shanejonas shanejonas merged commit 9698b0a into main Sep 26, 2023
107 checks passed
@shanejonas shanejonas deleted the polling-controller branch September 26, 2023 14:41
MajorLift pushed a commit that referenced this pull request Oct 11, 2023
## Explanation

> Originally was in #1673 but
pulled out to get this in on its own.

Adds an abstract class (currently named `PollingController`. The start
and stop methods are parameterized by `networkClientId`'s and
`pollingToken`'s and polling intervals are stored in class variables by
`chainId` so that multiple `networkClients`/`chainIds` can poll
simultaneously. `executePoll` is an abstract method to be implemented by
the controller itself so that this pattern could be generalized and be
agnostic to what is being executed on the polling interval.


## References
Related to #1673
Related to MetaMask/MetaMask-planning#1314

---------

Co-authored-by: Alex Donesky <adonesky@gmail.com>
MajorLift pushed a commit that referenced this pull request Oct 11, 2023
## Explanation

> Originally was in #1673 but
pulled out to get this in on its own.

Adds an abstract class (currently named `PollingController`. The start
and stop methods are parameterized by `networkClientId`'s and
`pollingToken`'s and polling intervals are stored in class variables by
`chainId` so that multiple `networkClients`/`chainIds` can poll
simultaneously. `executePoll` is an abstract method to be implemented by
the controller itself so that this pattern could be generalized and be
agnostic to what is being executed on the polling interval.


## References
Related to #1673
Related to MetaMask/MetaMask-planning#1314

---------

Co-authored-by: Alex Donesky <adonesky@gmail.com>
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.

6 participants