github.com/matrixorigin/matrixone@v1.2.0/pkg/txn/storage/memorystorage/memorytable/transaction.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 memorytable 16 17 import ( 18 "fmt" 19 "sync" 20 "sync/atomic" 21 22 "github.com/matrixorigin/matrixone/pkg/common/moerr" 23 ) 24 25 // Transaction represents a transaction 26 // a transaction may contains multiple operations on multiple tables 27 // a transaction commits atomically 28 type Transaction struct { 29 State *Atomic[TransactionState] 30 BeginTime Time 31 tables transactionTableMap 32 } 33 34 type transactionTableMap struct { 35 sync.Mutex 36 Map map[int64]*transactionTable 37 } 38 39 type transactionTable struct { 40 table interface { 41 sync.Locker 42 commit(tx *Transaction, state any, oldState any, commitTime Time) (any, error) 43 rollback(oldState any) 44 abort(*Transaction) error 45 } 46 initState any 47 state atomic.Value 48 } 49 50 // NewTransaction creates a new transaction using beginTime as snapshot time 51 func NewTransaction( 52 beginTime Time, 53 ) *Transaction { 54 return &Transaction{ 55 State: NewAtomic(Active), 56 BeginTime: beginTime, 57 tables: transactionTableMap{ 58 Map: make(map[int64]*transactionTable), 59 }, 60 } 61 } 62 63 // TransactionState represents state of a transaction 64 type TransactionState uint8 65 66 // String returns the text form of a state 67 func (t TransactionState) String() string { 68 switch t { 69 case Active: 70 return "active" 71 case Committed: 72 return "committed" 73 case Aborted: 74 return "aborted" 75 } 76 panic(fmt.Sprintf("unknown state: %v", uint8(t))) 77 } 78 79 const ( 80 // Active is the default state of a newly created transaction 81 Active TransactionState = iota 82 // Committed is the state of a committed transaction 83 Committed 84 // Aborted is the state of a aborted transaction 85 Aborted 86 ) 87 88 // Commit commits the transaction 89 func (t *Transaction) Commit(commitTime Time) error { 90 if state := t.State.Load(); state != Active { 91 return moerr.NewTxnNotActiveNoCtx(state.String()) 92 } 93 committed := make(map[int64]any) // table id -> swapped state 94 t.tables.Lock() 95 defer t.tables.Unlock() 96 for id, table := range t.tables.Map { 97 table.table.Lock() 98 defer table.table.Unlock() 99 swapped, err := table.table.commit(t, table.state.Load(), table.initState, commitTime) 100 if err != nil { 101 102 // rollback previous committed 103 for id, swapped := range committed { 104 table := t.tables.Map[id] 105 table.table.rollback(swapped) 106 } 107 108 return err 109 } 110 committed[id] = swapped 111 } 112 t.State.Store(Committed) 113 return nil 114 } 115 116 // must call with t being locked 117 func (t *Table[K, V, R]) commit(tx *Transaction, state any, oldState any, commitTime Time) (any, error) { 118 119 currentState := t.state.Load() 120 txState := state.(*tableState[K, V]) 121 if !t.state.CompareAndSwap(oldState.(*tableState[K, V]), txState) { 122 newState, _, err := currentState.merge(txState) 123 if err != nil { 124 return nil, err 125 } 126 t.state.Store(newState) 127 } 128 if !t.disableHistory.Load() { 129 if !commitTime.IsEmpty() && len(t.history) > 0 { 130 last := t.history[len(t.history)-1] 131 if !commitTime.Greater(last.EndTime) { 132 return nil, moerr.NewInternalErrorNoCtx("commit time too old") 133 } 134 } 135 t.history = append(t.history, &history[K, V]{ 136 EndTime: commitTime, 137 EndState: currentState, 138 }) 139 } 140 141 return currentState, nil 142 } 143 144 // must call with t being locked 145 func (t *Table[K, V, R]) rollback(oldStateValue any) { 146 oldState := oldStateValue.(*tableState[K, V]) 147 t.state.Store(oldState) 148 for len(t.history) > 0 && t.history[len(t.history)-1].EndState != oldState { 149 t.history = t.history[:len(t.history)-1] 150 } 151 } 152 153 // Abort aborts the transaction 154 func (t *Transaction) Abort() error { 155 t.tables.Lock() 156 defer t.tables.Unlock() 157 for _, table := range t.tables.Map { 158 table.table.Lock() 159 defer table.table.Unlock() 160 if err := table.table.abort(t); err != nil { 161 return err 162 } 163 } 164 t.State.Store(Aborted) 165 return nil 166 } 167 168 func (t *Table[K, V, R]) abort(tx *Transaction) error { 169 return nil 170 }