github.com/pingcap/br@v5.3.0-alpha.0.20220125034240-ec59c7b6ce30+incompatible/pkg/cdclog/buffer.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 cdclog
    15  
    16  import (
    17  	"time"
    18  
    19  	"github.com/pingcap/errors"
    20  	"github.com/pingcap/log"
    21  	"github.com/pingcap/tidb/meta/autoid"
    22  	"github.com/pingcap/tidb/table"
    23  	"github.com/pingcap/tidb/types"
    24  	"go.uber.org/zap"
    25  
    26  	"github.com/pingcap/br/pkg/kv"
    27  )
    28  
    29  // TableBuffer represents the kv buffer of this table.
    30  // we restore one tableBuffer in one goroutine.
    31  // this is the concurrent unit of log restore.
    32  type TableBuffer struct {
    33  	KvPairs []kv.Row
    34  	count   int
    35  	size    int64
    36  
    37  	KvEncoder kv.Encoder
    38  	tableInfo table.Table
    39  	allocator autoid.Allocators
    40  
    41  	flushKVSize  int64
    42  	flushKVPairs int
    43  
    44  	colNames []string
    45  	colPerm  []int
    46  }
    47  
    48  func newKVEncoder(allocators autoid.Allocators, tbl table.Table) (kv.Encoder, error) {
    49  	encTable, err := table.TableFromMeta(allocators, tbl.Meta())
    50  	if err != nil {
    51  		return nil, errors.Trace(err)
    52  	}
    53  	return kv.NewTableKVEncoder(encTable, &kv.SessionOptions{
    54  		Timestamp: time.Now().Unix(),
    55  		// TODO get the version from TiDB cluster
    56  		// currently TiDB only support v1 and v2, and since 4.0
    57  		// the default RowFormatVersion is 2, so I think
    58  		// we can implement the row version retrieve from cluster in the future
    59  		// when TiDB decide to support v3 RowFormatVersion.
    60  		RowFormatVersion: "2",
    61  	}), nil
    62  }
    63  
    64  // NewTableBuffer creates TableBuffer.
    65  func NewTableBuffer(tbl table.Table, allocators autoid.Allocators, flushKVPairs int, flushKVSize int64) *TableBuffer {
    66  	tb := &TableBuffer{
    67  		KvPairs:      make([]kv.Row, 0, flushKVPairs),
    68  		flushKVPairs: flushKVPairs,
    69  		flushKVSize:  flushKVSize,
    70  	}
    71  	if tbl != nil {
    72  		tb.ReloadMeta(tbl, allocators)
    73  	}
    74  	return tb
    75  }
    76  
    77  // ResetTableInfo set tableInfo to nil for next reload.
    78  func (t *TableBuffer) ResetTableInfo() {
    79  	t.tableInfo = nil
    80  }
    81  
    82  // TableInfo returns the table info of this buffer.
    83  func (t *TableBuffer) TableInfo() table.Table {
    84  	return t.tableInfo
    85  }
    86  
    87  // TableID returns the table id of this buffer.
    88  func (t *TableBuffer) TableID() int64 {
    89  	if t.tableInfo != nil {
    90  		return t.tableInfo.Meta().ID
    91  	}
    92  	return 0
    93  }
    94  
    95  // ReloadMeta reload columns after
    96  // 1. table buffer created.
    97  // 2. every ddl executed.
    98  func (t *TableBuffer) ReloadMeta(tbl table.Table, allocator autoid.Allocators) {
    99  	columns := tbl.Meta().Cols()
   100  	colNames := make([]string, 0, len(columns))
   101  	colPerm := make([]int, 0, len(columns)+1)
   102  
   103  	for i, col := range columns {
   104  		colNames = append(colNames, col.Name.String())
   105  		colPerm = append(colPerm, i)
   106  	}
   107  	if kv.TableHasAutoRowID(tbl.Meta()) {
   108  		colPerm = append(colPerm, -1)
   109  	}
   110  	if t.allocator == nil {
   111  		t.allocator = allocator
   112  	}
   113  	t.tableInfo = tbl
   114  	t.colNames = colNames
   115  	t.colPerm = colPerm
   116  	// reset kv encoder after meta changed
   117  	t.KvEncoder = nil
   118  }
   119  
   120  func (t *TableBuffer) translateToDatum(row map[string]Column) ([]types.Datum, error) {
   121  	cols := make([]types.Datum, 0, len(row))
   122  	for _, col := range t.colNames {
   123  		val, err := row[col].ToDatum()
   124  		if err != nil {
   125  			return nil, errors.Trace(err)
   126  		}
   127  		cols = append(cols, val)
   128  	}
   129  	return cols, nil
   130  }
   131  
   132  func (t *TableBuffer) appendRow(
   133  	row map[string]Column,
   134  	item *SortItem,
   135  	encodeFn func(row []types.Datum,
   136  		rowID int64,
   137  		columnPermutation []int) (kv.Row, int, error),
   138  ) error {
   139  	cols, err := t.translateToDatum(row)
   140  	if err != nil {
   141  		return errors.Trace(err)
   142  	}
   143  	pair, size, err := encodeFn(cols, item.RowID, t.colPerm)
   144  	if err != nil {
   145  		return errors.Trace(err)
   146  	}
   147  	t.KvPairs = append(t.KvPairs, pair)
   148  	t.size += int64(size)
   149  	t.count++
   150  	return nil
   151  }
   152  
   153  // Append appends the item to this buffer.
   154  func (t *TableBuffer) Append(item *SortItem) error {
   155  	var err error
   156  	log.Debug("Append item to buffer",
   157  		zap.Stringer("table", t.tableInfo.Meta().Name),
   158  		zap.Any("item", item),
   159  	)
   160  	row := item.Data.(*MessageRow)
   161  
   162  	if t.KvEncoder == nil {
   163  		// lazy create kv encoder
   164  		log.Debug("create kv encoder lazily",
   165  			zap.Any("alloc", t.allocator), zap.Any("tbl", t.tableInfo))
   166  		t.KvEncoder, err = newKVEncoder(t.allocator, t.tableInfo)
   167  		if err != nil {
   168  			return errors.Trace(err)
   169  		}
   170  	}
   171  
   172  	if row.PreColumns != nil {
   173  		// remove old keys
   174  		log.Debug("process update event", zap.Any("row", row))
   175  		err := t.appendRow(row.PreColumns, item, t.KvEncoder.RemoveRecord)
   176  		if err != nil {
   177  			return errors.Trace(err)
   178  		}
   179  	}
   180  	if row.Update != nil {
   181  		// Add new columns
   182  		if row.PreColumns == nil {
   183  			log.Debug("process insert event", zap.Any("row", row))
   184  		}
   185  		err := t.appendRow(row.Update, item, t.KvEncoder.AddRecord)
   186  		if err != nil {
   187  			return errors.Trace(err)
   188  		}
   189  	}
   190  	if row.Delete != nil {
   191  		// Remove current columns
   192  		log.Debug("process delete event", zap.Any("row", row))
   193  		err := t.appendRow(row.Delete, item, t.KvEncoder.RemoveRecord)
   194  		if err != nil {
   195  			return errors.Trace(err)
   196  		}
   197  	}
   198  	return nil
   199  }
   200  
   201  // ShouldApply tells whether we should flush memory kv buffer to storage.
   202  func (t *TableBuffer) ShouldApply() bool {
   203  	// flush when reached flush kv len or flush size
   204  	return t.size >= t.flushKVSize || t.count >= t.flushKVPairs
   205  }
   206  
   207  // IsEmpty tells buffer is empty.
   208  func (t *TableBuffer) IsEmpty() bool {
   209  	return t.size == 0
   210  }
   211  
   212  // Clear reset the buffer.
   213  func (t *TableBuffer) Clear() {
   214  	t.KvPairs = t.KvPairs[:0]
   215  	t.count = 0
   216  	t.size = 0
   217  }