OASIS Mailing List ArchivesView the OASIS mailing list archive below
or browse/search using MarkMail.

 


Help: OASIS Mailing Lists Help | MarkMail Help

mqtt message

[Date Prev] | [Thread Prev] | [Thread Next] | [Date Next] -- [Date Index] | [Thread Index] | [List Home]


Subject: [OASIS Issue Tracker] (MQTT-234) Shared Subscriptions


    [ https://issues.oasis-open.org/browse/MQTT-234?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=62467#comment-62467 ] 

Peter Niblett commented on MQTT-234:
------------------------------------

Summary of the discussion at the April 2016 face/face meeting

1. Use case and requirements

To allow multiple consumer instances (which we should assume could be running on separate processors or servers) to share the load of processing incoming messages. The simplest case to consider is one where the messages are all published to the same MQTT topic and you want to have multiple consumer instances subscribed to that topic, but without having duplicate copies of each message being sent to each consumer.  There's an extension to this where the consumer instances use a wild-carded subscription to receive messages published to multiple topics.  Again the requirement is to allow multiple consumer instances without having them each receive all the incoming messages.  

It must be possible to allow the number of consumer instances to grow and shrink over time, without the publishers having to be aware of this. In particular this provides a level of fault-tolerance, as if 1 of n consumer fails there are still n-1 left to process the messages.

Maintaining message order is not a key requirement, but it's something that an MQTT server might want to offer.

There is the possibility that the set of consumer instances might want to share the load for one topic, but also each get a copy of every message for another topic. This isn't as contrived as it might sound: you could have one topic that distributes work (these messages should be delivered to one and only one consumer instance) and another that sends configuration parameters or sends commands that all the instances need to see.

To simplify things we agreed that "consumer instance" can be represented by an MQTT Session, in other words each instance must make its own distinct MQTT connection and if a piece of application code makes multiple MQTT connections then we view that as multiple "consumer instances". 

Note that, within a logical MQTT server (which may itself be distributed across several physical servers), every Session has a unique id which is referred to as a "clientID". In cases where cleanSession=false this is always assigned by the client itself, in cases where cleanSession=true it can be assigned either by the client or the server. 


2. Model

We discussed a number of ways in which this could be achieved, building on what we have today and ended up in effect validating the "shared subscription" approach that has been followed in this JIRA. I will start with a description of  the non-shared subscription support in 3.1.1

a) Non-shared subscriptions

Each Session can have zero or more subscriptions associated with it.  Each Subscription includes a Topic Filter, indicating the topic(s) for which messages are to be delivered on that Session, and a max QoS level. The Subscription is responsible for collecting messages that match the filter and transmitting them on the Session's MQTT connection (according to the appropriate QoS)  if and when that connection is active.
The Session cannot have more than one subscription with the same Topic Filter, so the Topic Filter can be used as a key to identify the subscription within that Session.  In other words every subscription within the MQTT server can be uniquely identified by the combination of clientID and Topic Filter
The 3.1.1 standard allows a Session to have two or more subscriptions whose Topic Filters overlap. It requires the Server to deliver at least one copy of the message in this case, but permits it to deliver more than one.  This is discussed in mqtt-58 and mqtt-217
The Session owns its subscriptions (in UML terms it's a 1 -> * composition relationship). Subscriptions are created by performing a subscribe against the session and are deleted by unsubscribe. They are are also all deleted when the owning Session ends  (recall that if cleanSession=true this includes any time when the MQTT connection terminates). 

b) Shared Subscriptions

The proposal is to add a new kind of Subscription, which we term a "Shared Subscription". As with the non-shared case, it has a Topic Filter and a Max QoS, and is responsible for collecting and delivering messages. However it differs from a non-shared subscription in the following ways:

A shared subscription is owned by the MQTT Server, and not by any one Session.  Instead of being identified by clientID+Topic Filter it is uniquely identified by the combination of a "ShareName" +  Topic Filter.
A shared subscription is associated with one or more MQTT sessions 
An MQTT session can be associated with zero or more shared subscriptions. 
When the server receives a request to subscribe to a shared subscription, it checks to see if the subscription already exists or not. If it doesn't it creates one. It both cases it then associates the subscribing Session with that subscription. 
The association between an MQTT Session and a shared subscription is broken by Unsubscribe. The association also, obviously, ends when a Session disappears.
When the count of MQTT Sessions associated with a shared subscription drops to zero, that subscription is automatically destroyed and thus stops accumulating any new messages.
A shared subscription delivers each message that matches its Topic Filter via one of its associated sessions. The specification does not dictate how it chooses which one to pick. The requirement to send deliver each message via only one Session is relaxed in the case of QoS 1, but only in cases where a failure occurs. 


Note that this lets you have a single cleanSession=false Session which you use to create and maintain the shared subscription, and to make sure that it never gets garbage-collected, while the Session(s) that actually receive the messages all use cleanSession = true.

3. Syntax options

The mechanism described above requires a ShareName.   We agreed that this will be asserted by the client applications, a bit like clientID used to be in 3.1.  There's no mechanism for getting the server to assign one.

The client needs a way to pass the ShareName. For example, let's suppose that it wants to use share name Foo and subscribe with a topic filter /Bar.  There are three options

i) It could supply it on Connect, and the name would then qualify all Subscribes and Unsubscribes that it issues. In our example it would pass sharename Foo on Connect and then subscribe to /Bar in the normal way. The server would have remembered the sharename Foo from the Connect and use it when it sees the subscribe.  We rejected this approach because we want to allow cases where the clients want to use multiple shared subscriptions with different ShareNames, or wish to use a mixture of shared and non-shared (technically this could be done using cleanSession=false and reconnecting multiple times, but that's ugly).

ii) Add the ShareName as a new parameter on Subscribe and Unsubscribe. A Subscribe request to /Bar without the ShareName would be treated as a non-shared subscription

iii) Devise a syntax to represent the sharename as part of the Topic Filter parameter of Subscribe/Unsubscribe. In effect this is repurposing the Topic Filter to be the Subscription Identifier (that's what it really is on Unsubscribe anyway). 

Here are some suboptions for iii)

a) Use one of the spare bits in byte 1 to indicate that this is a Shared version of Subscribe/Unsubscribe.   Change the format of the TopicFilter (or whatever we decide to call it) to be sharename:topic filter , where : is a separator character that we forbid use of in the sharename (we could use a different separator if people prefer).   Our example would then be  Foo:/Bar.  The separator isn't strictly necessary if we forbid / in the sharename, but I think it makes things less confusing to have it as otherwise the string looks too much like a conventional topic filter.

b) Don't use the spare bit, but start the Topic filter with the sequence $$, e.g. $$Foo:/Bar.  This approach would take away the ability of an application to do a non-shared subscription to a topic that starts $$ but my assumption here is that nobody today is likely to be using topics that start $$

c) If we don't want to take that risk, then we could use a trigger sequence that is completely illegal today, such as ++ or ##, e.g. ++Foo:/Bar

4. Mixed subscriptions

In most cases we would expect the multiple consumer instances to be clones of each other  - i.e. they all Connect with the same parameters (with the exception of the clientId) and they all make the same shared subscriptions.  However the approach we chose to use in 3 allows a shared subscription to be be shared between dissimilar consumers, so we need to be clear about what happens in this case...

i) Mixed cleanSession true/false (some consumers connect with cleanSession=true, others connect with it false. I don't see this as being a real issue.  The contract between the Shared Subscription and its associated Sessions is independent of the cleanSession flag. That just controls the lifetime of the Sessions. If we've started to deliver a QoS 2 message to a cleanSession=true session and that crashes while the message is in flight, it just gets lost.

ii) Mixed maxQoS on subscriptions, e.g. two consumers subscribe to the same Topic with the same sharename, but with different requested MaxQoS parameters. The simplest approach would be to ban this, by saying that the first subscriber gets to set the maxQoS parameter for the shared subscription, and that subsequent subscribers must specify a MaxQos at least as high as the first subscriber. If they do then they receive the MaxQoS set by the first subscriber in their granted QoS. If they specify a lower MaxQoS their subscription is rejected with an error. 

5. Other details

i) Overlapping subscriptions.  Because the shared sessions are global I think the only sensible approach is for each message to be filtered independently by each shared subscription. So if I have two shared subscriptions ++Foo:/Bar and ++Foo:/# then they both get a copy of a message published on Bar, and each distributes it to its respective member sessions. Similarly if a Session has a non-shared subscription to /Bar or /# that shouldn't interfere with these shared subscriptions (even if they were associated with that Session).

ii) QoS 1. I think it makes sense to allow a server to redeliver a unacked QoS 1 message to another member of the shared subscription if it detects that it has lost connection to the original intended recipient. We probably wouldn't want to require this

iii) Nacks.  This is a case where the hard/soft error thing might be useful.  In the soft error case it might be handy to attempt to redeliver to another recipient, but we'd want to avoid the situation where the server sends it one at at time out to everyone only to get them nacked each time.  Again this might be a case for giving the server some latitude.

iv) Message ordering.  It would be nice if we could allow the server to assign messages to Sessions based on an order preservation scheme.  What that means here is that we should permit the server to choose to hang on to a message if its preferred recipient happens to off line, and not force it to send to one of the other Sessions just because they have an active connection.

v) Retained messages. These should only be sent to one of the consumers, and since they are supposed to be sent before any other messages the only sensible thing is to send them to the initial subscriber

> Shared Subscriptions
> --------------------
>
>                 Key: MQTT-234
>                 URL: https://issues.oasis-open.org/browse/MQTT-234
>             Project: OASIS Message Queuing Telemetry Transport (MQTT) TC
>          Issue Type: New Feature
>          Components: futures
>    Affects Versions: 3.1.1
>            Reporter: Andrew Banks
>            Priority: Critical
>
> Shared Subscriptions
> --------------------
> Shared subscriptions allow a set of clients to share the consumption of messages
> produced in response to a subscription. The set of subscribers is created by the use
> of a common share name. Shared subscriptions provide a facility similar to point to 
> point messaging in that the processing of messages is not limited to the availability 
> or capacity of a single consumer, the share name is analogous to the queue name in 
> that it names the list of messages to be processed.
> I suggest that a shared subscription be defined by the topic filter syntax:
>   $share:shareName:topicFilter
> - This specialised topic filter takes the place of the normal topic filter in the 
>   subscribe packet. 
> - The "$share:" prefix uses a reserved $ name and will therefore not cause a conflict
>   with other topic filters.
> - shareName is a string which labels the shared subscription and must not contain any 
>   ":" characters.
> - TopicFilter follows the existing topic filter rules.
> - Subscribing clients will be a member of the share only if both the shareName and
>   topicFilter are identical. So, a different shareName with the same topicFiler 
>   will create a different share. Similarly a shareName followed by a different topic
>   filter will create separate share.
> - Servers may decline to support shared subscriptions by not accepting subscribe
>   packets containing topic filters starting with the "$share:" prefix. However, if 
>   they do accept the "$share:" prefix they must follow this definition.
> - The subscribing clients may make both non durable and durable subscriptions by 
>   setting clean session true or false. The shared subscription ceases to exist if    
>   the number of clients subscribed to it reaches zero. If the shared subscription
>   ceases to exist the messages queued for processing are deleted by the server as 
>   with an non shared subscription.
> - A good server implementation will avoid allocating messages to subscribers with 
>   durable subscriptions while the client is disconnected. Instead the messages will be 
>   allocated when a suitable client connects. 
> - A Qos=1 message should be reallocated to another client sharing the same subscription
>   if the network connection to the client is lost before the PUBACK is received.
>   Qos=0 and Qos=2 messages must not be reallocated. 



--
This message was sent by Atlassian JIRA
(v6.2.2#6258)


[Date Prev] | [Thread Prev] | [Thread Next] | [Date Next] -- [Date Index] | [Thread Index] | [List Home]