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 }