github.com/matrixorigin/matrixone@v1.2.0/pkg/vm/engine/tae/logtail/table.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 "bytes" 19 "fmt" 20 "sync" 21 "sync/atomic" 22 23 "github.com/matrixorigin/matrixone/pkg/container/types" 24 "github.com/matrixorigin/matrixone/pkg/logutil" 25 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/db/dbutils" 26 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/iface/txnif" 27 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/model" 28 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/txn/txnbase" 29 "go.uber.org/zap" 30 ) 31 32 type RowT = *txnRow 33 type BlockT = *txnBlock 34 35 type smallTxn struct { 36 memo *txnif.TxnMemo 37 startTS types.TS 38 prepareTS types.TS 39 state txnif.TxnState 40 lsn uint64 41 } 42 43 func (txn *smallTxn) GetMemo() *txnif.TxnMemo { 44 return txn.memo 45 } 46 47 func (txn *smallTxn) GetTxnState(_ bool) txnif.TxnState { 48 return txn.state 49 } 50 51 func (txn *smallTxn) GetStartTS() types.TS { 52 return txn.startTS 53 } 54 55 func (txn *smallTxn) GetPrepareTS() types.TS { 56 return txn.prepareTS 57 } 58 59 func (txn *smallTxn) GetCommitTS() types.TS { 60 return txn.prepareTS 61 } 62 63 func (txn *smallTxn) GetLSN() uint64 { 64 return txn.lsn 65 } 66 67 type summary struct { 68 hasCatalogChanges bool 69 // TODO 70 // table ids 71 // maxLsn 72 } 73 74 type txnRow struct { 75 source atomic.Pointer[txnbase.Txn] 76 packed atomic.Pointer[smallTxn] 77 } 78 79 func (row *txnRow) GetLSN() uint64 { 80 if txn := row.source.Load(); txn != nil { 81 return txn.GetLSN() 82 } 83 txn := row.packed.Load() 84 return txn.GetLSN() 85 } 86 87 func (row *txnRow) GetMemo() *txnif.TxnMemo { 88 if txn := row.source.Load(); txn != nil { 89 return txn.GetMemo() 90 } 91 txn := row.packed.Load() 92 return txn.GetMemo() 93 } 94 95 func (row *txnRow) GetTxnState(waitIfcommitting bool) txnif.TxnState { 96 if txn := row.source.Load(); txn != nil { 97 return txn.GetTxnState(waitIfcommitting) 98 } 99 txn := row.packed.Load() 100 return txn.GetTxnState(waitIfcommitting) 101 } 102 103 func (row *txnRow) GetStartTS() types.TS { 104 if txn := row.source.Load(); txn != nil { 105 return txn.GetStartTS() 106 } 107 txn := row.packed.Load() 108 return txn.GetStartTS() 109 } 110 111 func (row *txnRow) GetPrepareTS() types.TS { 112 if txn := row.source.Load(); txn != nil { 113 return txn.GetPrepareTS() 114 } 115 txn := row.packed.Load() 116 return txn.GetPrepareTS() 117 } 118 119 func (row *txnRow) GetCommitTS() types.TS { 120 if txn := row.source.Load(); txn != nil { 121 return txn.GetCommitTS() 122 } 123 txn := row.packed.Load() 124 return txn.GetCommitTS() 125 } 126 127 func (row *txnRow) Length() int { return 1 } 128 func (row *txnRow) Window(_, _ int) *txnRow { return nil } 129 130 func (row *txnRow) IsCompacted() bool { 131 return row.packed.Load() != nil 132 } 133 134 func (row *txnRow) TryCompact() (compacted, changed bool) { 135 if txn := row.source.Load(); txn == nil { 136 return true, false 137 } else { 138 state := txn.GetTxnState(false) 139 if state == txnif.TxnStateCommitted || state == txnif.TxnStateRollbacked { 140 packed := &smallTxn{ 141 memo: txn.GetMemo(), 142 startTS: txn.GetStartTS(), 143 prepareTS: txn.GetPrepareTS(), 144 state: state, 145 lsn: txn.GetLSN(), 146 } 147 row.packed.Store(packed) 148 row.source.Store(nil) 149 return true, true 150 } 151 } 152 return false, false 153 } 154 155 type txnBlock struct { 156 sync.RWMutex 157 bornTS types.TS 158 rows []*txnRow 159 summary atomic.Pointer[summary] 160 } 161 162 func (blk *txnBlock) Length() int { 163 blk.RLock() 164 defer blk.RUnlock() 165 return len(blk.rows) 166 } 167 168 func (blk *txnBlock) TryCompact() (all bool, changed bool) { 169 blk.RLock() 170 defer blk.RUnlock() 171 if len(blk.rows) < cap(blk.rows) { 172 return false, false 173 } 174 if blk.rows[len(blk.rows)-1].IsCompacted() { 175 return true, false 176 } 177 all = true 178 for _, row := range blk.rows { 179 if compacted, _ := row.TryCompact(); !compacted { 180 all = false 181 break 182 } 183 } 184 if all { 185 changed = true 186 } 187 return 188 } 189 190 func (blk *txnBlock) IsAppendable() bool { 191 return blk != nil 192 } 193 194 func (blk *txnBlock) Append(row *txnRow) (err error) { 195 blk.Lock() 196 defer blk.Unlock() 197 blk.rows = append(blk.rows, row) 198 return 199 } 200 201 func (blk *txnBlock) Close() { 202 blk.Lock() 203 defer blk.Unlock() 204 blk.bornTS = types.TS{} 205 blk.rows = make([]*txnRow, 0) 206 } 207 208 func (blk *txnBlock) trySumary() { 209 summary := new(summary) 210 for _, row := range blk.rows { 211 if row.GetMemo().HasCatalogChanges() { 212 summary.hasCatalogChanges = true 213 break 214 } 215 } 216 blk.summary.CompareAndSwap(nil, summary) 217 } 218 219 func (blk *txnBlock) ForeachRowInBetween( 220 from, to types.TS, 221 rowOp func(row RowT) (goNext bool), 222 ) (outOfRange bool, readRows int) { 223 var rows []*txnRow 224 if blk.summary.Load() == nil { 225 blk.RLock() 226 rows = blk.rows[:len(blk.rows)] 227 capacity := cap(blk.rows) 228 blk.RUnlock() 229 if capacity == len(rows) && blk.summary.Load() == nil { 230 blk.trySumary() 231 } 232 } else { 233 rows = blk.rows 234 } 235 for _, row := range rows { 236 readRows += 1 237 ts := row.GetPrepareTS() 238 if ts.IsEmpty() || ts.Greater(&to) { 239 outOfRange = true 240 return 241 } 242 if ts.Less(&from) { 243 continue 244 } 245 246 if !rowOp(row) { 247 outOfRange = true 248 return 249 } 250 } 251 return 252 } 253 254 func (blk *txnBlock) String() string { 255 length := blk.Length() 256 var buf bytes.Buffer 257 _, _ = buf.WriteString( 258 fmt.Sprintf("TXNBLK-[%s][Len=%d]", blk.bornTS.ToString(), length)) 259 return buf.String() 260 } 261 262 type TxnTable struct { 263 *model.AOT[BlockT, RowT] 264 } 265 266 func (blk *txnBlock) Less(b BlockT) bool { 267 return blk.bornTS.Less(&b.bornTS) 268 } 269 270 func timeBasedTruncateFactory(ts types.TS) func(b BlockT) bool { 271 return func(b BlockT) bool { 272 return b.bornTS.GreaterEq(&ts) 273 } 274 } 275 276 func NewTxnTable(blockSize int, nowClock func() types.TS) *TxnTable { 277 factory := func(row RowT) BlockT { 278 ts := row.GetPrepareTS() 279 if ts == txnif.UncommitTS { 280 ts = nowClock() 281 } 282 return &txnBlock{ 283 bornTS: ts, 284 rows: make([]*txnRow, 0, blockSize), 285 } 286 } 287 return &TxnTable{ 288 AOT: model.NewAOT( 289 blockSize, 290 factory, 291 (*txnBlock).Less, 292 ), 293 } 294 } 295 296 func (table *TxnTable) TryCompact(from types.TS, rt *dbutils.Runtime) (to types.TS) { 297 snapshot := table.Snapshot() 298 snapshot.Ascend( 299 &txnBlock{bornTS: from}, 300 func(blk BlockT) bool { 301 allCompacted, changed := blk.TryCompact() 302 if changed { 303 rt.Logtail.CompactStats.Add(1) 304 } 305 to = blk.bornTS 306 return allCompacted 307 }) 308 return 309 } 310 311 func (table *TxnTable) AddTxn(txn txnif.AsyncTxn) (err error) { 312 row := &txnRow{} 313 row.source.Store(txn.GetBase().(*txnbase.Txn)) 314 err = table.Append(row) 315 return 316 } 317 318 func (table *TxnTable) TruncateByTimeStamp(ts types.TS) (cnt int) { 319 filter := timeBasedTruncateFactory(ts) 320 return table.Truncate(filter) 321 } 322 323 func (table *TxnTable) ForeachRowInBetween( 324 from, to types.TS, 325 skipBlkOp func(blk BlockT) bool, 326 rowOp func(row RowT) (goNext bool), 327 ) (readRows int) { 328 snapshot := table.Snapshot() 329 pivot := &txnBlock{bornTS: from} 330 outOfLeft := true 331 snapshot.Descend(pivot, func(blk BlockT) bool { 332 pivot.bornTS = blk.bornTS 333 outOfLeft = false 334 return false 335 }) 336 337 // from is smaller than the very first block and it is not special like 0-0, 0-1, 1-0 338 ts := types.BuildTS(1, 1) 339 if outOfLeft && from.Greater(&ts) { 340 minTs := types.TS{} 341 snapshot.Ascend(&txnBlock{}, func(blk *txnBlock) bool { 342 minTs = blk.bornTS 343 return false 344 }) 345 logutil.Info("[logtail] fetch with too small ts", zap.String("ts", from.ToString()), zap.String("minTs", minTs.ToString())) 346 } 347 snapshot.Ascend(pivot, func(blk BlockT) bool { 348 if blk.bornTS.Greater(&to) { 349 return false 350 } 351 352 if skipBlkOp != nil && skipBlkOp(blk) { 353 prepareTS := blk.rows[len(blk.rows)-1].GetPrepareTS() 354 return prepareTS.LessEq(&to) 355 } 356 outOfRange, cnt := blk.ForeachRowInBetween( 357 from, 358 to, 359 rowOp, 360 ) 361 readRows += cnt 362 363 return !outOfRange 364 }) 365 return 366 }