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 }