Back to Podcast Digest
dotnet1h 0m

On .NET Live - DuckDB & Orleans: When Fake Asynchrony Starves Your App!

TL;DR

  • DuckDB async calls can be fake async: Legend shows that ExecuteNonQueryAsync in this path ultimately wraps synchronous native work, so Orleans grains end up blocked while a Task only gives the illusion of asynchrony.

  • Interop and cancellation are the hidden traps: Crossing from managed .NET into DuckDB's native engine adds GC transition overhead, and cancellation is not prompt because the engine can only safely interrupt at certain points, especially during writes or remote reads.

  • A bounded worker pool beats thread pool panic: His fix is a custom synchronous work pool with separate primary and secondary channels, where the secondary can steal from the primary so writes keep moving while reads stay concurrent under DuckDB's MVCC model.

  • Pooling and reusable await machinery slash allocations: Benchmarks showed the optimized version cut allocations by about 40 percent versus a non-pooled variant in sequential dispatch, and by roughly 10x in concurrent cases, with an even larger 681x gap in one throughput benchmark.

  • Raw throughput lied because failures were hiding underneath: The direct approach hit almost 4,000 requests per second on one test, but it also produced large numbers of timeouts and failures, while the pooled version stayed around 900 to 1,300 requests per second with zero failures in the background ping workload.

  • The production risk is bigger than just being slow: In Orleans, thread pool starvation can make a silo look dead to the rest of the cluster, and Ruben Bond notes they have seen long operations in production effectively kill silos this way.

The Breakdown

A supposedly async DuckDB call in .NET can block Orleans grains long enough to trigger catastrophic thread pool starvation, with measured scheduler delays hitting 51 seconds and healthy silos getting marked dead. Legend Vulle shows why ADO.NET's async wrappers are often fake here, then builds a bounded primary-secondary worker pool that trades some raw throughput for dramatically better stability and near-zero failures.

Was This Useful?

Share