github.com/swiftstack/proxyfs@v0.0.0-20201223034610-5434d919416e/stats/sender.go (about)

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