ZIO is an awesome library to deal with the complicated part of effects, most notably concurrency and asynchronicity.
Sometimes, you want to create a “batch” like process, i.e. a fiber running in background and never terminating, which does some effect based on a trigger, like a period of time or reception of an external event. And of course you want to test its semantic. Fortunately, ZIO
provides a test environment with a test clock that you can adjust for your needs. Here, it becomes apparent why the clock effect is explicit in ZIO
: you can control it!
ZIO
test environment:
Ref
:
OK, so we have a batch that will record its triggering time in a Ref
at each of its executions
Great! In a couple of lines, we are ready to test our batch by injecting a test clock and adjusting time to what we need.
You got an error.
So, what’s going on? We see both prints, but the list is empty.
Actually, what happens is a race condition. Because concurrency is hard, and that’s why you let that to others.
The race condition is between the time it takes for adjust
to tell fibers “hey, you need to do what you should when time is passing” and our batch fiber to actually do what it should on the one hand, and the r.get
on the other hand. r.get
is really fast. More than fiber business.
prog2
, you get the expected result: Success! Really, success?
No, because you introduced non determinism in your test. It may happen that sometime, due to execution constraints like load, background fiber takes more time to adjust. Or, like in that case, you massively overestimated the sleep
duration and so your tests will take much more time than needed, most of that time spent sleeping.
So, what’s the correct solution? As always with concurrency problems, the correct answer is to encode your protocol by forcing a synchronisation point for each execution of your batch. This is tedious, since it means that you will need to clearly count each of them, but it’s the only way toward determinism.
To force synchronization, the simplest way is to use a Queue
and call one take
for each offer
.