diff --git a/cmd/common-main.go b/cmd/common-main.go index 5880eed70..8d775e2f7 100644 --- a/cmd/common-main.go +++ b/cmd/common-main.go @@ -393,6 +393,7 @@ func buildServerCtxt(ctx *cli.Context, ctxt *serverCtxt) (err error) { ctxt.ConnReadDeadline = ctx.Duration("conn-read-deadline") ctxt.ConnWriteDeadline = ctx.Duration("conn-write-deadline") ctxt.ConnClientReadDeadline = ctx.Duration("conn-client-read-deadline") + ctxt.ConnClientWriteDeadline = ctx.Duration("conn-client-write-deadline") ctxt.ShutdownTimeout = ctx.Duration("shutdown-timeout") ctxt.IdleTimeout = ctx.Duration("idle-timeout") diff --git a/cmd/globals.go b/cmd/globals.go index bcd1e6b57..b55ee5b98 100644 --- a/cmd/globals.go +++ b/cmd/globals.go @@ -160,10 +160,11 @@ type serverCtxt struct { FTP []string SFTP []string - UserTimeout time.Duration - ConnReadDeadline time.Duration - ConnWriteDeadline time.Duration - ConnClientReadDeadline time.Duration + UserTimeout time.Duration + ConnReadDeadline time.Duration + ConnWriteDeadline time.Duration + ConnClientReadDeadline time.Duration + ConnClientWriteDeadline time.Duration ShutdownTimeout time.Duration IdleTimeout time.Duration diff --git a/cmd/handler-utils.go b/cmd/handler-utils.go index 04e255d69..e86a72264 100644 --- a/cmd/handler-utils.go +++ b/cmd/handler-utils.go @@ -296,27 +296,23 @@ func collectAPIStats(api string, f http.HandlerFunc) http.HandlerFunc { bucket, _ := path2BucketObject(resource) - globalHTTPStats.currentS3Requests.Inc(api) - defer globalHTTPStats.currentS3Requests.Dec(api) - _, err = globalBucketMetadataSys.Get(bucket) // check if this bucket exists. - if bucket != "" && bucket != minioReservedBucket && err == nil { + countBktStat := bucket != "" && bucket != minioReservedBucket && err == nil + if countBktStat { globalBucketHTTPStats.updateHTTPStats(bucket, api, nil) } + globalHTTPStats.currentS3Requests.Inc(api) f.ServeHTTP(w, r) + globalHTTPStats.currentS3Requests.Dec(api) - tc, ok := r.Context().Value(mcontext.ContextTraceKey).(*mcontext.TraceCtxt) - if !ok { - return - } - + tc, _ := r.Context().Value(mcontext.ContextTraceKey).(*mcontext.TraceCtxt) if tc != nil { globalHTTPStats.updateStats(api, tc.ResponseRecorder) globalConnStats.incS3InputBytes(int64(tc.RequestRecorder.Size())) globalConnStats.incS3OutputBytes(int64(tc.ResponseRecorder.Size())) - if bucket != "" && bucket != minioReservedBucket && err == nil { + if countBktStat { globalBucketConnStats.incS3InputBytes(bucket, int64(tc.RequestRecorder.Size())) globalBucketConnStats.incS3OutputBytes(bucket, int64(tc.ResponseRecorder.Size())) globalBucketHTTPStats.updateHTTPStats(bucket, api, tc.ResponseRecorder) diff --git a/cmd/server-main.go b/cmd/server-main.go index 8747b7e26..d16c8a80c 100644 --- a/cmd/server-main.go +++ b/cmd/server-main.go @@ -106,6 +106,12 @@ var ServerFlags = []cli.Flag{ Hidden: true, EnvVar: "MINIO_CONN_CLIENT_READ_DEADLINE", }, + cli.DurationFlag{ + Name: "conn-client-write-deadline", + Usage: "custom connection WRITE deadline for outgoing requests", + Hidden: true, + EnvVar: "MINIO_CONN_CLIENT_WRITE_DEADLINE", + }, cli.DurationFlag{ Name: "conn-read-deadline", Usage: "custom connection READ deadline", @@ -356,9 +362,10 @@ func serverHandleCmdArgs(ctxt serverCtxt) { }) globalTCPOptions = xhttp.TCPOptions{ - UserTimeout: int(ctxt.UserTimeout.Milliseconds()), - ClientReadTimeout: ctxt.ConnClientReadDeadline, - Interface: ctxt.Interface, + UserTimeout: int(ctxt.UserTimeout.Milliseconds()), + ClientReadTimeout: ctxt.ConnClientReadDeadline, + ClientWriteTimeout: ctxt.ConnClientWriteDeadline, + Interface: ctxt.Interface, } // On macOS, if a process already listens on LOCALIPADDR:PORT, net.Listen() falls back diff --git a/internal/cachevalue/cache.go b/internal/cachevalue/cache.go index 1d6bca668..46b5c756e 100644 --- a/internal/cachevalue/cache.go +++ b/internal/cachevalue/cache.go @@ -70,16 +70,16 @@ type Cache[T any] struct { updating sync.Mutex } -// New allocates a new cached value instance. It must be initialized with -// `.InitOnce`. -func New[I any]() *Cache[I] { - return &Cache[I]{} +// New allocates a new cached value instance. Tt must be initialized with +// `.TnitOnce`. +func New[T any]() *Cache[T] { + return &Cache[T]{} } // NewFromFunc allocates a new cached value instance and initializes it with an // update function, making it ready for use. -func NewFromFunc[I any](ttl time.Duration, opts Opts, update func() (I, error)) *Cache[I] { - return &Cache[I]{ +func NewFromFunc[T any](ttl time.Duration, opts Opts, update func() (T, error)) *Cache[T] { + return &Cache[T]{ ttl: ttl, updateFn: update, opts: opts, @@ -88,7 +88,7 @@ func NewFromFunc[I any](ttl time.Duration, opts Opts, update func() (I, error)) // InitOnce initializes the cache with a TTL and an update function. It is // guaranteed to be called only once. -func (t *Cache[I]) InitOnce(ttl time.Duration, opts Opts, update func() (I, error)) { +func (t *Cache[T]) InitOnce(ttl time.Duration, opts Opts, update func() (T, error)) { t.Once.Do(func() { t.ttl = ttl t.updateFn = update @@ -97,8 +97,8 @@ func (t *Cache[I]) InitOnce(ttl time.Duration, opts Opts, update func() (I, erro } // Get will return a cached value or fetch a new one. -// If the Update function returns an error the value is forwarded as is and not cached. -func (t *Cache[I]) Get() (I, error) { +// Tf the Update function returns an error the value is forwarded as is and not cached. +func (t *Cache[T]) Get() (T, error) { v := t.valErr.Load() ttl := t.ttl vTime := t.lastUpdateMs.Load() diff --git a/internal/http/listener.go b/internal/http/listener.go index bda86485d..7e818ad3e 100644 --- a/internal/http/listener.go +++ b/internal/http/listener.go @@ -79,7 +79,8 @@ func (listener *httpListener) Accept() (conn net.Conn, err error) { case result, ok := <-listener.acceptCh: if ok { return deadlineconn.New(result.conn). - WithReadDeadline(listener.opts.ClientReadTimeout), result.err + WithReadDeadline(listener.opts.ClientReadTimeout). + WithWriteDeadline(listener.opts.ClientWriteTimeout), result.err } case <-listener.ctx.Done(): } @@ -124,10 +125,11 @@ func (listener *httpListener) Addrs() (addrs []net.Addr) { // TCPOptions specify customizable TCP optimizations on raw socket type TCPOptions struct { - UserTimeout int // this value is expected to be in milliseconds - ClientReadTimeout time.Duration // When the net.Conn is idle for more than ReadTimeout duration, we close the connection on the client proactively. - Interface string // this is a VRF device passed via `--interface` flag - Trace func(msg string) // Trace when starting. + UserTimeout int // this value is expected to be in milliseconds + ClientReadTimeout time.Duration // When the net.Conn is idle for more than ReadTimeout duration, we close the connection on the client proactively. + ClientWriteTimeout time.Duration // When the net.Conn is idle for more than WriteTimeout duration, we close the connection on the client proactively. + Interface string // this is a VRF device passed via `--interface` flag + Trace func(msg string) // Trace when starting. } // newHTTPListener - creates new httpListener object which is interface compatible to net.Listener.