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 }