@@ -14,6 +14,33 @@ namespace StackExchange.Redis;
1414internal abstract class BufferedStreamWriter ( Stream target , CancellationToken cancellationToken )
1515 : IBufferWriter < byte > , IDisposable , IAsyncDisposable
1616{
17+ /* What is this?
18+ *
19+ * Basically, an abstraction similar to Pipe - it has a separate write and read head, etc, but
20+ * the key difference is that it is focused on reducing context switches:
21+ *
22+ * - explicit flush is *synchronous*, simply marking the tail buffer as "ready to read"; this is actually pretty
23+ * similar to Pipe if we ignore back-pressure, which it kinda does by default
24+ * - implicit flush is implicit - i.e. when committed work fills a page, that page is flushed automatically
25+ * - the consumption API allows fully synchronous consumption, if desired
26+ *
27+ * At the moment, 3 concrete implementations are provided:
28+ * - BufferedAsyncStreamWriter: uses the thread-pool and async I/O to copy data to the target stream
29+ * - BufferedSyncStreamWriter: uses a dedicated thread to copy data to the target stream using sync IO
30+ * - PipeStreamWriter: uses the pre-existing Pipe API and pre-built PipeWriter.CopyTo(Stream)
31+ *
32+ * The intention is that:
33+ * - pub/sub always uses BufferedAsyncStreamWriter
34+ * - interactive uses BufferedSyncStreamWriter in low-connection-count scenarios,
35+ * and BufferedAsyncStreamWriter in high-connection-count scenarios (there's some missing work here in
36+ * tracking the count and transitioning from sync to async as we cross some threshold)
37+ *
38+ * So why the Pipe version? and why not *just* use Pipe? The custom sync/async versions out-perform Pipe, but
39+ * there's a dangling bug where occasionally it will stall - presumably some edge-case where the wake logic
40+ * is borked. When I debug that, I'll make the other versions the defaults, but for now: Pipe is the default,
41+ * with the others only available to me for testing.
42+ */
43+
1744 public enum WriteMode
1845 {
1946 Default ,
0 commit comments