Hi everyone,
I've spent this year working on refactors in component manager's routing code, and I'm currently in the middle of reimplementing our handling of service capabilities. During code review with Gary, we realized that my new implementation has subtly different semantics than the prior implementation. I could of course rework things to match the semantics of the old system, but the new semantics seem more flexible in an interesting way, so I'm reaching out to DF to ask if you all have any opinions on what is needed, preferred, or disliked here.
The key difference here comes down to whether or not we'd like consumers of filtered or renamed services to see live updates in the set of available instances from service providers.
When a consumer of a service capability that uses renames/filters tries to open a service capability the prior (and current) implementation will:
- Find all service providing components.
- Wait for each providing component to publish the service directory (e.g. `/svc/fuchsia.example.EchoService`) in its outgoing directory.
- Read the set of instances from each service directory.
- Create a new directory and populate it based on the entries in each service directory, following the given filter/rename rules.
- Return the created directory to the service consumer.
Whereas in my new implementation the flow is slightly different. My unmerged changes will:
- Find all service providing components.
- Wait for each providing component to publish the service directory in its outgoing directory.
- Create a dictionary.
- Set up a watcher on each service directory that will update that dictionary appropriately (following the filter/rename rules) whenever a service instance is added or removed.
- Wait for each watcher to reach idle.
- Return the dictionary to the service consumer (who may consume the dictionary by converting it into a `fuchsia.io.Directory`).
(note that the above steps have been simplified for brevity)
In short: the new implementation means that components that consume filtered/renamed services will see the service directory be updated if the instances available to it change.
As an example, imagine we have a service capability that's exposed from component `A` and consumed by component `B`. The service is made available to `B` by an offer with service instance renames from `1` to `2` and `3` to `4`. If `B` opens the service using `
fuchsia_component::client::open_service`, and `A` publishes `1` when it starts, then `B` will immediately a service instance named `2`. If `A` at some point in the future then publishes service instance `3`, `B` will then see both service instances `2` and `4` in the same directory channel that it originally opened. If `A` then stops running, `B` will see the same directory channel has no entries. Before my changes, `B` would need to re-open the service (thereby establishing a new `fuchsia.io.Directory` channel) to observe the new instance `4`, and would not be able to observe `A` stopping (as re-opening the service would cause `A` to start again).
I personally like the new semantics, as it more accurately tracks the state of the world and perhaps opens some novel use cases around relying on using a VFS watcher to observe filtered or renamed service instances coming and going, and the live-updating semantics better match the semantics for anonymized service aggregates, but services have a lot of subtle behavior and we want to be very careful to not accidentally change semantics that DF relies on or are planning on relying on.
Sorry for the lengthy email! Does anyone on DF have an opinion or interesting insight on this?
Thanks,
Claire