I have a problem with a long-time consuming migration, which I desired to run in the parallel (it can be runned in the parallel). Actually migration is about taking all records in the database and implement time- and resource- consuming operations on each of them.
Sometimes individual record migration hanging out, so I give 10 minutes to finish. If migration isn't finished, I want it to gracefully shut down without any exception (see below)
I'm also using poolboy erlang package to parallelize implementation since migration consumes not only the time, but resources too. The problem is that I don't know how to handle error when timeout happened and code is going to break. My supervision tree is:
defmodule MyReelty.Repo.Migrations.MoveVideosFromVimeoToB2 do
use Ecto.Migration
alias MyReelty.Repo
alias MyReelty.Repo.Migrations.MoveVideosFromVimeoToB2.Migrator
# parallel nature of migration force us to disable transaction
#disable_ddl_transaction true
#migrator_waiting_time 10 * 60 * 1000 # timeout
#poolboy_waiting_time #migrator_waiting_time + 10 * 1000 # give a time for graceful shutdown
#pool_name :migrator
#pool_size 3
#pool_config [
{ :name, { :local, #pool_name }},
{ :worker_module, Migrator },
{ :size, #pool_size },
{ :max_overflow, 0 },
{ :strategy, :fifo }
]
def up do
children = [
:poolboy.child_spec(#pool_name, #pool_config)
]
opts = [strategy: :one_for_one, name: MyReelty.Supervisor]
Supervisor.start_link(children, opts)
rows = Review |> Repo.all
IO.puts "Total amount of reviews is: #{length(rows)}"
parallel_migrations(rows)
end
def parallel_migrations(rows) do
Enum.map(rows, fn(row) ->
pooled_migration(#pool_name, row)
end)
end
def pooled_migration(pool, x) do
:poolboy.transaction(
pool,
(fn(pid) -> Migrator.move(pid, { x, #migrator_waiting_time }) end),
#poolboy_waiting_time
)
end
defmodule Migrator do
alias MyReelty.Repo
alias MyReelty.Review
use GenServer
def start_link(_) do
GenServer.start_link(__MODULE__, nil, [])
end
def move(server, { params, waiting_time }) do
GenServer.call(server, { :move, params }, waiting_time)
end
def handle_call({ :move, result }, _from, state) do
big_time_and_resource_consuming_task_here
{:reply, %{}, state}
end
end
end
The problem if migration of some record in the database takes more than 10 mins I have this kind of exception:
20:18:16.917 [error] Task #PID<0.282.0> started from #PID<0.70.0> terminating
** (stop) exited in: GenServer.call(#PID<0.278.0>, {:move, [2, "/videos/164064419", "w 35th st Springfield United States Illinois 60020"]}, 60000)
** (EXIT) time out
(elixir) lib/gen_server.ex:604: GenServer.call/3
(poolboy) src/poolboy.erl:76: :poolboy.transaction/3
(elixir) lib/task/supervised.ex:94: Task.Supervised.do_apply/2
(elixir) lib/task/supervised.ex:45: Task.Supervised.reply/5
(stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3
Function: #Function<5.53617785/0 in MyReelty.Repo.Migrations.MoveVideosFromVimeoToB2.parallel_migrations/1>
Args: []
20:18:16.918 [error] GenServer MyReelty.Repo terminating
** (stop) exited in: GenServer.call(#PID<0.278.0>, {:move, [2, "/videos/164064419", "w 35th st Springfield United States Illinois 60020"]}, 60000)
** (EXIT) time out
Last message: {:EXIT, #PID<0.70.0>, {:timeout, {GenServer, :call, [#PID<0.278.0>, {:move, [2, "/videos/164064419", "w 35th st Springfield United States Illinois 60020"]}, 60000]}}}
State: {:state, {:local, MyReelty.Repo}, :one_for_one, [{:child, #PID<0.231.0>, DBConnection.Poolboy, {:poolboy, :start_link, [[name: {:local, MyReelty.Repo.Pool}, strategy: :fifo, size: 1, max_overflow: 0, worker_module: DBConnection.Poolboy.Worker], {Postgrex.Protocol, [types: true, username: "adik", types: true, name: MyReelty.Repo.Pool, otp_app: :my_reelty, repo: MyReelty.Repo, adapter: Ecto.Adapters.Postgres, database: "my_reelty_dev", hostname: "localhost", extensions: [{Geo.PostGIS.Extension, [library: Geo]}, {Ecto.Adapters.Postgres.DateTime, []}, {Postgrex.Extensions.JSON, [library: Poison]}], pool_size: 1, pool_timeout: 5000, timeout: 15000, adapter: Ecto.Adapters.Postgres, database: "my_dev", hostname: "localhost", pool_size: 10, pool: DBConnection.Poolboy, port: 5432]}]}, :permanent, 5000, :worker, [:poolboy]}], :undefined, 3, 5, [], 0, Ecto.Repo.Supervisor, {MyReelty.Repo, :my_reelty, Ecto.Adapters.Postgres, [otp_app: :my_reelty, repo: MyReelty.Repo, adapter: Ecto.Adapters.Postgres, database: "my_reelty_dev", hostname: "localhost", extensions: [{Geo.PostGIS.Extension, [library: Geo]}], pool_size: 1]}}
I tried to insert terminate/2 or handle_info/2 to Migrator and play with it, but I even haven't reached this functions to be invoked. How can I handle timeouts and prevent them to break my migration?
UPDATED
I used #johlo's hint, but I still getting time out. My function is:
def init(_) do
Process.flag(:trap_exit, true)
{:ok, %{}}
end
When the Migrator.move/2 (i.e. the GenServer.call) function times out it will crash the entire MoveVideosFromVimeoToB2 process since that's the actual process that makes the GenServer call.
The solution here is to catch the timeout in the anonymous function in pooled_migration, something like (I'm not very familiar with Elixir syntax, so it might not compile, but you should get the idea) :
def pooled_migration(pool, x) do
:poolboy.transaction(
pool,
(fn(pid) ->
try do
Migrator.move(pid, { x, #migrator_waiting_time })
catch
:exit, reason ->
# Ignore error, log it or something else
:ok
end
end),
#poolboy_waiting_time
)
end
It's not the Migrator process that times out, it's the GenServer call to the Migrator that does and we need to try-catch that.
Also note that the Migrator process isn't killed it is still running, see the timeouts section in the GenServer call documentation.
UPDATE:
As #asiniy mentions in the comments the #poolboy_waiting_time should be set to :infinity so the poolboy.transaction function doesn't throw a timeout error when waiting for a free Migrator worker process. Since the Migrator will exit eventually this is safe.
Related
Ive been trying to instantiate a genserver process that will subscribe to PubSub in Phoenix framework, these are my files and errors:
config.ex:
config :excalibur, Excalibur.Endpoint,
pubsub: [
adapter: Phoenix.PubSub.PG2,
name: Excalibur.PubSub
]
Module using genserver:
defmodule RulesEngine.Router do
import RulesEngine.Evaluator
use GenServer
alias Excalibur.PubSub
def start_link(_) do
GenServer.start_link(__MODULE__, name: __MODULE__)
end
def init(_) do
{:ok, {Phoenix.PubSub.subscribe(PubSub, :evaluator)}}
IO.puts("subscribed")
end
# Callbacks
def handle_info(%{}) do
IO.puts("received")
end
def handle_call({:get, key}, _from, state) do
{:reply, Map.fetch!(state, key), state}
end
end
and when i do a iex -S mix this error happens:
** (Mix) Could not start application excalibur: Excalibur.Application.start(:normal, []) returned an error: shutdown: failed to start child: RulesEngine.Router
** (EXIT) an exception was raised:
** (FunctionClauseError) no function clause matching in Phoenix.PubSub.subscribe/2
(phoenix_pubsub 1.1.2) lib/phoenix/pubsub.ex:151: Phoenix.PubSub.subscribe(Excalibur.PubSub, :evaluator)
(excalibur 0.1.0) lib/rules_engine/router.ex:11: RulesEngine.Router.init/1
(stdlib 3.12.1) gen_server.erl:374: :gen_server.init_it/2
(stdlib 3.12.1) gen_server.erl:342: :gen_server.init_it/6
(stdlib 3.12.1) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
So which is the correct way to spin up a Genserver that will subscribe to the PubSub topic ?
As by documentation, Phoenix.PubSub.subscribe/3 has the following spec:
#spec subscribe(t(), topic(), keyword()) :: :ok | {:error, term()}
where #type topic :: binary(). That said, your init/1 should look like
def init(_) do
{:ok, {Phoenix.PubSub.subscribe(PubSub, "evaluator")}}
|> IO.inspect(label: "subscribed")
end
Please note I also altered IO.puts/1 to IO.inspect/2 to preserve the returned value.
I have a problem with communication with the workers created in my dynamicSupervisor. after start all my workers i try make a call to one by pid and is generated one error (always).
At the beginning of the child I keep the pid with ets. Then when I want to make a call to this child I get the pid from the ets by an identifier. So far so good.
The problem is when I do the following:
GenServer.call(
pid,
{:action_project, %{project_id: project_id, pid: :erlang.pid_to_list(pid)}}
)
returns following error:
[error] GenServer #PID<0.606.0> terminating
** (RuntimeError) attempted to call GenServer #PID<0.606.0> but no handle_call/3 clause was provided
(backercamp) lib/gen_server.ex:693: MyApplication.ProjectWorker.handle_call/3
(stdlib) gen_server.erl:661: :gen_server.try_handle_call/4
(stdlib) gen_server.erl:690: :gen_server.handle_msg/6
(stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Last message (from #PID<0.680.0>): {:action_project, %{pid: '<0.606.0>', project_id: "23"}}
State: %{pid: '<0.606.0>', project_id: "23"}
Client #PID<0.680.0> is alive
(stdlib) gen.erl:169: :gen.do_call/4
(elixir) lib/gen_server.ex:921: GenServer.call/3
What's the problem with this call?
DynamicSupervisor code:
defmodule MyApplication.Supervisor do
use DynamicSupervisor
alias MyApplication.Management
def start_link(arg) do
DynamicSupervisor.start_link(__MODULE__, arg, name: __MODULE__)
end
def init(arg) do
DynamicSupervisor.init(arg)
end
def start_project_worker(project_id) do
spec = {MyApplication.ProjectWorker, %{project_id: project_id}}
DynamicSupervisor.start_child(__MODULE__, spec)
end
def start_all_project_workers() do
Enum.each(Management.list_all_projects(), fn %{id: project_id} ->
IO.puts("Started for project Id [#{project_id}]")
# 1s between workers
:timer.sleep(1000)
start_project_worker("#{project_id}")
end)
end
def action_project(pid, project_id) do
GenServer.call(
pid,
{:action_project, %{project_id: project_id, pid: :erlang.pid_to_list(pid)}}
)
end
end
Worker code:
defmodule MyApplication.ProjectWorker do
use GenServer, restart: :transient
alias MyApplication.Settings
alias MyApplication.Management
def start_link(state) do
GenServer.start_link(__MODULE__, state)
end
def init(state) do
schedule_action_project(Settings.get_frequency_ms())
state = Map.put(state, :pid, :erlang.pid_to_list(self()))
persist_state(state)
{:ok, state}
end
def handle_info(:schedule_action_project, %{project_id: _project_id, pid: _pid} = state) do
action_by_state(state)
{:noreply, state}
end
def handle_call({:action_project}, %{project_id: _project_id, pid: _pid} = state) do
case action_by_state(state) do
true ->
terminate(state)
false ->
{:reply, state, state}
end
end
defp persist_state(state) do
IO.puts(" :: Add project_id [#{state.project_id}] and pid #{state.pid}")
:ets.insert_new(:project_backup, {state.project_id, state.pid})
end
defp delete_persist_state(project_id) do
IO.puts(" :: Delete project_id [#{project_id}]")
:ets.delete(:project_backup, project_id)
end
defp schedule_action_project(time) do
IO.puts(" :: Schedule_action_project [#{time}]")
Process.send_after(self(), :schedule_action_project, time)
end
defp terminate(%{project_id: project_id, pid: _pid} = state) do
IO.puts(
" :: Stop processed, everything is done! project_id [#{state.project_id}] and pid #{
state.pid
}"
)
delete_persist_state(project_id)
{:stop, :normal, state}
end
defp action_by_state(%{project_id: _project_id, pid: _pid} = state) do
action_by_project(Management.get_project!(state.project_id))
end
defp action_by_project(%Project{} = project) do
#do something in project
end
end
The issue is within a handler. You messed all the arguments up. state is an internal state of GenServer, you do not pass it through, you receive it in the callback. The following signature will match if your state is a map having keys project_id and pid and you call it like GenServer.call(pid, {:action_project}).
def handle_call({:action_project}, %{project_id: _project_id, pid: _pid} = state)
You should change this handler to match your caller:
def handle_call(
{:action_project, %{project_id: _project_id, pid: _pid}},
_from,
state
)
Also note that handle_call callback receives three parameters (unlike handle_cast, the second one being the source of the message.
Sidenote: handle_info callback has the wrong signature as well.
When trying to start my process from a DynamicSupervisor im getting the following error:
{:error,
{:EXIT,
{:badarg,
[
{:erlang, :apply,
[
BfgEngine.MarketService,
:start_link,
{{BfgEngine.MarketService, :start_link, ["1111"]}, :permanent, 5000,
:worker, [BfgEngine.MarketService]}
], []},
{:supervisor, :do_start_child_i, 3, [file: 'supervisor.erl', line: 379]},
{:supervisor, :handle_call, 3, [file: 'supervisor.erl', line: 404]},
{:gen_server, :try_handle_call, 4, [file: 'gen_server.erl', line: 661]},
{:gen_server, :handle_msg, 6, [file: 'gen_server.erl', line: 690]},
{:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 249]}
]}}}
The code im using is:
def start_market(market_id) do
spec = {MarketService, market_id}
DynamicSupervisor.start_child(__MODULE__, spec)
end
However it is not clear to me what is going wrong. What argument to which function is it thats not correct? How do I break down and read the given error message?
Update:
This is the init method of my supervisor:
#impl true
def init(initial_arg) do
DynamicSupervisor.init(
strategy: :one_for_one,
extra_arguments: [initial_arg]
)
end
Update 2:
This is the start_link of market_service:
def start_link(market_id) when is_bitstring(market_id) do
GenServer.start_link(__MODULE__, market_id, name: via_tuple(market_id))
end
Im using the default child_spec im getting from GenServer
Update 3:
Changing to:
def start_market(market_id) do
spec = {MarketService, market_id: market_id}
DynamicSupervisor.start_child(__MODULE__, spec)
end
Gives:
{:error,
{:undef,
[
{BfgEngine.MarketService, :start_link, [[], [market_id: "222"]], []},
{DynamicSupervisor, :start_child, 3,
[file: 'lib/dynamic_supervisor.ex', line: 654]},
{DynamicSupervisor, :handle_start_child, 2,
[file: 'lib/dynamic_supervisor.ex', line: 640]},
{:gen_server, :try_handle_call, 4, [file: 'gen_server.erl', line: 661]},
{:gen_server, :handle_msg, 6, [file: 'gen_server.erl', line: 690]},
{:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 249]}
]}}
Changing to:
def start_market(market_id) do
spec = {MarketService, :market_id, market_id}
DynamicSupervisor.start_child(__MODULE__, spec)
end
Gives:
** (ArgumentError) supervisors expect each child to be one of:
* a module
* a {module, arg} tuple
* a child specification as a map with at least the :id and :start fields
* or a tuple with 6 elements generated by Supervisor.Spec (deprecated)
Got: {BfgEngine.MarketService, :market_id, "222"}
(elixir) lib/supervisor.ex:657: Supervisor.init_child/1
(elixir) lib/supervisor.ex:744: Supervisor.child_spec/2
(elixir) lib/dynamic_supervisor.ex:304: DynamicSupervisor.start_child/2
You got badarg exception to function erlang:apply/3 when there are three arguments BfgEngine.MarketService, :start_link and {{BfgEngine.MarketService, :start_link, ["1111"]}, :permanent, 5000, :worker, [BfgEngine.MarketService]} and it happen in function supervisor:do_start_child_i/3.
The arguments to the function erlang:apply/3 should be MFA a.k.a Module, Function, Arguments. {{BfgEngine.MarketService, :start_link, ["1111"]}, :permanent, 5000, :worker, [BfgEngine.MarketService]} is not Arguments because it obviously is not a list of arguments. From your code, I can guess the error is the content of variable spec. You should provide some proplist or map. I don't know, you should read the documentation of DynamicSupervisor more carefully.
The error message
As of understanding the error message thrown by Elixir, you can refer to the official Erlang documentation. The section about errors and exceptions from Learn You Some Erlang For Great Good can help. The answer of #Hynek -Pichi Vychodil is also accurate.
Your specific problem
As #Milan Jaric mentioned, your error would come from:
def start_market(market_id) do
spec = {MarketService, market_id}
DynamicSupervisor.start_child(__MODULE__, spec)
end
But not only! DynamicSupervisor.start_child(__MODULE__, spec) is calling MarketService.start_link/1!
Your problem lies in the combination of this function from the DynamicSupervisor module, and the way you parse values in MarketService.start_link/1:
def start_link(market_id) when is_bitstring(market_id) do
GenServer.start_link(__MODULE__, market_id, name: via_tuple(market_id))
end
Indeed, this code should work, if you also have implemented MarketService.init/1 correctly. I am not able to reproduce the error. Are you sure market_id is really a bitstring?
Personally, I had based my code on the official documentation:
defmodule MySupervisor do
use DynamicSupervisor
def start_link(init_arg) do
DynamicSupervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
end
def start_child(foo, bar, baz) do
# If MyWorker is not using the new child specs, we need to pass a map:
# spec = %{id: MyWorker, start: {MyWorker, :start_link, [foo, bar, baz]}}
spec = {MyWorker, foo: foo, bar: bar, baz: baz}
DynamicSupervisor.start_child(__MODULE__, spec)
end
#impl true
def init(init_arg) do
DynamicSupervisor.init(
strategy: :one_for_one,
extra_arguments: [init_arg]
)
end
end
As you can see, they suggest to use a Keyword list here:
spec = {MyWorker, foo: foo, bar: bar, baz: baz}
It works only if you've implemented MyWorker.start_link/1 as follows:
def start_link(args) do
foo = Keyword.fetch!(args, :foo)
bar = Keyword.fetch!(args, :bar)
baz = Keyword.fetch!(args, :baz)
Genserver.start_link(__MODULE__, {foo, bar, baz}, [])
def init({foo, bar, baz}) do
# do something...
state = {foo, bar, baz}
{:ok, state}
In your case, if you change start_market/1 to:
def start_market(market_id) do
spec = {MarketService, market_id: market_id}
DynamicSupervisor.start_child(__MODULE__, spec)
end
It will not work because this MarketService.start_link/1 will fail:
def start_link(market_id) when is_bitstring(market_id) do
GenServer.start_link(__MODULE__, market_id, name: via_tuple(market_id))
end
market_id here is not a bitstring but a Keyword list. So you got to modify MarketService.start_link/1 function to:
def start_link(args) when is_list(args) do
market_id = Keyword.fetch!(args, :market_id)
GenServer.start_link(__MODULE__, market_id, name: via_tuple(market_id))
end
And write a MarketService.init/1 as follows:
def init(market_id) do
# do something... Let's keep it simple for the example:
state = market_id
{:ok, state}
end
Helpful resources
José Valim's answer on a similar issue posted on Elixir Forum
Official module-based DynamicSupervisor documentation
A well-written series of articles about GenServer and Supervisors in Elixir...
...and its corresponding source code
It is hard to tell from posted code, but could you try changing start_market to:
def start_market(market_id) do
spec = {MarketService, :market_id, market_id}
DynamicSupervisor.start_child(__MODULE__, spec)
end
UPDATE (Below are two options):
def start_market(market_id) do
spec = &{
id: MarketService,
start: {MarketService, start_link, [market_id]},
type: :worker
}
DynamicSupervisor.start_child(__MODULE__, spec)
end
OR
def start_market(market_id) do
spec = {MarketService, [market_id]}
DynamicSupervisor.start_child(__MODULE__, spec)
end
I want to communicate between Elixir and Python. I don't want to use NIFs and stuff - I prefer loosely coupled using zeroMQ as this will allow me to use other languages than Python later. I am using the chumak library which is a native implementation of zeromq in Erlang, and seems well maintained. I have used it successfully in the past for pub sub.
Apart from pub-sub, I'm finding that req-rep and req-router sockets work fine. However dealer-router does not. This is really important because only dealer and router give you true async in zeromq.
Here is the python code for the router side:
import zmq
context = zmq.Context()
rout = context.socket(zmq.ROUTER)
rout.bind("tcp://192.168.1.192:8760")
Here is the Elixir req code which works fine...
iex(1)> {ok, sock1} = :chumak.socket(:req, 'reqid')
{:ok, #PID<0.162.0>}
iex(2)> {ok, _peer} = :chumak.connect(sock1, :tcp, '192.168.1.192', 8760)
{:ok, #PID<0.164.0>}
iex(3)> :chumak.send(sock1, 'hello from req socket')
:ok
.... because I get it on the Python side:
In [5]: xx = rout.recv_multipart()
In [6]: xx
Out[6]: ['reqid', '', 'hello from req socket']
However, here is what I get if I try a dealer socket on the Elixir side:
iex(4)> {ok, sock2} = :chumak.socket(:dealer, 'dealid')
{:ok, #PID<0.170.0>}
iex(5)> {ok, _peer} = :chumak.connect(sock2, :tcp, '192.168.1.192', 8760)
{:ok, #PID<0.172.0>}
iex(6)> :chumak.send(sock2, 'hello from dealer socket')
{:error, :not_implemented_yet}
iex(7)> :chumak.send_multipart(sock2, ['a', 'b', 'hello from dealer socket'])
22:13:38.705 [error] GenServer #PID<0.172.0> terminating
** (FunctionClauseError) no function clause matching in :chumak_protocol.encode_more_message/3
(chumak) /home/tbrowne/code/elixir/chutest/deps/chumak/src/chumak_protocol.erl:676: :chumak_protocol.encode_more_message('a', :null, %{})
(stdlib) lists.erl:1354: :lists.mapfoldl/3
(chumak) /home/tbrowne/code/elixir/chutest/deps/chumak/src/chumak_protocol.erl:664: :chumak_protocol.encode_message_multipart/3
(chumak) /home/tbrowne/code/elixir/chutest/deps/chumak/src/chumak_peer.erl:159: :chumak_peer.handle_cast/2
(stdlib) gen_server.erl:616: :gen_server.try_dispatch/4
(stdlib) gen_server.erl:686: :gen_server.handle_msg/6
(stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3
Last message: {:"$gen_cast", {:send, ['a', 'b', 'hello from dealer socket'], {#PID<0.160.0>, #Reference<0.79795089.2401763329.172383>}}}
State: {:state, :ready, '192.168.1.192', 8760, :client, [], :dealer, 'dealid', [], {3, 0}, #Port<0.4968>, {:decoder, :ready, 0, nil, nil, {:some, 3}, {:some, 0}, %{}, :null, false}, #PID<0.170.0>, {[], []}, [], false, false, false, :null, %{}}
22:13:38.710 [info] [:unhandled_handle_info, {:module, :chumak_socket}, {:msg, {:EXIT, #PID<0.172.0>, {:function_clause, [{:chumak_protocol, :encode_more_message, ['a', :null, %{}], [file: '/home/tbrowne/code/elixir/chutest/deps/chumak/src/chumak_protocol.erl', line: 676]}, {:lists, :mapfoldl, 3, [file: 'lists.erl', line: 1354]}, {:chumak_protocol, :encode_message_multipart, 3, [file: '/home/tbrowne/code/elixir/chutest/deps/chumak/src/chumak_protocol.erl', line: 664]}, {:chumak_peer, :handle_cast, 2, [file: '/home/tbrowne/code/elixir/chutest/deps/chumak/src/chumak_peer.erl', line: 159]}, {:gen_server, :try_dispatch, 4, [file: 'gen_server.erl', line: 616]}, {:gen_server, :handle_msg, 6, [file: 'gen_server.erl', line: 686]}, {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 247]}]}}}]
As you can see I get this huge error on the :chumak.send_multipart, while :chumak.send doesn't even work. What's going on here?
The dealer socket works fine by the way from the Python side:
import zmq
context = zmq.Context()
deal = context.socket(zmq.DEALER)
deal.setsockopt_string(zmq.IDENTITY, u"Thomas")
deal.connect("tcp://192.168.1.192:8760")
deal.send("hello from python deal")
Now on router side:
In [5]: xx = rout.recv_multipart()
In [6]: xx
Out[6]: ['reqid', '', 'hello from req socket']
In [7]: dd = rout.recv_multipart()
In [8]: dd
Out[8]: ['Thomas', 'hello from python deal']
So I'm wondering if I have a syntax, or type error, in my Elixir chumak dealer socket, or if it's simply a bug. I have tried this on both amd64 and armv7l architectures and the problem is identical.
All the elixir code is based on the Erlang version in the chumak example for dealer-router.
My mix.exs deps looks like this:
[
{:chumak, "~> 1.2"},
{:msgpack, "~> 0.7.0"}
]
the only obvious thing I see is your use of send_multipart. Its signature in the source:
-spec send_multipart(SocketPid::pid(), [Data::binary()]) -> ok.
you are doing this:
:chumak.send_multipart(sock2, ['a', 'b', 'hello from dealer socket'])
------------
iex(2)> is_binary('a')
false
iex(3)> is_binary('hello from dealer socket')
false
Otherwise, I can not see much of a difference between your code and the example code that is in chumak's repo.
This is a piece of code in place. When I add this to the cron with timeout the entire array gets saved twice. When I remove timeout nothing gets saved
In this scenario we would want to save the array results (coming in from an api) with over 100k records to be saved to the db. I have used bulk insert and TinyTds gems here
ActiveRecord::Base.establish_connection(adapter: 'sqlserver', host: "xxx", username: "xxx", password: "xxx", database: "xxx", azure: true, port: 1433, timeout: 5000)
class Report < ActiveRecord::Base
self.primary_key = 'id'
end
my_array = [] #count of 100000 records
Report.bulk_insert(:account_owner_id) do |worker|
my_array.drop(2).each do |arr|
worker.add account_owner_id: arr[0]
end
end
You can try removing timeout and adding ignore: true to your bulk insert as shown here. There may be an insert that is failing.
Report.bulk_insert(:account_owner_id, ignore: true) do |worker|