csokun

csokun

Hi, I’m trying to understand how to properly implement GenServer state handoff in a cluster environment.

I searched around and managed to get a simple GenServer up & running elixir_libcluster_horde_demo/lib/mix_app1/greeting.ex at main · csokun/elixir_libcluster_horde_demo · GitHub using libcluster & horde.

Based on my reading, I should be able to perform state handoff by simply:

  • Add Process.flag(:trap_exit, true) in init/2
  • Persist state via terminate/2, using Horde.Registry.put_meta/3
  • Reload existing state on init/2 or handle_continue/2 using Horde.Registry.meta/2

Each time I kill the old process using :init.stop() a new process is successfully spawned on another node. However, the newly spawned process is unable to find the existing state calling Horde.Registry.meta(...) always return :error

But, if I manually invoke Horde.Registry.put_meta(...) then :init.stop() the newly spawned process can restore the state.

Is there something wrong with my implementation? Why calling Horde.Registry.put_meta(...) within terminate/2 does not save the GenServer state.

Showing Posts 1 to 5

hubertlepicki

hubertlepicki

I strongly suspect what is happening here is that you’re shutting down the whole node before Horde.Registry manages to propagate its state across other nodes in the cluster. CRDTs are eventually consistent and it’s not an immediate changes that you’re putting onto registry, they have to be synced back to the rest of the cluster.

I never did what you’re trying to do, with using Horde to pass the state to itself so not sure how to handle it and if it’s good idea at all.

What I found a reliable way to pass the state in the above situation, and also to ensure that a required process indeed starts on the other nodes was to use PostgreSQL to store the state. In my case there were two scenarios: one in which I scheduled an Oban job to start a corresponding process on other nodes, and in it’s payload I was passing the state, in another case I was just saving the state to a database and process would be started on-demand on other nodes when required and would pick it up.

csokun

csokun OP

Thank @hubertlepicki I think you nailed it I added Process.sleep(1000) after calling Horde.Registry.put_meta/3 and the state get persisted.

anuaralfetahe

anuaralfetahe

I believe using Process.sleep is not a very reliable option since the distribution of state may take longer or less due to the underlying network. I typically address similar cases using message passing.

hubertlepicki

hubertlepicki

I agree but having a glance at the Horde.Registry doesn’t seem to expose any public function / mechanism to check if the changes applied locally got synced, also it may be good enough.

@derekkraan any recommendations?

derekkraan

derekkraan

Hi, sorry for taking so long to respond, Horde’s backing store (DeltaCRDT) is eventually-consistent, and there is no mechanism for checking to see if the locally-applied changes have been synced.

Using Horde.Registry.put_meta/3 during shutdown is unlikely to work well, as @hubertlepicki has pointed out, since syncing is performed asynchronously, and since you’re in the process of shutting down the node, it’s highly likely that the node will stop before any syncing can happen.

My recommendation for now, as Horde is not persisted, would be to only use Horde for data that you can afford to lose. Process state handoff would be better implemented using a database like Postgres or Redis, or perhaps something else.

— All posts loaded —

Where Next? Top

Trending in Questions Top

katta
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
achenet
Hello, I’m trying to build a basic Phoenix web-app, and I’d like to use Tailwind. However, when I launch mix phx.server, I get an error...
New
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
New
Cxx-mlr
I’m working on a small exercise involving update_in/3, and I came up with this solution: data = %{ name: "Periodic Table", category:...
New
ChrisAmelia
I’ve got trouble wrapping my head around the order in which functions are called in this snippet (from Phoenix’s authentication): toke...
New
dillonoconnor
Is there any way to avoid the Hologram compiler running when using iex? It seems like the front-end code could potentially be disregarded...
New
thiagogsr
** (ArgumentError) expected :max_attempts to be a positive integer, got: {:@, [line: 10, column: 19], [{:max_attempts, [line: 10, column:...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews