View Source Blockade

unit tests

table-of-contents

Table of contents

description

Description

Blockade provides a simple way to queue and dispatch events to subscribers.

Blockade enables you to set a priority level on the event queue. This allows you to handle events later or discard them completely when dispatched with lower priority than the current priority level on the event queue. This can be handy in situations where you want to lock down the system and not process any events for some duration of time. In such cases, you can set a high priority level. Later, when the system is ready to handle events again, you can lower the priority level, and the queued events will be automatically dispatched (unless the discard_events option is set to true).

Blockade is built on top of the Erlang pg module, which means it will work in a distributed environment. Each node starts the blockade supervisor with the same name parameter. When dispatching events, the events will be received by all subscribers across the cluster. When process dies or is terminated, the process will be unsubscribed from the event queue automatically.

Please read the documentation for the blockade module for more information on the available functions.

setup

Setup

Blockade can be started under supervision tree in your application. When starting blockade you need to provide a name for the event queue. This name is used when dispatching events to the queue.

elixir

Elixir

  1. Add the following to your mix.exs file:

    {:blockade, "~> 0.1"},
  2. Start blockade under your supervision tree

    alias :blockade, as: Blockade
    
      def init(_) do
        children = [
          {Blockade, %{name: :my_event_queue}},
        ]
    
        opts = [strategy: :one_for_one]
        Supervisor.init(children, opts)
      end

erlang

Erlang

Add the following to your rebar.config file:

  1. Add blockade as a dependency

    {deps, [{blockade, "0.1.0"}]}.
  2. Start blockade under your supervision tree

    init(Args) ->
        Children = [
            #{id => blockade, start => {blockade, start_link, [#{name => my_event_queue}]}}
        ],
    
        {ok, {{one_for_one, 5, 10}, Children}}.

example-usage

Example usage

Note When adding handlers to the event queue, the current process will be subscribed to the event queue. If subscribed multiple times, the process will receive the event multiple times.

elixir-1

Elixir

> alias :blockade, as: Blockade
> Blockade.add_handler(:my_event_queue, :event_key)
:ok
> Blockade.dispatch(:my_event_queue, :event_key, "event_payload")
:ok
> Blockade.dispatch(:my_event_queue, :event_key, "something_else")
:ok
> flush()
{:event_key, "event_payload"}
{:event_key, "something_else"}

erlang-1

Erlang

> blockade:add_handler(my_event_queue, event_key).
ok
> blockade:dispatch(my_event_queue, event_key, "event_payload").
ok
> blockade:dispatch(my_event_queue, event_key, "something_else").
ok
> flush().
{event_key, "event_payload"}
{event_key, "something_else"}

using-priority-option

Using priority option

> alias :blockade, as: Blockade

# Subscribe current process to event with the key :some_event_key
> Blockade.add_handler(:my_event_queue, :some_event_key)
:ok

# Set priority to 100 and reset back to 0 (default) after 30 seconds. Do not discard any queued events. 
# If discard events is set to true, events with lower priority than the current priority will be discarded immediately after dispatching the event.
> Blockade.set_priority(:my_event_queue, 100, %{reset_after: 30000, discard_events: false}) 
:ok

# Immediately dispatch event with priority 100 and check local mailbox for any events. 
# There should be none if checked within 30 seconds after setting priority.
> Blockade.dispatch(:my_event_queue, :some_event_key, "priority_test")
:ok
> flush()
:ok

# Wait 30 seconds and check local mailbox again. There should be one event now.
> flush()
{:some_event_key, "priority_test"}

# Now lets try dispatching events with priority option.

# Set priority to 150 and discard any queued events with lower priority.
# This time the event queue will keep the priority and does not reset back to 0 because we did not provide the reset_after option.
> Blockade.set_priority(:my_event_queue, 150, %{discard_events: true})
:ok

> Blockade.dispatch(:my_event_queue, :some_event_key, "will_be_discarded", %{priority: 149})
:ok
> Blockade.dispatch(:my_event_queue, :some_event_key, "will_be_received prio 150", %{priority: 150})
:ok
> Blockade.dispatch(:my_event_queue, :some_event_key, {:my_data, "will_be_received prio 151"}, %{priority: 151})
:ok
> flush()
# We can see that the dispatched event with lower priority than 150 was discarded.
{:some_event_key, "will_be_received prio 150"}
{:some_event_key, {:my_data, "will_be_received prio 151"}}

We can also set the discard_event option to true per dispatch call meaning the event will be discarded if priority level on the event queue is higher than the priority level on the dispatch call:

> Blockade.set_priority(:my_event_queue, 150, %{discard_events: false})
> Blockade.dispatch(:my_event_queue, :some_event_key, "will_be_discarded", %{priority: 149, discard_event: true})
:ok
> flush()
# The event will be discarded and wont be received by the handler.

distributed-environment-dispatching

Distributed environment dispatching

By default blockade will dispatch events to all subscribers across the cluster. If you want to dispatch to local handlers only, you can set the members option to local when dispatching the event:

# This event will be received by all handlers across the cluster.
> Blockade.dispatch(:my_event_queue, :some_event_key, "global_dispatch", %{members: :global})
:ok

# This event will be received by local node handlers only.
> Blockade.dispatch(:my_event_queue, :some_event_key, "local_dispatch", %{members: :local})
:ok

Note If dispatching events to all members across the cluster is not desired, you can start blockade with different names on different nodes. This way you can have multiple event queues across the cluster each completly isolated from each other. For example you can use the local node name as the event queue name which is unique across the cluster.

development

Development

setup-with-docker

Setup with Docker

  • docker-compose up -d
  • docker exec -it blockade_blockade_1 sh
  • rebar3 compile

running-tests

Running tests

$ rebar3 compile && ct_run -dir test -logdir test_logs -pa ./_build/default/lib/blockade/ebin -setcookie cookievalue