Specification

Scopes

Scopes designate channel instances on the unified bus. Channel instances are themselves hierarchical, hence scopes also reflect this structure.

There is a string-based notation for scopes: /parent/sub/subsubscope/. A scope is valid if it matches the regular expression: /([a-zA-Z0-9]+/)*.

Events

Conceptually and when represented in programs, RSB events consist of the following components:

Name Type Comment Required
sequence number 32-bit unsigned integer Assigned by informer yes
id UUID Unique Id of the event lazily derived
scope Scope object Destination scope yes
method ASCII string   no
data type     no?
event payload Domain-specific object   no
meta-data (see below) see below no
cause vector set of EventIds see below no

The id component of an event is derived from the sequence number of the event and the id of the participant that sent the event as follows:

  1. Construct a string representation of the sequence number
    • Radix 16
    • Lower-case letters
    • Zero-padding to width 8
  2. Use the UUID v.5 algorithm to construct the event id
    • namespace: id of the sending participant
    • name: the string constructed above

Examples / Test Cases:

sequence number        0
sender id              D8FBFEF4-4EB0-4C89-9716-C425DED3C527

sequence number string "00000000"

event id               v5-uuid(D8FBFEF4-4EB0-4C89-9716-C425DED3C527, "00000000")
=> 84F43861-433F-5253-AFBB-A613A5E04D71
sequence number        378
sender id              BF948D47-618F-4B04-AAC5-0AB5A1A79267

sequence number string "0000017a"

event id               v5-uuid(BF948D47-618F-4B04-AAC5-0AB5A1A79267, "0000017a")
=> BD27BE7D-87DE-5336-BECA-44FC60DE46A0

Meta Data

Events carry a set of meta data with themselves. Some meta data items are specified and processed by RSB while others are user-defined and not interpreted by RSB.

All timestamps are expressed in Coordinated Universal Time (UTC) and stored with microsecond precision (even if the clock source used by RSB cannot actually provide microsecond precision).

The current set of specified, required meta data items is given below:

sender id

ID (a UUID) of the sending participant.

create time

A timestamp that is automatically filled with the time the event object was in the client program. This should usually reflect the time at which the notified condition most likely occurred in the sender. If event objects are reused, this timestamp has to be reset manually by the client.

send time

The time at which the generated notification for an event was sent on the bus (after serialization).

receive time

The time at which an event is received by a listener in its encoded form.

deliver time

The time at which an event was decoded and will be dispatched to the client as soon as possible (set directly before passing it to the client handler).

The following meta data items are user-defined:

user times

A set of user-defined keys and associated timestamps

user infos

A set of key-value user-defined options with string keys and values.

Note

create time, send time and user times are computed using the clock source of the sending process, whereas receive time and deliver time are filled using the clock source of receiving participant’s process.

Cause Vector

Each event can have a set of causing event ids (the idea is based on [Luckham2001PEI]). The user who sends an event needs to insert the respective event ids manually if required.

Currently, there is no specification regarding how these ids shall be used. Especially the handling of questions related to transitivity has not yet been solved.

URIs

Generic URIs

Syntax:

rsb:[PATH][#FRAGMENT]

Components of the URL are interpreted as follows:

This may resolve to:

  • Service and/or Participant

    • If there is only one of these entities this is enough for resolving it

    • If multiple entities reside on the scope, a single instance can be selected using their ID:

      rsb:/hierarchical/service/definition/further/to/participant#UniqueIDOfParticipant[UUID]
  • Nothing

These generic URIs require a global naming service.

Transport-specific URLs

Syntax:

[SCHEME:][//HOST][:PORT][PATH][?QUERY][#FRAGMENT]
transport://<location.transport.specific[:PORT]>/hierarchical/service/definition/further/to/participant

Components of the URL are interpreted as follows:

  • SCHEME -> transport name (e.g spread)
  • HOST -> Transport-specific “host” option (e.g. host that runs the daemon for Spread transport)
  • PORT -> Transport-specific “port” option (e.g. port on which daemon listens for Spread transport)
  • PATH -> A scope which designates one of the following things
  • QUERY -> “freestyle” transport-specific options
  • FRAGMENT ->

Examples

The following examples demonstrate generic URIs:

rsb:
The channel designated by the scope /.
rsb:/
The channel designated by the scope /.
rsb:/foo/bar
The channel designated by the scope /foo/bar.
rsb:/foo/bar#10838319-09A4-4D15-BD59-5E054CDB4403
The participant with ID 10838319-09A4-4D15-BD59-5E054CDB4403.

The following example demonstrate how to specify bus connections when creating participants:

`` ``
Participate in channel with scope / using the default transport configuration.
spread:
Participate in channel with scope / using the Spread transport with its default configuration.
inprocess:
Participate in channel with scope / using the in-process transport with its default configuration.
spread://localhost:5555
Participate in channel with scope / via the Spread daemon running on localhost and listening on port 5555.
inprocess://someotherhost
Syntactically correct, but does not make sense.
spread:/foo/bar
Participate in channel with scope /foo/bar using the default transport configuration.
spread:?maxfragmentsize=10000
Participate in channel with scope / using the Spread transport with default host and port and a maximum event fragment size of 10000 bytes.
spread:?maxfragmentsize=10000&tcpnodelay=yes
Likewise, but in addition with tcpnodelay option set to yes.

Implementations

Language File(s)
C++ not yet implemented
Java not yet implemented
Python not yet implemented
Common Lisp “0.7” branch of https://code.cor-lab.org/git/rsb.git./cl/cl-rsb/src/uris.lisp

Spread Transport

TCP-Socket-based Transport

The TCP-socket-based transport layers a very simple protocol on top of ordinary TCP sockets:

  • Processes act as either TCP-clients or -servers for a common port. There is one server and zero or more clients. Each client-server connection is a bi-direction stream of notification s which are sent and received by participant s in the respective processes.
  • Each process may host zero, one or many participant s. Within a process, the participants share a connection.

The following messages are exchanged:

Name Size [bytes] Content Comment
mzero 4 four 0 bytes only used during handshake
msize 4 size of payload in mpayload little-endian
mpayload variable payload blob size is specified by previous msize

Note

The handshake part of the protocol (explained below) is required to prevent the following scenario from happening:

  1. A client process connects to the TCP-socket of the server (without handshake)
  2. The client process creates a listener waiting for events from some remote participant
  3. The client process causes some remote participant to send an event which the listener should receive
  4. The event is not delivered to the listener since the connection is not yet fully established despite the fact that the listener was established before the event was caused.

Client Perspective

From the client’s perspective, the protocol consist of

  1. connect to the server socket
  2. send mzero
  3. receiving mzero and
  4. concurrently send and receive length-delimited (via msize) notification messages mpayload

digraph client_states {
"new";
"handshake-in-progress";
"closed";
"new" -> "handshake-in-progress" [label="send(m_zero)"];
"handshake-in-progress" -> "established" [label="receive() : m_zero"];
"handshake-in-progress" -> "closed" [label="reset | error"];
"established" -> "closed" [label="reset | error"];

subgraph cluster_established_send {
  label="sending states when in state \"established\""
  "established-send" [label="established"];
  "size-sent";
  "closed-send" [label="closed"];
  "established-send" -> "size-sent" [label="send(m_size)"];
  "size-sent" -> "established-send" [label="send(m_payload)"];
  "established-send" -> "closed-send" [label="reset | error"];
  "size-sent" -> "closed-send" [label="reset | error"];
};

 subgraph cluster_established_receive {
  label="receiving states when in state \"established\""
  "established-receive" [label="established"];
  "size-received";
  "closed-receive" [label="closed"];
  "established-receive" -> "size-received" [label="receive() : m_size"];
  "size-received" -> "established-receive" [label="receive() : m_payload"];
  "established-receive" -> "closed-receive" [label="reset | error"];
  "size-received" -> "closed-receive" [label="reset | error"];
};
}

Server Perspective

The server establishes a listening TCP socket on the configured port. When a connection is accepted, the server continues to accept other connections and concurrently performs the following protocol on the new connection:

  1. accept client connection
  2. receive mzero from the client
  3. send mzero in reply
  4. concurrently send and received notifications using length-delimited encoding via msize and mpayload

digraph server_states {
"new";
"handshake-in-progress";
"established";
"closed";
"new" -> "handshake-in-progress" [label="receive() : m_zero"];
"handshake-in-progress" -> "established" [label="send(m_zero)"];
"handshake-in-progress" -> "closed" [label="reset | error"];
"established" -> "closed" [label="reset | error"];

 subgraph cluster_established_send {
  label="sending states when in state \"established\""
  "established-send" [label="established"];
  "size-sent";
  "closed-send" [label="closed"];
  "established-send" -> "size-sent" [label="send(m_size)"];
  "size-sent" -> "established-send" [label="send(m_payload)"];
  "established-send" -> "closed-send" [label="reset | error"];
  "size-sent" -> "closed-send" [label="reset | error"];
};

 subgraph cluster_established_receive {
  label="receiving states when in state \"established\""
  "established-receive" [label="established"];
  "size-received";
  "closed-receive" [label="closed"];
  "established-receive" -> "size-received" [label="receive() : m_size"];
  "size-received" -> "established-receive" [label="receive() : m_payload"];
  "established-receive" -> "closed-receive" [label="reset | error"];
  "size-received" -> "closed-receive" [label="reset | error"];
};
}

Example

# handshake
C -> S 0x00 0x00 0x00 0x00
S -> C                     0x00 0x00 0x00 0x00 0x00
# established
C -> S 0x23 0x00 0x00 0x00                          # 35-byte payload follows
C -> S 0x12 0x34 0x56 0x78 0x9a ...                 # 35-byte payload blob
C -> S 0x03 0x00 0x00 0x00                          # 3-byte payload follows
C -> S 0x12 0x34 0x56                               # 3-byte payload blob
...

Implementations

Language File(s)
C++ “0.7” branch of https://code.cor-lab.org/git/rsb.git./cpp/core/src/rsb/transport/socket
Java not yet implemented
Python “0.7” branch of https://code.cor-lab.org/git/rsb.git./python/core/rsb/transport/socket/__init__.py
Common Lisp “0.7” branch of https://code.cor-lab.org/git/rsb.git./cl/cl-rsb/src/transport/socket