@@ -200,16 +200,91 @@ the environment variable takes precedence over the passed config.
200200
201201The environment variable ` SPECTATOR_OUTPUT_LOCATION ` can be set to ` none ` to disable metrics collection.
202202
203- ## Line Buffer
204-
205- The ` NewConfigWithBuffer ` factory function takes a ` bufferSize ` parameter that configures an optional
206- ` LineBuffer ` , which caches protocol lines locally, before flushing them to ` spectatord ` . Flushes occur
207- under two conditions: (1) the buffer size is exceeded, or (2) five seconds has elapsed. The buffer is
208- available for the ` UdpWriter ` and the ` UnixgramWriter ` , where performance matters most. The ` LineBuffer `
209- is disabled by default (with size zero) in the standard ` NewConfig ` factory function, to ensure that the
210- default operation of the library works under most circumstances. For high-performance scenarios, a 60KB
211- buffer size is recommended. The maximum buffer size for udp sockets and unix domain sockets on Linux is
212- 64KB, so stay under this limit.
203+ ## Buffers
204+
205+ Three modes of operation are available, for applications that operate at different scales:
206+
207+ * ** Small.** No buffer (size 0 bytes). Write immediately to the socket upon every metric update, up to
208+ ~ 150K lines/sec, with delays from 1 to 450 us, depending on thread count. No metrics are dropped, due
209+ to mutex locks.
210+ * ** Medium.** LineBuffer (size <= 65536 bytes), which writes to the socket upon overflow, or upon a
211+ flush interval, up to ~ 1M lines/sec, with delays from 0.1 to 32 us, depending on thread count. No metrics
212+ are dropped. Status metrics are published to monitor usage.
213+ * ** Large.** LowLatencyBuffer (size > 65536 bytes), which writes to the socket on a flush interval, up
214+ to ~ 1M lines/sec, with delays from 0.6 to 7 us, depending on thread count. The true minimum size is 2 *
215+ CPU * 60KB, or 122,880 bytes for 1 CPU. Metrics may be dropped. Status metrics are published to monitor
216+ usage.
217+
218+ The buffers are available for the UdpWriter and the UnixWriter.
219+
220+ ### Line Buffer (bufferSize <= 65536)
221+
222+ This is a single string buffer, protected by a mutex, that offers write performance up to ~ 1M lines/sec
223+ (spectatord maximum), with a latency per write ranging from 0.1 to 32 us, depending upon the number of
224+ threads in use.
225+
226+ Metrics are flushed from the buffer when an overflow occurs, and periodically by a timer, according to
227+ the flush interval. Thus, if there are periods of time when metric publishing is slow, metrics will still
228+ be delivered from the buffer on time. Note that the spectatord publish interval is every 5 seconds, which
229+ is a good starting choice for this configuration. This buffer will block, and it will not drop lines.
230+
231+ The LineBuffer reports two metrics, which can be used to monitor buffer performance:
232+
233+ * ` spectator-go.lineBuffer.bytesWritten ` - A counter reporting bytes/sec written to spectatord.
234+ * ` spectator-go.lineBuffer.overflows ` - A counter reporting overflows/sec, which are flushes before the
235+ interval.
236+
237+ Example configuration:
238+
239+ ```
240+ config, _ := NewConfigWithBuffer("udp", nil, nil, 61440, 5*time.Second)
241+ ```
242+
243+ ### Low Latency Buffer (bufferSize > 65536)
244+
245+ LowLatencyBuffer (bufferSize > 65536), which builds arrays of buffers that are optimized for introducing
246+ the least amount of latency in highly multithreaded applications that record many metrics. It offers write
247+ performance up to ~ 1 M lines/sec (spectatord maximum), with a latency per write ranging from 0.6 to 7 us,
248+ depending upon the number of threads in use.
249+
250+ This is achieved by spreading data access across a number of different mutexes, and only writing buffers
251+ from a goroutine that runs periodically, according to the flushInterval. There is a front buffer and a back
252+ buffer, and these are rotated during the periodic flush. The inactive buffer is flushed, while the active
253+ buffer continues to receive metric writes from the application. Within each buffer, there are numCPU shards,
254+ and each buffer shard has N chunks, where a chunk is set to 60KB, to allow the data to fit within the
255+ spectatord socket buffers with room for one last protocol line. This buffer will not block, and it can drop
256+ lines, if it overflows.
257+
258+ As a sizing example, if you have an 8 CPU system, and you want to allocate 5 MB to each buffer shard, and
259+ there are two buffers (front and back), then you need to configure a buffer size of 83,886,080 bytes. Each
260+ buffer shard will have 85 chunks, each of which is protected by a separate mutex.
261+
262+ ```
263+ 2 buffers (front/back) * 8 CPU (shard count) * 5,242,880 bytes/shard * = 83,886,080 bytes total
264+ ```
265+
266+ Pairing this with a 1-second flush interval will result in a configuration that can handle ~ 85K lines/sec
267+ writes to spectatord. Note that the spectatord publish interval is every 5 seconds, so you have some room to
268+ experiment with different buffer sizes and publish intervals.
269+
270+ While the bufferSize can be set as low as 65537, it will guarantee a minimum size of 2 * CPU * 60KB, to
271+ ensure that there is always at least 1 chunk per shard. On a system with 1 CPU, this will be 122,880 bytes,
272+ and on a system with 4 CPU, this will be 491,520 bytes.
273+
274+ The LowLatencyBuffer reports metrics, which can be used to monitor buffer performance:
275+
276+ * ` spectator-go.lowLatencyBuffer.bytesWritten ` - A counter reporting bytes/sec written to spectatord.
277+ * ` spectator-go.lowLatencyBuffer.overflows ` - A counter reporting overflows/sec, which are drops.
278+ * ` spectator-go.lowLatencyBuffer.pctUsage ` - A gauge reporting the percent usage of the buffers.
279+
280+ When using the LowLatencyBuffer, it is recommended to watch the ` spectatord.parsedCount ` metric, to ensure
281+ that you have sufficient headroom against the maximum data ingestion rate of ~ 1M lines/sec for ` spectatord ` .
282+
283+ Example configuration:
284+
285+ ```
286+ config, _ := NewConfigWithBuffer("udp", nil, nil, 83886080, 1*time.Second)
287+ ```
213288
214289## Batch Usage
215290
0 commit comments