diff --git a/weed/util/net_timeout.go b/weed/util/net_timeout.go index f235a77b3..c0bba1bef 100644 --- a/weed/util/net_timeout.go +++ b/weed/util/net_timeout.go @@ -1,10 +1,11 @@ package util import ( - "github.com/seaweedfs/seaweedfs/weed/glog" "net" "time" + "github.com/seaweedfs/seaweedfs/weed/glog" + "github.com/seaweedfs/seaweedfs/weed/stats" ) @@ -39,6 +40,7 @@ type Conn struct { isClosed bool bytesRead int64 bytesWritten int64 + lastWrite time.Time } func (c *Conn) Read(b []byte) (count int, e error) { @@ -58,8 +60,28 @@ func (c *Conn) Read(b []byte) (count int, e error) { func (c *Conn) Write(b []byte) (count int, e error) { if c.WriteTimeout != 0 { - // minimum 4KB/s - err := c.Conn.SetWriteDeadline(time.Now().Add(c.WriteTimeout * time.Duration(c.bytesWritten/40000+1))) + now := time.Now() + // Calculate timeout with two components: + // 1. Base timeout scaled by cumulative data (minimum 4KB/s throughput) + // 2. Additional grace period if there was a gap since last write (for chunk fetch delays) + timeoutMultiplier := time.Duration(c.bytesWritten/40000 + 1) + baseTimeout := c.WriteTimeout * timeoutMultiplier + + // If it's been a while since last write, add grace time for server-side chunk fetches + // But cap it to avoid keeping slow clients connected indefinitely + if !c.lastWrite.IsZero() { + timeSinceLastWrite := now.Sub(c.lastWrite) + if timeSinceLastWrite > c.WriteTimeout { + // Add grace time, but cap at 3x base timeout to handle slow clients + graceTime := timeSinceLastWrite + if graceTime > baseTimeout*3 { + graceTime = baseTimeout * 3 + } + baseTimeout += graceTime + } + } + + err := c.Conn.SetWriteDeadline(now.Add(baseTimeout)) if err != nil { return 0, err } @@ -68,6 +90,7 @@ func (c *Conn) Write(b []byte) (count int, e error) { if e == nil { stats.BytesOut(int64(count)) c.bytesWritten += int64(count) + c.lastWrite = time.Now() } return }