-
Notifications
You must be signed in to change notification settings - Fork 18.8k
Description
Proposal Details
Summary
This proposal requests the addition of a Writer type that implements io.Writer with built-in rate limiting capabilities to control write operations' bandwidth consumption. This feature would enable developers to easily implement bandwidth throttling for network connections, file operations, and other I/O scenarios.
Motivation
Current Problem
Currently, implementing rate limiting for I/O operations requires:
- Manual integration of rate limiting logic with I/O operations
- Scattered rate limiting code throughout applications
- Complex coordination between multiple rate limiters (global vs per-connection)
- No standardized approach for bandwidth control
Use Cases
- Network Bandwidth Management: Limit upload/download speeds for individual connections or globally
- Server Resource Protection: Prevent individual clients from consuming excessive bandwidth
- Fair Resource Allocation: Ensure equitable bandwidth distribution among multiple clients
- Background Process Throttling: Limit bandwidth usage of background sync operations
- Testing and Simulation: Simulate network conditions with controlled bandwidth
Proposed Solution
API Design
// Writer is an io.Writer that applies rate limiting to write operations.
//
// For example, to limit a reader's read speed to 1KB/s:
//
// limits := rate.NewLimiter(rate.Every(time.Second/1024.0), 1024)
// reader := io.TeeReader(os.Stdin, NewWriter(limits))
//
// Or, to limit a writer's write speed to 1KB/s:
//
// writer := io.MultiWriter(os.Stdout, NewWriter(limits))
type Writer struct {
li *rate.Limiter
}
// Write writes data to the underlying writer, applying rate limiting based on
// the configured limits.
func (l *Writer) Write(p []byte) (int, error) {
l.li.WaitN(context.Background(), len(p))
return len(p), nil
}
// NewWriter creates a new Writer that limits write operations by a limiter.
func NewWriter(limiter *rate.Limiter) *Writer {
return &Writer{li: limits}
}Usage Examples
Basic Reader Rate Limiting
limits := rate.NewLimiter(rate.Every(time.Second/1024.0), 1024) // 1KB per second
reader := io.TeeReader(os.Stdin, NewWriter(limits))
reader.Read(...)Basic Writer Rate Limiting
limits := rate.NewLimiter(rate.Every(time.Second/1024.0), 1024) // 1KB per second
writer := io.MultiWriter(os.Stdout, NewWriter(limits))
writer.Write(...)Network Connection Rate Limiting
Containing both global and per-connection limits:
l, _ := net.Listen("tcp", "127.0.0.1:8080")
// A global connection limit for all connections. 1MB per second.
globalLimits := rate.NewLimiter(rate.Every(time.Second/1024/1024), 1024*1024)
for {
cli, _ := l.Accept()
// A single connection limit. 1KB per second.
singleLimits := rate.NewLimiter(rate.Every(time.Second/1024), 1024) // 1KB per second
// Make a reader that applies both global and single connection limits.
var cliReader io.Reader = cli
cliReader = io.TeeReader(cli, NewWriter(globalLimits)) // Apply global connection read limit
cliReader = io.TeeReader(cliReader, NewWriter(singleLimits)) // Apply single connection read limit
go func() {
io.Copy(io.Discard, cliReader) // Discard all data read from the connection, simulating processing.
}()
}Conclusion
The addition of a rate-limited Writer type would provide a clean, composable solution for bandwidth control in Go applications. It follows established patterns in the standard library (io.TeeReader and io.MultiWriter) while addressing a common need in network programming, file operations, and resource management scenarios.
This feature would significantly simplify the implementation of bandwidth-aware applications and provide a standardized approach to rate limiting across the Go ecosystem.