github.com/matrixorigin/matrixone@v1.2.0/pkg/vm/engine/tae/logtail/mgr.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 "fmt" 20 "sync" 21 "sync/atomic" 22 "time" 23 24 "github.com/matrixorigin/matrixone/pkg/container/types" 25 "github.com/matrixorigin/matrixone/pkg/logutil" 26 "github.com/matrixorigin/matrixone/pkg/pb/api" 27 "github.com/matrixorigin/matrixone/pkg/pb/logtail" 28 "github.com/matrixorigin/matrixone/pkg/pb/timestamp" 29 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/catalog" 30 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/db/dbutils" 31 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/iface/txnif" 32 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/logstore/sm" 33 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/txn/txnbase" 34 "go.uber.org/zap" 35 ) 36 37 const ( 38 LogtailHeartbeatDuration = time.Millisecond * 2 39 ) 40 41 func MockCallback(from, to timestamp.Timestamp, closeCB func(), tails ...logtail.TableLogtail) error { 42 defer closeCB() 43 if len(tails) == 0 { 44 return nil 45 } 46 s := fmt.Sprintf("get logtail\nfrom %v, to %v, tails cnt %d", from, to, len(tails)) 47 for _, tail := range tails { 48 s = fmt.Sprintf("%s\nts %v, dbid %d, tid %d,entries cnt %d", s, tail.Ts, tail.Table.DbId, tail.Table.TbId, len(tail.Commands)) 49 for _, entry := range tail.Commands { 50 s = fmt.Sprintf("%s\n db name %s, table name %s, insert %v, batch length %d\n %v", 51 s, entry.DatabaseName, entry.TableName, entry.EntryType == api.Entry_Insert, entry.Bat.Vecs[0].Len, entry.Bat.Attrs) 52 for i, vec := range entry.Bat.Vecs { 53 s = fmt.Sprintf("%s\n %v, type %v, len %d", s, entry.Bat.Attrs[i], vec.Type, vec.Len) 54 } 55 } 56 } 57 logutil.Infof(s) 58 return nil 59 } 60 61 type callback struct { 62 cb func(from, to timestamp.Timestamp, closeCB func(), tails ...logtail.TableLogtail) error 63 } 64 65 func (cb *callback) call(from, to timestamp.Timestamp, closeCB func(), tails ...logtail.TableLogtail) error { 66 // for debug 67 // MockCallback(from,to,tails...) 68 return cb.cb(from, to, closeCB, tails...) 69 } 70 71 // Logtail manager holds sorted txn handles. Its main jobs: 72 // 73 // - Insert new txn handle 74 // - Efficiently iterate over arbitrary range of txn handles on a snapshot 75 // - Truncate unneceessary txn handles according to GC timestamp 76 type Manager struct { 77 txnbase.NoopCommitListener 78 table *TxnTable 79 rt *dbutils.Runtime 80 truncated types.TS 81 nowClock func() types.TS // nowClock is from TxnManager 82 83 previousSaveTS types.TS 84 logtailCallback atomic.Pointer[callback] 85 collectLogtailQueue sm.Queue 86 waitCommitQueue sm.Queue 87 eventOnce sync.Once 88 89 nextCompactTS types.TS 90 } 91 92 func NewManager(rt *dbutils.Runtime, blockSize int, nowClock func() types.TS) *Manager { 93 mgr := &Manager{ 94 rt: rt, 95 table: NewTxnTable( 96 blockSize, 97 nowClock, 98 ), 99 nowClock: nowClock, 100 } 101 mgr.collectLogtailQueue = sm.NewSafeQueue(10000, 100, mgr.onCollectTxnLogtails) 102 mgr.waitCommitQueue = sm.NewSafeQueue(10000, 100, mgr.onWaitTxnCommit) 103 104 return mgr 105 } 106 107 type txnWithLogtails struct { 108 txn txnif.AsyncTxn 109 tails *[]logtail.TableLogtail 110 closeCB func() 111 } 112 113 func (mgr *Manager) onCollectTxnLogtails(items ...any) { 114 for _, item := range items { 115 txn := item.(txnif.AsyncTxn) 116 if txn.IsReplay() { 117 continue 118 } 119 builder := NewTxnLogtailRespBuilder(mgr.rt) 120 entries, closeCB := builder.CollectLogtail(txn) 121 txn.GetStore().DoneWaitEvent(1) 122 txnWithLogtails := &txnWithLogtails{ 123 txn: txn, 124 tails: entries, 125 closeCB: closeCB, 126 } 127 mgr.waitCommitQueue.Enqueue(txnWithLogtails) 128 } 129 } 130 func (mgr *Manager) onWaitTxnCommit(items ...any) { 131 for _, item := range items { 132 txn := item.(*txnWithLogtails) 133 state := txn.txn.GetTxnState(true) 134 if state != txnif.TxnStateCommitted { 135 if state != txnif.TxnStateRollbacked { 136 panic(fmt.Sprintf("wrong state %v", state)) 137 } 138 continue 139 } 140 mgr.generateLogtailWithTxn(txn) 141 } 142 } 143 func (mgr *Manager) Stop() { 144 mgr.collectLogtailQueue.Stop() 145 mgr.waitCommitQueue.Stop() 146 } 147 func (mgr *Manager) Start() { 148 mgr.waitCommitQueue.Start() 149 mgr.collectLogtailQueue.Start() 150 } 151 152 func (mgr *Manager) generateLogtailWithTxn(txn *txnWithLogtails) { 153 callback := mgr.logtailCallback.Load() 154 if callback != nil { 155 to := txn.txn.GetPrepareTS() 156 var from types.TS 157 if mgr.previousSaveTS.IsEmpty() { 158 from = to 159 } else { 160 from = mgr.previousSaveTS 161 } 162 mgr.previousSaveTS = to 163 // Send ts in order to initialize waterline of logtail service 164 mgr.eventOnce.Do(func() { 165 logutil.Infof("init waterline to %v", from.ToString()) 166 callback.call(from.ToTimestamp(), from.ToTimestamp(), txn.closeCB) 167 }) 168 callback.call(from.ToTimestamp(), to.ToTimestamp(), txn.closeCB, *txn.tails...) 169 } else { 170 txn.closeCB() 171 } 172 } 173 174 // OnEndPrePrepare is a listener for TxnManager. When a txn completes PrePrepare, 175 // add it to the logtail manager 176 func (mgr *Manager) OnEndPrePrepare(txn txnif.AsyncTxn) { 177 if txn.GetStore().GetTransactionType() == txnif.TxnType_Heartbeat { 178 return 179 } 180 mgr.table.AddTxn(txn) 181 } 182 func (mgr *Manager) OnEndPrepareWAL(txn txnif.AsyncTxn) { 183 txn.GetStore().AddWaitEvent(1) 184 mgr.collectLogtailQueue.Enqueue(txn) 185 } 186 187 // GetReader get a snapshot of all txn prepared between from and to. 188 func (mgr *Manager) GetReader(from, to types.TS) *Reader { 189 return &Reader{ 190 from: from, 191 to: to, 192 table: mgr.table, 193 } 194 } 195 196 func (mgr *Manager) GCByTS(ctx context.Context, ts types.TS) { 197 if ts.Equal(&mgr.truncated) { 198 return 199 } 200 mgr.truncated = ts 201 cnt := mgr.table.TruncateByTimeStamp(ts) 202 logutil.Info("[logtail] GC", zap.String("ts", ts.ToString()), zap.Int("deleted", cnt)) 203 } 204 205 func (mgr *Manager) TryCompactTable() { 206 mgr.nextCompactTS = mgr.table.TryCompact(mgr.nextCompactTS, mgr.rt) 207 } 208 209 func (mgr *Manager) GetTableOperator( 210 from, to types.TS, 211 catalog *catalog.Catalog, 212 dbID, tableID uint64, 213 scope Scope, 214 visitor catalog.Processor, 215 ) *BoundTableOperator { 216 reader := mgr.GetReader(from, to) 217 return NewBoundTableOperator( 218 catalog, 219 reader, 220 scope, 221 dbID, 222 tableID, 223 visitor, 224 ) 225 } 226 227 func (mgr *Manager) RegisterCallback(cb func(from, to timestamp.Timestamp, closeCB func(), tails ...logtail.TableLogtail) error) error { 228 callbackFn := &callback{ 229 cb: cb, 230 } 231 mgr.logtailCallback.Store(callbackFn) 232 return nil 233 }