github.com/matrixorigin/matrixone@v0.7.0/pkg/vm/engine/tae/logtail/logtailer.go (about)

     1  // Copyright 2021 Matrix Origin
     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  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package logtail
    16  
    17  import (
    18  	"context"
    19  
    20  	pkgcatalog "github.com/matrixorigin/matrixone/pkg/catalog"
    21  	"github.com/matrixorigin/matrixone/pkg/container/types"
    22  	"github.com/matrixorigin/matrixone/pkg/logutil"
    23  	"github.com/matrixorigin/matrixone/pkg/pb/api"
    24  	"github.com/matrixorigin/matrixone/pkg/pb/logtail"
    25  	"github.com/matrixorigin/matrixone/pkg/pb/timestamp"
    26  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/catalog"
    27  	"go.uber.org/zap"
    28  )
    29  
    30  // Logtailer provides logtail for the specified table.
    31  type Logtailer interface {
    32  	// RangeLogtail returns logtail for all tables within the range (from, to].
    33  	RangeLogtail(
    34  		ctx context.Context, from, to timestamp.Timestamp,
    35  	) ([]logtail.TableLogtail, error)
    36  
    37  	// TableLogtail returns logtail for the specified table.
    38  	//
    39  	// NOTE: If table not exist, logtail.TableLogtail shouldn't be a simple zero value.
    40  	TableLogtail(
    41  		ctx context.Context, table api.TableID, from, to timestamp.Timestamp,
    42  	) (logtail.TableLogtail, error)
    43  }
    44  
    45  var _ Logtailer = (*LogtailerImpl)(nil)
    46  
    47  type LogtailerImpl struct {
    48  	ckpClient CheckpointClient
    49  	mgr       *Manager
    50  	c         *catalog.Catalog
    51  }
    52  
    53  func NewLogtailer(
    54  	ckpClient CheckpointClient,
    55  	mgr *Manager,
    56  	c *catalog.Catalog) *LogtailerImpl {
    57  	return &LogtailerImpl{
    58  		ckpClient: ckpClient,
    59  		mgr:       mgr,
    60  		c:         c,
    61  	}
    62  }
    63  
    64  // TableLogtail returns logtail for the specified table.
    65  func (l *LogtailerImpl) TableLogtail(
    66  	ctx context.Context, table api.TableID, from, to timestamp.Timestamp,
    67  ) (logtail.TableLogtail, error) {
    68  	req := api.SyncLogTailReq{
    69  		CnHave: &from,
    70  		CnWant: &to,
    71  		Table:  &table,
    72  	}
    73  	resp, err := HandleSyncLogTailReq(ctx, l.ckpClient, l.mgr, l.c, req, true)
    74  	ret := logtail.TableLogtail{}
    75  	if err != nil {
    76  		return ret, err
    77  	}
    78  	ret.CkpLocation = resp.CkpLocation
    79  	ret.Ts = &to
    80  	ret.Table = &table
    81  	ret.Commands = nonPointerEntryList(resp.Commands)
    82  	return ret, nil
    83  }
    84  
    85  // RangeLogtail returns logtail for all tables within the range (from, to].
    86  func (l *LogtailerImpl) RangeLogtail(
    87  	ctx context.Context, from, to timestamp.Timestamp,
    88  ) ([]logtail.TableLogtail, error) {
    89  	start := types.BuildTS(from.PhysicalTime, from.LogicalTime)
    90  	end := types.BuildTS(to.PhysicalTime, to.LogicalTime)
    91  
    92  	ckpLoc, checkpointed, err := l.ckpClient.CollectCheckpointsInRange(ctx, start, end)
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  
    97  	if checkpointed.GreaterEq(end) {
    98  		u64Max := uint64(0)
    99  		u64Max -= 1
   100  		return []logtail.TableLogtail{{
   101  			CkpLocation: ckpLoc,
   102  			Ts:          &to,
   103  			Table:       &api.TableID{DbId: u64Max, TbId: u64Max},
   104  		}}, nil
   105  	} else if ckpLoc != "" {
   106  		start = checkpointed.Next()
   107  	}
   108  
   109  	reader := l.mgr.GetReader(start, end)
   110  	resps := make([]logtail.TableLogtail, 0, 8)
   111  
   112  	// collect resp for the three system tables
   113  	if reader.HasCatalogChanges() {
   114  		for _, scope := range []Scope{ScopeDatabases, ScopeTables, ScopeColumns} {
   115  			resp, err := l.getCatalogRespBuilder(scope, reader, ckpLoc).build()
   116  			if err != nil {
   117  				return nil, err
   118  			}
   119  			resps = append(resps, resp)
   120  		}
   121  	}
   122  
   123  	// collect resp for every dirty normal table
   124  	dirties, _ := reader.GetDirty()
   125  	for _, table := range dirties.Tables {
   126  		did, tid := table.DbID, table.ID
   127  		resp, err := l.getTableRespBuilder(did, tid, reader, ckpLoc).build()
   128  		if err != nil {
   129  			// fixme: log err and continue?
   130  			return resps, err
   131  		}
   132  		resps = append(resps, resp)
   133  	}
   134  	return resps, nil
   135  }
   136  
   137  func (l *LogtailerImpl) getTableRespBuilder(did, tid uint64, reader *Reader, ckpLoc string) *tableRespBuilder {
   138  	return &tableRespBuilder{
   139  		did:    did,
   140  		tid:    tid,
   141  		scope:  ScopeUserTables,
   142  		reader: reader,
   143  		c:      l.c,
   144  	}
   145  }
   146  
   147  func (l *LogtailerImpl) getCatalogRespBuilder(scope Scope, reader *Reader, ckpLoc string) *tableRespBuilder {
   148  	b := &tableRespBuilder{
   149  		did:    pkgcatalog.MO_CATALOG_ID,
   150  		scope:  ScopeUserTables,
   151  		reader: reader,
   152  		c:      l.c,
   153  	}
   154  	switch scope {
   155  	case ScopeDatabases:
   156  		b.tid = pkgcatalog.MO_DATABASE_ID
   157  	case ScopeTables:
   158  		b.tid = pkgcatalog.MO_TABLES_ID
   159  	case ScopeColumns:
   160  		b.tid = pkgcatalog.MO_COLUMNS_ID
   161  	}
   162  	return b
   163  }
   164  
   165  type tableRespBuilder struct {
   166  	did, tid uint64
   167  	ckpLoc   string
   168  	scope    Scope
   169  	reader   *Reader
   170  	c        *catalog.Catalog
   171  }
   172  
   173  func (b *tableRespBuilder) build() (logtail.TableLogtail, error) {
   174  	resp, err := b.collect()
   175  	if err != nil {
   176  		return logtail.TableLogtail{}, err
   177  	}
   178  	if len(resp.Commands) == 0 {
   179  		logutil.Info("[Logtail] empty table logtail", zap.Any("t_id", b.tid))
   180  		return logtail.TableLogtail{}, err
   181  	}
   182  	ret := logtail.TableLogtail{}
   183  	ret.CkpLocation = resp.CkpLocation
   184  	to := b.reader.to.ToTimestamp()
   185  	ret.Ts = &to
   186  	ret.Table = &api.TableID{DbId: b.did, TbId: b.tid}
   187  	ret.Commands = nonPointerEntryList(resp.Commands)
   188  	return ret, nil
   189  }
   190  
   191  func (b *tableRespBuilder) collect() (api.SyncLogTailResp, error) {
   192  	var builder RespBuilder
   193  	if b.scope == ScopeUserTables {
   194  		dbEntry, err := b.c.GetDatabaseByID(b.did)
   195  		if err != nil {
   196  			logutil.Info("[Logtail] not found", zap.Any("db_id", b.did))
   197  			return api.SyncLogTailResp{}, nil
   198  		}
   199  		tableEntry, err := dbEntry.GetTableEntryByID(b.tid)
   200  		if err != nil {
   201  			logutil.Info("[Logtail] not found", zap.Any("t_id", b.tid))
   202  			return api.SyncLogTailResp{}, nil
   203  		}
   204  		builder = NewTableLogtailRespBuilder(b.ckpLoc, b.reader.from, b.reader.to, tableEntry)
   205  	} else {
   206  		builder = NewCatalogLogtailRespBuilder(b.scope, b.ckpLoc, b.reader.from, b.reader.to)
   207  	}
   208  	op := NewBoundTableOperator(b.c, b.reader, b.scope, b.did, b.tid, builder)
   209  	err := op.Run()
   210  	if err != nil {
   211  		return api.SyncLogTailResp{}, err
   212  	}
   213  
   214  	return builder.BuildResp()
   215  }
   216  
   217  // TODO: remvove this after push mode is stable
   218  func nonPointerEntryList(src []*api.Entry) []api.Entry {
   219  	es := make([]api.Entry, len(src))
   220  	for i, e := range src {
   221  		es[i] = *e
   222  	}
   223  	return es
   224  }