AMQP Helpers

Build and TestCoverage Status

Non opinionated AMQP helpers for common scenarios.

Installation

To use AMQPHelpers you need AMQP. You can install both by adding amqp and amqp_helpers to your list of dependencies in mix.exs:

def deps do
  [
    {:amqp, "~> 4.0"},
    {:amqp_helpers, "~> 1.1"}
  ]
end

Motivation

This library provides several wrappers and utilities around some common use cases of AMQP. It provides a simple interface, following common OTP patterns and Elixir library guidelines to avoid any opinionated interface. Other generalist abstractions can be built on top of AMQPHelpers to provide ergonomics or any other feature not strictly tied to AMQP.

Right now, the utilities built in this library are suited for uses that try to optimize AMQP for high throughput and for scenarios that require data safety at any cost.

Comparisons With Other Libraries

This is how this library relates to other available libraries in order to highlight the motivation behind this library:

In summary, this library provides helpers tied to specific use cases of AMQP, without any kind of abstraction over it. If you are looking to support different kinds of transports in your library, check out libraries like Broadway or Rambla.

AMQP Good Practices

This library enforces some good practices that have no downside for any application using AMQP and ease the development of AMQP related features.

The first one is the use of an AMQP implementation behind a behaviour, called AMQPHelpers.Adapter. This allow us to provide stub, mocks or even different transport layers that mimic the AMQP interface.

The second one is the use of application connection/channels. This an AMQP v2.x feature which replaces (or aid) previous connections supervisors or channel pools. As general thumb rule, your application should have at most two connection (in/out) and one channel per multiplexing process.

User Case Scenarios

These are the AMQP use cases are covered in AMQP Helpers right now:

There are some other features, like High Availability, Observability, Exclusivity, etc. that can be achieved in both scenarios but are not explicitly covered here.

High Throughput

To achieve the best performance in terms of message delivery some trade-off must be done, which usually impacts the reliability and/or coherence of the system.

Durability should be disabled. In other words, messages will not be persisted, so messages could be lost in a broker outage scenario. This can be configured by declaring queues as non-durable and publishing messages with persistent set to false.

Acknowledges, from any communication direction, should be disabled. This means that the publisher should not confirm deliveries, and consumers should not acknowledge messages. Messages could be lost on the flight because of network or edge issues. Publisher confirms are not enabled by default, so nothing has to be done in terms of publishing. Consuming requires disabling acknowledging, which can be done by setting the no_ack flag on.

The AMQPHelpers.HighThroughput module provides functions that enforce these requirements for publishing and consuming. These are simple wrappers around AMQP library functions. They add very little aside from being explicit about a feature of some scenario (performance intensive).

Some other considerations should have taken into account when declaring queues for this purpose, which are:

Reliability

For reliability, Acknowledgements and Confirms are used, which have an impact in performance. Reliability is not only achieved at client level, broker configuration and monitoring are also important topics here. You can read RabbitMQ’s Reliability Guide for more information.

The AMQPHelpers.Reliability.Consumer and AMQPHelpers.Reliability.Producer provide processes to consume and publish messages in a reliable fashion. The Consumer uses standard AMQP acknowledges mechanism to notify that messages are consumed successfully. The Producer uses the Publisher Confirms extension to get notified about successfully message deliveries.

A system which uses the provided Reliability.Consumer and Reliability.Producer will guarantee that no message is lost, and at least one message is delivered. For high availability you can pair this processes with mirrored queues. For consistency, you can use deduplication plugin or quorum queues (Which also guarantees message order).

Testing

This library provides an AMQP interface at AMQPHelpers.Adapter which can be used with libraries like Mox to mock any part of the AMQP interface. Check out the tests of this library to see some examples.

Two adapter implementations are provided with this library:

All the functionally exposed by the library support in one or another way a configurable adapter (defaults to AMQP implementation).

References