Skip to content

proposal: x/time: rate-limited Writer for bandwidth control #75491

@mohanson

Description

@mohanson

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:

  1. Manual integration of rate limiting logic with I/O operations
  2. Scattered rate limiting code throughout applications
  3. Complex coordination between multiple rate limiters (global vs per-connection)
  4. No standardized approach for bandwidth control

Use Cases

  1. Network Bandwidth Management: Limit upload/download speeds for individual connections or globally
  2. Server Resource Protection: Prevent individual clients from consuming excessive bandwidth
  3. Fair Resource Allocation: Ensure equitable bandwidth distribution among multiple clients
  4. Background Process Throttling: Limit bandwidth usage of background sync operations
  5. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    LibraryProposalIssues describing a requested change to the Go standard library or x/ libraries, but not to a toolProposal

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions