github.com/matrixorigin/matrixone@v0.7.0/pkg/txn/storage/memorystorage/memtable/physical_row.go (about) 1 // Copyright 2022 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 memtable 16 17 import ( 18 "database/sql" 19 "fmt" 20 "io" 21 "sync/atomic" 22 "time" 23 24 "github.com/matrixorigin/matrixone/pkg/common/moerr" 25 "github.com/matrixorigin/matrixone/pkg/txn/storage/memorystorage/memorytable" 26 ) 27 28 type PhysicalRow[ 29 K memorytable.Ordered[K], 30 V any, 31 ] struct { 32 Key K 33 LastUpdate time.Time 34 Versions []Version[V] 35 //TODO version GC 36 } 37 38 type Version[T any] struct { 39 ID int64 40 BornTx *Transaction 41 BornTime Time 42 LockTx *Transaction 43 LockTime Time 44 Value T 45 } 46 47 var nextVersionID int64 48 49 // Read reads the visible value from Values 50 // readTime's logical time should be monotonically increasing in one transaction to reflect commands order 51 func (p *PhysicalRow[K, V]) Read(now Time, tx *Transaction) (value V, err error) { 52 version, err := p.readVersion(now, tx) 53 if version != nil { 54 value = version.Value 55 } 56 return 57 } 58 59 func (p *PhysicalRow[K, V]) readVersion(now Time, tx *Transaction) (*Version[V], error) { 60 if tx.State.Load() != Active { 61 panic("should not call Read") 62 } 63 64 for i := len(p.Versions) - 1; i >= 0; i-- { 65 value := p.Versions[i] 66 if value.Visible(now, tx.ID, tx.IsolationPolicy.Read) { 67 switch tx.IsolationPolicy.Read { 68 case ReadCommitted: 69 case ReadSnapshot: 70 // BornTx must be committed to be visible here 71 if value.BornTx.ID != tx.ID && value.BornTime.After(tx.BeginTime) { 72 continue 73 } 74 case ReadNoStale: 75 // BornTx must be committed to be visible here 76 if value.BornTx.ID != tx.ID && value.BornTime.After(tx.BeginTime) { 77 return &value, moerr.NewTxnReadConflictNoCtx("%s %s", tx.ID, value.BornTx.ID) 78 } 79 } 80 return &value, nil 81 } 82 } 83 84 return nil, sql.ErrNoRows 85 } 86 87 func (v *Version[T]) Visible(now Time, txID string, policy ReadPolicy) bool { 88 89 // the following algorithm is from https://momjian.us/main/writings/pgsql/mvcc.pdf 90 // "[Mike Olson] says 17 march 1993: the tests in this routine are correct; if you think they’re not, you’re wrongand you should think about it again. i know, it happened to me." 91 92 // inserted by current tx 93 if v.BornTx.ID == txID { 94 // inserted before the read time 95 if v.BornTime.Before(now) { 96 // not been deleted 97 if v.LockTx == nil { 98 return true 99 } 100 // deleted by current tx after the read time 101 if v.LockTx.ID == txID && v.LockTime.After(now) { 102 return true 103 } 104 } 105 } 106 107 // inserted by a committed tx 108 if v.BornTx.State.Load() == Committed { 109 // not been deleted 110 if v.LockTx == nil { 111 // for isolation levels stricter than read-committed, instead of checking timestamps here, let the caller do it. 112 return true 113 } 114 // being deleted by current tx after the read time 115 if v.LockTx.ID == txID && v.LockTime.After(now) { 116 return true 117 } 118 // deleted by another tx but not committed 119 if v.LockTx.ID != txID && v.LockTx.State.Load() != Committed { 120 return true 121 } 122 // deleted by another committed tx after the read time 123 if policy == ReadSnapshot { 124 if v.LockTx.ID != txID && v.LockTx.State.Load() == Committed && v.LockTime.After(now) { 125 return true 126 } 127 } 128 } 129 130 return false 131 } 132 133 func (p *PhysicalRow[K, V]) Insert( 134 now Time, 135 tx *Transaction, 136 value V, 137 ) ( 138 newRow *PhysicalRow[K, V], 139 version *Version[V], 140 err error, 141 ) { 142 143 if tx.State.Load() != Active { 144 panic("should not call Insert") 145 } 146 147 for i := len(p.Versions) - 1; i >= 0; i-- { 148 value := p.Versions[i] 149 if value.Visible(now, tx.ID, tx.IsolationPolicy.Read) { 150 if value.LockTx != nil && value.LockTx.State.Load() != Aborted { 151 // locked by active or committed tx 152 return nil, nil, moerr.NewTxnWriteConflictNoCtx("%s %s", tx.ID, value.LockTx.ID) 153 } 154 if value.BornTx.ID != tx.ID && value.BornTime.After(tx.BeginTime) { 155 return nil, nil, moerr.NewTxnWriteConflictNoCtx("%s %s", tx.ID, value.BornTx.ID) 156 } 157 } 158 } 159 160 p = p.clone() 161 p.LastUpdate = time.Now() 162 163 version = &Version[V]{ 164 ID: atomic.AddInt64(&nextVersionID, 1), 165 BornTx: tx, 166 BornTime: now, 167 Value: value, 168 } 169 p.Versions = append(p.Versions, *version) 170 171 return p, version, nil 172 } 173 174 func (p *PhysicalRow[K, V]) Delete( 175 now Time, 176 tx *Transaction, 177 ) ( 178 newRow *PhysicalRow[K, V], 179 version *Version[V], 180 err error, 181 ) { 182 if tx.State.Load() != Active { 183 panic("should not call Delete") 184 } 185 186 for i := len(p.Versions) - 1; i >= 0; i-- { 187 value := p.Versions[i] 188 if value.Visible(now, tx.ID, tx.IsolationPolicy.Read) { 189 if value.LockTx != nil && value.LockTx.State.Load() != Aborted { 190 return nil, nil, moerr.NewTxnWriteConflictNoCtx("%s %s", tx.ID, value.LockTx.ID) 191 } 192 if value.BornTx.ID != tx.ID && value.BornTime.After(tx.BeginTime) { 193 return nil, nil, moerr.NewTxnWriteConflictNoCtx("%s %s", tx.ID, value.BornTx.ID) 194 } 195 196 p = p.clone() 197 p.LastUpdate = time.Now() 198 value.LockTx = tx 199 value.LockTime = now 200 p.Versions[i] = value 201 202 return p, &value, nil 203 } 204 } 205 206 return nil, nil, sql.ErrNoRows 207 } 208 209 func (p *PhysicalRow[K, V]) Update( 210 now Time, 211 tx *Transaction, 212 newValue V, 213 ) ( 214 newRow *PhysicalRow[K, V], 215 version *Version[V], 216 err error, 217 ) { 218 219 if tx.State.Load() != Active { 220 panic("should not call Update") 221 } 222 223 for i := len(p.Versions) - 1; i >= 0; i-- { 224 value := p.Versions[i] 225 if value.Visible(now, tx.ID, tx.IsolationPolicy.Read) { 226 227 if value.LockTx != nil && value.LockTx.State.Load() != Aborted { 228 return nil, nil, moerr.NewTxnWriteConflictNoCtx("%s %s", tx.ID, value.LockTx.ID) 229 } 230 231 if value.BornTx.ID != tx.ID && value.BornTime.After(tx.BeginTime) { 232 return nil, nil, moerr.NewTxnWriteConflictNoCtx("%s %s", tx.ID, value.BornTx.ID) 233 } 234 235 p = p.clone() 236 p.LastUpdate = time.Now() 237 238 value.LockTx = tx 239 value.LockTime = now 240 p.Versions[i] = value 241 242 version = &Version[V]{ 243 ID: atomic.AddInt64(&nextVersionID, 1), 244 BornTx: tx, 245 BornTime: now, 246 Value: newValue, 247 } 248 p.Versions = append(p.Versions, *version) 249 250 return p, version, nil 251 } 252 } 253 254 return nil, nil, sql.ErrNoRows 255 } 256 257 func (p *PhysicalRow[K, V]) clone() *PhysicalRow[K, V] { 258 newRow := *p 259 newRow.Versions = make([]Version[V], len(p.Versions)) 260 copy(newRow.Versions, p.Versions) 261 return &newRow 262 } 263 264 func (p *PhysicalRow[K, V]) dump(w io.Writer) { 265 for _, value := range p.Versions { 266 fmt.Fprintf(w, "born tx %s, born time %s, value %v", 267 value.BornTx.ID, 268 value.BornTime.String(), 269 value.Value, 270 ) 271 if value.LockTx != nil { 272 fmt.Fprintf(w, " lock tx %s, lock time %s", 273 value.LockTx.ID, 274 value.LockTime.String(), 275 ) 276 } 277 fmt.Fprintf(w, "\n") 278 } 279 }