github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/rest/rpc-stats.go (about)

     1  // Copyright (c) 2015-2022 MinIO, Inc.
     2  //
     3  // This file is part of MinIO Object Storage stack
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // This program is distributed in the hope that it will be useful
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package rest
    19  
    20  import (
    21  	"net/http"
    22  	"net/http/httptrace"
    23  	"sync/atomic"
    24  	"time"
    25  )
    26  
    27  var globalStats = struct {
    28  	errs uint64
    29  
    30  	tcpDialErrs     uint64
    31  	tcpDialCount    uint64
    32  	tcpDialTotalDur uint64
    33  }{}
    34  
    35  // RPCStats holds information about the DHCP/TCP metrics and errors
    36  type RPCStats struct {
    37  	Errs uint64
    38  
    39  	DialAvgDuration uint64
    40  	DialErrs        uint64
    41  }
    42  
    43  // GetRPCStats returns RPC stats, include calls errors and dhcp/tcp metrics
    44  func GetRPCStats() RPCStats {
    45  	s := RPCStats{
    46  		Errs:     atomic.LoadUint64(&globalStats.errs),
    47  		DialErrs: atomic.LoadUint64(&globalStats.tcpDialErrs),
    48  	}
    49  	if v := atomic.LoadUint64(&globalStats.tcpDialCount); v > 0 {
    50  		s.DialAvgDuration = atomic.LoadUint64(&globalStats.tcpDialTotalDur) / v
    51  	}
    52  	return s
    53  }
    54  
    55  // Return a function which update the global stats related to tcp connections
    56  func setupReqStatsUpdate(req *http.Request) (*http.Request, func()) {
    57  	var dialStart, dialEnd int64
    58  
    59  	trace := &httptrace.ClientTrace{
    60  		ConnectStart: func(network, addr string) {
    61  			atomic.StoreInt64(&dialStart, time.Now().UnixNano())
    62  		},
    63  		ConnectDone: func(network, addr string, err error) {
    64  			if err == nil {
    65  				atomic.StoreInt64(&dialEnd, time.Now().UnixNano())
    66  			}
    67  		},
    68  	}
    69  
    70  	return req.WithContext(httptrace.WithClientTrace(req.Context(), trace)), func() {
    71  		if ds := atomic.LoadInt64(&dialStart); ds > 0 {
    72  			if de := atomic.LoadInt64(&dialEnd); de == 0 {
    73  				atomic.AddUint64(&globalStats.tcpDialErrs, 1)
    74  			} else if de >= ds {
    75  				atomic.AddUint64(&globalStats.tcpDialCount, 1)
    76  				atomic.AddUint64(&globalStats.tcpDialTotalDur, uint64(dialEnd-dialStart))
    77  			}
    78  		}
    79  	}
    80  }