github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/testing_utils/cdc_state_checker/state.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  package main
    15  
    16  import (
    17  	"encoding/json"
    18  	"regexp"
    19  
    20  	"go.uber.org/zap"
    21  
    22  	"github.com/pingcap/errors"
    23  	"github.com/pingcap/log"
    24  	"github.com/pingcap/ticdc/cdc/kv"
    25  	"github.com/pingcap/ticdc/cdc/model"
    26  	"github.com/pingcap/ticdc/pkg/orchestrator"
    27  	"github.com/pingcap/ticdc/pkg/orchestrator/util"
    28  )
    29  
    30  type cdcReactorState struct {
    31  	Owner              model.CaptureID
    32  	Captures           map[model.CaptureID]*model.CaptureInfo
    33  	ChangefeedStatuses map[model.ChangeFeedID]*model.ChangeFeedStatus
    34  	TaskPositions      map[model.ChangeFeedID]map[model.CaptureID]*model.TaskPosition
    35  	TaskStatuses       map[model.ChangeFeedID]map[model.CaptureID]*model.TaskStatus
    36  }
    37  
    38  var (
    39  	captureRegex    = regexp.MustCompile(regexp.QuoteMeta(kv.CaptureInfoKeyPrefix) + "/(.+)")
    40  	changefeedRegex = regexp.MustCompile(regexp.QuoteMeta(kv.JobKeyPrefix) + "/(.+)")
    41  	positionRegex   = regexp.MustCompile(regexp.QuoteMeta(kv.TaskPositionKeyPrefix) + "/(.+?)/(.+)")
    42  	statusRegex     = regexp.MustCompile(regexp.QuoteMeta(kv.TaskStatusKeyPrefix) + "/(.+?)/(.+)")
    43  )
    44  
    45  func newCDCReactorState() *cdcReactorState {
    46  	return &cdcReactorState{
    47  		Captures:           make(map[model.CaptureID]*model.CaptureInfo),
    48  		ChangefeedStatuses: make(map[model.ChangeFeedID]*model.ChangeFeedStatus),
    49  		TaskPositions:      make(map[model.ChangeFeedID]map[model.CaptureID]*model.TaskPosition),
    50  		TaskStatuses:       make(map[model.ChangeFeedID]map[model.CaptureID]*model.TaskStatus),
    51  	}
    52  }
    53  
    54  func (s *cdcReactorState) Update(key util.EtcdKey, value []byte, isInit bool) error {
    55  	if key.String() == kv.CaptureOwnerKey {
    56  		if value == nil {
    57  			log.Info("Owner lost", zap.String("old-owner", s.Owner))
    58  			return nil
    59  		}
    60  
    61  		log.Info("Owner updated", zap.String("old-owner", s.Owner),
    62  			zap.ByteString("new-owner", value))
    63  		s.Owner = string(value)
    64  		return nil
    65  	}
    66  
    67  	if matches := captureRegex.FindSubmatch(key.Bytes()); matches != nil {
    68  		captureID := string(matches[1])
    69  
    70  		if value == nil {
    71  			log.Info("Capture deleted",
    72  				zap.String("captureID", captureID),
    73  				zap.Reflect("old-capture", s.Captures[captureID]))
    74  
    75  			delete(s.Captures, captureID)
    76  			return nil
    77  		}
    78  
    79  		var newCaptureInfo model.CaptureInfo
    80  		err := json.Unmarshal(value, &newCaptureInfo)
    81  		if err != nil {
    82  			return errors.Trace(err)
    83  		}
    84  
    85  		if oldCaptureInfo, ok := s.Captures[captureID]; ok {
    86  			log.Info("Capture updated",
    87  				zap.String("captureID", captureID),
    88  				zap.Reflect("old-capture", oldCaptureInfo),
    89  				zap.Reflect("new-capture", newCaptureInfo))
    90  		} else {
    91  			log.Info("Capture added",
    92  				zap.String("captureID", captureID),
    93  				zap.Reflect("new-capture", newCaptureInfo))
    94  		}
    95  
    96  		s.Captures[captureID] = &newCaptureInfo
    97  		return nil
    98  	}
    99  
   100  	if matches := changefeedRegex.FindSubmatch(key.Bytes()); matches != nil {
   101  		changefeedID := string(matches[1])
   102  
   103  		if value == nil {
   104  			log.Info("Changefeed deleted",
   105  				zap.String("changefeedID", changefeedID),
   106  				zap.Reflect("old-changefeed", s.ChangefeedStatuses))
   107  
   108  			delete(s.ChangefeedStatuses, changefeedID)
   109  			return nil
   110  		}
   111  
   112  		var newChangefeedStatus model.ChangeFeedStatus
   113  		err := json.Unmarshal(value, &newChangefeedStatus)
   114  		if err != nil {
   115  			return errors.Trace(err)
   116  		}
   117  
   118  		if oldChangefeedInfo, ok := s.ChangefeedStatuses[changefeedID]; ok {
   119  			log.Info("Changefeed updated",
   120  				zap.String("changefeedID", changefeedID),
   121  				zap.Reflect("old-changefeed", oldChangefeedInfo),
   122  				zap.Reflect("new-changefeed", newChangefeedStatus))
   123  		} else {
   124  			log.Info("Changefeed added",
   125  				zap.String("changefeedID", changefeedID),
   126  				zap.Reflect("new-changefeed", newChangefeedStatus))
   127  		}
   128  
   129  		s.ChangefeedStatuses[changefeedID] = &newChangefeedStatus
   130  
   131  		return nil
   132  	}
   133  
   134  	if matches := positionRegex.FindSubmatch(key.Bytes()); matches != nil {
   135  		captureID := string(matches[1])
   136  		changefeedID := string(matches[2])
   137  
   138  		if value == nil {
   139  			log.Info("Position deleted",
   140  				zap.String("captureID", captureID),
   141  				zap.String("changefeedID", changefeedID),
   142  				zap.Reflect("old-position", s.TaskPositions[changefeedID][captureID]))
   143  
   144  			delete(s.TaskPositions[changefeedID], captureID)
   145  			if len(s.TaskPositions[changefeedID]) == 0 {
   146  				delete(s.TaskPositions, changefeedID)
   147  			}
   148  
   149  			return nil
   150  		}
   151  
   152  		var newTaskPosition model.TaskPosition
   153  		err := json.Unmarshal(value, &newTaskPosition)
   154  		if err != nil {
   155  			return errors.Trace(err)
   156  		}
   157  
   158  		if _, ok := s.TaskPositions[changefeedID]; !ok {
   159  			s.TaskPositions[changefeedID] = make(map[model.CaptureID]*model.TaskPosition)
   160  		}
   161  
   162  		if position, ok := s.TaskPositions[changefeedID][captureID]; ok {
   163  			log.Info("Position updated",
   164  				zap.String("captureID", captureID),
   165  				zap.String("changefeedID", changefeedID),
   166  				zap.Reflect("old-position", position),
   167  				zap.Reflect("new-position", newTaskPosition))
   168  		} else {
   169  			log.Info("Position created",
   170  				zap.String("captureID", captureID),
   171  				zap.String("changefeedID", changefeedID),
   172  				zap.Reflect("new-position", newTaskPosition))
   173  		}
   174  
   175  		s.TaskPositions[changefeedID][captureID] = &newTaskPosition
   176  
   177  		return nil
   178  	}
   179  
   180  	if matches := statusRegex.FindSubmatch(key.Bytes()); matches != nil {
   181  		captureID := string(matches[1])
   182  		changefeedID := string(matches[2])
   183  
   184  		if value == nil {
   185  			log.Info("Status deleted",
   186  				zap.String("captureID", captureID),
   187  				zap.String("changefeedID", changefeedID),
   188  				zap.Reflect("old-status", s.TaskStatuses[changefeedID][captureID]))
   189  
   190  			delete(s.TaskStatuses[changefeedID], captureID)
   191  			if len(s.TaskStatuses[changefeedID]) == 0 {
   192  				delete(s.TaskStatuses, changefeedID)
   193  			}
   194  
   195  			return nil
   196  		}
   197  
   198  		var newTaskStatus model.TaskStatus
   199  		err := json.Unmarshal(value, &newTaskStatus)
   200  		if err != nil {
   201  			return errors.Trace(err)
   202  		}
   203  
   204  		if _, ok := s.TaskStatuses[changefeedID]; !ok {
   205  			s.TaskStatuses[changefeedID] = make(map[model.CaptureID]*model.TaskStatus)
   206  		}
   207  
   208  		if status, ok := s.TaskStatuses[changefeedID][captureID]; ok {
   209  			log.Info("Status updated",
   210  				zap.String("captureID", captureID),
   211  				zap.String("changefeedID", changefeedID),
   212  				zap.Reflect("old-status", status),
   213  				zap.Reflect("new-status", newTaskStatus))
   214  		} else {
   215  			log.Info("Status updated",
   216  				zap.String("captureID", captureID),
   217  				zap.String("changefeedID", changefeedID),
   218  				zap.Reflect("new-status", newTaskStatus))
   219  		}
   220  
   221  		s.TaskStatuses[changefeedID][captureID] = &newTaskStatus
   222  
   223  		return nil
   224  	}
   225  
   226  	log.Debug("Etcd operation ignored", zap.String("key", key.String()), zap.ByteString("value", value))
   227  	return nil
   228  }
   229  
   230  func (s *cdcReactorState) GetPatches() [][]orchestrator.DataPatch {
   231  	return nil
   232  }