-
Notifications
You must be signed in to change notification settings - Fork 1
Replace config subscriber thread with handler-based config update mechanism #352
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
Changes from all commits
4608d75
dbc2e98
68087f1
1025490
afe24b0
5376269
cc3ec4a
098023f
a928d1d
d90f5fb
d57e09a
4923e48
d0cbf0f
df7514a
6514c8b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| # SPDX-License-Identifier: BSD-3-Clause | ||
| # Copyright (c) 2025 Scipp contributors (https://github.com/scipp) | ||
| import json | ||
| import logging | ||
|
|
||
| from ..core.handler import Config, Handler | ||
| from ..core.message import Message, MessageKey | ||
| from ..kafka.helpers import beamlime_command_topic | ||
|
|
||
|
|
||
| class ConfigHandler(Handler[bytes, None]): | ||
| """ | ||
| Handler for configuration messages. | ||
|
|
||
| This handler processes configuration messages and updates the configuration | ||
| dictionary accordingly. It is used by StreamProcessor to handle configuration | ||
| updates received from a Kafka topic. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| logger | ||
| Logger to use | ||
| config | ||
| Configuration object to update | ||
| """ | ||
|
|
||
| @staticmethod | ||
| def message_key(instrument: str) -> MessageKey: | ||
| return MessageKey(topic=beamlime_command_topic(instrument), source_name='') | ||
|
|
||
| def __init__(self, *, logger: logging.Logger | None = None, config: Config): | ||
| super().__init__(logger=logger, config=config) | ||
| self._store = config | ||
|
|
||
| def get(self, key: str, default=None): | ||
| return self._store.get(key, default) | ||
|
|
||
| def handle(self, messages: list[Message[bytes]]) -> list[Message[None]]: | ||
| """ | ||
| Process configuration messages and update the configuration. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| messages: | ||
| List of messages containing configuration updates | ||
|
|
||
| Returns | ||
| ------- | ||
| : | ||
| Empty list as this handler doesn't produce output messages | ||
| """ | ||
| for message in messages: | ||
| try: | ||
| key = message.value['key'] | ||
| value = json.loads(message.value['value'].decode('utf-8')) | ||
| self._logger.info( | ||
| 'Updating config: %s = %s at %s', key, value, message.timestamp | ||
| ) | ||
| self._store[key] = value | ||
| except Exception as e: # noqa: PERF203 | ||
| self._logger.error('Error processing config message: %s', e) | ||
| return [] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -29,6 +29,15 @@ def da00_to_scipp( | |
|
|
||
|
|
||
| def _to_da00_variable(name: str, var: sc.Variable) -> dataarray_da00.Variable: | ||
| if var.dtype == sc.DType.datetime64: | ||
| timedelta = var - sc.epoch(unit=var.unit) | ||
| return dataarray_da00.Variable( | ||
| name=name, | ||
| data=timedelta.values, | ||
| axes=list(var.dims), | ||
| shape=var.shape, | ||
| unit=f'datetime64[{var.unit}]', | ||
| ) | ||
|
Comment on lines
+32
to
+40
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changes in this file are unrelated. The |
||
| return dataarray_da00.Variable( | ||
| name=name, | ||
| data=var.values, | ||
|
|
@@ -39,4 +48,7 @@ def _to_da00_variable(name: str, var: sc.Variable) -> dataarray_da00.Variable: | |
|
|
||
|
|
||
| def _to_scipp_variable(var: dataarray_da00.Variable) -> sc.Variable: | ||
| if var.unit.startswith('datetime64'): | ||
| unit = var.unit.split('[')[1].rstrip(']') | ||
| return sc.epoch(unit=unit) + sc.array(dims=var.axes, values=var.data, unit=unit) | ||
| return sc.array(dims=var.axes, values=var.data, unit=var.unit) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,6 +14,29 @@ def close(self) -> None: | |
| pass | ||
|
|
||
|
|
||
| class MultiConsumer: | ||
| """ | ||
| Message source for multiple Kafka consumers. | ||
|
|
||
| This class allows for consuming messages from multiple Kafka consumers with | ||
| different configuration. In particular, we need to use different topic offsets for | ||
| data topics vs. config/command topics. | ||
| """ | ||
|
|
||
| def __init__(self, consumers): | ||
| self._consumers = consumers | ||
|
|
||
| def consume(self, num_messages: int, timeout: float) -> list[KafkaMessage]: | ||
| messages = [] | ||
| for consumer in self._consumers: | ||
| messages.extend(consumer.consume(num_messages, timeout)) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think they shouldn't have same timeout for all different topics, unless it's very small
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Summary from discussion: May want different config for control messages, but unclear what is correct approach right now. Will leave as-is for now and address when we have information on how this works in practice. |
||
| return messages | ||
|
|
||
| def close(self) -> None: | ||
| for consumer in self._consumers: | ||
| consumer.close() | ||
|
|
||
|
|
||
| class KafkaMessageSource(MessageSource[KafkaMessage]): | ||
| """ | ||
| Message source for messages from Kafka. | ||
|
|
||
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.
We now have to pass the registry, since we want to be able to insert a custom handler for config messages, i.e., the processor cannot create the registry itself.