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 }