github.com/matrixorigin/matrixone@v0.7.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/iface/txnif" 26 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/model" 27 "go.uber.org/zap" 28 ) 29 30 type RowT = *txnRow 31 type BlockT = *txnBlock 32 33 type summary struct { 34 hasCatalogChanges bool 35 // TODO 36 // table ids 37 // maxLsn 38 } 39 40 type txnRow struct { 41 txnif.AsyncTxn 42 } 43 44 func (row *txnRow) Length() int { return 1 } 45 func (row *txnRow) Window(_, _ int) *txnRow { return nil } 46 47 type txnBlock struct { 48 sync.RWMutex 49 bornTS types.TS 50 rows []*txnRow 51 summary atomic.Pointer[summary] 52 } 53 54 func (blk *txnBlock) Length() int { 55 blk.RLock() 56 defer blk.RUnlock() 57 return len(blk.rows) 58 } 59 60 func (blk *txnBlock) IsAppendable() bool { 61 return blk != nil 62 } 63 64 func (blk *txnBlock) Append(row *txnRow) (err error) { 65 blk.Lock() 66 defer blk.Unlock() 67 blk.rows = append(blk.rows, row) 68 return 69 } 70 71 func (blk *txnBlock) Close() { 72 blk.Lock() 73 defer blk.Unlock() 74 blk.bornTS = types.TS{} 75 blk.rows = make([]*txnRow, 0) 76 } 77 78 func (blk *txnBlock) trySumary() { 79 summary := new(summary) 80 for _, row := range blk.rows { 81 if row.GetMemo().HasCatalogChanges() { 82 summary.hasCatalogChanges = true 83 break 84 } 85 } 86 blk.summary.CompareAndSwap(nil, summary) 87 } 88 89 func (blk *txnBlock) ForeachRowInBetween( 90 from, to types.TS, 91 rowOp func(row RowT) (goNext bool), 92 ) (outOfRange bool, readRows int) { 93 var rows []*txnRow 94 if blk.summary.Load() == nil { 95 blk.RLock() 96 rows = blk.rows[:len(blk.rows)] 97 capacity := cap(blk.rows) 98 blk.RUnlock() 99 if capacity == len(rows) && blk.summary.Load() == nil { 100 blk.trySumary() 101 } 102 } else { 103 rows = blk.rows 104 } 105 for _, row := range rows { 106 readRows += 1 107 ts := row.GetPrepareTS() 108 if ts.IsEmpty() || ts.Greater(to) { 109 outOfRange = true 110 return 111 } 112 if ts.Less(from) { 113 continue 114 } 115 116 if !rowOp(row) { 117 outOfRange = true 118 return 119 } 120 } 121 return 122 } 123 124 func (blk *txnBlock) String() string { 125 length := blk.Length() 126 var buf bytes.Buffer 127 _, _ = buf.WriteString( 128 fmt.Sprintf("TXNBLK-[%s][Len=%d]", blk.bornTS.ToString(), length)) 129 return buf.String() 130 } 131 132 type TxnTable struct { 133 *model.AOT[BlockT, RowT] 134 } 135 136 func blockCompareFn(a, b BlockT) bool { 137 return a.bornTS.Less(b.bornTS) 138 } 139 140 func timeBasedTruncateFactory(ts types.TS) func(b BlockT) bool { 141 return func(b BlockT) bool { 142 return b.bornTS.GreaterEq(ts) 143 } 144 } 145 146 func NewTxnTable(blockSize int, clock *types.TsAlloctor) *TxnTable { 147 factory := func(row RowT) BlockT { 148 ts := row.GetPrepareTS() 149 if ts == txnif.UncommitTS { 150 ts = clock.Alloc() 151 } 152 return &txnBlock{ 153 bornTS: ts, 154 rows: make([]*txnRow, 0, blockSize), 155 } 156 } 157 return &TxnTable{ 158 AOT: model.NewAOT( 159 blockSize, 160 factory, 161 blockCompareFn, 162 ), 163 } 164 } 165 166 func (table *TxnTable) AddTxn(txn txnif.AsyncTxn) (err error) { 167 row := &txnRow{ 168 AsyncTxn: txn, 169 } 170 err = table.Append(row) 171 return 172 } 173 174 func (table *TxnTable) TruncateByTimeStamp(ts types.TS) (cnt int) { 175 filter := timeBasedTruncateFactory(ts) 176 return table.Truncate(filter) 177 } 178 179 func (table *TxnTable) ForeachRowInBetween( 180 from, to types.TS, 181 skipBlkOp func(blk BlockT) bool, 182 rowOp func(row RowT) (goNext bool), 183 ) (readRows int) { 184 snapshot := table.Snapshot() 185 pivot := &txnBlock{bornTS: from} 186 outOfLeft := true 187 snapshot.Descend(pivot, func(blk BlockT) bool { 188 pivot.bornTS = blk.bornTS 189 outOfLeft = false 190 return false 191 }) 192 193 // from is smaller than the very first block and it is not special like 0-0, 0-1, 1-0 194 if outOfLeft && from.Greater(types.BuildTS(1, 1)) { 195 minTs := types.TS{} 196 snapshot.Ascend(&txnBlock{}, func(blk *txnBlock) bool { 197 minTs = blk.bornTS 198 return false 199 }) 200 logutil.Warn("[logtail] fetch with too small ts", zap.String("ts", from.ToString()), zap.String("minTs", minTs.ToString())) 201 } 202 snapshot.Ascend(pivot, func(blk BlockT) bool { 203 if blk.bornTS.Greater(to) { 204 return false 205 } 206 207 if skipBlkOp != nil && skipBlkOp(blk) { 208 return blk.rows[len(blk.rows)-1].GetPrepareTS().LessEq(to) 209 } 210 outOfRange, cnt := blk.ForeachRowInBetween( 211 from, 212 to, 213 rowOp, 214 ) 215 readRows += cnt 216 217 return !outOfRange 218 }) 219 return 220 }