Protects against slow or unresponsive nodes during file transfers by enforcing per-operation deadlines and adding an overall timeout for file downloads.
- adds `DeadlineConn` wrapper that resets read/write deadlines after each I/O operation, ensuring data must flow continuously
- replaces `FileClient` with `FileDownloader` - a background download manager that:
- limits concurrent downloads to 2x CPU count
- deduplicates in-flight requests for the same file
- cancels obsolete downloads when a newer file version becomes available
- enforces an overall download timeout (default 10 minutes)
Closes #286
41 lines
1.2 KiB
Go
41 lines
1.2 KiB
Go
package net
|
|
|
|
import (
|
|
"net"
|
|
"time"
|
|
)
|
|
|
|
// DeadlineConn wraps a net.Conn with per-operation deadline resets.
|
|
// This protects against slow or unresponsive nodes by ensuring each read/write must complete within the specified
|
|
// timeout.
|
|
//
|
|
// Each Read() call sets a fresh read deadline, and each Write() call sets a fresh write deadline.
|
|
// As long as data flows, the deadlines are continually extended.
|
|
// If no bytes arrive/are sent within the timeout, the operation fails with os.ErrDeadlineExceeded.
|
|
type DeadlineConn struct {
|
|
net.Conn
|
|
|
|
ReadDeadline time.Duration
|
|
WriteDeadline time.Duration
|
|
}
|
|
|
|
// Read implements io.Reader with per-read deadline enforcement.
|
|
func (c *DeadlineConn) Read(p []byte) (int, error) {
|
|
if c.ReadDeadline > 0 {
|
|
_ = c.SetReadDeadline(time.Now().Add(c.ReadDeadline))
|
|
}
|
|
|
|
//nolint:wrapcheck // io.Reader interface requires unwrapped errors
|
|
return c.Conn.Read(p)
|
|
}
|
|
|
|
// Write implements io.Writer with per-write deadline enforcement.
|
|
func (c *DeadlineConn) Write(p []byte) (int, error) {
|
|
if c.WriteDeadline > 0 {
|
|
_ = c.SetWriteDeadline(time.Now().Add(c.WriteDeadline))
|
|
}
|
|
|
|
//nolint:wrapcheck // io.Writer interface requires unwrapped errors
|
|
return c.Conn.Write(p)
|
|
}
|