-
Couldn't load subscription status.
- Fork 6
Some final preparations #108
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
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 good in general but I would remove the autostart and change examples to use the dispatcher as a context manager. Also IMHO it needs clarification of how the retry interval works.
I would also really think about the implications of (re)creating the actor each time a dispatch arrives, just to make sure we are not missing anything important, but maybe we can delay this even for after the release, I guess it won't be such a huge impact if we change it in the short term.
| exc_info=True, | ||
| exc_info=e, | ||
| ) | ||
| self._start_failed_dispatches[identity] = dispatch |
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.
OK, so this is another difference with how are we managing things in the app, since actors are created and removed each time a dispatch comes instead of preserving the instance and just start/stop the actor with dispatches.
I'm still not sure which approach is best, and am starting to think maybe it is worth thinking about this a bit before deciding.
|
RELEASE_NOTES.md
Outdated
| * Actor management with dispatches has been simplified: | ||
| * `Dispatcher.start_dispatching(dispatch_type, actor_factory, merge_strategy)` to manage your actor for the given type and merge strategy. All you need provide is an actor factory. | ||
| * `Dispatcher.stop_dispatching(dispatch_type)` to stop dispatching for the given type. | ||
| * `Dispatcher.is_dispatching(dispatch_type)` to check if dispatching is active for the given type. |
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.
TODO need to rename this to is_managed
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.
To be honest I'm less happy with this iteration than with the previous one.
-
I don't get very well why the new
initial_fetch_timeoutis needed. Can it normally happen that it takes too long? Is it a last resort thing? I'm not sure why this has to be done at this level, and it isn't the user that should wrap the initialization in atimeout()if they don't want to wait for too long. Wouldn't it make more sense to provide a method likeawait dispatcher.initialization_finished()? -
I find the retry logic extremely convoluted. Why can't we just have one task with a loop like the in the edge app? Right now to try to figure out how the retry works I need to look into like 3 different places, the retry logic is very spread out.
-
I don't think it is a good idea to have 2 modes now, the persistent and non-persistent mode, it also adds a lot of complexity for something we don't know we'll need yet. It was just a comment for something to look into in the future.
-
Nitpick: commit message style: "Dispatches that failed to start will now be retried after a delay." ends with
., is not in imperative (like "Retry dispatches that failed to start after a delay"). Also what does it mean that a "dispatch failed to start". I guess is the actor that failed to start? But it is not entirely clear to me. Sometimes a one-line title is not enough. Also the commit in the previous point could answer all the questions I have there.
I really find this iteration much harder to read and to follow, and I don't see we are gaining much from the added complexity. 😟 And for me it was practically ready to be merged. Reading my comments maybe it wasn't obvious that I felt like this, so sorry about that.
I think in the future we should try to merge more quickly and iterate over the merged stuff instead, otherwise it can get a bit tiring to iterate over and over the same PR but with the code changing radically.
| _logger.error( | ||
| "Failed to start actor for dispatch type %r: %s", | ||
| "Failed to start actor for dispatch type %r", | ||
| dispatch.type, | ||
| e, | ||
| exc_info=True, | ||
| exc_info=e, |
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.
You can just use _logger.exception() instead of explicitly passing the exc_info.
That part was added after I started using the As a result a normal startup would give a warning for microgrids where there are >30 dispatches and I didn't like that default behavior.
I am sorry, but it's a fact that we need it. At least the non-persistent behavior (for sahas power setting actor). And in my opinion the changes are quite minimal and non-invasive.
"very spread out"' three interaction points seem not to match that statement.. and the class itself is just 300 lines, so I don't feel this puts the total complexity above acceptable either. You can't spread things a lot in 300 lines :P
Imo, we're gaining a lot.
|
OK, so I think it then can make sense to have some
Do we? Why? Having always removing and re-creating the actor should always work, right? Re-using the actor is sort of just an optimization.
Yeah, so the mode that was previously implemented (always delete the actor when it is stopped) is the one that needs to be supported no matter what. The other mode, IMHO is not necessary, it can be implemented in the future transparently, and only delete an actor when the dispatch is completely done (it is done and have no recurrence rules, or has recurrence rules, but there are no more next events). In principle I don't think we need a mode to keep the actor alive always, forever. So I think we shouldn't do futurology and implement stuff we don't need yet, or don't know if it will be needed, as it adds development, maintenance and learning effort.
Maybe in terms of code, but I think it also adds complexity in terms of interface, now users need to think about this, and if we want to do a 1.0 soon(ish), it means if it was a mistake, we need to keep maintaining it.
3 places is three times more than 1, is a 300% increase. So yeah, very Jokes aside, I still don't get why the "one simple loop" approach we have in the edge app is not sufficient here. I'm not all that worried about adding complexity, just about adding unnecessary complexity.
But again, the point was never to remove "Sahas mode", so we only gained a mode that we don't even know if we need at all (and also my initial hunch is we don't, which makes me even more hesitant about adding it). Even more, if we merged the previous iteration by just removing Anyway, I'd prefer much more to merge the previous iteration than this one, but if you feel too strongly about reverting, I can live with it, as long as the bug about the dangling task is fixed. Then we can change stuff in followup PRs. |
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.
Even when there are a couple of comments that I think should probably be addressed before releasing, at this point I think I would much rather merge as is than to keep iterating, as each time I kind of need to review the PR entirely, when only a few parts changed. It is much easier and quicker to review a new PR instead.
RELEASE_NOTES.md
Outdated
| await dispatcher.start_dispatching( | ||
| dispatch_type="EXAMPLE_TYPE", | ||
| actor_factory=MyActor.new_with_dispatch, # now async factory! | ||
| merge_strategy=MergeStrategy.MergeByType, |
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.
Is this right? Shouldn't it be just MergeByType?
| dispatch: The dispatch information to retry. | ||
| """ | ||
| task = asyncio.create_task(self._retry_after_delay(dispatch)) | ||
| self._tasks.add(task) |
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.
Now you need to finalize this object too, otherwise when the dispatcher that instantiates it is destroyed, all these tasks will be dangling until the garbage collection destroys them. My feeling is this class needs to end up being a BackgroundService too.
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.
those tasks only hang around 60 seconds or so anyway then they destroy themselves through the done callback below..
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 agree it is not too bad, but there could be situation on program termination that the loop can emit warnings when there are pending tasks and the loop is stopped, specially in tests. So I agree it is not urgent, but eventually I would implement proper finalization for this, as most of the time that we say "meh, who cares about finalization", it eventually comes back to bite us :D
| actor = self._actors.get(identity) | ||
|
|
||
| if actor: | ||
| await actor.stop(msg) | ||
|
|
||
| del self._actors[identity] | ||
| else: | ||
| _logger.warning( | ||
| "Actor for dispatch type %r is not running", stopping_dispatch.type | ||
| ) |
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.
Tip:
| actor = self._actors.get(identity) | |
| if actor: | |
| await actor.stop(msg) | |
| del self._actors[identity] | |
| else: | |
| _logger.warning( | |
| "Actor for dispatch type %r is not running", stopping_dispatch.type | |
| ) | |
| if actor := self._actors.pop(identity, None): | |
| await actor.stop(msg) | |
| else: | |
| _logger.warning( | |
| "Actor for dispatch type %r is not running", stopping_dispatch.type | |
| ) |
Signed-off-by: Mathias L. Baumann <[email protected]>
Signed-off-by: Mathias L. Baumann <[email protected]>
Signed-off-by: Mathias L. Baumann <[email protected]>
Signed-off-by: Mathias L. Baumann <[email protected]>
Signed-off-by: Mathias L. Baumann <[email protected]>
Signed-off-by: Mathias L. Baumann <[email protected]>
Signed-off-by: Mathias L. Baumann <[email protected]>
Signed-off-by: Mathias L. Baumann <[email protected]>
Signed-off-by: Mathias L. Baumann <[email protected]>
Signed-off-by: Mathias L. Baumann <[email protected]>
autostarttoDispatcher()service