I have created a very basic Elixir supervisor and worker, to test hot code reloading feature of Erlang VM. Here is the supervisor:
defmodule RelTest do
use Application
# See http://elixir-lang.org/docs/stable/elixir/Application.html
# for more information on OTP Applications
def start(_type, _args) do
import Supervisor.Spec, warn: false
port = Application.get_env(:APP_NAME, :listen_port, 9000)
{:ok, socket} = :gen_tcp.listen(port, [:binary, active: false, reuseaddr: true])
# Define workers and child supervisors to be supervised
children = [
# Starts a worker by calling: RelTest.Worker.start_link(arg1, arg2, arg3)
# worker(RelTest.Worker, [arg1, arg2, arg3]),
worker(Task, [fn -> TestListener.start(socket) end])
]
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: RelTest.Supervisor]
Supervisor.start_link(children, opts)
end
end
Basically, I'm starting a Task worker, which is:
defmodule TestListener do
require Logger
def start(socket) do
{:ok, client} = :gen_tcp.accept(socket)
Logger.info "A client connected"
Task.async(fn -> loop(client) end)
start(socket)
end
def loop(socket) do
case :gen_tcp.recv(socket, 0) do
{:ok, _} ->
say_hello(socket)
Logger.info "Said hello to client ;)"
loop(socket)
{:error, _} ->
Logger.info "Oops, client had error :("
:gen_tcp.close(socket)
end
end
def say_hello(socket) do
:ok = :gen_tcp.send(socket, <<"Hey there!\n">>)
end
end
This is version 0.1.0. So I run these:
MIX_ENV=prod mix compile
Mix_ENV=prod mix release
and I get a nice release. I run it with ./rel/rel_test/bin/rel_test console and everything works. Now I'm going to bump the code and version, so here is the version 0.1.1 of listener:
defmodule TestListener do
require Logger
def start(socket) do
{:ok, client} = :gen_tcp.accept(socket)
Logger.info "A client connected"
Task.async(fn -> loop(client) end)
start(socket)
end
def loop(socket) do
case :gen_tcp.recv(socket, 0) do
{:ok, _} ->
say_hello(socket)
Logger.info "Said hello to client ;)"
loop(socket)
{:error, _} ->
Logger.info "Oops, client had error :("
:gen_tcp.close(socket)
end
end
def say_hello(socket) do
:ok = :gen_tcp.send(socket, <<"Hey there, next version!\n">>)
end
end
Now I run
MIX_ENV=prod mix compile
Mix_ENV=prod mix release
and appup is created successfully, then to do hot upgrade
./rel/rel_test/bin/rel_test upgrade "0.1.1"
and the upgrade works, but it kills my listener after upgrade.
I tested with a nc localhost 9000 (9000 is the port of listener), staying connected and running upgrade command. Connection gets killed and I get a message in console:
=SUPERVISOR REPORT==== 31-Aug-2016::23:40:09 ===
Supervisor: {local,'Elixir.RelTest.Supervisor'}
Context: child_terminated
Reason: killed
Offender: [{pid,<0.601.0>},
{id,'Elixir.Task'},
{mfargs,
{'Elixir.Task',start_link,
[#Fun<Elixir.RelTest.0.117418367>]}},
{restart_type,permanent},
{shutdown,5000},
{child_type,worker}]
So why this happens? Is it something I'm missing, or it is the expected behavior? Is it not the use case of hot code reloading?
I have read LYSE, but the author says the running code should keep running, only the external calls made after the upgrade are to be served with new version.
Then why kill the worker?
Related
When my sidekiq worker runs out of memory, I don't know what it was working on which caused this. Is there a way to log this? (other than obvious solutions like logging when starting each job).
One idea I have is to catch SIGTERM, something like this:
Sidekiq.configure_server do |config|
signals = %w[INT TERM]
signals.each do |signal|
old_handler = Signal.trap(signal) do
info = []
workers = Sidekiq::Workers.new
workers.each do |_process_id, _thread_id, worker|
info << work
end
## -> Do something with info <- ##
## StatsTracker.count("#{job.name} being processed when shutdown")
if old_handler.respond_to?(:call)
old_handler.call
else
exit
end
end
end
end
In my Ruby on Rails project I have 3 workers. They are in /lib/workers/worker_a.rb, /lib/workers/worker_b.rb, and /lib/workers/worker_c.rb.
Worker A is defined like
class WorkerA
def run!
logger.info "Starting worker A"
loop do
begin
do workerAWork
sleep 1.5 if workerAWork
rescue => e
logger.error e
sleep 3
end
end
end
end
WorkerA.new.run! if __FILE__==$0
Worker B is defined very similarly:
class WorkerB
def run!
logger.info "Starting Worker B"
loop do
begin
do workerBWork
sleep 1.5 if workerBWork
rescue => e
logger.error e
sleep 3
end
end
end
end
WorkerA.new.run! if __FILE__==$0
Worker C again is defined like above.
All 3 workers are doing what the app needs them to do. However, during deployment using Docker, I would need to open 3 terminals and start each worker using bin/rails runner ./lib/workers/worker_a.rb,bin/rails runner ./lib/workers/worker_b.rb, bin/rails runner ./lib/workers/worker_c.rb.
If I create a shell script that looks like
bin/rails runner ./lib/workers/worker_a.rb
bin/rails runner ./lib/workers/worker_b.rb
bin/rails runner ./lib/workers/worker_c.rb
Only the first worker will start and the terminal will hang there as there is a loop. The loop will prevent workers B and C from starting.
Is there a way to write the script such that they can be started one after another?
Thanks!
We have a Rails 3.2 app and will listening on a RabbitMQ queue using Bunny. The code for the listener will be like:
require 'bunny'
class TestBunny
def self.do
connection = Bunny.new(host: RABBITMQ_HOST, user: RABBITMQ_USERNAME, pass: RABBITMQ_PASSWORD, automatically_recover: false)
connection.start
channel = connection.create_channel
queue = channel.queue('custom_reports_queue', durable: false)
channel.prefetch(1)
puts ' [*] Waiting for messages. To exit press CTRL+C'
begin
queue.subscribe(manual_ack: true, block: true) do |delivery_info, _properties, body|
puts " [x] Received '#{body}'"
# imitate some work
sleep body.count('.').to_i
puts ' [x] Done'
OfflineReportMailer.success.deliver
channel.ack(delivery_info.delivery_tag)
end
rescue Interrupt => _
connection.close
end
end
I have converted the above to a rake task:
RAILS_ENV=local rake report_queue:listen
It needs to be always running. How would I call this? Possibly as a rake background task? We are using nginx / unicorn as our webserver.
i'm new to elixir
target - read simplet text file
started with
mix new pluralsight_tweet --sup
pilot project deployed success
file structure
code in file_reader.ex
defmodule PluralsightTweet.FileReader do
def get_strings_to_tweet(path) do
File.read!(path)
end
end
try to compile using
iex -S mix
give errors
whats the issue and how can i fix this case
application.ex
defmodule PluralsightTweet.Application do
# See http://elixir-lang.org/docs/stable/elixir/Application.html
# for more information on OTP Applications
#moduledoc false
use Application
def start(_type, _args) do
import Supervisor.Spec, warn: false
# Define workers and child supervisors to be supervised
children = [
# Starts a worker by calling: PluralsightTweet.Worker.start_link(arg1, arg2, arg3)
# worker(PluralsightTweet.Worker, [arg1, arg2, arg3]),
]
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: PluralsightTweet.Supervisor]
Supervisor.start_link(children, opts)
end
end
I am working on rabbitmq and trying to push a method to a queue from my ruby on rails app and I am running a server side ruby script to read the queue and execute the method which is send in the payload. Here is my client side code.
module Rabbitesh
require 'amqp'
#debugger
def self.call_rabbits(payload,queue_name)
AMQP.start(:host => "localhost") do |connection|
channel = AMQP::Channel.new(connection)
queue = channel.queue(queue_name)
channel.default_exchange.publish(payload, :routing_key => queue.name)
#EM.add_timer(0.01) do
connection.close do
#end
end
end
end
end
This is now I call the Rabbitmq function
Rabbitesh::call_rabbits(obj,"welcome_mail")
where "welcome_mail" is the queue_name
This is the server side script
require 'rubygems'
require 'amqp'
require 'daemons'
options = { :backtrace => true, :dir => '.', :log_output => true}
Daemons.run_proc('raabbitmq_daemon',options) do
AMQP.start(:host => "localhost") do |connection|
channel = AMQP::Channel.new(connection)
queue = channel.queue("welcome_mail")
Signal.trap("INT") do
connection.close do
EM.stop { exit }
end
end
puts " [*] Waiting for messages. To exit press CTRL+C"
queue.subscribe do |body|
UserMailers.welcome_organic(body).deliver
end
end
end
The problem is when my rails app calls the rabbitmq function the console stops there saying "updating client properties" and though I will be running my server side ruby script, it will not read the queue and take execute the process. I am not able to understand whats wrong with the code, kindly help me out.