Skip to content

Commit 5195d0b

Browse files
committed
Add option to only send changed configurations
When this option is enabled, configurations will be sent to the receiver only if they are different from the previously received configuration. Signed-off-by: Leandro Lucarella <[email protected]>
1 parent c7d8a52 commit 5195d0b

File tree

1 file changed

+31
-0
lines changed

1 file changed

+31
-0
lines changed

src/frequenz/sdk/config/_manager.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,19 @@
44
"""Management of configuration."""
55

66
import asyncio
7+
import logging
78
import pathlib
89
from collections.abc import Mapping, Sequence
910
from datetime import timedelta
1011
from typing import Any, Final
1112

1213
from frequenz.channels import Broadcast, Receiver
14+
from frequenz.channels.experimental import WithPrevious
1315

1416
from ._managing_actor import ConfigManagingActor
1517

18+
_logger = logging.getLogger(__name__)
19+
1620

1721
class ConfigManager:
1822
"""A manager for configuration files.
@@ -99,13 +103,24 @@ async def new_receiver( # pylint: disable=too-many-arguments # noqa: DOC502
99103
self,
100104
*,
101105
wait_for_first: bool = False,
106+
skip_unchanged: bool = True,
102107
) -> Receiver[Mapping[str, Any] | None]:
103108
"""Create a new receiver for the configuration.
104109
110+
This method has a lot of features and functionalities to make it easier to
111+
receive configurations.
112+
105113
Note:
106114
If there is a burst of configuration updates, the receiver will only
107115
receive the last configuration, older configurations will be ignored.
108116
117+
### Skipping superfluous updates
118+
119+
If `skip_unchanged` is set to `True`, then a configuration that didn't change
120+
compared to the last one received will be ignored and not sent to the receiver.
121+
The comparison is done using the *raw* `dict` to determine if the configuration
122+
has changed.
123+
109124
### Waiting for the first configuration
110125
111126
If `wait_for_first` is `True`, the receiver will wait for the first
@@ -129,6 +144,8 @@ async def new_receiver( # pylint: disable=too-many-arguments # noqa: DOC502
129144
time, a timeout error will be raised. If receiving was successful, the
130145
first configuration can be simply retrieved by calling
131146
[`consume()`][frequenz.channels.Receiver.consume] on the receiver.
147+
skip_unchanged: Whether to skip sending the configuration if it hasn't
148+
changed compared to the last one received.
132149
133150
Returns:
134151
The receiver for the configuration.
@@ -140,8 +157,22 @@ async def new_receiver( # pylint: disable=too-many-arguments # noqa: DOC502
140157
recv_name = f"{self}_receiver"
141158
receiver = self.config_channel.new_receiver(name=recv_name, limit=1)
142159

160+
if skip_unchanged:
161+
receiver = receiver.filter(WithPrevious(not_equal_with_logging))
162+
143163
if wait_for_first:
144164
async with asyncio.timeout(self.wait_for_first_timeout.total_seconds()):
145165
await receiver.ready()
146166

147167
return receiver
168+
169+
170+
def not_equal_with_logging(
171+
old_config: Mapping[str, Any], new_config: Mapping[str, Any]
172+
) -> bool:
173+
"""Return whether the two mappings are not equal, logging if they are the same."""
174+
if old_config == new_config:
175+
_logger.info("Configuration has not changed, skipping update")
176+
_logger.debug("Old configuration being kept: %r", old_config)
177+
return False
178+
return True

0 commit comments

Comments
 (0)