Loading…
To Thread or Not to Thread: An In-Depth Look at Ruby’s Execution Models
2023-10-18
- Source
- Shopify
- Published
- Added to Yomu
Summary
Threaded servers such as Puma and thread-based Sidekiq are popular Ruby deployment choices because threads can increase throughput without a proportional increase in memory. The post compares process and thread models using a memory model that separates static application memory from per-request processing memory, then refines it with Copy on Write (CoW), where forked processes share pages until writes force copies. It explains why RSS can double-count shared pages, recommends PSS and Linux’s /proc/$PID/smaps_rollup for more meaningful measurements, and gives a production example in which about two-thirds of static memory is shared. The conclusion is metric-dependent: preload applications to improve CoW, prefer fewer larger containers when appropriate, consider Unicorn for reliable request timeouts, and start web workloads at two threads unless substantial I/O wait justifies more; job processors may benefit from higher counts.
Context
Ruby applications increasingly use threaded web servers and job processors. The stated motivation is to understand the throughput, memory, latency, request-timeout, and Copy on Write trade-offs involved in choosing processes or threads and selecting a thread count.
Approach / What changed
The post builds a simplified memory model for processes and threads, then adds Copy on Write behavior to explain shared and private memory. It compares RSS with PSS and Linux smaps_rollup measurements, uses production memory figures to estimate sharing, and discusses preloading, container sizing, server selection, and thread-count guidance.
Takeaways
- RSS can overstate memory usage for forked servers because shared pages are reported for each process; PSS and /proc/$PID/smaps_rollup provide more useful views of actual ownership and sharing.
- Preloading the application before forking improves Copy on Write efficiency by allowing more static memory to remain shared; delayed writes and other runtime changes can reduce that benefit.
- For threaded web workloads, the guidance is to start with two threads unless the application spends more than half its time waiting on I/O, while job processors can benefit from higher thread counts because they are generally more I/O intensive.