GenServer in elixir delaying task - erlang

I am trying to show a post to the first friend of a person first and other's after making a delay of 1 min. For that I am using GenServer.
The problem is that the first friend as well as the other friends are getting the post after 1 min.
Here is my code of GenServer:
defmodule Phoenix.SchedulePost do
use GenServer
def start_link(state) do
GenServer.start_link(__MODULE__, state)
end
def init(state) do
schedule_post(state)
{:ok, state}
end
# handling looby
def handle_info(:postSchedule, state) do
#sending posts to others
{:noreply, state}
end
# scheduling a task
defp schedule_post(state) do
IO.puts "scheduling the task"
Process.send_after(self(), :postSchedule, 60*1000)
end
end
I am starting a GenServer process for each post request and sending it to the first friend. Here is the code:
def handle_in("post:toFrstFrnd", %{"friendId"=>friendId,"body" => body}, socket) do
newSocket = PhoenixWeb.SocketBucket.get_socket(friendId)
if newSocket != nil do
push newSocket, "post:toFrstFrnd", %{"friendId": friendId,"body": body}
end
Phoenix.SchedulePost.start_link(postId)
{:noreply, socket}
end
Help me out, thank you in advance.

Note: I know it's a rather old question, but maybe someone else has a similar problem and ends up here.
I think you want to trigger an action initially and then another action a minute later. The problem with your code is that you call the schedule_post method in your init method and it does nothing for a minute. After one minute you send a message to the process itself, whereupon handle_info method takes over. But now it is already much too late for the initial action.
Here is an example how you could do it:
defmodule Phoenix.SchedulePost do
use GenServer
def start_link(state \\ []) do
GenServer.start_link(__MODULE__, state)
end
def init(state) do
send(self(), :first_action)
{:ok, state}
end
def handle_info(:first_action, state) do
IO.puts("Called immediately")
# Do something here...
Process.send_after(self(), :second_action, 60 * 1000)
{:noreply, state}
end
def handle_info(:second_action, state) do
IO.puts "Called after 1 min"
# Do something here...
{:noreply, state}
end
end
But keep in mind that the process will continue to live even after the second action is done. It will not terminate automatically, you have to take care of that.

Related

Elixir GenServer handle_cast not being invoked

I am having an issue with Handle Cast. After the module loads it calls periodic_comm which after every 5 seconds calls the save_data method. In my save_data method the logs are being printed but the statement does nothing.
GenServer.cast(__MODULE__, {:save_load_data, data})
The handle cast method is defined here
def handle_cast({:save_load_data, data}, state) do
Logger.error("uin saving data====================================")
# state = Map.put_new(state, :load_data, data)
# Logger.debug "updated state : #{inspect state}"
{:noreply, state}
end
The entire file looks like this
defmodule Core.Inverter.Manager do
# core inverter process that handles the inverter communication
use GenServer
require Logger
def start_link(_) do
Logger.info("putting some string")
GenServer.start_link(__MODULE__, %{}, name: __MODULE__)
end
#impl true
def init(_) do
Logger.info("inverter Manager init'ed")
{:ok, %{configs: nil, inverter_connection: nil, inverter_impl: nil}}
end
# public api
# initialize the inverter and start periodic comm
def init_inverter(configs) do
GenServer.cast(__MODULE__, {:init_inverter, configs})
GenServer.cast(__MODULE__, {:start_periodic_comm, configs})
end
def inverter_off() do
GenServer.call(__MODULE__, :inverter_off)
end
#impl true
def handle_cast({:init_inverter, configs}, state) do
Logger.debug("initializing the inverter")
# fetching inverter type/configurations
inverter_impl = get_inverter_impl(configs)
# initializing inverter comm channel
inverter_connection = inverter_impl.init(configs)
state = %{
configs: configs,
inverter_connection: inverter_connection,
inverter_impl: inverter_impl
}
Logger.warn("inverter type #{state.inverter_impl}")
{:noreply, state}
end
def handle_cast({:start_periodic_comm, configs}, state) do
Logger.warn(
"Actually Starting inverter periodic COMM with inverter type #{state.inverter_impl}"
)
start_periodic_comm(state)
{:noreply, state}
end
#impl true
def handle_call(
:inverter_off,
_from,
%{inverter_impl: inverter_impl, inverter_connection: inverter_connection} = state
) do
{:ok, inverter_connection} = inverter_impl.inverter_off(inverter_connection)
{:reply, :ok, %{state | inverter_connection: inverter_connection}}
end
def handle_cast({:save_load_data, data}, state) do
Logger.error("uin saving data====================================")
# state = Map.put_new(state, :load_data, data)
# Logger.debug "updated state : #{inspect state}"
{:noreply, state}
end
defp start_periodic_comm(state) do
periodic_comm(state)
end
defp periodic_comm(state) do
# call functions from the inverter_type module, e.g infit.ex
receive do
after
5000 ->
{:ok, data} = state.inverter_impl.get_load_data(state)
save_data(data)
end
periodic_comm(state)
end
# defp save_data() do end
def save_data(data) do
Logger.debug("saving data")
Logger.debug("in savinf data data is : #{inspect(data)}")
GenServer.cast(__MODULE__, {:save_load_data, data})
end
defp get_inverter_impl(configs) do
# add case statements here
Core.Inverter.Infini
end
end
I'm not so familiar with elixir's syntax, but it seems to me that periodic_comm is looping:
defp periodic_comm(state) do
# call functions from the inverter_type module, e.g infit.ex
receive do
after
5000 ->
{:ok, data} = state.inverter_impl.get_load_data(state)
save_data(data)
end
periodic_comm(state)
end
regardless of the result of save_data, the thread is endlessly looping over calls to periodic_comm, so it gets no chance to receive and execute the save_load_data message.
In order to fix it, you should refactor the server to have the logic in handle_X without looping, which receive all kind of messages. You can use erlang:start_timer (I don't know the elixir's counterpart) or rely on the Timeout in the gen_server return values to receive timeout messages.

GenServer timeout handler not being called

I've got a pretty simple setup: one GenServer, a sort of cache, which spawns child GenServers with a timeout, which they handle by sending the parent a message about their inactivity.
The child passes tests that confirm it sends {:inactive, my_id} after a specified timeout. The problem is this only happens as long as the child never receives a call to get the data in its state, in which case it never times out.
Why should handling one call prevent timeout? Is there a way to handle calls without obstructing timeout?
Full test case here: https://github.com/thure/so-genserver-timeout
Child:
defmodule GenServerTimeoutBattery.Child do
use GenServer
def start_link(child_id, timeout_duration, parent_pid) do
GenServer.start_link(__MODULE__, [child_id, timeout_duration, parent_pid], [name: String.to_atom(child_id)])
end
def get_data(child_id) do
GenServer.call(String.to_atom(child_id), :get_data)
end
#impl true
def init([child_id, timeout_duration, parent_pid]) do
IO.puts('Timeout of #{timeout_duration} set for')
IO.inspect(child_id)
{
:ok,
%{
data: "potato",
child_id: child_id,
parent_process: parent_pid
},
timeout_duration
}
end
#impl true
def handle_call(:get_data, _from, state) do
IO.puts('Get data for #{state.child_id}')
{
:reply,
state.data,
state
}
end
#impl true
def handle_info(:timeout, state) do
# Hibernates and lets the parent decide what to do.
IO.puts('Sending timeout for #{state.child_id}')
if is_pid(state.parent_process), do: send(state.parent_process, {:inactive, state.child_id})
{
:noreply,
state,
:hibernate
}
end
end
Test:
defmodule GenServerTimeoutBattery.Tests do
use ExUnit.Case
alias GenServerTimeoutBattery.Child
test "child sends inactivity signal on timeout" do
id = UUID.uuid4(:hex)
assert {:ok, cpid} = Child.start_link(id, 2000, self())
# If this call to `get_data` is removed, test passes.
assert "potato" == Child.get_data(id)
assert_receive {:inactive, child_id}, 3000
assert child_id == id
assert :ok = GenServer.stop(cpid)
end
end
Turns out setting a timeout on init applies a timeout that is only relevant until it receives a call or cast.
Each call or cast can then set its own timeout. If no timeout is specified, this defaults to :infinity. The docs are not explicit on this point, though now it makes sense to me.

Runtime-dynamic compute graph using Elixir Genstage

I'd like to be able to dynamically change a compute pipeline at runtime, but it seems that GenStage requires the compute graph to be defined at compile time through the subscribe_to: [...] mechanism. Is there a way to create dynamic compute graphs? For example in the below, I'd like to switch, at runtime, between the "subtract 7" and "subtract 4" vertices in my pipeline graph.
Is this possible using GenStage? I will likely have very complex pipelines so I need a solution that scales to changing graphs in complex ways, as opposed to ad-hoc solutions such as in this case, say, parameterising the integer to subtract. I'd like to be able to add or remove entire sub-trees, switch between subtrees, and add nodes into the graph including splicing them into the middle of any subtree including the main tree.
Please refer to EDIT further down
Here is the initial producer:
defmodule GenstageTest.Producer do
use GenStage
def start_link(initial \\ 1) do
GenStage.start_link(__MODULE__, initial, name: __MODULE__)
end
def init(counter), do: {:producer, counter}
def handle_demand(demand, state) do
events = Enum.to_list(state..(state + demand - 1))
{:noreply, events, state + demand}
end
end
Here is one of the producer_consumers:
defmodule GenstageTest.PcTimesFive do
use GenStage
def start_link do
GenStage.start_link(__MODULE__, :state_doesnt_matter, name: __MODULE__)
end
def init(state) do
{:producer_consumer, state, subscribe_to: [GenstageTest.PcAddOne]}
end
def handle_events(events, _from, state) do
numbers =
events
|> Enum.map(&(&1 * 5))
{:noreply, numbers, state}
end
end
and here is the final consumer:
defmodule GenstageTest.Consumer do
use GenStage
def start_link do
GenStage.start_link(__MODULE__, :state_doesnt_matter)
end
def init(state) do
{:consumer, state, subscribe_to: [GenstageTest.PcDivTwo]}
end
def handle_events(events, _from, state) do
for event <- events do
IO.inspect({self(), event, state})
end
# As a consumer we never emit events
{:noreply, [], state}
end
end
I
It is all modelled off the Elixir School Genstage tutorial.
All the modules and the mix.exs can be found on github.
EDIT 3 days later after partial answer from #AquarHEAD L.
I have managed to get runtime subscriptions working. Here are some modified producers, producer_consumers, and consumers respectively:
Producer:
defmodule GenstageTest.Producer do
use GenStage
def start_link(initial \\ 1) do
GenStage.start_link(__MODULE__, initial, name: __MODULE__)
end
def init(counter), do: {:producer, counter}
def handle_demand(demand, state) do
events = Enum.to_list(state..(state + demand - 1))
{:noreply, events, state + demand}
end
def handle_info({:doprint}, state) do
IO.puts "yep"
{:noreply, [], state}
end
def handle_info({:cancel, sublink}, state) do
GenStage.cancel sublink, []
{:noreply, [], state}
end
end
Producter_consumer:
defmodule GenstageTest.PcAddOne do
use GenStage
def start_link do
GenStage.start_link(__MODULE__, :state_doesnt_matter, name: __MODULE__)
end
def init(state) do
{:producer_consumer, state}
end
def handle_events(events, _from, state) do
numbers =
events
|> Enum.map(&(&1 + 1))
{:noreply, numbers, state}
end
end
Consumer:
defmodule GenstageTest.Consumer do
use GenStage
def start_link do
GenStage.start_link(__MODULE__, :state_doesnt_matter)
end
def init(state) do
{:consumer, state}
end
def handle_events(events, _from, state) do
for event <- events do
IO.inspect event
#File.write("/home/tbrowne/scratch/output.txt",
# Kernel.inspect(event) <> " ", [:append])
:timer.sleep(100)
end
# As a consumer we never emit events
{:noreply, [], state}
end
end
Now once these are all available in the lib directory (remember to add {:gen_stage, "~> 0.11"} to your mix.exs deps), or copied and pasted into IEX, then the following will work perfectly:
{:ok, p} = GenstageTest.Producer.start_link(0)
{:ok, a1} = GenstageTest.PcAddOne.start_link()
{:ok, c} = GenstageTest.Consumer.start_link()
{:ok, link1} = GenStage.sync_subscribe(a1, to: p, min_demand: 0, max_demand: 1, cancel: :transient)
{:ok, link2} = GenStage.sync_subscribe(c, to: a1, min_demand: 0, max_demand: 1, cancel: :transient)
The issue now is though, that I still don't know how to cancel the subscription. There is a cancel function and there is also a stop function. GenStage.stop(c) for example seems to do nothing, while my various attempts at GenStage.cancel/3 only give errors.
To recap, what I need now is to be able to stop certain stages and replace them with others. What is the syntax for cancelling a subcsription, and from where is it called? It is not well explained in the docs as there is no concrete example.
You can absolutely change the pipeline at runtime, checkout the first example in GenStage documentation, you can also use the :manual mode to fine control the demand. There's also API to cancel subscription. I think these are enough to dynamically manage GenStage pipelines.
Why not implement your own GenStage.Dispatcher? Here is behaviour

Is it possible to send a message to all child processes in elixir/erlang?

Let's imagine that I'm spawning multiple child processes in elixir.
defmodule Child do
def start(name) do
receive do
msg -> IO.puts "Message received by #{name}: #{inspect msg}"
end
end
end
defmodule Parent do
def main do
child1 = spawn_link (fn -> Child.start("1") end)
child2 = spawn_link (fn -> Child.start("2") end)
child3 = spawn_link (fn -> Child.start("3") end)
end
end
Is there anyway that I can send a message to all of the children of my current process (or some other process)?
send_to_children self(), "hello to all children"
As in, some way that I can tell the runtime to broadcast a message to all of processes linked to a current process? I could of course store all of the spawned pids in a data structure of some kind and loop over it to do this, but if there is some kind of canonical way to do this it seems like it would be more efficient and less error prone.
Since you're using spawn_link, you can fetch list of all linked processes and send a message to them:
defmodule Child do
def start(name) do
receive do
msg -> IO.puts "Message received by #{name}: #{inspect msg}"
end
end
end
defmodule Parent do
def main do
child1 = spawn_link (fn -> Child.start("1") end)
child2 = spawn_link (fn -> Child.start("2") end)
child3 = spawn_link (fn -> Child.start("3") end)
{:links, links} = Process.info(self, :links)
for pid <- links do
send pid, :foo
end
end
end
Parent.main
:timer.sleep(1000)
Output:
Message received by 2: :foo
Message received by 1: :foo
Message received by 3: :foo
I don't think it's possible to get a list of child processes of a process directly: http://erlang.org/pipermail/erlang-questions/2013-April/073125.html. There are ways if you spawn them from a Supervisor but not for arbitrary cases.
did you look at PubSub? The only restriction is that all your processes will be named the same https://hexdocs.pm/elixir/master/Registry.html#module-using-as-a-pubsub
{:ok, _} = Registry.start_link(:duplicate, Registry.PubSubTest)
# process 1
{:ok, _} = Registry.register(Registry.PubSubTest, "room_1", [])
# process 2
{:ok, _} = Registry.regiser(Registry.PubSubTest, "room_1", [])
Registry.dispatch(Registry.PubSubTest, "room_1", fn entries ->
for {pid, _} <- entries, do: send(pid, {:broadcast, "world"})
end)
#=> :ok

How to create GenServer child process from spawn function?

I'm using ExTwitter Stream to track tweets in realtime and broadcast them via channel endpoint. I would like to create one process per event and assign to it one twitter stream listener, then when new subscriber join to the same event get previous stream state and also receive and broadcast new tweets.
How to create GenServer process from:
stream = ExTwitter.stream_filter(track: hashtags)
pid = spawn(fn ->
for tweet <- stream do
IO.puts tweet.text
MyApp.Endpoint.broadcast! "stream", "tweet", %{tweet: tweet.text}
end
end)
and assign it by event_id as child in following module:
defmodule MyApp.TwitterStream.Monitor do
require IEx
#moduledoc """
Store twitter stream per event_id
"""
use GenServer
def create(event_id, hashtags, coords) do
case GenServer.whereis(ref(event_id)) do
nil ->
Supervisor.start_child(MyApp.TwitterStream.Supervisor, [event_id, hashtags, coords])
_twitter_stream ->
IEx.pry
# return previous ExTwitter stream state and broadcast new tweets
{:error, :twitter_stream_already_exists}
end
end
def start_link(event_id, hashtags, coords) do
# stream = ExTwitter.stream_filter(track: hashtags)
# pid = spawn(fn ->
# for tweet <- stream do
# IO.puts tweet.text
# MyApp.Endpoint.broadcast! "stream", "tweet", %{tweet: tweet.text}
# end
# end)
GenServer.start_link(__MODULE__, %{hashtags: hashtags, coords: coords}, name: ref(event_id))
end
def stop(event_id) do
try_call(event_id, :stop)
end
def info(event_id) do
try_call(event_id, :info)
end
def handle_call(:stop, _from, state) do
# ExTwitter.stream_control(pid, :stop)
{:stop, :normal, :ok, state}
end
def handle_call(:info, _from, state) do
{:reply, state, state}
end
defp try_call(event_id, call_function) do
case GenServer.whereis(ref(event_id)) do
nil ->
{:error, :invalid_twitter_stream}
twitter_stream ->
GenServer.call(twitter_stream, call_function)
end
end
defp ref(event_id) do
{:global, {:twitter_stream, event_id}}
end
end
How to receive new tweets or eventually stop twitter stream outside the event Monitor?
Supervisor:
defmodule MyApp.TwitterStream.Supervisor do
use Supervisor
def start_link do
Supervisor.start_link(__MODULE__, :ok, name: __MODULE__)
end
def init(:ok) do
children = [
worker(MyApp.TwitterStream.Monitor, [], restart: :temporary)
]
supervise(children, strategy: :simple_one_for_one)
end
end

Resources