Skip to content

add LowLatencyBuffer for improved performance#118

Merged
copperlight merged 1 commit intoNetflix:mainfrom
copperlight:low-latency-buffer
Jul 11, 2025
Merged

add LowLatencyBuffer for improved performance#118
copperlight merged 1 commit intoNetflix:mainfrom
copperlight:low-latency-buffer

Conversation

@copperlight
Copy link
Collaborator

@copperlight copperlight commented Jul 10, 2025

The introduction of the LineBuffer allowed spectator-go to approach the ~1M lines/sec maximum ingestion rate of spectatord, without dropping protocol lines. The maximum size of this buffer must remain less than the 64KB socket buffer size. The line rate of this buffer comes at the cost of latency per increment, which can range from 4 to 15 us, depending upon thread count and metrics updates. This delay is too slow for highly concurrent applications.

This change adds a LowLatencyBuffer which allows for much larger buffer sizes, into the hundreds of MB, while maintaining low latency. The line rate for this buffer can approach the ~1M lines/sec maximum ingestion rate of spectatord, but if the application runs too fast, the flush interval is too big, and the buffers are not large enough, then it will drop metrics. However, it continue delivering low latency under all of these conditions, which is achieved with arrays of mutexes that scale based upon CPU count and buffer size.

Thus, this library now has three modes of operation available, for applications that operate at different scales:

  • Small. No buffer (size 0 bytes). Write immediately to the socket upon every metric update, up to ~150K lines/sec, with delays from 1 to 450 us, depending on thread count. No metrics are dropped.
  • Medium. LineBuffer (size <= 65536 bytes), which writes to the socket upon overflow, or upon a flush interval, up to ~1M lines/sec, with delays from 1 to 32 us, depending on thread count. No metrics are dropped. Status metrics are published to monitor usage.
  • Large. LowLatencyBuffer (size > 65536 bytes), which writes to the socket on a flush interval, up to ~1M lines/sec, with delays from 1 to 7 us, depending on thread count. The true minimum size is 2 * CPU * 60KB, or 122,880 bytes for 1 CPU. Metrics may be dropped. Status metrics are published to monitor usage.

The buffers are available for the UdpWriter and the UnixWriter.

Summary of additional changes:

  • Extract additional writers from writers.go to keep them separate
  • Refactor the Writer interface, to support buffers
  • Update GitHub Actions and Makefile for Go 1.23 and Go 1.24
  • Update golangci-lint and config to version 2.2.1

@copperlight copperlight force-pushed the low-latency-buffer branch 7 times, most recently from c8a212a to 2e8f0f9 Compare July 10, 2025 22:49
Copy link

@KBaichoo KBaichoo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, thank you for adding this

The introduction of the LineBuffer allowed spectator-go to approach the ~1M lines/sec
maximum ingestion rate of spectatord, without dropping protocol lines. The maximum size
of this buffer must remain less than the 64KB socket buffer size. The line rate of this
buffer comes at the cost of latency per increment, which can range from 4 to 15 us,
depending upon thread count and metrics updates. This delay is too slow for highly
concurrent applications.

This change adds a LowLatencyBuffer which allows for much larger buffer sizes, into the
hundreds of MB, while maintaining low latency. The line rate for this buffer can approach
the ~1M lines/sec maximum ingestion rate of spectatord, but if the application runs too
fast, the flush interval is too big, and the buffers are not large enough, then it will
drop metrics. However, it continue delivering low latency under all of these conditions,
which is achieved with arrays of mutexes that scale based upon CPU count and buffer size.

Thus, this library now has three modes of operation available, for applications that
operate at different scales:

* **Small.** No buffer (size 0 bytes). Write immediately to the socket upon every metric
update, up to ~150K lines/sec, with delays from 1 to 450 us, depending on thread count. No
metrics are dropped.
* **Medium.** LineBuffer (size <= 65536 bytes), which writes to the socket upon overflow,
or upon a flush interval, up to ~1M lines/sec, with delays from 1 to 32 us, depending on
thread count. No metrics are dropped. Status metrics are published to monitor usage.
* **Large.** LowLatencyBuffer (size > 65536 bytes), which writes to the socket on a flush
interval, up to ~1M lines/sec, with delays from 1 to 7 us, depending on thread count. The
true minimum size is 2 * CPU * 60KB, or 122,880 bytes for 1 CPU. Metrics may be dropped.
Status metrics are published to monitor usage.

The buffers are available for the UdpWriter and the UnixWriter.

Summary of additional changes:

- Extract additional writers from writers.go to keep them separate
- Refactor the Writer interface, to support buffers
- Update GitHub Actions and Makefile for Go 1.23 and Go 1.24
- Update golangci-lint and config to version 2.2.1
@copperlight copperlight merged commit db96b2c into Netflix:main Jul 11, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants