Skip to content

Expose duration through DispatchInfo #121

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

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 6 additions & 59 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,69 +2,16 @@

## Summary

This release introduces a more flexible and powerful mechanism for managing dispatch events with new strategies for merging intervals, enhanced customization options, and better overall alignment with evolving SDK dependencies. It also simplifies actor initialization while maintaining robust support for diverse dispatch scenarios.
<!-- Here goes a general summary of what this release is about -->

## Upgrading

A new simplified way to manage actors has been introduced:
<!-- Here goes notes on how to upgrade from previous versions, including deprecations and what they should be replaced with -->

Change your code from:
```python
dispatcher = Dispatcher(
microgrid_id=microgrid_id,
server_url=url,
key=key
)
dispatcher.start()

status_receiver = dispatcher.new_running_state_event_receiver("EXAMPLE_TYPE")

managing_actor = ActorDispatcher(
actor_factory=MyActor.new_with_dispatch,
running_status_receiver=status_receiver,
)

await run(managing_actor)
```

to

```python
async with Dispatcher(
microgrid_id=microgrid_id,
server_url=url,
key=key
) as dispatcher:
await dispatcher.start_managing(
dispatch_type="EXAMPLE_TYPE",
actor_factory=MyActor.new_with_dispatch, # now async factory!
merge_strategy=MergeByType,
)
await dispatcher
```

Further changes:

* `Dispatcher.start` is no longer `async`. Remove `await` when calling it.
* Two properties have been replaced by methods that require a type as parameter.
* `Dispatcher.lifecycle_events` has been replaced by the method `Dispatcher.new_lifecycle_events_receiver(self, dispatch_type: str)`.
* `Dispatcher.running_status_change` has been replaced by the method `Dispatcher.new_running_state_event_receiver(self, dispatch_type: str, merge_strategy: MergeStrategy)`.
* The managing actor constructor no longer requires the `dispatch_type` parameter. Instead you're expected to pass the type to the new_receiver function.
* The `DispatchManagingActor` class has been renamed to `DispatchActorsService`.
* It's interface has been simplified and now only requires an actor factory and a running status receiver.
* It only starts/stops a single actor at a time now instead of a set of actors.
* Refer to the updated [usage example](https://frequenz-floss.github.io/frequenz-dispatch-python/latest/reference/frequenz/dispatch/#frequenz.dispatch.DispatchActorsService) for more information.
* `DispatchUpdate` was renamed to `DispatchInfo`.
## New Features

- Dispatch durations are now exposed through `DispatchInfo`s.

## New Features
## Bug Fixes

* A new feature "merge strategy" (`MergeByType`, `MergeByTypeTarget`) has been added to the `Dispatcher.new_running_state_event_receiver` method. Using it, you can automatically merge consecutive and overlapping dispatch start/stop events of the same type. E.g. dispatch `A` starting at 10:10 and ending at 10:30 and dispatch `B` starts at 10:30 until 11:00, with the feature enabled this would in total trigger one start event, one reconfigure event at 10:30 and one stop event at 11:00.
* The SDK dependency was widened to allow versions up to (excluding) v1.0.0-rc1800.
* Actor management with dispatches has been simplified:
* `Dispatcher.start_managing(dispatch_type, actor_factory, merge_strategy, retry_interval)` to manage your actor for the given type and merge strategy. All you need provide is an actor factory.
* `Dispatcher.stop_managing(dispatch_type)` to stop dispatching for the given type.
* `Dispatcher.is_managed(dispatch_type)` to check if dispatching is active for the given type.
* Dispatches that failed to start will now be retried after a delay.
* A new method `Dispatcher.wait_for_initialization()` has been added to wait for all actors to be initialized.
* When using `async with Dispatcher(..) as dispatcher`, the dispatcher will first wait for the dispatch service to be initialized before entering the block.
<!-- Here goes notable bug fixes that are worth a special mention or explanation -->
4 changes: 4 additions & 0 deletions src/frequenz/dispatch/_actor_dispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ class DispatchInfo:
options: dict[str, Any]
"""Additional options."""

duration: timedelta | None
"""The duration of the dispatch."""

Comment on lines 32 to +37
Copy link
Contributor

Choose a reason for hiding this comment

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

Just suggestion: duration can be in options dict.
However I don't know why you need this so feel free to ignore this comment.


class ActorDispatcher(BackgroundService):
"""Helper class to manage actors based on dispatches.
Expand Down Expand Up @@ -229,6 +232,7 @@ async def _start_actor(self, dispatch: Dispatch) -> None:
components=dispatch.target,
dry_run=dispatch.dry_run,
options=dispatch.payload,
duration=dispatch.duration,
Copy link
Contributor

Choose a reason for hiding this comment

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

This is tricky and can be misleading. If you use the high-level interface and you are listening to the start/stop events, or your actor is managed, this duration might not match the time your actor is really running if some dispatches were merged.

I think a better approach would be to include a list of active dispatches, and just pass the full Dispatch object there. Another approach would be to pass here a "merged duration" instead of the raw duration of the current dispatch. Depending on the use case one could be more useful than the other.

Copy link
Contributor

Choose a reason for hiding this comment

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

I moved the conversation to #119, I think it is more appropriate to discuss the design in the issue.

)

identity = self._dispatch_identity(dispatch)
Expand Down
Loading