In a way, this summer has been an ordinary one, apart from the floods. Perhaps El Niño had something to do with it.
My son is now nine months old. He smiles a lot. Cries a lot, too. He's already cruising along the furniture nonstop. Already?
So with that little life update out of the way, I’d like to share a project update.
Fyntr v0.4.8 Released
I've released a new version of Fyntr. This version changes how Fyntr handles writes to each TCP connection. Each connection now has a dedicated writer actor.
Previously, asynchronous write tasks were spawned whenever the scheduler popped data from the queue.
Those tasks shared a connection behind a Mutex.
Each task had to acquire the lock before writing in order to prevent concurrent writes.
When a connection was extremely slow or the other side stopped reading, one write task could remain blocked while another waited for the lock. Those pending write tasks held data that wasn't counted toward the internal limit. This meant that data could continue to accumulate outside the limit.
In the new design, a dedicated writer actor handles writes sequentially for each connection. Fyntr no longer needs tasks to wait for locks between writes.
Under normal use, nothing seemed obviously wrong. However, a reviewer had previously noticed that the amount of internal pending data could continue to increase. The issue seemed fundamental and hard to fix, so, to be honest, I pretended not to see it for a while. As it turned out, that was actually happening under backpressure.
Did This Actually Make a Difference?
For this test, I simulated backpressure. The other side stopped reading temporarily, and then resumed reading. I measured whether Fyntr could recover and drain all generated records within 10 seconds after it resumed reading. I ran this test 10 times with each version. The new impl recovered within the test deadline in all 10 trials, while the old impl didn't recover in any of the trials.
I also compared the peak RSS between the two impls. Under normal load, it was a bit higher. But in the paused-backend scenario (S7), the median was about 45% lower with the new impl.
The maximum record write duration was also interesting. At first glance, the write duration with the new impl was about 7–8 times as long, but this isn't simply a performance degradation. If anything, this shows where the waiting actually happens. With the new impl, the writer actually waits when the other side stops reading, without spawning more write tasks.
Under normal load, throughput was mostly unchanged, while CPU cost per byte increased marginally.
The calendar says it's already autumn. Fyntr is still a small personal project, but I'm happy with where it is going.
