github.com/matrixorigin/matrixone@v1.2.0/pkg/gossip/delegate.go (about)

     1  // Copyright 2021 - 2023 Matrix Origin
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package gossip
    16  
    17  import (
    18  	"encoding/binary"
    19  	"fmt"
    20  
    21  	"github.com/hashicorp/memberlist"
    22  	"github.com/matrixorigin/matrixone/pkg/pb/gossip"
    23  	"github.com/matrixorigin/matrixone/pkg/pb/query"
    24  	"github.com/matrixorigin/matrixone/pkg/pb/statsinfo"
    25  	"github.com/matrixorigin/matrixone/pkg/queryservice/client"
    26  	"github.com/pierrec/lz4/v4"
    27  	"go.uber.org/zap"
    28  )
    29  
    30  var (
    31  	binaryEnc = binary.BigEndian
    32  )
    33  
    34  type Module interface {
    35  	// Data returns all data that need to send to other nodes.
    36  	// The second return value is the left size of the limit size.
    37  	Data(limit int) ([]gossip.GossipData, int)
    38  }
    39  
    40  type delegate struct {
    41  	logger *zap.Logger
    42  	//cacheServiceAddr is the service address of the remote cache server.
    43  	cacheServiceAddr string
    44  
    45  	// dataCacheKey keeps the key cache of local set/delete
    46  	// items and remote key info.
    47  	// dataCacheKey *DataCacheKey
    48  	dataCacheKey *BaseStore[query.CacheKey]
    49  
    50  	// statsInfoKey keeps the keys of stats info.
    51  	statsInfoKey *BaseStore[statsinfo.StatsInfoKey]
    52  }
    53  
    54  func newDelegate(logger *zap.Logger, addr string) *delegate {
    55  	d := &delegate{
    56  		logger:           logger,
    57  		cacheServiceAddr: addr,
    58  		dataCacheKey:     newBaseStore[query.CacheKey](addr),
    59  		statsInfoKey:     newBaseStore[statsinfo.StatsInfoKey](addr),
    60  	}
    61  	return d
    62  }
    63  
    64  func (d *delegate) getDataCacheKey() client.KeyRouter[query.CacheKey] {
    65  	return d.dataCacheKey
    66  }
    67  
    68  func (d *delegate) getStatsInfoKey() client.KeyRouter[statsinfo.StatsInfoKey] {
    69  	return d.statsInfoKey
    70  }
    71  
    72  // NodeMeta implements the memberlist.Delegate interface.
    73  func (d *delegate) NodeMeta(limit int) []byte {
    74  	meta := []byte(d.cacheServiceAddr)
    75  	if len(meta) > limit {
    76  		panic(fmt.Sprintf("meta size %d is larger then limit %d", len(meta), limit))
    77  	}
    78  	return meta
    79  }
    80  
    81  // NotifyMsg implements the memberlist.Delegate interface.
    82  func (d *delegate) NotifyMsg(data []byte) {
    83  	if len(data) == 0 {
    84  		return
    85  	}
    86  	var item gossip.GossipData
    87  	if err := item.Unmarshal(data); err != nil {
    88  		d.logger.Error("failed to unmarshal cache item from buf", zap.Error(err))
    89  		return
    90  	}
    91  	switch t := item.Data.(type) {
    92  	case *gossip.GossipData_CacheKeyItem:
    93  		d.dataCacheKey.mu.Lock()
    94  		defer d.dataCacheKey.mu.Unlock()
    95  		m := d.dataCacheKey.mu.keyTarget
    96  
    97  		if t.CacheKeyItem.Operation == gossip.Operation_Set {
    98  			m[t.CacheKeyItem.CacheKey] = t.CacheKeyItem.TargetAddress
    99  		} else if t.CacheKeyItem.Operation == gossip.Operation_Delete {
   100  			delete(m, t.CacheKeyItem.CacheKey)
   101  		}
   102  
   103  	case *gossip.GossipData_StatsInfoKeyItem:
   104  		d.statsInfoKey.mu.Lock()
   105  		defer d.statsInfoKey.mu.Unlock()
   106  		m := d.statsInfoKey.mu.keyTarget
   107  
   108  		if t.StatsInfoKeyItem.Operation == gossip.Operation_Set {
   109  			m[t.StatsInfoKeyItem.StatsInfoKey] = t.StatsInfoKeyItem.TargetAddress
   110  		} else if t.StatsInfoKeyItem.Operation == gossip.Operation_Delete {
   111  			delete(m, t.StatsInfoKeyItem.StatsInfoKey)
   112  		}
   113  	}
   114  }
   115  
   116  func (d *delegate) broadcastData(limit int) []gossip.GossipData {
   117  	left := limit
   118  	items, left := d.dataCacheKey.Data(left)
   119  	data := make([]gossip.GossipData, len(items))
   120  	for _, item := range items {
   121  		data = append(data, gossip.GossipData{
   122  			Data: &gossip.GossipData_CacheKeyItem{
   123  				CacheKeyItem: &gossip.CacheKeyItem{
   124  					Operation:     item.Operation,
   125  					TargetAddress: item.TargetAddress,
   126  					CacheKey:      *item.Key.(*gossip.CommonItem_CacheKey).CacheKey,
   127  				},
   128  			},
   129  		})
   130  	}
   131  	if left == 0 {
   132  		return data
   133  	}
   134  
   135  	items, _ = d.statsInfoKey.Data(left)
   136  	for _, item := range items {
   137  		data = append(data, gossip.GossipData{
   138  			Data: &gossip.GossipData_StatsInfoKeyItem{
   139  				StatsInfoKeyItem: &gossip.StatsInfoKeyItem{
   140  					Operation:     item.Operation,
   141  					TargetAddress: item.TargetAddress,
   142  					StatsInfoKey:  *item.Key.(*gossip.CommonItem_StatsInfoKey).StatsInfoKey,
   143  				},
   144  			},
   145  		})
   146  	}
   147  
   148  	return data
   149  }
   150  
   151  // GetBroadcasts implements the memberlist.Delegate interface.
   152  func (d *delegate) GetBroadcasts(overhead, limit int) [][]byte {
   153  	// For the case that node leave from cluster.
   154  	if d == nil {
   155  		return nil
   156  	}
   157  	items := d.broadcastData(limit - overhead)
   158  	data := make([][]byte, 0, len(items))
   159  	for _, item := range items {
   160  		bytes, err := item.Marshal()
   161  		if err != nil {
   162  			d.logger.Error("failed to marshal item", zap.Error(err))
   163  			return nil
   164  		}
   165  		data = append(data, bytes)
   166  	}
   167  	return data
   168  }
   169  
   170  func (d *delegate) dataCacheState() map[string]*query.CacheKeys {
   171  	d.dataCacheKey.mu.Lock()
   172  	defer d.dataCacheKey.mu.Unlock()
   173  	targetCacheKeys := make(map[string]*query.CacheKeys)
   174  	for key, target := range d.dataCacheKey.mu.keyTarget {
   175  		if _, ok := targetCacheKeys[target]; !ok {
   176  			targetCacheKeys[target] = &query.CacheKeys{}
   177  		}
   178  		targetCacheKeys[target].Keys = append(targetCacheKeys[target].Keys, key)
   179  	}
   180  	return targetCacheKeys
   181  }
   182  
   183  func (d *delegate) statsInfoState() map[string]*statsinfo.StatsInfoKeys {
   184  	d.dataCacheKey.mu.Lock()
   185  	defer d.dataCacheKey.mu.Unlock()
   186  	targetStatsInfo := make(map[string]*statsinfo.StatsInfoKeys)
   187  	for key, target := range d.statsInfoKey.mu.keyTarget {
   188  		if _, ok := targetStatsInfo[target]; !ok {
   189  			targetStatsInfo[target] = &statsinfo.StatsInfoKeys{}
   190  		}
   191  		targetStatsInfo[target].Keys = append(targetStatsInfo[target].Keys, key)
   192  	}
   193  	return targetStatsInfo
   194  }
   195  
   196  // LocalState implements the memberlist.Delegate interface.
   197  func (d *delegate) LocalState(join bool) []byte {
   198  	// If this a join action, there is no user data on this node.
   199  	if join {
   200  		return nil
   201  	}
   202  	targetState := gossip.TargetState{
   203  		Data: map[string]*gossip.LocalState{},
   204  	}
   205  
   206  	for addr, st := range d.dataCacheState() {
   207  		_, ok := targetState.Data[addr]
   208  		if !ok {
   209  			targetState.Data[addr] = &gossip.LocalState{}
   210  		}
   211  		targetState.Data[addr].CacheKeys = *st
   212  	}
   213  
   214  	for addr, st := range d.statsInfoState() {
   215  		_, ok := targetState.Data[addr]
   216  		if !ok {
   217  			targetState.Data[addr] = &gossip.LocalState{}
   218  		}
   219  		targetState.Data[addr].StatsInfoKeys = *st
   220  	}
   221  
   222  	data, err := targetState.Marshal()
   223  	if err != nil {
   224  		d.logger.Error("failed to marshal cache data", zap.Error(err))
   225  		return nil
   226  	}
   227  	dst := make([]byte, lz4.CompressBlockBound(len(data)+4))
   228  	l := lz4.Compressor{}
   229  	n, err := l.CompressBlock(data, dst[4:])
   230  	if err != nil {
   231  		d.logger.Error("failed to compress cache data", zap.Error(err))
   232  		return nil
   233  	}
   234  	binaryEnc.PutUint32(dst, uint32(len(data)))
   235  	return dst[:n+4]
   236  }
   237  
   238  // MergeRemoteState implements the memberlist.Delegate interface.
   239  func (d *delegate) MergeRemoteState(buf []byte, join bool) {
   240  	// We do NOT merge remote user data if this is a pull/push action.
   241  	// Means that, we only accept user data from a remote node when this
   242  	// is a new node and is trying to join to the gossip cluster.
   243  	if !join {
   244  		return
   245  	}
   246  	sz := binaryEnc.Uint32(buf)
   247  	dst := make([]byte, sz)
   248  	n, err := lz4.UncompressBlock(buf[4:], dst)
   249  	if err != nil {
   250  		d.logger.Error("failed to uncompress cache data from buf", zap.Error(err))
   251  		return
   252  	}
   253  	dst = dst[:n]
   254  	targetState := gossip.TargetState{}
   255  	if err := targetState.Unmarshal(dst); err != nil {
   256  		d.logger.Error("failed to unmarshal cache data", zap.Error(err))
   257  		return
   258  	}
   259  
   260  	for target, state := range targetState.Data {
   261  		d.dataCacheKey.update(target, state.CacheKeys.Keys)
   262  		d.statsInfoKey.update(target, state.StatsInfoKeys.Keys)
   263  	}
   264  }
   265  
   266  // NotifyJoin implements the memberlist.EventDelegate interface.
   267  func (d *delegate) NotifyJoin(*memberlist.Node) {}
   268  
   269  // NotifyLeave implements the memberlist.EventDelegate interface.
   270  func (d *delegate) NotifyLeave(node *memberlist.Node) {
   271  	cacheServiceAddr := string(node.Meta)
   272  	if len(cacheServiceAddr) == 0 {
   273  		return
   274  	}
   275  	d.dataCacheKey.mu.Lock()
   276  	defer d.dataCacheKey.mu.Unlock()
   277  	for key, target := range d.dataCacheKey.mu.keyTarget {
   278  		if cacheServiceAddr == target {
   279  			delete(d.dataCacheKey.mu.keyTarget, key)
   280  		}
   281  	}
   282  }
   283  
   284  // NotifyUpdate implements the memberlist.EventDelegate interface.
   285  func (d *delegate) NotifyUpdate(*memberlist.Node) {}