Skip to content

Commit

Permalink
Features spec: implement only emitting a leave if there was an existi…
Browse files Browse the repository at this point in the history
…ng member

per #211

The actual behaviour change here is small (only whether non-sync leaves
where there is no matching member currently present should emit a leave
event), but I found the current structure of this spec section quite
confusing (why are broadcast and members-map add/remove requirements
defined separately, despite having the same requirements?), so ended up
rewriting it. (Without deprecating the old spec items since this isn't
actually a behaviour change except in that one case)
  • Loading branch information
SimonWoolf committed Nov 13, 2024
1 parent 596ab64 commit 6873208
Show file tree
Hide file tree
Showing 2 changed files with 13 additions and 6 deletions.
2 changes: 1 addition & 1 deletion meta.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
versions:
# Must conform to Semantic Versioning (https://semver.org/).
# Should never need to use a pre-release suffix.
specification: 3.0.0
specification: 3.0.1

# Must be an Integer value.
# Previously, prior to version 2 (i.e. versions 0.8, 1.0, 1.1 and 1.2) it was a Decimal value.
Expand Down
17 changes: 12 additions & 5 deletions textile/features.textile
Original file line number Diff line number Diff line change
Expand Up @@ -741,16 +741,23 @@ h3(#realtime-presence). RealtimePresence

* @(RTP1)@ When a channel @ATTACHED@ @ProtocolMessage@ is received, the @ProtocolMessage@ may contain a @HAS_PRESENCE@ bit flag indicating that it will perform a presence sync, see "TR3":#TR3 . (Note that this does not imply that there are definitely members present, only that there may be; the sync may be empty). If the flag is 1, the server will shortly perform a @SYNC@ operation as described in "RTP18":#RTP18 . If that flag is 0 or there is no @flags@ field, the presence map should be considered in sync immediately with no members present on the channel
* @(RTP2)@ A @PresenceMap@ should be used to maintain a list of members present on a channel. Broadly, this is is a map of "memberKeys":#TP3h to presence messages, all with @PRESENT@ actions (during a sync there may also be ones with an @ABSENT@ action, see "RTP2f":#RTP2f).
** @(RTP2a)@ All incoming presence messages must be compared for newness with the matching member already in the @PresenceMap@, if one exists, where "matching" means they share the same @memberKey@ (or equivalently, they share both @connectionId@ and @clientId@)
** @(RTP2a)@ An incoming presence message is defined to "satisfy the newness check" if either there is no matching message already present in the @PresenceMap@ (where "matching" means they share the same @memberKey@; equivalently, they share both @connectionId@ and @clientId@); or there is a matching message, but the incoming event is @RTP2b@-newer than the existing one
** @(RTP2b)@ To compare for newness:
*** @(RTP2b1)@ If either presence message has a @connectionId@ which is not an initial substring of its @id@, compare them by @timestamp@ numerically. (This will be the case when one of them is a 'synthesized leave' event sent by realtime to indicate a connection disconnected unexpectedly 15s ago. Such messages will have an @id@ that does not correspond to its @connectionId@, as it wasn't actually published by that connection)
**** @(RTP2b1a)@ If the timestamps compare equal, the newly-incoming message is considered newer than the existing one
*** @(RTP2b2)@ Else split the @id@ of both presence messages (which will be of the form @connid:msgSerial:index@, e.g. @aaaaaa:0:0@) on the separator @:@, and parse the latter two as integers. Compare them first by @msgSerial@ numerically, then (if @msgSerial@ is equal) by @index@ numerically, larger being newer in both cases
** @(RTP2c)@ As there are no guarantees that during a @SYNC@ operation presence events will arrive in order, all presence messages from a @SYNC@ must also be compared for newness in the same way as they would from a @PRESENCE@
** @(RTP2d)@ When a presence message with an action of @ENTER@, @UPDATE@, or @PRESENT@ arrives, it should be added to the presence map with the action set to @PRESENT@
** @(RTP2e)@ If a @SYNC@ is not in progress, then when a presence message with an action of @LEAVE@ arrives, that @memberKey@ should be deleted from the presence map, if present
** @(RTP2f)@ If a @SYNC@ is in progress, then when a presence message with an action of @LEAVE@ arrives, it should be stored in the presence map with the action set to @ABSENT@. When the @SYNC@ completes, any @ABSENT@ members should be deleted from the presence map. (This is because in a @SYNC@, we might receive a @LEAVE@ before the corresponding @ENTER@).
** @(RTP2g)@ Any incoming presence message that passes the newness check should be emitted on the @RealtimePresence@ object, with an event name set to its original action. Note: this action may not be the same one that it will have when stored in the presence map. For example: an incoming presence message with an @ENTER@ action will be emitted as an @enter@ event, and the emitted presence message will have its action set to @ENTER@. However, it will be stored in the presence map with a @PRESENT@ action.
** @(RTP2d)@ When a presence message with an action of @ENTER@, @UPDATE@, or @PRESENT@ arrives, if and only if it satisfies the @RTP2a@ newness check:
*** @(RTP2d1)@ It must be emitted to @RealtimePresence@ subscribers (with its original presence action, and an event name set to the stringified form of that action)
*** @(RTP2d2)@ It must be added to the presence map with the action set to @PRESENT@
** @(RTP2h)@ When a presence message with an action of @LEAVE@ arrives, if and only if there is a member with a matching @memberKey@ currently in the presence map and the incoming event satisfies the @RTP2b@ newness check against that member:
*** @(RTP2h1)@ If a @SYNC@ is not in progress:
**** @(RTP2h1a)@ The incoming message must be emitted to @RealtimePresence@ subscribers (with an event name set to the stringified form of the @LEAVE@ action)
**** @(RTP2h1b)@ That member must be deleted from the presence map
*** @(RTP2h2)@ If a @SYNC@ is in progress:
**** @(RTP2h2a)@ The incoming message must be stored in the presence map with the action set to @ABSENT@
**** @(RTP2h2b)@ When the @SYNC@ completes, then all @ABSENT@ members in the presence map must be deleted. (No leave events should be emitted other than those required by @RTP19@)
** @(RTP2g)@ This clause has been replaced by @RTP2d1@, @RTP2d2@, @RTP2e1@, @RTP2e2@, @RTP2f1@, and @RTP2f2@.
* @(RTP18)@ The realtime system reserves the right to initiate a sync of the presence members at any point once a channel is attached. A server initiated sync provides Ably with a means to send a complete list of members present on the channel at any point
** @(RTP18a)@ The client library determines that a new sync has started whenever a @SYNC@ @ProtocolMessage@ is received with a @channel@ attribute and a new sync sequence identifier in the @channelSerial@ attribute. The @channelSerial@ is used as the sync cursor and is a two-part identifier @<sync sequence id>:<cursor value>@. If a new sequence identifier is sent from Ably, then the client library must consider that to be the start of a new sync sequence and any previous in-flight sync should be discarded
** @(RTP18b)@ The sync operation for that sequence identifier has completed once the cursor is empty; that is, when the @channelSerial@ looks like @<sync sequence id>:@
Expand Down

0 comments on commit 6873208

Please sign in to comment.