SlideShare una empresa de Scribd logo
1 de 48
Descargar para leer sin conexión
GenServer in Action
 Elixir Club 11

Ternopil, 2018
Elixir Process
Process
iex(1)> spawn fn -> 1 + 1 end
#PID<0.90.0>
iex(2)> caller = self()
#PID<0.88.0>
iex(3)> spawn fn -> send caller, {:result, 1 + 1} end
#PID<0.93.0>
iex(4)> receive do
...(4)> {:result, result} -> result
...(4)> end
2
Process
iex(5)> spawn fn ->
...(5)> Process.sleep(6_000)
...(5)> send caller, {:result, 1 + 1}
...(5)> end
#PID<0.106.0>
iex(6)> receive do
...(6)> {:result, result} -> result
...(6)> end
⏳⏳⏳⏳⏳⌛
2
Process
iex(7)> spawn fn ->
...(7)> Process.sleep(666_666_666)
...(7)> send caller, {:result, 1 + 1}
...(7)> end
#PID<0.99.0>
iex(8)> spawn fn -> :nothing end
#PID<0.101.0>
iex(9)> spawn fn -> raise "error" end
#PID<0.103.0>
…
iex(11)> receive do
...(11)> {:result, result} -> result
...(11)> end
⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳
💤 💤 💤 CTRL+C
Process: timeout
iex(1)> caller = self()
#PID<0.88.0>
iex(2)> spawn fn ->
...(2)> Process.sleep(666_666_666)
...(2)> send caller, {:result, 1 + 1}
...(2)> end
#PID<0.94.0>
iex(3)> receive do
...(3)> {:result, result} -> {:ok, result}
...(3)> after
...(3)> 5_000 -> {:error, :timeout}
...(3)> end
⏳⏳⏳⏳⌛{:error, :timeout}
Process: message queue
iex(4)> send self(), {:message, 1}
iex(5)> send self(), {:another_message, 1}
iex(6)> send self(), {:message, 2}
iex(7)> send self(), {:another_message, 2}
iex(8)> send self(), {:message, 3}
iex(9)> send self(), {:another_message, 3}
iex(10)> Process.info(self(), :messages)
{:messages,
[
message: 1,
another_message: 1,
message: 2,
another_message: 2,
message: 3,
another_message: 3
]}
Process: message queue
{:messages,
[
message: 1,
another_message: 10,
message: 2,
another_message: 20,
message: 3,
another_message: 30
]}
iex(11)> receive do {:another_message, n} -> n end
10
iex(12)> receive do {:another_message, n} -> n end
20
iex(13)> Process.info(self(), :messages)
{:messages, [message: 1, message: 2, message: 3, another_message: 30]}
iex(14)> receive do {:message, n} -> n end
1
iex(15)> Process.info(self(), :messages)
{:messages, [message: 2, message: 3, another_message: 30]}
iex(16)> receive do {:something, n} -> n after 0 -> :no_matching_message end
:no_matching_message
When to use
processes?
Concurrent Tasks
Task.async / Task.await
ex(17)> task = Task.async(fn -> 1 + 1 end)
%Task{
owner: #PID<0.88.0>,
pid: #PID<0.106.0>,
ref: #Reference<0.1183799544.2034761732.30152>
}
iex(18)> Task.await(task)
2
Concurrent Tasks
Task.async / Task.await
iex(19)> tasks = Enum.map(1..10, &Task.async(fn ->
...(19)> Process.sleep(3_000)
...(19)> &1 * 100
...(19)> end))
[…]
iex(20)> Process.sleep(5_000)
:ok
iex(21)> Enum.map(tasks, &Task.await(&1))
[100, 200, 300, 400, 500, 600, 700, 800, 900, 1000]
Concurrent Tasks
Task.await timeout
await(task, timeout  5000)
…
A timeout, in milliseconds, can be given with default value
of 5000. If the timeout is exceeded, then the current
process will exit.
…
iex(22)> Task.async(fn -> Process.sleep(6_000) end) |> Task.await()
** (exit) exited in: Task.await(%Task{owner: #PID<0.88.0>, pid:
#PID<0.156.0>, ref: #Reference<0.891801449.431751175.151026>}, 5000)
** (EXIT) time out
(elixir) lib/task.ex:501: Task.await/2
Storing state
defmodule Storage do
def recursive(state) do
receive do
{:add, caller_pid, value} ->
new_state = state + value
send(caller_pid, {:result, new_state})
recursive(new_state)
end
end
end
iex(2)> storage_pid = spawn(fn -> Storage.recursive(0) end)
iex(3)> send(storage_pid, {:add, self(), 2})
iex(4)> send(storage_pid, {:add, self(), 2})
iex(5)> send(storage_pid, {:add, self(), 2})
iex(6)> flush()
{:result, 2}
{:result, 4}
{:result, 6}
Storing state
Agent
iex(7)> {:ok, pid} = Agent.start_link(fn -> 0 end)
{:ok, #PID<0.101.0>}
iex(8)> Agent.update(pid, fn state -> state + 1 end)
:ok
iex(9)> Agent.get(pid, fn state -> state end)
1
update(agent, fun, timeout  5000)
get(agent, module, fun, args, timeout  5000)
GenServer
GenServer
A behaviour module for implementing the server of a client-
server relation.

A GenServer is a process like any other Elixir process and it
can be used to keep state, execute code asynchronously
and so on. 
GenServer
defmodule Stack do
use GenServer
# Client
def start_link(default) do
GenServer.start_link(__MODULE__, default)
end
def push(pid, item) do
GenServer.cast(pid, {:push, item})
end
def pop(pid) do
GenServer.call(pid, :pop)
end
# Server (callbacks)
def handle_call(:pop, _from, [h | t]) do
{:reply, h, t}
end
def handle_cast({:push, item}, state) do
{:noreply, [item | state]}
end
end
GenServer
defmodule Stack do
use GenServer
# Client
def start_link(default) do
GenServer.start_link(__MODULE__, default)
end
def push(pid, item) do
GenServer.cast(pid, {:push, item})
end
def pop(pid) do
GenServer.call(pid, :pop)
end
# Server (callbacks)
def handle_call(:pop, _from, [h | t]) do
{:reply, h, t}
end
def handle_cast({:push, item}, state) do
{:noreply, [item | state]}
end
end
Executed in caller process
GenServer
defmodule Stack do
use GenServer
# Client
def start_link(default) do
GenServer.start_link(__MODULE__, default)
end
def push(pid, item) do
GenServer.cast(pid, {:push, item})
end
def pop(pid) do
GenServer.call(pid, :pop)
end
# Server (callbacks)
def handle_call(:pop, _from, [h | t]) do
{:reply, h, t}
end
def handle_cast({:push, item}, state) do
{:noreply, [item | state]}
end
end
Executed in server process
GenServer: timeout
call(server, request, timeout  5000)
def push(pid, item) do
GenServer.cast(pid, {:push, item})
end
def pop(pid) do
GenServer.call(pid, :pop)
end
# Server (callbacks)
@impl true
def handle_call(:pop, _from, [h | t]) do
Process.sleep(30_000)
{:reply, h, t}
end
@impl true
def handle_cast({:push, item}, state) do
{:noreply, [item | state]}
end
Executed in caller process
Executed in server process
GenServer: timeout
iex(2)> {:ok, pid} = Stack.start_link([])
{:ok, #PID<0.95.0>}
iex(3)> Enum.each(1..5, &Stack.push(pid, &1))
:ok
ex(4)> Stack.pop(pid)
** (exit) exited in: GenServer.call(#PID<0.95.0>, :pop, 5000)
** (EXIT) time out
(elixir) lib/gen_server.ex:836: GenServer.call/3
iex(4)> Stack.pop(pid)
** (exit) exited in: GenServer.call(#PID<0.95.0>, :pop, 5000)
** (EXIT) time out
(elixir) lib/gen_server.ex:836: GenServer.call/3
iex(4)> Stack.pop(pid)
** (exit) exited in: GenServer.call(#PID<0.95.0>, :pop, 5000)
** (EXIT) time out
(elixir) lib/gen_server.ex:836: GenServer.call/3
iex(4)> Stack.pop(pid)
** (exit) exited in: GenServer.call(#PID<0.95.0>, :pop, 5000)
** (EXIT) time out
(elixir) lib/gen_server.ex:836: GenServer.call/3
iex(4)> Process.info(pid, :messages)
{:messages,
[
{:"$gen_call", {#PID<0.88.0>, #Reference<0.3022523149.3093823489.83754>},
:pop},
{:"$gen_call", {#PID<0.88.0>, #Reference<0.3022523149.3093823489.83774>},
:pop},
{:"$gen_call", {#PID<0.88.0>, #Reference<0.3022523149.3093823489.83794>},
:pop}
]}
Executed in caller process
Executed in server process
GenServer: timeout
ex(5)> Process.info(pid, :messages)
{:messages,
[
{:"$gen_call", {#PID<0.88.0>,
#Reference<0.3022523149.3093823489.83794>},
:pop}
]}
ex(6)> Process.info(pid, :messages)
{:messages, []}
iex(7)> flush()
{#Reference<0.3022523149.3093823489.83730>, 5}
{#Reference<0.3022523149.3093823489.83754>, 4}
{#Reference<0.3022523149.3093823489.83774>, 3}
{#Reference<0.3022523149.3093823489.83794>, 2}
:ok
Digital Signature
Microservice
Digital Signature Microservice
• Microservice - part of the Ukrainian eHealth system
infrastructure

• Digital signature validation 

• Ukrainian standard DSTU 4145-2002

• Integration with proprietary C library via NIF
Digital Signature Microservice
Digital Signature
Microservice
NIF
Proprietary
DSTU
4145-2002
LIB
Digitally signed
content
< 5s
Decoded content
Signer info
Signature validation
Certificates
OSCP Server
{:ok, result} =
DigitalSignatureLib.processPKCS7Data(data, certs, true)
result ==
%{
content: "{"hello": "world"}",
is_valid: true,
signer: %{
common_name: "XXX YYY ZZZ",
country_name: "UA",
drfo: "1234567890",
edrpou: "",
given_name: "XX YY",
locality_name: "М. КИЇВ",
organization_name: "ФІЗИЧНА ОСОБА",
organizational_unit_name: "",
state_or_province_name: "",
surname: "XX",
title: ""
},
validation_error_message: ""
}
Sounds easy?
Digital Signature Microservice
DigitalSignatureLib.processPKCS7Data(…)
DigitalSignatureLib.processPKCS7Data(…)
DigitalSignatureLib.processPKCS7Data(…)
DigitalSignatureLib.processPKCS7Data(…)
Elixir Process
Elixir Process
Elixir Process
Elixir Process
Incoming Requests
200 OK
200 OK
200 OK
200 OK
Responses
Problem 1: concurrency
tasks =
Enum.map(1..25, fn _ ->
Task.async(fn ->
DigitalSignatureLib.processPKCS7Data(data, certs, true)
end)
end)
Enum.map(tasks, fn task ->
{:ok, result} = Task.await(task, 30000)
IO.inspect(result)
end)
%{
content: ***
}
%{
content: ***
}
%{
content: ***
}
%{
content: ***
}
*** Error in `/usr/local/lib/erlang/erts-9.3.3/bin/beam.smp': double free or corruption (fasttop): 0x00007f88e401d060 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x70bfb)[0x7f8962452bfb]
/lib/x86_64-linux-gnu/libc.so.6(+0x76fc6)[0x7f8962458fc6]
/lib/x86_64-linux-gnu/libc.so.6(+0x7780e)[0x7f896245980e]
/usr/local/lib/libUACryptoQ.so.1(_ZN9DataBlockaSERKS_+0x3c)[0x7f88a7bd785c]
/usr/local/lib/libUACryptoQ.so.1(_ZN4DataaSERKS_+0x11)[0x7f88a7bd6771]
/usr/local/lib/libUACryptoQ.so.1(_ZN7DataSet3NewERK4Data+0x3c)[0x7f88a7bd81cc]
/usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC16ObjectIdentifier10EncodeBodyEv+0x1fc)[0x7f88a7bf238c]
/usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC5Token6EncodeEv+0x46)[0x7f88a7bef236]
/usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC11ObjectToken10EncodeBodyEv+0x17)[0x7f88a7bef3c7]
/usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC5Token6EncodeEv+0x46)[0x7f88a7bef236]
/usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC11Constructed10EncodeBodyEv+0x45)[0x7f88a7be97d5]
/usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC11Constructed6EncodeEv+0x39)[0x7f88a7bec089]
/usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC11Constructed10EncodeBodyEv+0x45)[0x7f88a7be97d5]
/usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC11Constructed6EncodeEv+0x39)[0x7f88a7bec089]
/usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC10CMSContent6EncodeEv+0x95)[0x7f88a7c0a2d5]
/usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC11ObjectToken10EncodeBodyEv+0x3a)[0x7f88a7bef3ea]
/usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC5Token6EncodeEv+0x46)[0x7f88a7bef236]
/usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC11Constructed10EncodeBodyEv+0x45)[0x7f88a7be97d5]
/usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC11Constructed6EncodeEv+0x39)[0x7f88a7bec089]
/usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC10CMSContent6EncodeEv+0x95)[0x7f88a7c0a2d5]
/usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC11ObjectToken10EncodeBodyEv+0x3a)[0x7f88a7bef3ea]
/usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC5Token6EncodeEv+0x46)[0x7f88a7bef236]
/usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC11Constructed10EncodeBodyEv+0x45)[0x7f88a7be97d5]
/usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC11Constructed6EncodeEv+0x39)[0x7f88a7bec089]
/usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC17ObjectWithContent6EncodeEv+0x1b)[0x7f88a7be946b]
/usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC11ObjectToken10EncodeBodyEv+0x3a)[0x7f88a7bef3ea]
/usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC5Token6EncodeEv+0x46)[0x7f88a7bef236]
/usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC11Constructed10EncodeBodyEv+0x45)[0x7f88a7be97d5]
/usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC11Constructed6EncodeEv+0x39)[0x7f88a7bec089]
/usr/local/lib/libUACryptoQ.so.1(_ZN3UAC13CMSSignedData15VerifySignatureEPNS_11CertificateEP11_UAC_STREAM+0x9f5)[0x7f88a7c12965]
/usr/local/lib/libUACryptoQ.so.1(UAC_SignedDataVerify+0x2ba)[0x7f88a7c4ff1a]
/home/digital_signature.lib/_build/test/lib/digital_signature_lib/priv/digital_signature_lib_nif.so(Check+0x3c5)[0x7f891c0fc7f5]
/home/digital_signature.lib/_build/test/lib/digital_signature_lib/priv/digital_signature_lib_nif.so(+0x2bac)[0x7f891c0fbbac]
/usr/local/lib/erlang/erts-9.3.3/bin/beam.smp(erts_call_dirty_nif+0x19c)[0x5ce04c]
/usr/local/lib/erlang/erts-9.3.3/bin/beam.smp(erts_dirty_process_main+0x209)[0x445949]
/usr/local/lib/erlang/erts-9.3.3/bin/beam.smp[0x4f4a4d]
/usr/local/lib/erlang/erts-9.3.3/bin/beam.smp[0x675985]
/lib/x86_64-linux-gnu/libpthread.so.0(+0x7494)[0x7f8962990494]
/lib/x86_64-linux-gnu/libc.so.6(clone+0x3f)[0x7f89624caacf]
======= Memory map: ========
00400000-0073b000 r-xp 00000000 08:01 2376229 /usr/local/lib/erlang/erts-9.3.3/bin/beam.smp
0093a000-0093b000 r--p 0033a000 08:01 2376229 /usr/local/lib/erlang/erts-9.3.3/bin/beam.smp
0093b000-00956000 rw-p 0033b000 08:01 2376229 /usr/local/lib/erlang/erts-9.3.3/bin/beam.smp
00956000-0097d000 rw-p 00000000 00:00 0
0231e000-02349000 rw-p 00000000 00:00 0 [heap]
Problem 1 solution: Gen Server
...
use GenServer
# Callbacks
...
def handle_call({:process_signed_content, signed_content, check}, _from,
{certs_cache_ttl, certs}) do
check = unless is_boolean(check), do: true
processing_result = do_process_signed_content(signed_content, certs, check,
SignedData.new())
{:reply, processing_result, {certs_cache_ttl, certs}}
end
...
# Client
...
def process_signed_content(signed_content, check) do
GenServer.call(__MODULE__, {:process_signed_content, signed_content, check})
end
Problem 1 solution: Gen Server
NifService (GenServer)
NifService.process_signed_content(…)
NifService.process_signed_content(…)
NifService.process_signed_content(…)
NifService.process_signed_content(…)
Requests
Message queue
DigitalSignatureLib.processPKCS7Data(…)
Responses
Problem 2: timeouts
NifService (GenServer)
Multiple concurrent
requests (processes)
2s
2s
2s
2s
Message queue
Sequential
responses
200 OK
2s
200 OK
4s
500 Error
5s
500 Error
5s
call(server, request, timeout  5000)
** (EXIT) time out
** (EXIT) time out
Problem 2: timeouts
NifService (GenServer)
Multiple concurrent
requests (processes)
2s
2s
Message queue
Sequential
responses
call(server, request, timeout  5000)
2s
2s
500 Error
5s
500 Error
5s
Problem 2 solution: architecture
DS
DS
DS
DS DS
DS
DS
DS
Load
Balancer
Reduce pressure on individual MS
Problem 2 solution: catch Exit
def process_signed_content(signed_content, check) do
gen_server_timeout =
Confex.fetch_env(:digital_signature_api,
:nif_service_timeout)
try do
GenServer.call(__MODULE__, {:process_signed_content,
signed_content, check}, gen_server_timeout)
catch
:exit, {:timeout, error} ->
{:error, {:nif_service_timeout, error}}
end
end
424 Failed Dependency
Problem 3: message queue
NifService (GenServer)
Processed requests
2s
2s
2s
2s
Message queue
Sequential
responses
200 OK
2s
200 OK
4s
424
5s
424
5s
2s
2s
Expired requests
New requests
Problem 3 solution: pass
message expiration time
...
# Callbacks
def handle_call({:process_signed_content, signed_content, check, message_exp_time}, _from, certs_cache_ttl, certs})
do
processing_result =
if NaiveDateTime.diff(message_exp_time, NaiveDateTime.utc_now(), :millisecond) > 250 do
check = unless is_boolean(check), do: true
do_process_signed_content(signed_content, certs, check, SignedData.new())
else
{:error, {:nif_service_timeout, "messaqe queue timeout"}}
end
{:reply, processing_result, {certs_cache_ttl, certs}}
End
...
# Client
def process_signed_content(signed_content, check) do
gen_server_timeout = Confex.fetch_env!(:digital_signature_api, :nif_service_timeout)
message_exp_time = NaiveDateTime.add(NaiveDateTime.utc_now(), gen_server_timeout, :millisecond)
try do
GenServer.call(__MODULE__, {:process_signed_content, signed_content, check, message_exp_time}, gen_server_timeout)
catch
:exit, {:timeout, error} ->
{:error, {:nif_service_timeout, error}}
end
end
...
Problem 3 solution: pass
message expiration time
NifService (GenServer)
…
Expired
Expired
Within threshold
Message queue
…
Problem 3 advanced solution:
QueueService
NifService
DigitalSignatureLib.
processPKCS7Data(…)
QueueService
:queue
Monitoring & Control
Memory management
Binary terms which are larger than 64 bytes are not stored in a process private
heap. 

They are called Refc Binary (Reference Counted Binary) and are stored in a
large Shared Heap which is accessible by all processes who have the pointer
of that Refc Binaries. 

That pointer is called ProcBin and is stored in a process private heap.

The GC for the shared heap is reference counting.

Collecting those Refc messages depends on collecting of all ProcBin objects
even ones that are inside the middleware process. 

Unfortunately because ProcBins are just a pointer hence they are so cheap
and it could take so long to happen a GC inside the middleware process.
NifService
Memory management
Shared Heap
Refc Binary
Refc Binary
Refc Binary
Requests Responses
ProcBin
ProcBin
ProcBin
ProcBin
ProcBin
ProcBin
ProcBin
ProcBin
ProcBin
GC
GC
GC
:erlang.garbage_collect(self())
Processes
Garbage Collect
...
# Callbacks
def init(certs_cache_ttl) do
certs = CertAPI.get_certs_map()
Process.send_after(self(), :refresh, certs_cache_ttl)
{:ok, {certs_cache_ttl, certs}}
end
...
def handle_info(:refresh, {certs_cache_ttl, _certs}) do
certs = CertAPI.get_certs_map()
# GC
:erlang.garbage_collect(self())
Process.send_after(self(), :refresh, certs_cache_ttl)
{:noreply, {certs_cache_ttl, certs}}
end
...
Garbage Collect
handle_call(request, from, state)
{:reply, reply, new_state, timeout() | :hibernate}
Hibernating a GenServer causes garbage collection and leaves
a continuous heap that minimises the memory used by the
process
Returning {:reply, reply, new_state, timeout} is similar to
{:reply, reply, new_state} except handle_info(:timeout,
new_state) will be called after timeout milliseconds if no
messages are received
Useful links
Saša Jurić "Elixir in Action, Second Edition”
https://www.manning.com/books/elixir-in-action-second-edition

Andrea Leopardi - Concurrent and Resilient Connections to Outside the
BEAM (ElixirConfEU 2016)
https://www.youtube.com/watch?time_continue=1884&v=U1Ry7STEFiY

GenServer call time-outs
https://cultivatehq.com/posts/genserver-call-timeouts/

Elixir Memory - Not Quite Free
https://stephenbussey.com/2018/05/09/elixir-memory-not-quite-free.html

Erlang Garbage Collection Details and Why It Matters
https://hamidreza-s.github.io/erlang%20garbage%20collection%20memory%20layout%20soft%20realtime/
2015/08/24/erlang-garbage-collection-details-and-why-it-matters.html
Thank You
Yurii Bodarev

https://github.com/ybod
Questions?

Más contenido relacionado

La actualidad más candente

La actualidad más candente (20)

The Ring programming language version 1.5.3 book - Part 19 of 194
The Ring programming language version 1.5.3 book - Part 19 of 194The Ring programming language version 1.5.3 book - Part 19 of 194
The Ring programming language version 1.5.3 book - Part 19 of 194
 
The Ring programming language version 1.6 book - Part 71 of 189
The Ring programming language version 1.6 book - Part 71 of 189The Ring programming language version 1.6 book - Part 71 of 189
The Ring programming language version 1.6 book - Part 71 of 189
 
The Ring programming language version 1.6 book - Part 55 of 189
The Ring programming language version 1.6 book - Part 55 of 189The Ring programming language version 1.6 book - Part 55 of 189
The Ring programming language version 1.6 book - Part 55 of 189
 
JavaScript ∩ WebAssembly
JavaScript ∩ WebAssemblyJavaScript ∩ WebAssembly
JavaScript ∩ WebAssembly
 
The Ring programming language version 1.10 book - Part 89 of 212
The Ring programming language version 1.10 book - Part 89 of 212The Ring programming language version 1.10 book - Part 89 of 212
The Ring programming language version 1.10 book - Part 89 of 212
 
The Ring programming language version 1.8 book - Part 84 of 202
The Ring programming language version 1.8 book - Part 84 of 202The Ring programming language version 1.8 book - Part 84 of 202
The Ring programming language version 1.8 book - Part 84 of 202
 
Erlang/OTP in Riak
Erlang/OTP in RiakErlang/OTP in Riak
Erlang/OTP in Riak
 
The Ring programming language version 1.5.2 book - Part 26 of 181
The Ring programming language version 1.5.2 book - Part 26 of 181The Ring programming language version 1.5.2 book - Part 26 of 181
The Ring programming language version 1.5.2 book - Part 26 of 181
 
Rのスコープとフレームと環境と
Rのスコープとフレームと環境とRのスコープとフレームと環境と
Rのスコープとフレームと環境と
 
Rデバッグあれこれ
RデバッグあれこれRデバッグあれこれ
Rデバッグあれこれ
 
The Ring programming language version 1.4 book - Part 18 of 30
The Ring programming language version 1.4 book - Part 18 of 30The Ring programming language version 1.4 book - Part 18 of 30
The Ring programming language version 1.4 book - Part 18 of 30
 
QA Fest 2019. Saar Rachamim. Developing Tools, While Testing
QA Fest 2019. Saar Rachamim. Developing Tools, While TestingQA Fest 2019. Saar Rachamim. Developing Tools, While Testing
QA Fest 2019. Saar Rachamim. Developing Tools, While Testing
 
The Ring programming language version 1.9 book - Part 48 of 210
The Ring programming language version 1.9 book - Part 48 of 210The Ring programming language version 1.9 book - Part 48 of 210
The Ring programming language version 1.9 book - Part 48 of 210
 
C++ practical
C++ practicalC++ practical
C++ practical
 
Dts x dicoding #2 memulai pemrograman kotlin
Dts x dicoding #2 memulai pemrograman kotlinDts x dicoding #2 memulai pemrograman kotlin
Dts x dicoding #2 memulai pemrograman kotlin
 
The Ring programming language version 1.6 book - Part 15 of 189
The Ring programming language version 1.6 book - Part 15 of 189The Ring programming language version 1.6 book - Part 15 of 189
The Ring programming language version 1.6 book - Part 15 of 189
 
The Ring programming language version 1.2 book - Part 16 of 84
The Ring programming language version 1.2 book - Part 16 of 84The Ring programming language version 1.2 book - Part 16 of 84
The Ring programming language version 1.2 book - Part 16 of 84
 
The Ring programming language version 1.10 book - Part 94 of 212
The Ring programming language version 1.10 book - Part 94 of 212The Ring programming language version 1.10 book - Part 94 of 212
The Ring programming language version 1.10 book - Part 94 of 212
 
The Ring programming language version 1.5.3 book - Part 25 of 184
The Ring programming language version 1.5.3 book - Part 25 of 184The Ring programming language version 1.5.3 book - Part 25 of 184
The Ring programming language version 1.5.3 book - Part 25 of 184
 
The Ring programming language version 1.7 book - Part 16 of 196
The Ring programming language version 1.7 book - Part 16 of 196The Ring programming language version 1.7 book - Part 16 of 196
The Ring programming language version 1.7 book - Part 16 of 196
 

Similar a GenServer in Action – Yurii Bodarev

Part 3-functions1-120315220356-phpapp01
Part 3-functions1-120315220356-phpapp01Part 3-functions1-120315220356-phpapp01
Part 3-functions1-120315220356-phpapp01
Abdul Samee
 
how to reuse code
how to reuse codehow to reuse code
how to reuse code
jleed1
 
Can you fix the errors- It isn't working when I try to run import s.pdf
Can you fix the errors- It isn't working when I try to run    import s.pdfCan you fix the errors- It isn't working when I try to run    import s.pdf
Can you fix the errors- It isn't working when I try to run import s.pdf
aksachdevahosymills
 
JavaScript Advanced - Useful methods to power up your code
JavaScript Advanced - Useful methods to power up your codeJavaScript Advanced - Useful methods to power up your code
JavaScript Advanced - Useful methods to power up your code
Laurence Svekis ✔
 

Similar a GenServer in Action – Yurii Bodarev (20)

Gevent rabbit rpc
Gevent rabbit rpcGevent rabbit rpc
Gevent rabbit rpc
 
Elixir -Tolerância a Falhas para Adultos - GDG Campinas
Elixir  -Tolerância a Falhas para Adultos - GDG CampinasElixir  -Tolerância a Falhas para Adultos - GDG Campinas
Elixir -Tolerância a Falhas para Adultos - GDG Campinas
 
Introducción a Elixir
Introducción a ElixirIntroducción a Elixir
Introducción a Elixir
 
Python profiling
Python profilingPython profiling
Python profiling
 
شرح مقرر البرمجة 2 لغة جافا - الوحدة الثالثة
شرح مقرر البرمجة 2   لغة جافا - الوحدة الثالثةشرح مقرر البرمجة 2   لغة جافا - الوحدة الثالثة
شرح مقرر البرمجة 2 لغة جافا - الوحدة الثالثة
 
Gevent be or not to be
Gevent be or not to beGevent be or not to be
Gevent be or not to be
 
«Gevent — быть или не быть?» Александр Мокров, Positive Technologies
«Gevent — быть или не быть?» Александр Мокров, Positive Technologies«Gevent — быть или не быть?» Александр Мокров, Positive Technologies
«Gevent — быть или не быть?» Александр Мокров, Positive Technologies
 
Django Celery
Django Celery Django Celery
Django Celery
 
A deep dive into PEP-3156 and the new asyncio module
A deep dive into PEP-3156 and the new asyncio moduleA deep dive into PEP-3156 and the new asyncio module
A deep dive into PEP-3156 and the new asyncio module
 
Unix Programs
Unix ProgramsUnix Programs
Unix Programs
 
Part 3-functions1-120315220356-phpapp01
Part 3-functions1-120315220356-phpapp01Part 3-functions1-120315220356-phpapp01
Part 3-functions1-120315220356-phpapp01
 
Think Async: Asynchronous Patterns in NodeJS
Think Async: Asynchronous Patterns in NodeJSThink Async: Asynchronous Patterns in NodeJS
Think Async: Asynchronous Patterns in NodeJS
 
Promise: async programming hero
Promise: async programming heroPromise: async programming hero
Promise: async programming hero
 
PythonOOP
PythonOOPPythonOOP
PythonOOP
 
how to reuse code
how to reuse codehow to reuse code
how to reuse code
 
Java/Scala Lab: Анатолий Кметюк - Scala SubScript: Алгебра для реактивного пр...
Java/Scala Lab: Анатолий Кметюк - Scala SubScript: Алгебра для реактивного пр...Java/Scala Lab: Анатолий Кметюк - Scala SubScript: Алгебра для реактивного пр...
Java/Scala Lab: Анатолий Кметюк - Scala SubScript: Алгебра для реактивного пр...
 
Can you fix the errors- It isn't working when I try to run import s.pdf
Can you fix the errors- It isn't working when I try to run    import s.pdfCan you fix the errors- It isn't working when I try to run    import s.pdf
Can you fix the errors- It isn't working when I try to run import s.pdf
 
FS2 for Fun and Profit
FS2 for Fun and ProfitFS2 for Fun and Profit
FS2 for Fun and Profit
 
JavaScript Advanced - Useful methods to power up your code
JavaScript Advanced - Useful methods to power up your codeJavaScript Advanced - Useful methods to power up your code
JavaScript Advanced - Useful methods to power up your code
 
Elixir and OTP Apps introduction
Elixir and OTP Apps introductionElixir and OTP Apps introduction
Elixir and OTP Apps introduction
 

Más de Elixir Club

Más de Elixir Club (20)

Kubernetes + Docker + Elixir - Alexei Sholik, Andrew Dryga | Elixir Club Ukraine
Kubernetes + Docker + Elixir - Alexei Sholik, Andrew Dryga | Elixir Club UkraineKubernetes + Docker + Elixir - Alexei Sholik, Andrew Dryga | Elixir Club Ukraine
Kubernetes + Docker + Elixir - Alexei Sholik, Andrew Dryga | Elixir Club Ukraine
 
Integrating 3rd parties with Ecto - Eduardo Aguilera | Elixir Club Ukraine
Integrating 3rd parties with Ecto -  Eduardo Aguilera | Elixir Club UkraineIntegrating 3rd parties with Ecto -  Eduardo Aguilera | Elixir Club Ukraine
Integrating 3rd parties with Ecto - Eduardo Aguilera | Elixir Club Ukraine
 
— An async template - Oleksandr Khokhlov | Elixir Club Ukraine
— An async template  -  Oleksandr Khokhlov | Elixir Club Ukraine— An async template  -  Oleksandr Khokhlov | Elixir Club Ukraine
— An async template - Oleksandr Khokhlov | Elixir Club Ukraine
 
BEAM architecture handbook - Andrea Leopardi | Elixir Club Ukraine
BEAM architecture handbook - Andrea Leopardi  | Elixir Club UkraineBEAM architecture handbook - Andrea Leopardi  | Elixir Club Ukraine
BEAM architecture handbook - Andrea Leopardi | Elixir Club Ukraine
 
You ain't gonna need write a GenServer - Ulisses Almeida | Elixir Club Ukraine
You ain't gonna need write a GenServer - Ulisses Almeida  | Elixir Club UkraineYou ain't gonna need write a GenServer - Ulisses Almeida  | Elixir Club Ukraine
You ain't gonna need write a GenServer - Ulisses Almeida | Elixir Club Ukraine
 
— Knock, knock — An async templates — Who’s there? - Alexander Khokhlov | ...
 — Knock, knock — An async templates — Who’s there? - Alexander Khokhlov  |  ... — Knock, knock — An async templates — Who’s there? - Alexander Khokhlov  |  ...
— Knock, knock — An async templates — Who’s there? - Alexander Khokhlov | ...
 
Performance measurement methodology — Maksym Pugach | Elixir Evening Club 3
Performance measurement methodology — Maksym Pugach | Elixir Evening Club 3Performance measurement methodology — Maksym Pugach | Elixir Evening Club 3
Performance measurement methodology — Maksym Pugach | Elixir Evening Club 3
 
Erlang cluster. How is it? Production experience. — Valerii Vasylkov | Elixi...
Erlang cluster. How is it? Production experience. —  Valerii Vasylkov | Elixi...Erlang cluster. How is it? Production experience. —  Valerii Vasylkov | Elixi...
Erlang cluster. How is it? Production experience. — Valerii Vasylkov | Elixi...
 
Promo Phx4RailsDevs - Volodya Sveredyuk
Promo Phx4RailsDevs - Volodya SveredyukPromo Phx4RailsDevs - Volodya Sveredyuk
Promo Phx4RailsDevs - Volodya Sveredyuk
 
Web of today — Alexander Khokhlov
Web of today —  Alexander KhokhlovWeb of today —  Alexander Khokhlov
Web of today — Alexander Khokhlov
 
ElixirConf Eu 2018, what was it like? – Eugene Pirogov
ElixirConf Eu 2018, what was it like? – Eugene PirogovElixirConf Eu 2018, what was it like? – Eugene Pirogov
ElixirConf Eu 2018, what was it like? – Eugene Pirogov
 
Implementing GraphQL API in Elixir – Victor Deryagin
Implementing GraphQL API in Elixir – Victor DeryaginImplementing GraphQL API in Elixir – Victor Deryagin
Implementing GraphQL API in Elixir – Victor Deryagin
 
WebPerformance: Why and How? – Stefan Wintermeyer
WebPerformance: Why and How? – Stefan WintermeyerWebPerformance: Why and How? – Stefan Wintermeyer
WebPerformance: Why and How? – Stefan Wintermeyer
 
Russian Doll Paradox: Elixir Web without Phoenix - Alex Rozumii
Russian Doll Paradox: Elixir Web without Phoenix - Alex RozumiiRussian Doll Paradox: Elixir Web without Phoenix - Alex Rozumii
Russian Doll Paradox: Elixir Web without Phoenix - Alex Rozumii
 
Practical Fault Tolerance in Elixir - Alexei Sholik
Practical Fault Tolerance in Elixir - Alexei SholikPractical Fault Tolerance in Elixir - Alexei Sholik
Practical Fault Tolerance in Elixir - Alexei Sholik
 
Phoenix and beyond: Things we do with Elixir - Alexander Khokhlov
Phoenix and beyond: Things we do with Elixir - Alexander KhokhlovPhoenix and beyond: Things we do with Elixir - Alexander Khokhlov
Phoenix and beyond: Things we do with Elixir - Alexander Khokhlov
 
Monads are just monoids in the category of endofunctors - Ike Kurghinyan
Monads are just monoids in the category of endofunctors - Ike KurghinyanMonads are just monoids in the category of endofunctors - Ike Kurghinyan
Monads are just monoids in the category of endofunctors - Ike Kurghinyan
 
Craft effective API with GraphQL and Absinthe - Ihor Katkov
Craft effective API with GraphQL and Absinthe - Ihor KatkovCraft effective API with GraphQL and Absinthe - Ihor Katkov
Craft effective API with GraphQL and Absinthe - Ihor Katkov
 
Elixir in a service of government - Alex Troush
Elixir in a service of government - Alex TroushElixir in a service of government - Alex Troush
Elixir in a service of government - Alex Troush
 
Pattern matching in Elixir by example - Alexander Khokhlov
Pattern matching in Elixir by example - Alexander KhokhlovPattern matching in Elixir by example - Alexander Khokhlov
Pattern matching in Elixir by example - Alexander Khokhlov
 

Último

Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Victor Rentea
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
panagenda
 

Último (20)

Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
 
[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
CNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In PakistanCNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In Pakistan
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024
 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : Uncertainty
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024
 
Spring Boot vs Quarkus the ultimate battle - DevoxxUK
Spring Boot vs Quarkus the ultimate battle - DevoxxUKSpring Boot vs Quarkus the ultimate battle - DevoxxUK
Spring Boot vs Quarkus the ultimate battle - DevoxxUK
 

GenServer in Action – Yurii Bodarev

  • 1. GenServer in Action  Elixir Club 11 Ternopil, 2018
  • 3. Process iex(1)> spawn fn -> 1 + 1 end #PID<0.90.0> iex(2)> caller = self() #PID<0.88.0> iex(3)> spawn fn -> send caller, {:result, 1 + 1} end #PID<0.93.0> iex(4)> receive do ...(4)> {:result, result} -> result ...(4)> end 2
  • 4. Process iex(5)> spawn fn -> ...(5)> Process.sleep(6_000) ...(5)> send caller, {:result, 1 + 1} ...(5)> end #PID<0.106.0> iex(6)> receive do ...(6)> {:result, result} -> result ...(6)> end ⏳⏳⏳⏳⏳⌛ 2
  • 5. Process iex(7)> spawn fn -> ...(7)> Process.sleep(666_666_666) ...(7)> send caller, {:result, 1 + 1} ...(7)> end #PID<0.99.0> iex(8)> spawn fn -> :nothing end #PID<0.101.0> iex(9)> spawn fn -> raise "error" end #PID<0.103.0> … iex(11)> receive do ...(11)> {:result, result} -> result ...(11)> end ⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳⏳ 💤 💤 💤 CTRL+C
  • 6. Process: timeout iex(1)> caller = self() #PID<0.88.0> iex(2)> spawn fn -> ...(2)> Process.sleep(666_666_666) ...(2)> send caller, {:result, 1 + 1} ...(2)> end #PID<0.94.0> iex(3)> receive do ...(3)> {:result, result} -> {:ok, result} ...(3)> after ...(3)> 5_000 -> {:error, :timeout} ...(3)> end ⏳⏳⏳⏳⌛{:error, :timeout}
  • 7. Process: message queue iex(4)> send self(), {:message, 1} iex(5)> send self(), {:another_message, 1} iex(6)> send self(), {:message, 2} iex(7)> send self(), {:another_message, 2} iex(8)> send self(), {:message, 3} iex(9)> send self(), {:another_message, 3} iex(10)> Process.info(self(), :messages) {:messages, [ message: 1, another_message: 1, message: 2, another_message: 2, message: 3, another_message: 3 ]}
  • 8. Process: message queue {:messages, [ message: 1, another_message: 10, message: 2, another_message: 20, message: 3, another_message: 30 ]} iex(11)> receive do {:another_message, n} -> n end 10 iex(12)> receive do {:another_message, n} -> n end 20 iex(13)> Process.info(self(), :messages) {:messages, [message: 1, message: 2, message: 3, another_message: 30]} iex(14)> receive do {:message, n} -> n end 1 iex(15)> Process.info(self(), :messages) {:messages, [message: 2, message: 3, another_message: 30]} iex(16)> receive do {:something, n} -> n after 0 -> :no_matching_message end :no_matching_message
  • 10. Concurrent Tasks Task.async / Task.await ex(17)> task = Task.async(fn -> 1 + 1 end) %Task{ owner: #PID<0.88.0>, pid: #PID<0.106.0>, ref: #Reference<0.1183799544.2034761732.30152> } iex(18)> Task.await(task) 2
  • 11. Concurrent Tasks Task.async / Task.await iex(19)> tasks = Enum.map(1..10, &Task.async(fn -> ...(19)> Process.sleep(3_000) ...(19)> &1 * 100 ...(19)> end)) […] iex(20)> Process.sleep(5_000) :ok iex(21)> Enum.map(tasks, &Task.await(&1)) [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000]
  • 12. Concurrent Tasks Task.await timeout await(task, timeout 5000) … A timeout, in milliseconds, can be given with default value of 5000. If the timeout is exceeded, then the current process will exit. … iex(22)> Task.async(fn -> Process.sleep(6_000) end) |> Task.await() ** (exit) exited in: Task.await(%Task{owner: #PID<0.88.0>, pid: #PID<0.156.0>, ref: #Reference<0.891801449.431751175.151026>}, 5000) ** (EXIT) time out (elixir) lib/task.ex:501: Task.await/2
  • 13. Storing state defmodule Storage do def recursive(state) do receive do {:add, caller_pid, value} -> new_state = state + value send(caller_pid, {:result, new_state}) recursive(new_state) end end end iex(2)> storage_pid = spawn(fn -> Storage.recursive(0) end) iex(3)> send(storage_pid, {:add, self(), 2}) iex(4)> send(storage_pid, {:add, self(), 2}) iex(5)> send(storage_pid, {:add, self(), 2}) iex(6)> flush() {:result, 2} {:result, 4} {:result, 6}
  • 14. Storing state Agent iex(7)> {:ok, pid} = Agent.start_link(fn -> 0 end) {:ok, #PID<0.101.0>} iex(8)> Agent.update(pid, fn state -> state + 1 end) :ok iex(9)> Agent.get(pid, fn state -> state end) 1 update(agent, fun, timeout 5000) get(agent, module, fun, args, timeout 5000)
  • 16. GenServer A behaviour module for implementing the server of a client- server relation. A GenServer is a process like any other Elixir process and it can be used to keep state, execute code asynchronously and so on. 
  • 17. GenServer defmodule Stack do use GenServer # Client def start_link(default) do GenServer.start_link(__MODULE__, default) end def push(pid, item) do GenServer.cast(pid, {:push, item}) end def pop(pid) do GenServer.call(pid, :pop) end # Server (callbacks) def handle_call(:pop, _from, [h | t]) do {:reply, h, t} end def handle_cast({:push, item}, state) do {:noreply, [item | state]} end end
  • 18. GenServer defmodule Stack do use GenServer # Client def start_link(default) do GenServer.start_link(__MODULE__, default) end def push(pid, item) do GenServer.cast(pid, {:push, item}) end def pop(pid) do GenServer.call(pid, :pop) end # Server (callbacks) def handle_call(:pop, _from, [h | t]) do {:reply, h, t} end def handle_cast({:push, item}, state) do {:noreply, [item | state]} end end Executed in caller process
  • 19. GenServer defmodule Stack do use GenServer # Client def start_link(default) do GenServer.start_link(__MODULE__, default) end def push(pid, item) do GenServer.cast(pid, {:push, item}) end def pop(pid) do GenServer.call(pid, :pop) end # Server (callbacks) def handle_call(:pop, _from, [h | t]) do {:reply, h, t} end def handle_cast({:push, item}, state) do {:noreply, [item | state]} end end Executed in server process
  • 20. GenServer: timeout call(server, request, timeout 5000) def push(pid, item) do GenServer.cast(pid, {:push, item}) end def pop(pid) do GenServer.call(pid, :pop) end # Server (callbacks) @impl true def handle_call(:pop, _from, [h | t]) do Process.sleep(30_000) {:reply, h, t} end @impl true def handle_cast({:push, item}, state) do {:noreply, [item | state]} end Executed in caller process Executed in server process
  • 21. GenServer: timeout iex(2)> {:ok, pid} = Stack.start_link([]) {:ok, #PID<0.95.0>} iex(3)> Enum.each(1..5, &Stack.push(pid, &1)) :ok ex(4)> Stack.pop(pid) ** (exit) exited in: GenServer.call(#PID<0.95.0>, :pop, 5000) ** (EXIT) time out (elixir) lib/gen_server.ex:836: GenServer.call/3 iex(4)> Stack.pop(pid) ** (exit) exited in: GenServer.call(#PID<0.95.0>, :pop, 5000) ** (EXIT) time out (elixir) lib/gen_server.ex:836: GenServer.call/3 iex(4)> Stack.pop(pid) ** (exit) exited in: GenServer.call(#PID<0.95.0>, :pop, 5000) ** (EXIT) time out (elixir) lib/gen_server.ex:836: GenServer.call/3 iex(4)> Stack.pop(pid) ** (exit) exited in: GenServer.call(#PID<0.95.0>, :pop, 5000) ** (EXIT) time out (elixir) lib/gen_server.ex:836: GenServer.call/3 iex(4)> Process.info(pid, :messages) {:messages, [ {:"$gen_call", {#PID<0.88.0>, #Reference<0.3022523149.3093823489.83754>}, :pop}, {:"$gen_call", {#PID<0.88.0>, #Reference<0.3022523149.3093823489.83774>}, :pop}, {:"$gen_call", {#PID<0.88.0>, #Reference<0.3022523149.3093823489.83794>}, :pop} ]} Executed in caller process Executed in server process
  • 22. GenServer: timeout ex(5)> Process.info(pid, :messages) {:messages, [ {:"$gen_call", {#PID<0.88.0>, #Reference<0.3022523149.3093823489.83794>}, :pop} ]} ex(6)> Process.info(pid, :messages) {:messages, []} iex(7)> flush() {#Reference<0.3022523149.3093823489.83730>, 5} {#Reference<0.3022523149.3093823489.83754>, 4} {#Reference<0.3022523149.3093823489.83774>, 3} {#Reference<0.3022523149.3093823489.83794>, 2} :ok
  • 24. Digital Signature Microservice • Microservice - part of the Ukrainian eHealth system infrastructure • Digital signature validation • Ukrainian standard DSTU 4145-2002 • Integration with proprietary C library via NIF
  • 25. Digital Signature Microservice Digital Signature Microservice NIF Proprietary DSTU 4145-2002 LIB Digitally signed content < 5s Decoded content Signer info Signature validation Certificates OSCP Server
  • 26. {:ok, result} = DigitalSignatureLib.processPKCS7Data(data, certs, true) result == %{ content: "{"hello": "world"}", is_valid: true, signer: %{ common_name: "XXX YYY ZZZ", country_name: "UA", drfo: "1234567890", edrpou: "", given_name: "XX YY", locality_name: "М. КИЇВ", organization_name: "ФІЗИЧНА ОСОБА", organizational_unit_name: "", state_or_province_name: "", surname: "XX", title: "" }, validation_error_message: "" }
  • 27. Sounds easy? Digital Signature Microservice DigitalSignatureLib.processPKCS7Data(…) DigitalSignatureLib.processPKCS7Data(…) DigitalSignatureLib.processPKCS7Data(…) DigitalSignatureLib.processPKCS7Data(…) Elixir Process Elixir Process Elixir Process Elixir Process Incoming Requests 200 OK 200 OK 200 OK 200 OK Responses
  • 28.
  • 29. Problem 1: concurrency tasks = Enum.map(1..25, fn _ -> Task.async(fn -> DigitalSignatureLib.processPKCS7Data(data, certs, true) end) end) Enum.map(tasks, fn task -> {:ok, result} = Task.await(task, 30000) IO.inspect(result) end)
  • 30. %{ content: *** } %{ content: *** } %{ content: *** } %{ content: *** } *** Error in `/usr/local/lib/erlang/erts-9.3.3/bin/beam.smp': double free or corruption (fasttop): 0x00007f88e401d060 *** ======= Backtrace: ========= /lib/x86_64-linux-gnu/libc.so.6(+0x70bfb)[0x7f8962452bfb] /lib/x86_64-linux-gnu/libc.so.6(+0x76fc6)[0x7f8962458fc6] /lib/x86_64-linux-gnu/libc.so.6(+0x7780e)[0x7f896245980e] /usr/local/lib/libUACryptoQ.so.1(_ZN9DataBlockaSERKS_+0x3c)[0x7f88a7bd785c] /usr/local/lib/libUACryptoQ.so.1(_ZN4DataaSERKS_+0x11)[0x7f88a7bd6771] /usr/local/lib/libUACryptoQ.so.1(_ZN7DataSet3NewERK4Data+0x3c)[0x7f88a7bd81cc] /usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC16ObjectIdentifier10EncodeBodyEv+0x1fc)[0x7f88a7bf238c] /usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC5Token6EncodeEv+0x46)[0x7f88a7bef236] /usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC11ObjectToken10EncodeBodyEv+0x17)[0x7f88a7bef3c7] /usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC5Token6EncodeEv+0x46)[0x7f88a7bef236] /usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC11Constructed10EncodeBodyEv+0x45)[0x7f88a7be97d5] /usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC11Constructed6EncodeEv+0x39)[0x7f88a7bec089] /usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC11Constructed10EncodeBodyEv+0x45)[0x7f88a7be97d5] /usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC11Constructed6EncodeEv+0x39)[0x7f88a7bec089] /usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC10CMSContent6EncodeEv+0x95)[0x7f88a7c0a2d5] /usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC11ObjectToken10EncodeBodyEv+0x3a)[0x7f88a7bef3ea] /usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC5Token6EncodeEv+0x46)[0x7f88a7bef236] /usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC11Constructed10EncodeBodyEv+0x45)[0x7f88a7be97d5] /usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC11Constructed6EncodeEv+0x39)[0x7f88a7bec089] /usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC10CMSContent6EncodeEv+0x95)[0x7f88a7c0a2d5] /usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC11ObjectToken10EncodeBodyEv+0x3a)[0x7f88a7bef3ea] /usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC5Token6EncodeEv+0x46)[0x7f88a7bef236] /usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC11Constructed10EncodeBodyEv+0x45)[0x7f88a7be97d5] /usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC11Constructed6EncodeEv+0x39)[0x7f88a7bec089] /usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC17ObjectWithContent6EncodeEv+0x1b)[0x7f88a7be946b] /usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC11ObjectToken10EncodeBodyEv+0x3a)[0x7f88a7bef3ea] /usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC5Token6EncodeEv+0x46)[0x7f88a7bef236] /usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC11Constructed10EncodeBodyEv+0x45)[0x7f88a7be97d5] /usr/local/lib/libUACryptoQ.so.1(_ZNK3UAC11Constructed6EncodeEv+0x39)[0x7f88a7bec089] /usr/local/lib/libUACryptoQ.so.1(_ZN3UAC13CMSSignedData15VerifySignatureEPNS_11CertificateEP11_UAC_STREAM+0x9f5)[0x7f88a7c12965] /usr/local/lib/libUACryptoQ.so.1(UAC_SignedDataVerify+0x2ba)[0x7f88a7c4ff1a] /home/digital_signature.lib/_build/test/lib/digital_signature_lib/priv/digital_signature_lib_nif.so(Check+0x3c5)[0x7f891c0fc7f5] /home/digital_signature.lib/_build/test/lib/digital_signature_lib/priv/digital_signature_lib_nif.so(+0x2bac)[0x7f891c0fbbac] /usr/local/lib/erlang/erts-9.3.3/bin/beam.smp(erts_call_dirty_nif+0x19c)[0x5ce04c] /usr/local/lib/erlang/erts-9.3.3/bin/beam.smp(erts_dirty_process_main+0x209)[0x445949] /usr/local/lib/erlang/erts-9.3.3/bin/beam.smp[0x4f4a4d] /usr/local/lib/erlang/erts-9.3.3/bin/beam.smp[0x675985] /lib/x86_64-linux-gnu/libpthread.so.0(+0x7494)[0x7f8962990494] /lib/x86_64-linux-gnu/libc.so.6(clone+0x3f)[0x7f89624caacf] ======= Memory map: ======== 00400000-0073b000 r-xp 00000000 08:01 2376229 /usr/local/lib/erlang/erts-9.3.3/bin/beam.smp 0093a000-0093b000 r--p 0033a000 08:01 2376229 /usr/local/lib/erlang/erts-9.3.3/bin/beam.smp 0093b000-00956000 rw-p 0033b000 08:01 2376229 /usr/local/lib/erlang/erts-9.3.3/bin/beam.smp 00956000-0097d000 rw-p 00000000 00:00 0 0231e000-02349000 rw-p 00000000 00:00 0 [heap]
  • 31. Problem 1 solution: Gen Server ... use GenServer # Callbacks ... def handle_call({:process_signed_content, signed_content, check}, _from, {certs_cache_ttl, certs}) do check = unless is_boolean(check), do: true processing_result = do_process_signed_content(signed_content, certs, check, SignedData.new()) {:reply, processing_result, {certs_cache_ttl, certs}} end ... # Client ... def process_signed_content(signed_content, check) do GenServer.call(__MODULE__, {:process_signed_content, signed_content, check}) end
  • 32. Problem 1 solution: Gen Server NifService (GenServer) NifService.process_signed_content(…) NifService.process_signed_content(…) NifService.process_signed_content(…) NifService.process_signed_content(…) Requests Message queue DigitalSignatureLib.processPKCS7Data(…) Responses
  • 33. Problem 2: timeouts NifService (GenServer) Multiple concurrent requests (processes) 2s 2s 2s 2s Message queue Sequential responses 200 OK 2s 200 OK 4s 500 Error 5s 500 Error 5s call(server, request, timeout 5000) ** (EXIT) time out ** (EXIT) time out
  • 34. Problem 2: timeouts NifService (GenServer) Multiple concurrent requests (processes) 2s 2s Message queue Sequential responses call(server, request, timeout 5000) 2s 2s 500 Error 5s 500 Error 5s
  • 35. Problem 2 solution: architecture DS DS DS DS DS DS DS DS Load Balancer Reduce pressure on individual MS
  • 36. Problem 2 solution: catch Exit def process_signed_content(signed_content, check) do gen_server_timeout = Confex.fetch_env(:digital_signature_api, :nif_service_timeout) try do GenServer.call(__MODULE__, {:process_signed_content, signed_content, check}, gen_server_timeout) catch :exit, {:timeout, error} -> {:error, {:nif_service_timeout, error}} end end 424 Failed Dependency
  • 37. Problem 3: message queue NifService (GenServer) Processed requests 2s 2s 2s 2s Message queue Sequential responses 200 OK 2s 200 OK 4s 424 5s 424 5s 2s 2s Expired requests New requests
  • 38. Problem 3 solution: pass message expiration time ... # Callbacks def handle_call({:process_signed_content, signed_content, check, message_exp_time}, _from, certs_cache_ttl, certs}) do processing_result = if NaiveDateTime.diff(message_exp_time, NaiveDateTime.utc_now(), :millisecond) > 250 do check = unless is_boolean(check), do: true do_process_signed_content(signed_content, certs, check, SignedData.new()) else {:error, {:nif_service_timeout, "messaqe queue timeout"}} end {:reply, processing_result, {certs_cache_ttl, certs}} End ... # Client def process_signed_content(signed_content, check) do gen_server_timeout = Confex.fetch_env!(:digital_signature_api, :nif_service_timeout) message_exp_time = NaiveDateTime.add(NaiveDateTime.utc_now(), gen_server_timeout, :millisecond) try do GenServer.call(__MODULE__, {:process_signed_content, signed_content, check, message_exp_time}, gen_server_timeout) catch :exit, {:timeout, error} -> {:error, {:nif_service_timeout, error}} end end ...
  • 39. Problem 3 solution: pass message expiration time NifService (GenServer) … Expired Expired Within threshold Message queue …
  • 40. Problem 3 advanced solution: QueueService NifService DigitalSignatureLib. processPKCS7Data(…) QueueService :queue Monitoring & Control
  • 41.
  • 42. Memory management Binary terms which are larger than 64 bytes are not stored in a process private heap. They are called Refc Binary (Reference Counted Binary) and are stored in a large Shared Heap which is accessible by all processes who have the pointer of that Refc Binaries. That pointer is called ProcBin and is stored in a process private heap. The GC for the shared heap is reference counting. Collecting those Refc messages depends on collecting of all ProcBin objects even ones that are inside the middleware process. Unfortunately because ProcBins are just a pointer hence they are so cheap and it could take so long to happen a GC inside the middleware process.
  • 43. NifService Memory management Shared Heap Refc Binary Refc Binary Refc Binary Requests Responses ProcBin ProcBin ProcBin ProcBin ProcBin ProcBin ProcBin ProcBin ProcBin GC GC GC :erlang.garbage_collect(self()) Processes
  • 44. Garbage Collect ... # Callbacks def init(certs_cache_ttl) do certs = CertAPI.get_certs_map() Process.send_after(self(), :refresh, certs_cache_ttl) {:ok, {certs_cache_ttl, certs}} end ... def handle_info(:refresh, {certs_cache_ttl, _certs}) do certs = CertAPI.get_certs_map() # GC :erlang.garbage_collect(self()) Process.send_after(self(), :refresh, certs_cache_ttl) {:noreply, {certs_cache_ttl, certs}} end ...
  • 45. Garbage Collect handle_call(request, from, state) {:reply, reply, new_state, timeout() | :hibernate} Hibernating a GenServer causes garbage collection and leaves a continuous heap that minimises the memory used by the process Returning {:reply, reply, new_state, timeout} is similar to {:reply, reply, new_state} except handle_info(:timeout, new_state) will be called after timeout milliseconds if no messages are received
  • 46. Useful links Saša Jurić "Elixir in Action, Second Edition” https://www.manning.com/books/elixir-in-action-second-edition Andrea Leopardi - Concurrent and Resilient Connections to Outside the BEAM (ElixirConfEU 2016) https://www.youtube.com/watch?time_continue=1884&v=U1Ry7STEFiY GenServer call time-outs https://cultivatehq.com/posts/genserver-call-timeouts/ Elixir Memory - Not Quite Free https://stephenbussey.com/2018/05/09/elixir-memory-not-quite-free.html Erlang Garbage Collection Details and Why It Matters https://hamidreza-s.github.io/erlang%20garbage%20collection%20memory%20layout%20soft%20realtime/ 2015/08/24/erlang-garbage-collection-details-and-why-it-matters.html