Elixir in Heterogeneous Infrastructure

 

Alex Rozumii, Toptal

Elixir Club, 28 January 2017

About me

What's up, doc?

ActionCable vs AnyCable

ActionCable broadcast performance

But how to connect different technologies?

Integrating with stateless node

HTTP API

Queue

Message passing

Message passing

Message passing

But wait!

Functions + Messages + Concurrency = Erlang

Connect to the real node

Erlix

Erlix

require "erlix"

Erlix::Node.init("ruby",nil)

c=Erlix::Connection.new("foo@kdr2-pc")

p=Erlix::Pid.new(c)

# {Pid, :test_atom}
c.esend("my_pid", Erlix::Tuple.new([p, Erlix::Atom.new("test_atom")]))

Erlport/Export

Erlport/Export

defmodule SomePythonCall do
  use Export.Python

  def call_python_method
    # path to our python files
    {:ok, py} = Python.start(python_path: Path.expand("lib/python"))

    # call "upcase" method from "test" file with "hello" argument
    py |> Python.call("test", "upcase", ["hello"])

    # same as above but prettier
    py |> Python.call(upcase("hello"), from_file: "test")
  end
end

Integrating with stateful node

"Hey man, we don't have such option!"

What database do you use?

Database preferences

*according to poll, 2014
 

SQLite

Supports

  • Triggers
  • Views

Redis

Redis

  • Lua
  • Pub/Sub
  • Keyspace notifications

Redis Lua + Pub/Sub

>>> import redis
>>> r = redis.StrictRedis()
>>> lua = """
... local res = {}
... for i = 2, #ARGV do
...     table.insert(res, 
     redis.call('publish', ARGV[i], ARGV[1]))
... end
... return res
... """
>>> multipublish = r.register_script(lua)
>>> multipublish(args=["message", "channel1", 
        "channel2", "channel3"])
[1L, 0L, 0L]

MySQL

MySQL

  • Stored functions
  • Triggers
  • Views
  • UDF

MySQL UDF

MySQL UDF

MySQL UDF

MySQL UDF

You're welcome!

PostgreSQL

PostgreSQL

  • Stored functions
  • Triggers
  • Views
  • Foreign Data Wrappers
  • JSON
  • Pub/Sub

PostgreSQL FDW

PostgreSQL FDW

PostgreSQL FDW

brain-geek/telepathy

worker(CitiesInsertListener, [Application.get_env(:app, Repo)]),



defmodule CitiesInsertListener do
  use Telepathy.Listener, table_name: "cities"

  def handle_insert(new, state) do
    CitiesListenerAgent.push_message(__MODULE__, new)
    
    {:noreply, state}
  end
end

brain-geek/telepathy

CREATE OR REPLACE FUNCTION <%= @function_name %>()
RETURNS trigger AS $$
BEGIN
  CASE TG_OP 
    WHEN 'INSERT' THEN
      PERFORM pg_notify(
        '<%= @channel_name %>',
        json_build_object(
          'table', TG_TABLE_NAME,
          'type', TG_OP,
          'old_data', NULL,
          'new_data', row_to_json(NEW)
        )::text
      );

brain-geek/telepathy

{:ok, notif_pid} = Postgrex.Notifications.start_link(db_connection)

Postgrex.Notifications.listen(notif_pid, "cities")

{:ok, %{notif_pid: notif_pid, pg_pid: pg_pid}}
...


def handle_info(raw_msg, state) do
    msg = Poison.decode! elem(raw_msg, 4)
...

Microservices are not the only option

Which one did you like?

Thanks!

Alex Rozumii
@brain-geek
alex@rozumiy.name

Elixir in Heterogeneous Infrastructure

By Alex Rozumii

Elixir in Heterogeneous Infrastructure

  • 1,660