yurko

yurko

GenServer advice needed - relationship between handle_call and handle_info that use the same state

Say I have a message queue implemented as a GenServer, something like this

#... def start_link ...

def init(state) do
  {:ok, schedule(state)}
end

# schedule processing every 500 ms
defp schedule(state) do
  Process.send_after(self(), :process, 500)
  state
end

def handle_info(:process, state) do
  {:noreply, process_queue(state)}
end

# catch all handle_info

def process_queue(state) do 
    # concurrently processes each entry and deletes it from the state
    # schedule(state)
end

def put(data) do
  GenServer.call(__MODULE__, {:put, entry})
end

def handle_call({:put, entry}, _from, state) do
  {:reply, :ok, [entry | state]}
end

The question is about the relationship between handle_call and handle_info that both use the same state. From what I understand there is no way the state breaks, meaning while process_queue does its job (which may take a while) any put calling handle_call will have to wait, is that right?

If I want put to return immediately and add entries to queue after handle_info is done, I’d have to switch from call to cast, and I’m good?

Is there a nicer / simpler approach to achieve that or is this one good? Shouldn’t I handle such state using ETS (that’s what I’m switching from at the moment)?

Most Liked

Qqwy

Qqwy

TypeCheck Core Team

Correct!

Yes! :slight_smile:

Not necessarily. Using ETS makes sense if you have a lot of data that you might want to search through. If you have a queue of ‘jobs’ (of whatever kind), then simply using a single process whose state is a list, map or queue should do the job just fine, without adding complexity of using a system ETS for the developer.

Depends on what you want to archieve exactly: If you want the items to be processed and they can be processed completely separately, then you could use Task.async or Task.async_stream to do the job, which means that process_queue will not block for long.

On the other hand, if it was possible to process items completely separately from the start, one could create Tasks right away, and not need to bother to use a queue at all.

So: What is this job queue for, exactly?

OvermindDL1

OvermindDL1

For the map version earlier on, if the actions you perform are already communicating to another process then you are already async, so you definitely should not use Task then as that would just slow it all down.

yurko

yurko

Some background: I am using parts of a working app in another, bigger app so some solutions have to be refactored or reimplemented.

I have lots of data, but it’s pretty uniform and I have to handle each entry all the time, no searching. Complexity of ETS wouldn’t be a problem though, because the current implementation already uses it, still makes sense to stick with GenServer’s state?

I will use Task.async_stream here, there is no question about that :slight_smile: The jobs are separate and each one might have to work with an external API so concurrent processing is a must, but still even asynchronously it will take some number of milliseconds to finish.

It was a “no queue” implementation originally :slight_smile: I don’t want to bore you with too much details but here is one case that justified the use of queue: since I have to use an external API it might have temporal problems or even one time errors, so at times I need to fallback to a simpler handler for a specific task and at other times - just keep an item in queue and schedule it for the next run since the error must be temporary but I don’t want to block for long.

Anyway thanks for the feedback, it seems there are no obvious problems with the above solution and I might use it.

I might also stick with mutable ETS state, the only problem with it is that it’s somewhat verbose to iterate over. Which one would you go with, @Qqwy ?

Where Next?

Popular in Questions Top

sergio
In Ruby, I can go: User.find_by(email: "foobar@email.com").update(email: "hello@email.com") How can I do something similar in Elixir? ...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? Ecto.Repo — Ecto v3.14.0 has exampl...
New
LegitStack
I’m trying to make a websocket server in Phoenix or raw Elixir. I heard about gun, I think I could use cowboy, but since I’m not that sma...
New
nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
dotdotdotPaul
Okay, I’m having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I’m sure I’...
New
jaysoifer
Is there a way to rollback a specific migration and only that one (“skipping” all the other ones)? Would mix ecto.rollback -v 200809061...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New

Other popular topics Top

Emily
I have VueJS GUIs with the project generated using Webpack. I have Elixir modules that will need to be used by the VueJS GUIs. I forese...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 43806 214
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New

We're in Beta

About us Mission Statement