= uSubscription
:toc:
:sectnums:

The key words "*MUST*", "*MUST NOT*", "*REQUIRED*", "*SHALL*", "*SHALL NOT*", "*SHOULD*", "*SHOULD NOT*", "*RECOMMENDED*", "*MAY*", and "*OPTIONAL*" in this document are to be interpreted as described in https://www.rfc-editor.org/info/bcp14[IETF BCP14 (RFC2119 & RFC8174)]

----
SPDX-FileCopyrightText: 2023 Contributors to the Eclipse Foundation

See the NOTICE file(s) distributed with this work for additional
information regarding copyright ownership.

This program and the accompanying materials are made available under
the terms of the Apache License Version 2.0 which is available at
https://www.apache.org/licenses/LICENSE-2.0
 
SPDX-FileType: DOCUMENTATION
SPDX-License-Identifier: Apache-2.0
----


Publisher & Subscribers Architecture pattern allows subscribers to subscribe to topics published by publisher that can be running on the same device and/or different devices who's connection to each other could even be temporal. 

Off the shelf broker and broker-less solutions, rely on specific Internet protocols that do not scale for automotive use cases nor do they support producers and consumers that talk different pub/sub protocols (ex. MQTT, DDS, SOME/IP, proprietary, etc...).

uSubscriptions purpose is to explain _how_ the distributed publisher-subscriber architecture pattern can be implemented through a ubiquitous language and communication protocol across the distributed devices. We will define the interfaces, sequences, and state machines to be able to support the use cases mentioned above.


NOTE: uSubscription interface is declared in link:../../../up-core-api/uprotocol/core/usubscription/v3/usubscription.proto[usubscription.proto] and follows link:../../../basics/error_model.adoc[uProtocol Error Model] for return codes.


== Topics

A topic describes what the calling applications (subscribers) wants to subscribe too. Topic are expressed in link:../../../basics/README.adoc#_uprotocol_uri[uProtocol URIs] format. Topics used in uSubscription APIs have the additional requirements of:

* *MUST* contain `UEntity` and `UResource` information
* `UE_VERSION` *MUST* contain the `MAJOR`, *MUST NOT* contain the `MINOR` or `PATCH`

NOTE: Only the `MAJOR` is used in topics to support backwards compatibility between minor version updates of uEs to avoid the need to re-subscribe to topics when the uE `MAJOR` was not updated.  Subscribers *MUST* re-subscribe to topics when the uE `MAJOR` version has changed

Example: `/body.access/1/door.front_left#Door`


== Subscription States

In the following section we will elaborate on the states that a subscription can transition in and out from based on the various APIs called by producers and consumers. 

NOTE: `SUBSCRIBE_PENDING` and `UNSUBSCRIBE_PENDING` states only apply to remote topic subscriptions, more details shall be provided below.

.Subscription State Machine
[width="100%",cols="17%,20%,19%,26%,18%",options="header",]
|===
|State |Description |Entry |Do |Exit

| `*UNSUBSCRIBED*`
|Subscriber uE is not subscribed but the topic exists
a|* Subscriber unsubscribed
|
|Subscribe() is called by a consumer

| `*SUBSCRIBED*`
|Subscriber uE is subscribed to the topic
|Subscription request has been processed and accepted
|
|* Subscriber has called Unsubscribe()

|`*SUBSCRIBE_PENDING*`
|Subscription is pending acknowledgement from the remote SubscriptionService
|1st Subscriber uE has called Subscribe() to a remote topic
|Forwards the subscription request to the destination device's SubscriptionService
|Received a response from the remote SubscriptionService

| `*UNSUBSCRIBE_PENDING*`
|Unsubscribe is pending processing by the producers remote uBus
|Last subscriber called Unsubscribe()
a|* Send an unsubscribe request to the remote uBus
* return link:../../../../up-core-api/uprotocol/ustatus.proto[UStatus] with `UCode.OK` and `SubscriptionStatus`
|
|===

.Subscription State Machine
image::subscription_sm.drawio.svg[State Machine]


== Subscription Change Notifications

The uSubscription service notifies observers when there is a change in a subscription states by publishing notification events to the topic `/core.usubscription/3/subscriptions#Update`.  Below are the specific nuances about the subscription change notification based on the observer type.

===  Subscribers

Subscribers are automatically registered to receive subscription change notifications for topics they have subscribed too through the Subscribe() API.

* Subscribers *MUST NOT* call the `RegisterForNotifications(`) as they are not permitted to receive notifications for subscription changes by other subscribers
* Subscription change notification *MUST* be sent whenever the `SubscriptionState` changes for said subscriber (ex, `SUBSCRIBE_PENDING` → `SUBSCRIBED`)

=== Publishers

* *MUST* *NOT* be permitted to call `RegisterForNotifications()` for topics they do not produce, i.e. the uEntity sending the request is not equal to the `UEntity` of the request message's `UUri` parameter.
* Subscription change notifications *MUST* be sent for changes to `SubscriptionState` for any subscriber that is subscribed to the topic

=== Dispatchers

Dispatchers are also permitted to register for subscription change notifications to facilitate the multicasting of messages. The mechanics and requirements of the dispatcher and uSubscription communication are platform deployment specific. 

== Event Delivery

The uSubscription service also allows to indicate to subscribers how they shall consume published events by means of the `EventDeliveryConfig` returned in the `SubscriptionResponse` message. For example, if the subscriber has to consume from a different topic or from different messaging infrastructure, this message will store the corresponding delivery semantics.

NOTE: Delivery semantics (if any) are deployment specific and not covered in this specification

== uSubscription Sequences

In the following section, we will elaborate on the various subscription flows for local and remote topics. When a consumer subscribes to a remote topic, it is the responsibility of the (local) uSubscription service to relay the subscription request to the remote uSubscription service as can be seen in the sequence diagrams below.

NOTE: Throughout this section we will use the sample topic `//Device1/uexample/1/resource#Event` to illustrate the various sequences. The above-mentioned topic will be replaced with `_topic_` in the diagrams



=== Subscription

Subscription flow will show how a subscriber can subscribe to the example topic when uApp is on the same device (local subscriptions) or remote device (remote subscriptions).

==== Within a uDevice

.Local Subscription Flow
image::local_subscribe.svg[Local Subscription Flow]

==== Between uDevices

.Remote Subscription Flow
image::remote_subscription.svg[Remote Subscription Flow]

* uSubscription *MUST* change the subscriber to itself (core.usubscription) when subscribing to remote topics, this allows the reverse flow (publication) to be properly multicasted to local subscribers by the local disaptcher (ex. uBus) when it queries the local uSubscription for a list of local subscribers 

=== Unsubscribe

==== Within a uDevice

.Local Unsubscribe Flow
image::unsub_local.svg[Unsubscribe Local Flow]

==== Between uDevices

.Remote Unsubscribe Flow
image::unsub_remote.svg[Unsubscribe Remote Flow]

* uSubscription *MUST* change the subscriber to itself (core.usubscription) when unsubscribing to remote topics 


== Timeout & Retry Logic

Subscribe (and unsubscribe) to remote topics are handled by RPC calls between uSubscription services running on the different devices. Given that devices are not always connected to each other, the onus is on uSubscription service to ensure that a command is received in time. Below are the common retry and timeout policies for USubscription service implementations to follow:   

* Remote requests *MUST* have a maximum timeout of 5 minutes
* All timed-out remote commands *MUST* be retied indefinitely until the business logic behind it no longer requires the command to be sent. (ex. the subscriber cals `Unsubscribe()` ) 
* Remote commands *MUST* be retried upon device to device connectivity (link up) and *MUST NOT* be tried when there is no device connectivity (link down)
