github.com/swiftstack/ProxyFS@v0.0.0-20210203235616-4017c267d62f/stats/sender.go (about)

     1  // Copyright (c) 2015-2021, NVIDIA CORPORATION.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package stats
     5  
     6  import (
     7  	"fmt"
     8  	"net"
     9  	"strconv"
    10  	// XXX TODO: Can't call logger from here since logger calls stats.
    11  	//"github.com/swiftstack/ProxyFS/logger"
    12  )
    13  
    14  func sender() {
    15  	var (
    16  		err             error
    17  		newStatNameLink *statNameLinkStruct
    18  		ok              bool
    19  		oldIncrement    uint64
    20  		stat            *statStruct
    21  		statBuffer      []byte = make([]byte, 0, 128)
    22  		statIncrement   uint64
    23  		statName        string
    24  		tcpConn         *net.TCPConn
    25  		udpConn         *net.UDPConn
    26  	)
    27  
    28  selectLoop:
    29  	for {
    30  		select {
    31  		case stat = <-globals.statChan:
    32  			statNameStr := *stat.name
    33  			oldIncrement, ok = globals.statDeltaMap[statNameStr]
    34  			if ok {
    35  				globals.statDeltaMap[statNameStr] = oldIncrement + stat.increment
    36  			} else {
    37  				globals.statDeltaMap[statNameStr] = stat.increment
    38  
    39  				newStatNameLink = &statNameLinkStruct{name: statNameStr, next: nil}
    40  				if nil == globals.tailStatNameLink {
    41  					globals.headStatNameLink = newStatNameLink
    42  				} else {
    43  					globals.tailStatNameLink.next = newStatNameLink
    44  				}
    45  				globals.tailStatNameLink = newStatNameLink
    46  			}
    47  			globals.Lock()
    48  			oldIncrement, ok = globals.statFullMap[statNameStr]
    49  			if ok {
    50  				globals.statFullMap[statNameStr] = oldIncrement + stat.increment
    51  			} else {
    52  				globals.statFullMap[statNameStr] = stat.increment
    53  			}
    54  			globals.Unlock()
    55  			statStructPool.Put(stat)
    56  		case _ = <-globals.stopChan:
    57  			globals.doneChan <- true
    58  			return
    59  		case <-globals.tickChan:
    60  			if nil == globals.headStatNameLink {
    61  				// Nothing to read
    62  				//fmt.Printf("stats sender: got tick but nothing to read\n")
    63  				continue selectLoop
    64  			}
    65  
    66  			// Handle up to maxStatsPerTimeout stats that we have right now
    67  			// Need to balance sending over UDP with servicing our stats channel
    68  			// since if the stats channel gets full the callers will block.
    69  			// XXX TODO: This should probably be configurable. And do we want to keep our own stats
    70  			//           on how many stats we handle per timeout? And how full the stats channel gets?
    71  			maxStatsPerTimeout := 20
    72  			statsHandled := 0
    73  			for (globals.headStatNameLink != nil) && (statsHandled < maxStatsPerTimeout) {
    74  
    75  				statName = globals.headStatNameLink.name
    76  				globals.headStatNameLink = globals.headStatNameLink.next
    77  				if nil == globals.headStatNameLink {
    78  					globals.tailStatNameLink = nil
    79  				}
    80  				statIncrement, ok = globals.statDeltaMap[statName]
    81  				if !ok {
    82  					err = fmt.Errorf("stats.sender() should be able to find globals.statDeltaMap[\"%v\"]", statName)
    83  					panic(err)
    84  				}
    85  				delete(globals.statDeltaMap, statName)
    86  				statsHandled++
    87  
    88  				// Write stat into our buffer
    89  				statBuffer = []byte(statName + ":" + strconv.FormatUint(statIncrement, 10) + "|c")
    90  
    91  				// Send stat
    92  				// XXX TODO: should we keep the conn around and reuse it inside this for loop?
    93  				if globals.useUDP {
    94  					udpConn, err = net.DialUDP("udp", globals.udpLAddr, globals.udpRAddr)
    95  					if nil != err {
    96  						continue selectLoop
    97  					}
    98  					_, err = udpConn.Write(statBuffer)
    99  					if nil != err {
   100  						continue selectLoop
   101  					}
   102  					err = udpConn.Close()
   103  					if nil != err {
   104  						continue selectLoop
   105  					}
   106  				} else { // globals.useTCP
   107  					tcpConn, err = net.DialTCP("tcp", globals.tcpLAddr, globals.tcpRAddr)
   108  					if nil != err {
   109  						continue selectLoop
   110  					}
   111  					_, err = tcpConn.Write(statBuffer)
   112  					if nil != err {
   113  						continue selectLoop
   114  					}
   115  					err = tcpConn.Close()
   116  					if nil != err {
   117  						continue selectLoop
   118  					}
   119  				}
   120  				// Clear buffer for next time
   121  				statBuffer = statBuffer[:0]
   122  			}
   123  			//fmt.Printf("handled %v stats on tick. Channel contains %v stats.\n", statsHandled, len(globals.statChan))
   124  		}
   125  	}
   126  }