Pub/Sub

Publishers and Subscribers.

Publisher

Full source: message/publisher.go

package message

type publisher interface {
    // Publish publishes provided messages to given topic.
   //
   // Publish can be synchronous or asynchronous - it depends of implementation.
   //
   // Most publishers implementations doesn't support atomic publishing of messages.
   // That means, that when publishing one of messages failed next messages will be not published.
   Publish(topic string, messages ...*Message) error
}

type Publisher interface {
    publisher

    // Close should flush unsent messages, if publisher is async.
   Close() error
}

Publishing multiple messages

Most publishers implementations don’t support atomic publishing of messages. That means, that when publishing one of the messages failed the next messages will be not published.

Async publish

Publish can be synchronous or asynchronous - it depends on implementation.

Close()

Close should flush unsent messages if the publisher is async. It’s important to not forget to close subscriber, in the other hand you may lose some of the messages.

Subscriber

Full source: message/subscriber.go

package message

type subscriber interface {
    // Subscribe returns output channel with messages from provided topic.
   // Channel is closed, when Close() was called to the subscriber.
   //
   // To receive next message, `Ack()` must be called on the received message.
   // If message processing was failed and message should be redelivered `Nack()` should be called.
   Subscribe(topic string) (chan *Message, error)
}

type Subscriber interface {
    subscriber

    // Close closes all subscriptions with their output channels and flush offsets etc. when needed.
   Close() error
}

Ack/Nack mechanism

It is Subscriber’s responsibility to handle Ack and Nack from a message. A proper implementation should wait for Ack or Nack before consuming the next message.

Important Subscriber’s implementation notice: Ack/offset to message’s storage/broker must be sent after Ack from Watermill’s message. If it wouldn’t, it is a possibility to lose messages when the process will die before messages were processed.

Close()

Close closes all subscriptions with their output channels and flush offsets etc. when needed.

At-least-once delivery

Watermill is build with at-least-once delivery semantics. That means, that when some error with occur when processing message and Ack cannot be sent the message will be redelivered.

You need to keep it in mind and build your application to be idempotent or implement deduplication mechanism.

Unfortunately, it’s not possible to create universal middleware for deduplication but we encourage you to make your own.

Universal tests

Every Pub/Sub is similar. To don’t implement separated tests for every Pub/Sub we create test suite which should be passed by any Pub/Sub implementation.

These tests can be found in message/infrastructure/test_pubsub.go.

Built-in implementations

To check available Pub/Subs implementation please check Pub/Sub’s implementations

Implementing custom Pub/Sub

When any implementation of Pub/Sub. Implementing custom Pub/Sub.

We will be also thankful for submitting merge requests with new Pub/Subs implementation.

You can also request new Pub/Sub implementation by submitting a new issue.

Keep going!

When you already know, how Pub/Sub is working we recommend to check Message Router component.