mandar1045

mandar1045

I’ve been fixing race condition / crash bugs in an open source org, for a few months now and almost all of them had the same problem. They only happen with a very specific order of events (process crashes mid-message, a reply arrives late, a timer fires at the wrong moment) so the normal ExUnit tests never catch them. We find them in prod, then I spend a day trying to reproduce it and 20 minutes fixing it.

So I started reading about how FoundationDB and TigerBeetle deal with this. They do “deterministic simulation testing”: run the whole system inside a simulator that controls time, message order, randomness and crashes, run it thousands of times with different seeds, and when something breaks you get a seed that replays the exact same failure every time.

I couldn’t find anything like this for Elixir and I’m thinking of building it. Very rough idea of what a test could look like:

simtest "cache stays consistent when workers crash", runs: 1_000 do
  start_supervised!(MyApp.CacheSupervisor)

  Sim.spawn_clients(10, fn -> MyApp.Cache.put(random_key(), random_value()) end)

  Sim.inject(:crash, MyApp.Cache.Worker, probability: 0.05)
  Sim.inject(:delay_messages, max: :timer.seconds(2))
  Sim.advance(:timer.minutes(10))

  assert MyApp.Cache.consistent?()
end

and when it fails you’d get the seed plus the smallest set of events that still breaks it, something like:

failed on run 412 (seed 8812734019)
shrunk to:
  1. worker 2 receives {:put, "a", 1}
  2. worker 2 crashes before replying
  3. supervisor restarts it, client retries the put

My plan for how it works: in test env, rewrite modules at compile time so send/receive, timers, monotonic_time and :rand go through a fake scheduler instead of the real one. Concuerror does something kind of similar for Erlang but it’s aimed at exhaustively exploring interleavings, not seeded fault injection with virtual time, and using it from Elixir is painful (at least it was for me).

Other stuff I looked at: gen_server_virtual_time and Klotho (both nice but only handle time), and StreamData (seeds, but for data not scheduling). If something already does all of this please tell me before I sink weeks into it lol.

Things I’m not sure about:

  • is compile time rewriting going to be too fragile? the alternative is making people use a special GenServer-ish behaviour, which feels worse to me but maybe it’s more realistic
  • I’m planning single node first and network partitions / multi node later. does that order make sense?
  • what would you need to see before trusting something like this? my idea is to run it against some well known libs and see if it finds real bugs

Also curious if you’ve had bugs like this in prod and how you ended up reproducing them.

If there’s a reason this can’t work on the BEAM I’d rather hear it now .

Showing Posts 1 to 3

jstimps

jstimps

Check out eta:

Erlang support was released in 0.1.0. Elixir support is on main but hasn’t been released yet.

Eta is still early, but I have been working to prove it’s efficacy in real projects, and have found several real bugs both in my own code and in open projects.

The basic idea of eta is that it suspends all the processes under test and schedules them one at a time using a deterministic scheduler.

It’s most effective when combined with a formal model like TLA+ and a checker like TLC. The model checks your design and DST can help confirm your code matches your design.

That modeling effort, combined with the writing of the DST harness means that formal verification requires a significant time investment in the development of your project, but in my experience it produces high quality bug reports for elusive defects, especially when paired with a capable LLM.

I’m currently working on an implementation of Flexible Paxos, with a large suite of formal verification backing it.

ausimian

ausimian

A process that I (the clankers) experimented with recently was:

  1. Spec in TLA+
  2. Model checking
  3. TLAPS proofs (mostly clankers)
  4. Pure functional state machine via gen_statem. Effects such as message sending are strictly returned through {next_event, internal, …}
  5. Some stateful property testing, hand-rolled, but I’m interested in trying out the new PropertyDamage library to replace this.

The final thing - the bit I thought was reasonably novel - was to get the clankers to emit TLC-compatible traces from the running code, and get TLC to validate them against the spec (see below).

The cool thing is you can just use erlang tracing to capture the handle_event entry and exit, which gets you a long way to producing the trace - no further instrumentation required.

jstimps

jstimps

Agreed, avoiding deep instrumentation is preferable. And especially if your system under test is already deterministic, you’re unlikely to need a framework to help.

However nondeterminism coming from outside a pure function (scheduling, network, disk, timers, etc) is where DST projects like eta are trying to help. Quick note on scheduling: I don’t know if the BEAM scheduler is actually deterministic or not, but from the perspective of the developer, I think we must treat it as nondet since we have little control over its low level decisions.

Trace validation looks like another great way to link code to a formal model.

— All posts loaded —

Where Next? Top

Trending in Discussions Top

AstonJ
The obligatory hello world thread! Who are you and where are you from? :stuck_out_tongue:
4663 61218 595
New
cblavier
Hey there, It’s been more than a year since we started using LiveView as our main UI library and building a whole library of UI componen...
515 12931 101
New
wmnnd
Hey folks, I wanted to share with you a project I’ve been working on for a while: It’s an Open Source (AGPLv3) newsletter tool. Think Ma...
New
raa-ziq
Hi everyone :waving_hand: I wanted to share something I’ve been building — NowBlind, a random chat app designed to help people discover ...
New
sntran
In my third year of college, around 2010, I found Erlang. A few years later, I found Elixir. I liked the ideas at once: many small proces...
New
gtcode
I’ve been tinkering with Lynx (by José) via hold_ledger. I used AI to write the code, a separate specification, the tests, and the Lean p...
New
Ganna
Hi everyone, I have 8 years of production experience with Erlang/OTP and I’m just starting to get to know Elixir. What would you learn f...
New

Other Trending Topics Top

dominicletz
Hey there, I haven’t seen a list of useful Elixir specific skills for the popular AI tools yet so I’m starting one here with my first. ...
New
mudasobwa
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
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
OndrejValenta
(I just needed to vent somewhere and LinkedIn is full of hope, or hype, I’m not sure which exactly) AI dream has many faces, but general...
New
kip
Localize is the next generation localisation library for Elixir. Think of it as ex_cldr version 3.0. The first version will be released ...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews