We were unable to load Disqus. If you are a moderator please see our troubleshooting guide.

Seungjin Kim • 5 years ago

This was an awesome tutorial going over GenServer! Thank you!

Question to expand on the last commentor -

What if app crashes right in the middle of handle_continue (thus not persisting in DB)?
Would it remember to keep trying handle_continue until success?

Also - do you recommend any other tutorials for GenServer side of things? Or would you write a part 3, covering (near) real-time communication and GenServer? :)

Alex Koutmos • 5 years ago

Sorry for the delay...just saw your message.

That is correct. If the application crashes before the GenServer has a chance to persist the state, then the state will be lost. You would have to handle this scenario in the terminate/2 callback. I.e what should the GenServer do before the process dies?

As far as reading material for GenServers, I would recommend going through the Hex docs https://hexdocs.pm/elixir/G.... It is probably the most comprehensive resource out there and has tons of great information.

Hope that helps and thanks for stopping by!

Jordano Moscoso • 4 years ago

Awesome article. Thanks so much for sharing this!

Alex Koutmos • 4 years ago

Glad you enjoyed the article!

Seungjin Kim • 5 years ago

Update_book and handle_call({:update}..) has a bug(s):

@impl true
def handle_call({:update, attrs}, _from, %Book{} = state) do
state
|> update_book(attrs)
|> case do
{:ok, %Book{} = updated_book} -> #it should be {:ok, %Book{} = updated_book, _}
{:reply, updated_book, updated_book, {:continue, :persist_book_changes}}

error ->
{:reply, error, state}
end
end

%Changeset{valid?: true} = changeset ->
updated_book = Changeset.apply_changes(changeset)
{updated_book, changeset} # this should be {:ok, updated_book, changset}

Andrey Nikiforov • 5 years ago

So you invent Memcached. Next step is to store data in ETS instead of GenServer, cause GenServer is 30x slower than ETS for read

Alex Koutmos • 5 years ago

While this approach may have some similarities to that of a Memchached/Redis backed architecture, it is fundamentally different. Firstly, you are leveraging the BEAM and it's constructs to perform all of your operations (this means no network calls, the possibility of distribution and using a distributed process registry+supervisor, and no serialization/deserialization costs to/from native Erlang terms). Secondly the GenServer gives you atomic access to the book's state. This would be a problem in a Redis/Memcached system unless you put a mutex around accessing the resource given that you first need to read, compare the order quantity to the available quantity, then perform a write and you can run into issues unless all of those operations happen atomically.

I thought about talking about ETS in this article...but it was getting quite long and I had to cut some content out. In brief, while you can use ETS for faster reads, there are some nuances when using ETS that put more responsibility on the developer not to introduce bugs. For example, while you could use update_element to perform an atomic update on the book, there is no validation there to verify that you have enough inventory on hand to satisfy the order. If you do the read and then the write, you run into the same issue as the Memcached/Redis implementation (i.e you need some way to block other operations). Perhaps something I can cover in another blog post :D.

Thanks for stopping by! Appreciate the comments :)

akopoko • 5 years ago

For the Actor model approach, since the data is stored in the GenServer's memory, won't it be erased if the app restarts?

Alex Koutmos • 5 years ago

Good question! The Phoenix API primarily interacts with the actors to do its work (given that the actors are atomic). For each handle_call that induces a side effect, a handle_continue is dispatched after the handle_call returns. This will persist the state of the actor out of band with the incoming HTTP request. On application init, the Registry and the DynamicSupervisor will be hydrated with the state of the DB and the cycle continues.

Hope that helps and thanks for stopping by!