github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/services/remotecluster/ping.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package remotecluster 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "time" 10 11 "github.com/masterhung0112/hk_server/v5/model" 12 "github.com/masterhung0112/hk_server/v5/shared/mlog" 13 ) 14 15 // pingLoop periodically sends a ping to all remote clusters. 16 func (rcs *Service) pingLoop(done <-chan struct{}) { 17 pingChan := make(chan *model.RemoteCluster, MaxConcurrentSends*2) 18 19 // create a thread pool to send pings concurrently to remotes. 20 for i := 0; i < MaxConcurrentSends; i++ { 21 go rcs.pingEmitter(pingChan, done) 22 } 23 24 go rcs.pingGenerator(pingChan, done) 25 } 26 27 func (rcs *Service) pingGenerator(pingChan chan *model.RemoteCluster, done <-chan struct{}) { 28 defer close(pingChan) 29 30 for { 31 start := time.Now() 32 33 // get all remotes, including any previously offline. 34 remotes, err := rcs.server.GetStore().RemoteCluster().GetAll(model.RemoteClusterQueryFilter{}) 35 if err != nil { 36 rcs.server.GetLogger().Log(mlog.LvlRemoteClusterServiceError, "Ping remote cluster failed (could not get list of remotes)", mlog.Err(err)) 37 select { 38 case <-time.After(PingFreq): 39 continue 40 case <-done: 41 return 42 } 43 } 44 45 for _, rc := range remotes { 46 if rc.SiteURL != "" { // filter out unconfirmed invites 47 pingChan <- rc 48 } 49 } 50 51 // try to maintain frequency 52 elapsed := time.Since(start) 53 if elapsed < PingFreq { 54 sleep := time.Until(start.Add(PingFreq)) 55 select { 56 case <-time.After(sleep): 57 case <-done: 58 return 59 } 60 } 61 } 62 } 63 64 // pingEmitter pulls Remotes from the ping queue (pingChan) and pings them. 65 // Pinging a remote cannot take longer than PingTimeoutMillis. 66 func (rcs *Service) pingEmitter(pingChan <-chan *model.RemoteCluster, done <-chan struct{}) { 67 for { 68 select { 69 case rc := <-pingChan: 70 if rc == nil { 71 return 72 } 73 74 online := rc.IsOnline() 75 76 if err := rcs.pingRemote(rc); err != nil { 77 rcs.server.GetLogger().Log(mlog.LvlRemoteClusterServiceWarn, "Remote cluster ping failed", 78 mlog.String("remote", rc.DisplayName), 79 mlog.String("remoteId", rc.RemoteId), 80 mlog.Err(err), 81 ) 82 } 83 84 if online != rc.IsOnline() { 85 if metrics := rcs.server.GetMetrics(); metrics != nil { 86 metrics.IncrementRemoteClusterConnStateChangeCounter(rc.RemoteId, rc.IsOnline()) 87 } 88 rcs.fireConnectionStateChgEvent(rc) 89 } 90 case <-done: 91 return 92 } 93 } 94 } 95 96 // pingRemote make a synchronous ping to a remote cluster. Return is error if ping is 97 // unsuccessful and nil on success. 98 func (rcs *Service) pingRemote(rc *model.RemoteCluster) error { 99 frame, err := makePingFrame(rc) 100 if err != nil { 101 return err 102 } 103 url := fmt.Sprintf("%s/%s", rc.SiteURL, PingURL) 104 105 resp, err := rcs.sendFrameToRemote(PingTimeout, rc, frame, url) 106 if err != nil { 107 return err 108 } 109 110 ping := model.RemoteClusterPing{} 111 err = json.Unmarshal(resp, &ping) 112 if err != nil { 113 return err 114 } 115 116 if err := rcs.server.GetStore().RemoteCluster().SetLastPingAt(rc.RemoteId); err != nil { 117 rcs.server.GetLogger().Log(mlog.LvlRemoteClusterServiceError, "Failed to update LastPingAt for remote cluster", 118 mlog.String("remote", rc.DisplayName), 119 mlog.String("remoteId", rc.RemoteId), 120 mlog.Err(err), 121 ) 122 } 123 rc.LastPingAt = model.GetMillis() 124 125 if metrics := rcs.server.GetMetrics(); metrics != nil { 126 sentAt := time.Unix(0, ping.SentAt*int64(time.Millisecond)) 127 elapsed := time.Since(sentAt).Seconds() 128 metrics.ObserveRemoteClusterPingDuration(rc.RemoteId, elapsed) 129 130 // we approximate clock skew between remotes. 131 skew := elapsed/2 - float64(ping.RecvAt-ping.SentAt)/1000 132 metrics.ObserveRemoteClusterClockSkew(rc.RemoteId, skew) 133 } 134 135 rcs.server.GetLogger().Log(mlog.LvlRemoteClusterServiceDebug, "Remote cluster ping", 136 mlog.String("remote", rc.DisplayName), 137 mlog.String("remoteId", rc.RemoteId), 138 mlog.Int64("SentAt", ping.SentAt), 139 mlog.Int64("RecvAt", ping.RecvAt), 140 mlog.Int64("Diff", ping.RecvAt-ping.SentAt), 141 ) 142 return nil 143 } 144 145 func makePingFrame(rc *model.RemoteCluster) (*model.RemoteClusterFrame, error) { 146 ping := model.RemoteClusterPing{ 147 SentAt: model.GetMillis(), 148 } 149 pingRaw, err := json.Marshal(ping) 150 if err != nil { 151 return nil, err 152 } 153 154 msg := model.NewRemoteClusterMsg(PingTopic, pingRaw) 155 156 frame := &model.RemoteClusterFrame{ 157 RemoteId: rc.RemoteId, 158 Msg: msg, 159 } 160 return frame, nil 161 } 162 163 func (rcs *Service) fireConnectionStateChgEvent(rc *model.RemoteCluster) { 164 rcs.mux.RLock() 165 listeners := make([]ConnectionStateListener, 0, len(rcs.connectionStateListeners)) 166 for _, l := range rcs.connectionStateListeners { 167 listeners = append(listeners, l) 168 } 169 rcs.mux.RUnlock() 170 171 for _, l := range listeners { 172 l(rc, rc.IsOnline()) 173 } 174 }