github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/etcd/etcdkey.go (about)

     1  // Copyright 2021 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 etcd
    15  
    16  import (
    17  	"fmt"
    18  	"strconv"
    19  	"strings"
    20  
    21  	"github.com/pingcap/log"
    22  	"github.com/pingcap/tiflow/cdc/model"
    23  	cerror "github.com/pingcap/tiflow/pkg/errors"
    24  )
    25  
    26  const (
    27  	// Note that tidb-dashboard depends on this prefix to filter out TiCDC keys.
    28  	// Ref: https://github.com/pingcap/tidb-dashboard/blob/1f39ee09c5352adbf23af8ec7e15020147ef9ca4/pkg/utils/topology/ticdc.go#L23
    29  	metaPrefix = "/__cdc_meta__"
    30  
    31  	ownerKey        = "/owner"
    32  	captureKey      = "/capture"
    33  	taskPositionKey = "/task/position"
    34  
    35  	// ChangefeedInfoKey is the key path for changefeed info
    36  	ChangefeedInfoKey = "/changefeed/info"
    37  	// ChangefeedStatusKey is the key path for changefeed status
    38  	ChangefeedStatusKey = "/changefeed/status"
    39  	// metaVersionKey is the key path for metadata version
    40  	metaVersionKey = "/meta/meta-version"
    41  	upstreamKey    = "/upstream"
    42  
    43  	// DeletionCounterKey is the key path for the counter of deleted keys
    44  	DeletionCounterKey = metaPrefix + "/meta/ticdc-delete-etcd-key-count"
    45  
    46  	// DefaultClusterAndNamespacePrefix is the default prefix of changefeed data
    47  	DefaultClusterAndNamespacePrefix = "/tidb/cdc/default/default"
    48  	// DefaultClusterAndMetaPrefix is the default prefix of cluster meta
    49  	DefaultClusterAndMetaPrefix = "/tidb/cdc/default" + metaPrefix
    50  
    51  	// MigrateBackupPrefix is the prefix of backup keys during a migration
    52  	migrateBackupPrefix = "/tidb/cdc/__backup__"
    53  )
    54  
    55  // CDCKeyType is the type of etcd key
    56  type CDCKeyType = int
    57  
    58  // the types of etcd key
    59  const (
    60  	CDCKeyTypeUnknown CDCKeyType = iota
    61  	CDCKeyTypeOwner
    62  	CDCKeyTypeCapture
    63  	CDCKeyTypeChangefeedInfo
    64  	CDCKeyTypeChangeFeedStatus
    65  	CDCKeyTypeTaskPosition
    66  	CDCKeyTypeMetaVersion
    67  	CDCKeyTypeUpStream
    68  )
    69  
    70  // CDCKey represents an etcd key which is defined by TiCDC
    71  /*
    72   Usage:
    73   we can parse a raw etcd key:
    74   ```
    75   	k := new(CDCKey)
    76   	rawKey := "/tidb/cdc/changefeed/info/test/changefeed"
    77  	err := k.Parse(rawKey)
    78   	c.Assert(k, check.DeepEquals, &CDCKey{
    79  			Tp:           CDCKeyTypeChangefeedInfo,
    80  			ChangefeedID: "test/changefeed",
    81  	})
    82   ```
    83  
    84   and we can generate a raw key from CDCKey
    85   ```
    86   	k := &CDCKey{
    87  			Tp:           CDCKeyTypeChangefeedInfo,
    88  			ChangefeedID: "test/changefeed",
    89  	}
    90   	c.Assert(k.String(), check.Equals, "/tidb/cdc/changefeed/info/test/changefeed")
    91   ```
    92  
    93  */
    94  type CDCKey struct {
    95  	Tp           CDCKeyType
    96  	ChangefeedID model.ChangeFeedID
    97  	CaptureID    string
    98  	OwnerLeaseID string
    99  	ClusterID    string
   100  	UpstreamID   model.UpstreamID
   101  	Namespace    string
   102  }
   103  
   104  // BaseKey is the common prefix of the keys with cluster id in CDC
   105  //
   106  // Note that tidb-dashboard depends on this prefix to filter out TiCDC keys.
   107  // Ref: https://github.com/pingcap/tidb-dashboard/blob/1f39ee09c5352adbf23af8ec7e15020147ef9ca4/pkg/utils/topology/ticdc.go#L22
   108  func BaseKey(clusterID string) string {
   109  	return fmt.Sprintf("/tidb/cdc/%s", clusterID)
   110  }
   111  
   112  // NamespacedPrefix returns the etcd prefix of changefeed data
   113  func NamespacedPrefix(clusterID, namespace string) string {
   114  	return BaseKey(clusterID) + "/" + namespace
   115  }
   116  
   117  // Parse parses the given etcd key
   118  func (k *CDCKey) Parse(clusterID, key string) error {
   119  	if !strings.HasPrefix(key, BaseKey(clusterID)) {
   120  		return cerror.ErrInvalidEtcdKey.GenWithStackByArgs(key)
   121  	}
   122  	key = key[len("/tidb/cdc"):]
   123  	parts := strings.Split(key, "/")
   124  	k.ClusterID = parts[1]
   125  	key = key[len(k.ClusterID)+1:]
   126  	if strings.HasPrefix(key, metaPrefix) {
   127  		key = key[len(metaPrefix):]
   128  		switch {
   129  		case strings.HasPrefix(key, ownerKey):
   130  			k.Tp = CDCKeyTypeOwner
   131  			k.CaptureID = ""
   132  			key = key[len(ownerKey):]
   133  			if len(key) > 0 {
   134  				key = key[1:]
   135  			}
   136  			k.OwnerLeaseID = key
   137  		case strings.HasPrefix(key, captureKey):
   138  			k.Tp = CDCKeyTypeCapture
   139  			k.CaptureID = key[len(captureKey)+1:]
   140  			k.OwnerLeaseID = ""
   141  		case strings.HasPrefix(key, metaVersionKey):
   142  			k.Tp = CDCKeyTypeMetaVersion
   143  		default:
   144  			return cerror.ErrInvalidEtcdKey.GenWithStackByArgs(key)
   145  		}
   146  	} else {
   147  		namespace := parts[2]
   148  		key = key[len(namespace)+1:]
   149  		k.Namespace = namespace
   150  		switch {
   151  		case strings.HasPrefix(key, ChangefeedInfoKey):
   152  			k.Tp = CDCKeyTypeChangefeedInfo
   153  			k.CaptureID = ""
   154  			k.ChangefeedID = model.ChangeFeedID{
   155  				Namespace: namespace,
   156  				ID:        key[len(ChangefeedInfoKey)+1:],
   157  			}
   158  			k.OwnerLeaseID = ""
   159  		case strings.HasPrefix(key, upstreamKey):
   160  			k.Tp = CDCKeyTypeUpStream
   161  			k.CaptureID = ""
   162  			id, err := strconv.ParseUint(key[len(upstreamKey)+1:], 10, 64)
   163  			if err != nil {
   164  				return err
   165  			}
   166  			k.UpstreamID = id
   167  		case strings.HasPrefix(key, ChangefeedStatusKey):
   168  			k.Tp = CDCKeyTypeChangeFeedStatus
   169  			k.CaptureID = ""
   170  			k.ChangefeedID = model.ChangeFeedID{
   171  				Namespace: namespace,
   172  				ID:        key[len(ChangefeedStatusKey)+1:],
   173  			}
   174  			k.OwnerLeaseID = ""
   175  		case strings.HasPrefix(key, taskPositionKey):
   176  			splitKey := strings.SplitN(key[len(taskPositionKey)+1:], "/", 2)
   177  			if len(splitKey) != 2 {
   178  				return cerror.ErrInvalidEtcdKey.GenWithStackByArgs(key)
   179  			}
   180  			k.Tp = CDCKeyTypeTaskPosition
   181  			k.CaptureID = splitKey[0]
   182  			k.ChangefeedID = model.ChangeFeedID{
   183  				Namespace: namespace,
   184  				ID:        splitKey[1],
   185  			}
   186  			k.OwnerLeaseID = ""
   187  		default:
   188  			return cerror.ErrInvalidEtcdKey.GenWithStackByArgs(key)
   189  		}
   190  	}
   191  	return nil
   192  }
   193  
   194  func (k *CDCKey) String() string {
   195  	switch k.Tp {
   196  	case CDCKeyTypeOwner:
   197  		if len(k.OwnerLeaseID) == 0 {
   198  			return BaseKey(k.ClusterID) + metaPrefix + ownerKey
   199  		}
   200  		return BaseKey(k.ClusterID) + metaPrefix + ownerKey + "/" + k.OwnerLeaseID
   201  	case CDCKeyTypeCapture:
   202  		return BaseKey(k.ClusterID) + metaPrefix + captureKey + "/" + k.CaptureID
   203  	case CDCKeyTypeChangefeedInfo:
   204  		return NamespacedPrefix(k.ClusterID, k.ChangefeedID.Namespace) + ChangefeedInfoKey +
   205  			"/" + k.ChangefeedID.ID
   206  	case CDCKeyTypeChangeFeedStatus:
   207  		return NamespacedPrefix(k.ClusterID, k.ChangefeedID.Namespace) + ChangefeedStatusKey +
   208  			"/" + k.ChangefeedID.ID
   209  	case CDCKeyTypeTaskPosition:
   210  		return NamespacedPrefix(k.ClusterID, k.ChangefeedID.Namespace) + taskPositionKey +
   211  			"/" + k.CaptureID + "/" + k.ChangefeedID.ID
   212  	case CDCKeyTypeMetaVersion:
   213  		return BaseKey(k.ClusterID) + metaPrefix + metaVersionKey
   214  	case CDCKeyTypeUpStream:
   215  		return fmt.Sprintf("%s%s/%d",
   216  			NamespacedPrefix(k.ClusterID, k.Namespace),
   217  			upstreamKey, k.UpstreamID)
   218  	}
   219  	log.Panic("unreachable")
   220  	return ""
   221  }