DEV Community

Wesley Conde
Wesley Conde

Posted on • Edited on

Goodbye, I/O Bottlenecks: Building an asynchronous ring-buffer logger with concurrent channels in Go

Physical Threads vs. Goroutines (the Go scheduler)

Traditional programming languages delegate concurrency by directly mapping virtual threads to operating system threads (1:1 model). Each system thread consumes roughly 1 MB of physical memory and requires costly hardware-managed context switching.

Go bypasses this bottleneck via the GMP cooperative scheduling model, running lightweight Goroutines managed entirely by the Go runtime. They start by consuming a mere 2 KB of stack memory and are dynamically multiplexed across physical threads by logical processors, maximizing hardware utilization.

Buffered channels as concurrent boundaries

Writing logs directly to the console or disk synchronously within the main request-handling thread of an API is a critical performance anti-pattern. Under heavy traffic, your application will spend more time waiting for the physical disk or stdout to respond than executing business rules.

To build an asynchronous, high-performance logging buffer, Orkai routes serialized log strings into a concurrent buffered channel (chan string). This channel acts as an ultra-fast in-memory queue. A dedicated background worker goroutine consumes these messages sequentially and performs the physical write in an isolated thread, leaving the main request flow completely non-blocking.

func (l *JSONLogger) asyncWorker() {
    defer l.asyncWg.Done()
    for {
        select {
        case logStr, ok := <-l.asyncChan:
            if !ok {
                return
            }
            _, _ = l.writer.Write([]byte(logStr)) // Physical disk/console I/O
        case <-l.asyncStop:
            l.flushChan() // Ensure all remaining logs in queue are flushed before exit
            return
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Zero-loss saturation fallback

What happens if the application faces a massive spike in traffic and produces logs faster than the background worker can physically write them to disk? In standard buffered channels, trying to write to a full channel immediately blocks the sender's execution path.

To prevent severe latency degradation under extreme load, we designed a smart Zero-Loss Saturation Fallback mechanism. Utilizing Go's non-blocking select statement, the Orkai Logger instantly detects if the in-memory queue has reached capacity. If full, the log safely bypasses the queue and is written synchronously directly to the destination (l.writer), avoiding data loss and avoiding thread starvation.

In addition, the Logger implements a true Graceful Shutdown: when the application is closing, the asyncStop channel is triggered, causing the worker to drain the remaining queue buffer (flushChan) completely before exiting.

func (l *JSONLogger) deliverLog(jsonStr string) {
    if l.asyncEnabled {
        select {
        case l.asyncChan <- jsonStr:
            // Enqueued in memory instantly in nanoseconds
        default:
            // Queue buffer is full! Bypass and write synchronously to prevent blocking
            _, _ = l.writer.Write([]byte(jsonStr))
        }
        return
    }
    _, _ = l.writer.Write([]byte(jsonStr))
}
Enter fullscreen mode Exit fullscreen mode

Technical terms explained

  • Channels: Native Go primitives that allow safe data sharing and communication between concurrent Goroutines without requiring manual mutex locks.
  • Graceful Shutdown: The process of ensuring an application processes and saves all active or queued transactions before shutting down, preventing file corruption.
  • Non-Blocking Select: Leveraging Go's select block alongside a default clause to execute alternate logic immediately if a channel send or receive would block.

Link: https://github.com/wesleyskap/orkai-observability

Top comments (0)