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  }