Skip to content
Andrey Sibiryov edited this page Jul 21, 2014 · 6 revisions

Common types

Object        ::= <Number> | <String> | <Tuple> | <Map>
Tuple         ::= ([<Object> [, <Object>]...])

General format

Every message is a MessagePack-ed tuple of three fields:

ChannelID     ::= <Number>
MessageID     ::= <Number>
Message       ::= (<ChannelID>, <MessageID>, <Tuple>)

Message ID is a service slot number you're going to call. Every service has its own set of slots which can be inspected by resolving this service via the Locator. Channel ID is a way to multiplex multiple dataflows inside a single TCP session. Channel ID is generated by the caller. Tuple is a slot-specific payload.

Locator protocol

I'll explain the messaging in detail using the Locator protocol, which is one of the key protocols to know when using the Cocaine Services. This service has a public slot named resolve, which searches for a specified service name and returns you the information about it, if available. This slot's payload tuple is as follows:

0, Resolve    ::= (<String>)

Every call results in a typed stream, which yields you response chunks and errors and is always closed in the end. Stream is implemented as a protocol, too:

0, Chunk      ::= <Object>
1, Error      ::= (<Number>, <String>)
2, Choke      ::= ()

When a remote server yields a new response chunk, it's returned with a Chunk message. Chunk's tuple type is determined by the service protocol. In our case, the Resolve method returns the following tuple and then closes the stream:

Address       ::= <String>
Port          ::= <Number>
Endpoint      ::= (<Address>, <Port>)
Version       ::= <Number>
DispatchGraph ::= {<SlotID> => (<String>, <DispatchGraph>, <DispatchGraph>) [, <SlotID> => (<String>, <DispatchGraph>, <DispatchGraph>)]...}

Response      ::= (<Endpoint>, <Version>, <DispatchGraph>)

If a remote service fails, it's signaled with an Error message. Error tuple consists of a service-specific error code and error message. For example, the Locator service could return the following error if no service is found with the specified name:

(ENOENT, "the specified service is not available")

When there's no more data in the stream, a Choke message is sent. A stream is always closed with a Choke message, either after all the chunks are sent, or after an error occured. Also, after an Error message is received, the only valid message is Choke.

Example

For example, if we want to resolve a local Logging service, it would be something like that on the wire:

-> (0, 1, ("logging"))
<- (0, 1, (("localhost", 12345), 1, {0 => ("emit", {}, {0 => ("chunk", nil, {}), 1 => ("error", nil, {}), 2 => ("choke", nil, {})})}))
<- (2, 1, ())

That means that the Logging service can be reached at port 12345 locally, its protocol version is 1, and it has one slot number 0, which maps to a method named 'emit', which protocol transition is empty and drain type is a stream (but that's another story).

Clone this wiki locally