github.com/pingcap/br@v5.3.0-alpha.0.20220125034240-ec59c7b6ce30+incompatible/pkg/cdclog/puller.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  	"context"
    18  
    19  	"github.com/pingcap/errors"
    20  	"github.com/pingcap/log"
    21  
    22  	"github.com/pingcap/br/pkg/storage"
    23  )
    24  
    25  // EventPuller pulls next event in ts order.
    26  type EventPuller struct {
    27  	ddlDecoder            *JSONEventBatchMixedDecoder
    28  	rowChangedDecoder     *JSONEventBatchMixedDecoder
    29  	currentDDLItem        *SortItem
    30  	currentRowChangedItem *SortItem
    31  
    32  	schema string
    33  	table  string
    34  
    35  	storage         storage.ExternalStorage
    36  	ddlFiles        []string
    37  	rowChangedFiles []string
    38  
    39  	ddlFileIndex        int
    40  	rowChangedFileIndex int
    41  }
    42  
    43  // NewEventPuller create eventPuller by given log files, we assume files come in ts order.
    44  func NewEventPuller(
    45  	ctx context.Context,
    46  	schema string,
    47  	table string,
    48  	ddlFiles []string,
    49  	rowChangedFiles []string,
    50  	storage storage.ExternalStorage) (*EventPuller, error) {
    51  	var (
    52  		ddlDecoder        *JSONEventBatchMixedDecoder
    53  		ddlFileIndex      int
    54  		rowChangedDecoder *JSONEventBatchMixedDecoder
    55  		rowFileIndex      int
    56  	)
    57  	if len(ddlFiles) == 0 {
    58  		log.Info("There is no ddl file to restore")
    59  	} else {
    60  		data, err := storage.ReadFile(ctx, ddlFiles[0])
    61  		if err != nil {
    62  			return nil, errors.Trace(err)
    63  		}
    64  		if len(data) != 0 {
    65  			ddlFileIndex++
    66  			ddlDecoder, err = NewJSONEventBatchDecoder(data)
    67  			if err != nil {
    68  				return nil, errors.Trace(err)
    69  			}
    70  		}
    71  	}
    72  
    73  	if len(rowChangedFiles) == 0 {
    74  		log.Info("There is no row changed file to restore")
    75  	} else {
    76  		data, err := storage.ReadFile(ctx, rowChangedFiles[0])
    77  		if err != nil {
    78  			return nil, errors.Trace(err)
    79  		}
    80  		if len(data) != 0 {
    81  			rowFileIndex++
    82  			rowChangedDecoder, err = NewJSONEventBatchDecoder(data)
    83  			if err != nil {
    84  				return nil, errors.Trace(err)
    85  			}
    86  		}
    87  	}
    88  
    89  	return &EventPuller{
    90  		schema: schema,
    91  		table:  table,
    92  
    93  		ddlDecoder:        ddlDecoder,
    94  		rowChangedDecoder: rowChangedDecoder,
    95  
    96  		ddlFiles:            ddlFiles,
    97  		rowChangedFiles:     rowChangedFiles,
    98  		ddlFileIndex:        ddlFileIndex,
    99  		rowChangedFileIndex: rowFileIndex,
   100  
   101  		storage: storage,
   102  	}, nil
   103  }
   104  
   105  // PullOneEvent pulls one event in ts order.
   106  // The Next event which can be DDL item or Row changed Item depends on next commit ts.
   107  func (e *EventPuller) PullOneEvent(ctx context.Context) (*SortItem, error) {
   108  	var (
   109  		err  error
   110  		data []byte
   111  	)
   112  	// ddl exists
   113  	if e.ddlDecoder != nil {
   114  		// current file end, read next file if next file exists
   115  		if !e.ddlDecoder.HasNext() && e.ddlFileIndex < len(e.ddlFiles) {
   116  			path := e.ddlFiles[e.ddlFileIndex]
   117  			data, err = e.storage.ReadFile(ctx, path)
   118  			if err != nil {
   119  				return nil, errors.Trace(err)
   120  			}
   121  			if len(data) > 0 {
   122  				e.ddlFileIndex++
   123  				e.ddlDecoder, err = NewJSONEventBatchDecoder(data)
   124  				if err != nil {
   125  					return nil, errors.Trace(err)
   126  				}
   127  			}
   128  		}
   129  		// set current DDL item first
   130  		if e.currentDDLItem == nil {
   131  			e.currentDDLItem, err = e.ddlDecoder.NextEvent(DDL)
   132  			if err != nil {
   133  				return nil, errors.Trace(err)
   134  			}
   135  		}
   136  	}
   137  	// dml exists
   138  	if e.rowChangedDecoder != nil {
   139  		// current file end, read next file if next file exists
   140  		if !e.rowChangedDecoder.HasNext() && e.rowChangedFileIndex < len(e.rowChangedFiles) {
   141  			path := e.rowChangedFiles[e.rowChangedFileIndex]
   142  			data, err = e.storage.ReadFile(ctx, path)
   143  			if err != nil {
   144  				return nil, errors.Trace(err)
   145  			}
   146  			if len(data) != 0 {
   147  				e.rowChangedFileIndex++
   148  				e.rowChangedDecoder, err = NewJSONEventBatchDecoder(data)
   149  				if err != nil {
   150  					return nil, errors.Trace(err)
   151  				}
   152  			}
   153  		}
   154  		if e.currentRowChangedItem == nil {
   155  			e.currentRowChangedItem, err = e.rowChangedDecoder.NextEvent(RowChanged)
   156  			if err != nil {
   157  				return nil, errors.Trace(err)
   158  			}
   159  		}
   160  	}
   161  
   162  	var returnItem *SortItem
   163  	switch {
   164  	case e.currentDDLItem != nil:
   165  		if e.currentDDLItem.LessThan(e.currentRowChangedItem) {
   166  			returnItem = e.currentDDLItem
   167  			e.currentDDLItem, err = e.ddlDecoder.NextEvent(DDL)
   168  			if err != nil {
   169  				return nil, errors.Trace(err)
   170  			}
   171  			break
   172  		}
   173  		fallthrough
   174  	case e.currentRowChangedItem != nil:
   175  		returnItem = e.currentRowChangedItem
   176  		e.currentRowChangedItem, err = e.rowChangedDecoder.NextEvent(RowChanged)
   177  		if err != nil {
   178  			return nil, errors.Trace(err)
   179  		}
   180  	default:
   181  		log.Info("puller finished")
   182  	}
   183  	return returnItem, nil
   184  }