github.com/matrixorigin/matrixone@v1.2.0/pkg/hakeeper/operator/builder.go (about)

     1  // Copyright 2020 PingCAP, Inc.
     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  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  // Portions of this file are additionally subject to the following
    15  // copyright.
    16  //
    17  // Copyright (C) 2021 Matrix Origin.
    18  //
    19  // Modified the behavior of the builder.
    20  
    21  package operator
    22  
    23  import (
    24  	"fmt"
    25  	"sort"
    26  
    27  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    28  
    29  	"github.com/matrixorigin/matrixone/pkg/pb/logservice"
    30  )
    31  
    32  // Builder is used to create operators. Usage:
    33  //
    34  //	op, err := NewBuilder(desc, cluster, shard).
    35  //	            RemovePeer(store1).
    36  //	            AddPeer(peer1).
    37  //	            Build(kind)
    38  //
    39  // The generated Operator will choose the most appropriate execution order
    40  // according to various constraints.
    41  type Builder struct {
    42  	// basic info
    43  	desc    string
    44  	shardID uint64
    45  	epoch   uint64
    46  
    47  	// operation record
    48  	originPeers peersMap
    49  	targetPeers peersMap
    50  
    51  	err error
    52  
    53  	// intermediate states
    54  	toAdd, toRemove peersMap // pending tasks.
    55  	steps           []OpStep // generated steps.
    56  }
    57  
    58  // NewBuilder creates a Builder.
    59  func NewBuilder(desc string, shardInfo logservice.LogShardInfo) *Builder {
    60  	b := &Builder{
    61  		desc:    desc,
    62  		shardID: shardInfo.ShardID,
    63  		epoch:   shardInfo.Epoch,
    64  	}
    65  
    66  	// origin peers
    67  	err := b.err
    68  	originPeers := newPeersMap()
    69  
    70  	for replicaID, uuid := range shardInfo.Replicas {
    71  		if uuid == "" {
    72  			err = moerr.NewInternalErrorNoCtx("cannot build operator for shard with nil peer")
    73  			break
    74  		}
    75  		originPeers.Set(uuid, replicaID)
    76  	}
    77  
    78  	b.originPeers = originPeers
    79  	b.targetPeers = originPeers.Copy()
    80  	b.err = err
    81  	return b
    82  }
    83  
    84  // AddPeer records an add Peer operation in Builder.
    85  func (b *Builder) AddPeer(uuid string, peer uint64) *Builder {
    86  	if b.err != nil {
    87  		return b
    88  	}
    89  	if uuid == "" {
    90  		b.err = moerr.NewInternalErrorNoCtx("cannot add peer to nil store")
    91  		return b
    92  	}
    93  	if old, ok := b.targetPeers[uuid]; ok {
    94  		b.err = moerr.NewInternalErrorNoCtx("cannot add peer %+v to %s: already have peer %+v on %s", peer, uuid, old, uuid)
    95  		return b
    96  	}
    97  	for oldUuid, old := range b.targetPeers {
    98  		if old == peer {
    99  			b.err = moerr.NewInternalErrorNoCtx("cannot add peer %+v to %s: already have peer %+v on %s", peer, uuid, old, oldUuid)
   100  			return b
   101  		}
   102  	}
   103  
   104  	b.targetPeers.Set(uuid, peer)
   105  	return b
   106  }
   107  
   108  // RemovePeer records a remove peer operation in Builder.
   109  func (b *Builder) RemovePeer(uuid string) *Builder {
   110  	if b.err != nil {
   111  		return b
   112  	}
   113  	if _, ok := b.targetPeers[uuid]; !ok {
   114  		b.err = moerr.NewInternalErrorNoCtx("cannot remove peer from %s: not found", uuid)
   115  	} else {
   116  		delete(b.targetPeers, uuid)
   117  	}
   118  	return b
   119  }
   120  
   121  // Build creates the Operator.
   122  func (b *Builder) Build() (*Operator, error) {
   123  	if b.err != nil {
   124  		return nil, b.err
   125  	}
   126  
   127  	brief := b.prepareBuild()
   128  
   129  	if b.err = b.buildSteps(); b.err != nil {
   130  		return nil, b.err
   131  	}
   132  
   133  	return NewOperator(brief, b.shardID, b.epoch, b.steps...), nil
   134  }
   135  
   136  func (b *Builder) prepareBuild() string {
   137  	b.toAdd = newPeersMap()
   138  	b.toRemove = newPeersMap()
   139  
   140  	for uuid, replicaID := range b.originPeers {
   141  		// new peer not exists
   142  		if _, ok := b.targetPeers[uuid]; !ok {
   143  			b.toRemove.Set(uuid, replicaID)
   144  		}
   145  	}
   146  
   147  	for uuid, replicaID := range b.targetPeers {
   148  		// old peer not exists.
   149  		if _, ok := b.originPeers[uuid]; !ok {
   150  			b.toAdd.Set(uuid, replicaID)
   151  		}
   152  	}
   153  
   154  	return b.brief()
   155  }
   156  
   157  func (b *Builder) buildSteps() error {
   158  	for len(b.toRemove) > 0 {
   159  		var targets []string
   160  		for target := range b.targetPeers {
   161  			targets = append(targets, target)
   162  		}
   163  		sort.Slice(targets, func(i, j int) bool { return targets[i] < targets[j] })
   164  
   165  		uuid, replicaID := b.toRemove.Get()
   166  		b.steps = append(b.steps, RemoveLogService{
   167  			Target:  targets[0],
   168  			Replica: Replica{uuid, b.shardID, replicaID, b.epoch},
   169  		})
   170  		delete(b.toRemove, uuid)
   171  		continue
   172  	}
   173  
   174  	for len(b.toAdd) > 0 {
   175  		var targets []string
   176  		for target := range b.originPeers {
   177  			targets = append(targets, target)
   178  		}
   179  		sort.Slice(targets, func(i, j int) bool { return targets[i] < targets[j] })
   180  
   181  		uuid, replicaID := b.toAdd.Get()
   182  		b.steps = append(b.steps, AddLogService{
   183  			Target: targets[0],
   184  			Replica: Replica{
   185  				UUID:      uuid,
   186  				ShardID:   b.shardID,
   187  				ReplicaID: replicaID,
   188  				Epoch:     b.epoch,
   189  			},
   190  		})
   191  		delete(b.toAdd, uuid)
   192  	}
   193  
   194  	if len(b.steps) == 0 {
   195  		return moerr.NewInternalErrorNoCtx("no operator step is built")
   196  	}
   197  	return nil
   198  }
   199  
   200  // generate brief description of the operator.
   201  func (b *Builder) brief() string {
   202  	switch {
   203  	case len(b.toAdd) > 0:
   204  		return fmt.Sprintf("add peer: store %s", b.toAdd)
   205  	case len(b.toRemove) > 0:
   206  		return fmt.Sprintf("rm peer: store %s", b.toRemove)
   207  	default:
   208  		return ""
   209  	}
   210  }
   211  
   212  // Replicas indexed by store's uuid.
   213  type peersMap map[string]uint64
   214  
   215  func newPeersMap() peersMap {
   216  	return make(map[string]uint64)
   217  }
   218  
   219  func (pm peersMap) Get() (string, uint64) {
   220  	for uuid, replicaID := range pm {
   221  		return uuid, replicaID
   222  	}
   223  	return "", 0
   224  }
   225  
   226  func (pm peersMap) Set(uuid string, replicaID uint64) {
   227  	pm[uuid] = replicaID
   228  }
   229  
   230  func (pm peersMap) String() string {
   231  	uuids := make([]string, 0, len(pm))
   232  	for uuid := range pm {
   233  		uuids = append(uuids, uuid)
   234  	}
   235  	return fmt.Sprintf("%v", uuids)
   236  }
   237  
   238  func (pm peersMap) Copy() peersMap {
   239  	var pm2 peersMap = make(map[string]uint64, len(pm))
   240  	for uuid, replicaID := range pm {
   241  		pm2.Set(uuid, replicaID)
   242  	}
   243  	return pm2
   244  }