# Service Discovery ## Summary and problem description Service discovery over IPC channel e.g. message queue or UNIX domain socket is not performant since larger data is transferred, which can lead to transmission of several frames. If lots of services are discovered at high-frequency e.g. at startup the IPC channel can become a bottleneck. ### Status quo in iceoryx v1.0 (Almond) #### Runtime * `cxx::expected PoshRuntime::findService(capro::ServiceDescription)` * Sends message over IPC channel #### RouDi * `runtime::IpcMessage {ProcessManager,PortManager}::findService(capro::ServiceDescription)` * Called after RouDi has received a request from the runtime * Sends back answer to `PoshRuntime` over IPC channel ## Requirements * The user interface shall have a poll and a push notification interface. * Request/Response and Publish/Subscribe topics shall be discoverable and distinguishable by the user. * It must be guaranteed that the user cannot compromise the service discovery * By acquiring memory chunks which are required for the service discovery communication * By sending false service discovery information on a user created service * A C interface shall be available * The memory overhead should be minimal to enable this feature in a wide range of environments * The performance shall be optimised for a default use case where most services are created on startup and changes in the service discovery are minimal during runtime. * It shall be possible that every application uses service discovery concurrently without compromising the system. ### Extended optional requirements for the future * The discovery shall inform the user if a given topic is usable for them. E.g. provide information about * The history size * The communication strategy, blocking vs. non blocking. * Number of publishers * Access rights of the shared memory segment on which the topic is offered * Number of subscribers, are to much already subscribed * The type of memory segment used by the topic. Interesting when devices like GPUs come into play. * Connect the service discovery via gateways so that a user can lookup * Services of a remote iceoryx instance connected via the dds gateway * Services of a different protocol like SomeIP or DDS * The user requires the ability to distinguish between local and remote iceoryx instance or protocol type ### Static discovery Design Draft * Static discovery shall be supported * On startup a config file is read by RouDi which contains * All topics (publish/subscribe) * All services (request/response) * The shared memory management segment is preconfigured by RouDi and contains already all hard wired publisher-, subscriber-, server- and client-ports and the application acquires those pre-created and pre-connected ports. * Create abstract interface for `ServiceRegistry` * Add new class to `StaticServiceRegistry::StaticServiceRegistry(std::map map)` * Notification-based callback is only called once and and the user gets the complete service registry * Polling with `FindService()` will always return the same result * Alternative would be a `finalize()` method after init phase ##### Alternative D: Discovery Class + Listener Create a new publisher in RouDi which sends a `ServiceRegistryTopic`. This publisher would be used both to signal a change in the service registry and to transmit the service discovery registry. The complete old service registry (saved locally) would be compared to the new service registry in a new class, extending the public user API. ![overview diagram](../website/images/overview-alternative-d.svg) ![sequence diagram](../website/images/sequence-diagram-alternative-d.svg) Pro: * Simple and consistent user API for both event-based and sychronous requests * Filtering for `findService` could be done inside the new class * No need for a change counter as ABA problem solved by POSH mechanisms * Not just delta of service registry is transmitted, but complete service registry info * Fast data transport * Close to DDS mechanism * Could replace `InterfacePort`s Con: * New publisher in RouDi needed * Memory consumption is high through to traditional pub/sub Note: * To avoid any out of memory issues with custom user-configured mempools or access rights problems, the publisher shall write into the `iceoryx_managment` segment (same applies for the introspection publishers) ##### Alternative E: Introduce new StatusPort/ConfigPort/BroadcastPort Traditional publishers and subscribers are not optimised for sending data that does not change very often or never (e.g. parameter, configurations or introspection data). In case the traditional publishers and subscribers are re-used as `StatusPort`s like in alternative D the memory consumption is high and only needed rarely in certain situations (like startup). For that reason a new class of ports called `StatusPort` shall be introduced. Internally only two memory chunks shall be used similar to `iox::concurrent::TACO` using a ABA counter to check if the writer has written while the reading was reading (Frankenstein object). The reader shall copy the `ServiceRegistryTopic` to a local copy from shared memory. Only trivially copyable data shall be supported. Client data shall not be accessed directly, but through a lambda. Pro: * `StatusPorts` can be used to communicate between two safety domains (e.g. ASIL-B and ASIL-D) as the subscriber would only need read access and not write access * Re-use the `StatusPort` for introspection 2.0, heartbeat/keepalive and to send static configurations * Fast data transport by using shared memory * Could replace `InterfacePort`s * Not just delta of service registry is transmitted, but complete service registry info * Simple and consistent user API for both event-based and sychronous requests * Filtering for `findService` could be done inside the new class Con: * New class, which needs RouDi infrastructure integration (e.g. requesting this port) * Copy is necessary * Starvation possible if the writer updates too often and the reader never finished its read (is a general lock-free problem and should be taken care by good documentation) ## Open issues