github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/spanz/span.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 spanz
    15  
    16  import (
    17  	"bytes"
    18  
    19  	"github.com/pingcap/log"
    20  	"github.com/pingcap/tidb/pkg/ddl"
    21  	"github.com/pingcap/tidb/pkg/kv"
    22  	"github.com/pingcap/tidb/pkg/tablecodec"
    23  	"github.com/pingcap/tidb/pkg/util/codec"
    24  	"github.com/pingcap/tiflow/cdc/processor/tablepb"
    25  	"github.com/pingcap/tiflow/pkg/errors"
    26  	"go.uber.org/zap"
    27  )
    28  
    29  const (
    30  	// JobTableID is the id of `tidb_ddl_job`.
    31  	JobTableID = ddl.JobTableID
    32  	// JobHistoryID is the id of `tidb_ddl_history`
    33  	JobHistoryID = ddl.HistoryTableID
    34  )
    35  
    36  // UpperBoundKey represents the maximum value.
    37  var UpperBoundKey = []byte{255, 255, 255, 255, 255}
    38  
    39  // HackSpan will set End as UpperBoundKey if End is Nil.
    40  func HackSpan(span tablepb.Span) tablepb.Span {
    41  	if span.StartKey == nil {
    42  		span.StartKey = []byte{}
    43  	}
    44  
    45  	if span.EndKey == nil {
    46  		span.EndKey = UpperBoundKey
    47  	}
    48  	return span
    49  }
    50  
    51  // GetTableRange returns the span to watch for the specified table
    52  // Note that returned keys are not in memcomparable format.
    53  func GetTableRange(tableID int64) (startKey, endKey []byte) {
    54  	tablePrefix := tablecodec.GenTablePrefix(tableID)
    55  	sep := byte('_')
    56  	recordMarker := byte('r')
    57  
    58  	var start, end kv.Key
    59  	// ignore index keys.
    60  	start = append(tablePrefix, sep, recordMarker)
    61  	end = append(tablePrefix, sep, recordMarker+1)
    62  	return start, end
    63  }
    64  
    65  // GetAllDDLSpan return all cdc interested spans for DDL.
    66  func GetAllDDLSpan() []tablepb.Span {
    67  	spans := make([]tablepb.Span, 0, 2)
    68  	start, end := GetTableRange(JobTableID)
    69  	spans = append(spans, tablepb.Span{
    70  		StartKey: ToComparableKey(start),
    71  		EndKey:   ToComparableKey(end),
    72  	})
    73  	start, end = GetTableRange(JobHistoryID)
    74  	spans = append(spans, tablepb.Span{
    75  		StartKey: ToComparableKey(start),
    76  		EndKey:   ToComparableKey(end),
    77  	})
    78  	return spans
    79  }
    80  
    81  // KeyInSpan check if k in the span range.
    82  func KeyInSpan(k tablepb.Key, span tablepb.Span) bool {
    83  	if StartCompare(k, span.StartKey) >= 0 &&
    84  		EndCompare(k, span.EndKey) < 0 {
    85  		return true
    86  	}
    87  
    88  	return false
    89  }
    90  
    91  // StartCompare compares two start keys.
    92  // The result will be 0 if lhs==rhs, -1 if lhs < rhs, and +1 if lhs > rhs
    93  func StartCompare(lhs []byte, rhs []byte) int {
    94  	if len(lhs) == 0 && len(rhs) == 0 {
    95  		return 0
    96  	}
    97  
    98  	// Nil means Negative infinity.
    99  	// It's difference with EndCompare.
   100  	if len(lhs) == 0 {
   101  		return -1
   102  	}
   103  
   104  	if len(rhs) == 0 {
   105  		return 1
   106  	}
   107  
   108  	return bytes.Compare(lhs, rhs)
   109  }
   110  
   111  // EndCompare compares two end keys.
   112  // The result will be 0 if lhs==rhs, -1 if lhs < rhs, and +1 if lhs > rhs
   113  func EndCompare(lhs []byte, rhs []byte) int {
   114  	if len(lhs) == 0 && len(rhs) == 0 {
   115  		return 0
   116  	}
   117  
   118  	// Nil means Positive infinity.
   119  	// It's difference with StartCompare.
   120  	if len(lhs) == 0 {
   121  		return 1
   122  	}
   123  
   124  	if len(rhs) == 0 {
   125  		return -1
   126  	}
   127  
   128  	return bytes.Compare(lhs, rhs)
   129  }
   130  
   131  // Intersect return to intersect part of lhs and rhs span.
   132  // Return error if there's no intersect part
   133  func Intersect(lhs tablepb.Span, rhs tablepb.Span) (span tablepb.Span, err error) {
   134  	if len(lhs.StartKey) != 0 && EndCompare(lhs.StartKey, rhs.EndKey) >= 0 ||
   135  		len(rhs.StartKey) != 0 && EndCompare(rhs.StartKey, lhs.EndKey) >= 0 {
   136  		return tablepb.Span{}, errors.ErrIntersectNoOverlap.GenWithStackByArgs(lhs, rhs)
   137  	}
   138  
   139  	start := lhs.StartKey
   140  
   141  	if StartCompare(rhs.StartKey, start) > 0 {
   142  		start = rhs.StartKey
   143  	}
   144  
   145  	end := lhs.EndKey
   146  
   147  	if EndCompare(rhs.EndKey, end) < 0 {
   148  		end = rhs.EndKey
   149  	}
   150  
   151  	return tablepb.Span{StartKey: start, EndKey: end}, nil
   152  }
   153  
   154  // IsSubSpan returns true if the sub span is parents spans
   155  func IsSubSpan(sub tablepb.Span, parents ...tablepb.Span) bool {
   156  	if bytes.Compare(sub.StartKey, sub.EndKey) >= 0 {
   157  		log.Panic("the sub span is invalid", zap.Reflect("subSpan", sub))
   158  	}
   159  	for _, parent := range parents {
   160  		if StartCompare(parent.StartKey, sub.StartKey) <= 0 &&
   161  			EndCompare(sub.EndKey, parent.EndKey) <= 0 {
   162  			return true
   163  		}
   164  	}
   165  	return false
   166  }
   167  
   168  // ToSpan returns a span, keys are encoded in memcomparable format.
   169  // See: https://github.com/facebook/mysql-5.6/wiki/MyRocks-record-format
   170  func ToSpan(startKey, endKey []byte) tablepb.Span {
   171  	return tablepb.Span{
   172  		StartKey: ToComparableKey(startKey),
   173  		EndKey:   ToComparableKey(endKey),
   174  	}
   175  }
   176  
   177  // ToComparableKey returns a memcomparable key.
   178  // See: https://github.com/facebook/mysql-5.6/wiki/MyRocks-record-format
   179  func ToComparableKey(key []byte) tablepb.Key {
   180  	return codec.EncodeBytes(nil, key)
   181  }