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

Simplify Actor Builders #1772

Merged

Conversation

didier-wenzek
Copy link
Contributor

@didier-wenzek didier-wenzek commented Feb 28, 2023

Proposed changes

WIP

  • Add a Config to each ServiceConsumer.
    So a ServiceConsumer can be connected to a ServiceProvider
    without any knowledge of the config used by the former to connect the latter.
  • Deprecate SimpleMessageBox::channel function
  • Replace the specific connect methods (e.g. with_c8y_http_proxy) with generic invocations of connect_to.

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Improvement (general improvements like code refactoring that doesn't explicitly fix a bug or add any new functionality)
  • Documentation Update (if none of the other choices apply)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

Paste Link to the issue

#1724

Checklist

  • I have read the CONTRIBUTING doc
  • I have signed the CLA (in all commits with git commit -s)
  • I ran cargo fmt as mentioned in CODING_GUIDELINES
  • I used cargo clippy as mentioned in CODING_GUIDELINES
  • I have added tests that prove my fix is effective or that my feature works
  • I have added necessary documentation (if appropriate)

Comment on lines 116 to 134
impl<T, Req, Res> ServiceConsumer<Req, Res, NoConfig> for T
where
Req: Message,
Res: Message,
T: MessageSink<Res> + MessageSource<Req, NoConfig>,
{
fn get_config(&self) -> NoConfig {
NoConfig
}

fn set_request_sender(&mut self, request_sender: DynSender<Req>) {
self.register_peer(self.get_config(), request_sender)
}

fn get_response_sender(&self) -> DynSender<Res> {
self.get_sender()
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I removed this generic implementation because too generic and preventing specializations due to Rust complaining of possible future conflicting implementations.

@didier-wenzek didier-wenzek changed the title Reconcile Service Producer/Consumer and Message Source/Sink Simplify Actor Builders Mar 2, 2023
@didier-wenzek didier-wenzek temporarily deployed to Test Pull Request March 2, 2023 19:27 — with GitHub Actions Inactive
@github-actions
Copy link
Contributor

github-actions bot commented Mar 2, 2023

Robot Results

✅ Passed ❌ Failed ⏭️ Skipped Total Pass %
140 0 5 140 100

Passed Tests

Name ⏱️ Duration Suite
Define Child device 1 ID 0.009 s C8Y Child Alarms Rpi
Normal case when the child device does not exist on c8y cloud 3.7439999999999998 s C8Y Child Alarms Rpi
Normal case when the child device already exists 0.78 s C8Y Child Alarms Rpi
Reconciliation when the new alarm message arrives, restart the mapper 1.8090000000000002 s C8Y Child Alarms Rpi
Reconciliation when the alarm that is cleared 5.585 s C8Y Child Alarms Rpi
Prerequisite Parent 17.465 s Child Conf Mgmt Plugin
Prerequisite Child 0.27 s Child Conf Mgmt Plugin
Child device bootstrapping 13.445 s Child Conf Mgmt Plugin
Snapshot from device 19.308 s Child Conf Mgmt Plugin
Child device config update 19.083 s Child Conf Mgmt Plugin
Configuration types should be detected on file change (without restarting service) 41.462 s Inotify Crate
Child devices support sending simple measurements 43.68 s Child Device Telemetry
Child devices support sending custom measurements 42.318 s Child Device Telemetry
Child devices support sending custom events 38.785 s Child Device Telemetry
Child devices support sending custom events overriding the type 31.515 s Child Device Telemetry
Child devices support sending custom alarms #1699 30.371 s Child Device Telemetry
Child devices support sending inventory data via c8y topic 21.56 s Child Device Telemetry
Main device support sending inventory data via c8y topic 20.894 s Child Device Telemetry
Successful firmware operation 59.709 s Firmware Operation
Install with empty firmware name 47.626 s Firmware Operation
Supports restarting the device 70.455 s Restart Device
Update tedge version from previous using Cumulocity 96.234 s Tedge Self Update
Successful shell command with output 3.693 s Shell Operation
Check Successful shell command with literal double quotes output 3.636 s Shell Operation
Execute multiline shell command 3.066 s Shell Operation
Failed shell command 3.1 s Shell Operation
Software list should be populated during startup 50.564 s Software
Install software via Cumulocity 59.986 s Software
Software list should only show currently installed software and not candidates 41.859 s Software
Stop tedge-agent service 0.194 s Log Path Config
Customize the log path 0.14 s Log Path Config
Initialize tedge-agent 0.138 s Log Path Config
Check created folders 0.07 s Log Path Config
Remove created custom folders 0.077 s Log Path Config
Install latest via script (from current branch) 26.822 s Install Tedge
Install specific version via script (from current branch) 16.971 s Install Tedge
Install latest tedge via script (from main branch) 22.44 s Install Tedge
Support starting and stopping services 35.445 s Service-Control
Supports a reconnect 44.719 s Test-Commands
Supports disconnect then connect 45.278 s Test-Commands
Update unknown setting 27.927 s Test-Commands
Update known setting 25.736 s Test-Commands
Stop c8y-configuration-plugin 0.143 s Health C8Y-Configuration-Plugin
Update the service file 0.152 s Health C8Y-Configuration-Plugin
Reload systemd files 0.852 s Health C8Y-Configuration-Plugin
Start c8y-configuration-plugin 0.225 s Health C8Y-Configuration-Plugin
Start watchdog service 10.218 s Health C8Y-Configuration-Plugin
Check PID of c8y-configuration-plugin 0.109 s Health C8Y-Configuration-Plugin
Kill the PID 0.175 s Health C8Y-Configuration-Plugin
Recheck PID of c8y-configuration-plugin 2.321 s Health C8Y-Configuration-Plugin
Compare PID change 0 s Health C8Y-Configuration-Plugin
Stop watchdog service 0.183 s Health C8Y-Configuration-Plugin
Remove entry from service file 0.192 s Health C8Y-Configuration-Plugin
Stop c8y-log-plugin 0.14 s Health C8Y-Log-Plugin
Update the service file 0.155 s Health C8Y-Log-Plugin
Reload systemd files 0.632 s Health C8Y-Log-Plugin
Start c8y-log-plugin 0.198 s Health C8Y-Log-Plugin
Start watchdog service 10.174 s Health C8Y-Log-Plugin
Check PID of c8y-log-plugin 0.067 s Health C8Y-Log-Plugin
Kill the PID 0.102 s Health C8Y-Log-Plugin
Recheck PID of c8y-log-plugin 2.164 s Health C8Y-Log-Plugin
Compare PID change 0.001 s Health C8Y-Log-Plugin
Stop watchdog service 0.087 s Health C8Y-Log-Plugin
Remove entry from service file 0.056 s Health C8Y-Log-Plugin
Stop tedge-mapper 0.187 s Health Tedge Mapper C8Y
Update the service file 0.142 s Health Tedge Mapper C8Y
Reload systemd files 0.819 s Health Tedge Mapper C8Y
Start tedge-mapper 0.117 s Health Tedge Mapper C8Y
Start watchdog service 10.35 s Health Tedge Mapper C8Y
Check PID of tedge-mapper 0.054 s Health Tedge Mapper C8Y
Kill the PID 0.066 s Health Tedge Mapper C8Y
Recheck PID of tedge-mapper 2.115 s Health Tedge Mapper C8Y
Compare PID change 0.001 s Health Tedge Mapper C8Y
Stop watchdog service 0.099 s Health Tedge Mapper C8Y
Remove entry from service file 0.093 s Health Tedge Mapper C8Y
Stop tedge-agent 0.203 s Health Tedge-Agent
Update the service file 0.099 s Health Tedge-Agent
Reload systemd files 0.502 s Health Tedge-Agent
Start tedge-agent 0.103 s Health Tedge-Agent
Start watchdog service 10.19 s Health Tedge-Agent
Check PID of tedge-mapper 0.079 s Health Tedge-Agent
Kill the PID 0.106 s Health Tedge-Agent
Recheck PID of tedge-agent 2.146 s Health Tedge-Agent
Compare PID change 0.001 s Health Tedge-Agent
Stop watchdog service 0.066 s Health Tedge-Agent
Remove entry from service file 0.134 s Health Tedge-Agent
Stop tedge-mapper-az 0.154 s Health Tedge-Mapper-Az
Update the service file 0.11 s Health Tedge-Mapper-Az
Reload systemd files 0.519 s Health Tedge-Mapper-Az
Start tedge-mapper-az 0.127 s Health Tedge-Mapper-Az
Start watchdog service 10.193 s Health Tedge-Mapper-Az
Check PID of tedge-mapper-az 0.121 s Health Tedge-Mapper-Az
Kill the PID 0.08 s Health Tedge-Mapper-Az
Recheck PID of tedge-agent 2.156 s Health Tedge-Mapper-Az
Compare PID change 0.001 s Health Tedge-Mapper-Az
Stop watchdog service 0.081 s Health Tedge-Mapper-Az
Remove entry from service file 0.242 s Health Tedge-Mapper-Az
Stop tedge-mapper-collectd 0.233 s Health Tedge-Mapper-Collectd
Update the service file 0.179 s Health Tedge-Mapper-Collectd
Reload systemd files 0.771 s Health Tedge-Mapper-Collectd
Start tedge-mapper-collectd 0.197 s Health Tedge-Mapper-Collectd
Start watchdog service 10.315 s Health Tedge-Mapper-Collectd
Check PID of tedge-mapper-collectd 0.13 s Health Tedge-Mapper-Collectd
Kill the PID 0.165 s Health Tedge-Mapper-Collectd
Recheck PID of tedge-mapper-collectd 2.2439999999999998 s Health Tedge-Mapper-Collectd
Compare PID change 0.001 s Health Tedge-Mapper-Collectd
Stop watchdog service 0.129 s Health Tedge-Mapper-Collectd
Remove entry from service file 0.077 s Health Tedge-Mapper-Collectd
c8y-log-plugin health status 5.648 s MQTT health endpoints
c8y-configuration-plugin health status 5.658 s MQTT health endpoints
Wrong package name 0.155 s Improve Tedge Apt Plugin Error Messages
Wrong version 0.156 s Improve Tedge Apt Plugin Error Messages
Wrong type 0.375 s Improve Tedge Apt Plugin Error Messages
tedge_connect_test_positive 0.562 s Tedge Connect Test
tedge_connect_test_negative 1.064 s Tedge Connect Test
tedge_connect_test_sm_services 6.795 s Tedge Connect Test
tedge_disconnect_test_sm_services 0.263 s Tedge Connect Test
Install thin-edge.io 12.219 s Call Tedge
call tedge -V 0.293 s Call Tedge
call tedge -h 0.124 s Call Tedge
call tedge -h -V 0.195 s Call Tedge
call tedge help 0.188 s Call Tedge
tedge config list 0.102 s Call Tedge Config List
tedge config list --all 0.112 s Call Tedge Config List
set/unset device.type 0.693 s Call Tedge Config List
set/unset device.key.path 0.769 s Call Tedge Config List
set/unset device.cert.path 0.59 s Call Tedge Config List
set/unset c8y.root.cert.path 0.504 s Call Tedge Config List
set/unset c8y.smartrest.templates 0.519 s Call Tedge Config List
set/unset az.root.cert.path 0.641 s Call Tedge Config List
set/unset az.mapper.timestamp 0.545 s Call Tedge Config List
set/unset mqtt.bind_address 0.634 s Call Tedge Config List
set/unset mqtt.port 0.679 s Call Tedge Config List
set/unset tmp.path 0.276 s Call Tedge Config List
set/unset logs.path 0.267 s Call Tedge Config List
set/unset run.path 0.429 s Call Tedge Config List
Get Put Delete 3.888 s Http File Transfer Api
Set keys should return value on stdout 0.098 s Tedge Config Get
Unset keys should not return anything on stdout and warnings on stderr 0.285 s Tedge Config Get
Invalid keys should not return anything on stdout and warnings on stderr 0.299 s Tedge Config Get

@didier-wenzek didier-wenzek marked this pull request as ready for review March 3, 2023 10:04
service: &mut impl ServiceProvider<Request, Response, Config>,
config: Config,
) -> Self
fn connected_to(mut self, service: &mut impl ServiceProvider<Request, Response, Config>) -> Self
Copy link
Contributor

Choose a reason for hiding this comment

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

Not a big fan of this name connected_to as it doesn't really convey how it's different from connect_to without looking at the signature/doc. But, then I was wondering if we really need both connect_to and connected_to? How about just keeping this one that returns the Self? A user can choose to ignore the retuned Self when he's not interested in chaining it any further.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not a big fan of this name connected_to as it doesn't really convey how it's different from connect_to without looking at the signature/doc.

Would with_connection help you to grasp from the name that this creates a new enhanced builder?

Similarly, connect_to can be renamed set_connection to stress this mutates the builder.

But, then I was wondering if we really need both connect_to and connected_to? How about just keeping this one that returns the Self? A user can choose to ignore the returned Self when he's not interested in chaining it any further.

This can work by taking a &mut self and returning the same &mut self once mutated.

Copy link
Contributor

Choose a reason for hiding this comment

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

If we're sticking with both the methods, then I'd prefer the with_xxx variant as it gives that "builder" feel to it.

Comment on lines +314 to +303
pub struct Concurrent;
pub struct Sequential;
Copy link
Contributor

Choose a reason for hiding this comment

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

Wondering if the actor builder code would have been simpler if these structs were variants of an enum instead. I didn't really get the benefits in having them as separate concrete types.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Enum variants are runtime things. Here, one needs to distinguish at compile time the kind of actor to build. One could also use explicit type annotations when calling the build() method - but that's not so easy as this method is used by the runtime which has no insight on which variant to build. Another option could be to use phantom types, however using these tag types seems more natural (at least to my eyes).

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, I kinda got the feeling that you wanted something static at compile time. And yes, I'd also prefer these explicit types rather than Phantom types to keep things simple.

pub fn new(server: S, config: &ServerConfig, kind: K) -> Self {
let service_name = server.name().to_string();
let box_builder = ServerMessageBoxBuilder::new(&service_name, config.capacity)
.with_max_concurrency(config.max_concurrency);
Copy link
Contributor

Choose a reason for hiding this comment

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

Building a concurrent message box irrespective of what the kind is?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is just a number at this stage. We can consider to attach this number to theConcurrent kind.

mqtt_config: mqtt_channel::Config,
) -> ServerActorBuilder<C8YJwtRetriever, Sequential> {
let server = C8YJwtRetriever { mqtt_config };
ServerActorBuilder::new(server, &ServerConfig::default(), Sequential)
Copy link
Contributor

Choose a reason for hiding this comment

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

The defaultServerConfig is "concurrent". Didn't you want to override that here for a Sequential actor?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The ServerConfig is neither concurrent nor sequential. It just provides numbers to be used by a concrete implementation.

I agree that this is misleading. One option to fix that is to add the kind (i.e. Sequential or Concurrent) to ServerConfig. I will try to do that.

@@ -15,8 +15,8 @@ async fn get_over_https() {
}

async fn spawn_http_actor() -> ClientMessageBox<HttpRequest, HttpResult> {
let mut builder = HttpActorBuilder::new().unwrap();
let handle = ClientMessageBox::new("Tester", &mut builder.box_builder);
let mut builder = HttpActor::new().builder();
Copy link
Contributor

Choose a reason for hiding this comment

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

That looks very unusual, creating a builder out of a concrete instance, as builders are typically used to build the instance. But here, it's the reverse. Something like HttpActor::builder() would have been more natural.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agree - even if I don't read the same way as you (getting a builder from an instance). Will see why I did that and how this can be changed.

So a ServiceConsumer can be connected to a ServiceProvider
without any knowledge of the config used by the former to connect the
latter.

Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
This function was too specialized and only usefull for testing.
Now tests will have to build message boxes exactly the same way as done
using actors. This adds a bit of burden on testing but reduce the number
of concepts as well as the gap between test and production code.

Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
The actor builders of server actors
(e.g HttpActor, JwtRetriever, ScriptActor)
share the same pattern. This pattern is now implemented as a type:
ServerActorBuilder that handles both sequential and concurrent servers.

Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
In order to stick with builder usual terms, the following methods have
been renamed:

- `ServiceConsumer::connected_to` as `ServiceConsumer::with_connection`
- `ServiceConsumer::connect_to` as `ServiceConsumer::set_connection`
- `ServiceProvider::connect_with` as `ServiceProvider::add_peer`

Futhermore, the type signature of `ServiceConsumer::set_connection`
has been changed to return a `&mut Self` allowing methods calls to be
chained.

For now, both methods `set_connection` and `with_connection` are kept.
One might consider to deprecate one - as their usage are close.

Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
@didier-wenzek didier-wenzek temporarily deployed to Test Pull Request March 7, 2023 10:00 — with GitHub Actions Inactive
Copy link
Contributor

@albinsuresh albinsuresh left a comment

Choose a reason for hiding this comment

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

The outstanding comments can be addressed in a follow-up PR as they are just marginal improvements.

@didier-wenzek didier-wenzek merged commit 2ee912f into thin-edge:main Mar 7, 2023
@didier-wenzek didier-wenzek deleted the refactor/simplify-builders branch March 7, 2023 14:09
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.

None yet

2 participants