github.com/matrixorigin/matrixone@v1.2.0/pkg/incrservice/allocator.go (about) 1 // Copyright 2023 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 incrservice 16 17 import ( 18 "context" 19 "sync/atomic" 20 "time" 21 22 "github.com/matrixorigin/matrixone/pkg/common/log" 23 "github.com/matrixorigin/matrixone/pkg/common/stopper" 24 "github.com/matrixorigin/matrixone/pkg/defines" 25 "github.com/matrixorigin/matrixone/pkg/txn/client" 26 "go.uber.org/zap" 27 ) 28 29 type allocator struct { 30 logger *log.MOLogger 31 store IncrValueStore 32 c chan action 33 stopper *stopper.Stopper 34 } 35 36 func newValueAllocator(store IncrValueStore) valueAllocator { 37 a := &allocator{ 38 logger: getLogger(), 39 c: make(chan action, 1024), 40 stopper: stopper.NewStopper("valueAllocator"), 41 store: store, 42 } 43 a.adjust() 44 a.stopper.RunTask(a.run) 45 return a 46 } 47 48 func (a *allocator) adjust() { 49 if a.store == nil { 50 a.store = NewMemStore() 51 } 52 } 53 54 func (a *allocator) allocate( 55 ctx context.Context, 56 tableID uint64, 57 key string, 58 count int, 59 txnOp client.TxnOperator) (uint64, uint64, error) { 60 c := make(chan struct{}) 61 //UT test find race here 62 var from, to atomic.Uint64 63 var err error 64 var err2 atomic.Value 65 err = a.asyncAllocate( 66 ctx, 67 tableID, 68 key, 69 count, 70 txnOp, 71 func( 72 v1, v2 uint64, 73 e error) { 74 from.Store(v1) 75 to.Store(v2) 76 if e != nil { 77 err2.Store(e) 78 } 79 close(c) 80 }) 81 if err2.Load() != nil && err2.Load().(error) != nil { 82 err = err2.Load().(error) 83 } 84 if err != nil { 85 return 0, 0, err 86 } 87 <-c 88 return from.Load(), to.Load(), err 89 } 90 91 func (a *allocator) asyncAllocate( 92 ctx context.Context, 93 tableID uint64, 94 col string, 95 count int, 96 txnOp client.TxnOperator, 97 apply func(uint64, uint64, error)) error { 98 accountId, err := getAccountID(ctx) 99 if err != nil { 100 return err 101 } 102 a.c <- action{ 103 txnOp: txnOp, 104 accountID: accountId, 105 actionType: allocType, 106 tableID: tableID, 107 col: col, 108 count: count, 109 applyAllocate: apply} 110 return nil 111 } 112 113 func (a *allocator) updateMinValue( 114 ctx context.Context, 115 tableID uint64, 116 col string, 117 minValue uint64, 118 txnOp client.TxnOperator) error { 119 var err error 120 var accountId uint32 121 accountId, err = getAccountID(ctx) 122 if err != nil { 123 return err 124 } 125 c := make(chan struct{}) 126 fn := func(e error) { 127 err = e 128 close(c) 129 } 130 a.c <- action{ 131 txnOp: txnOp, 132 accountID: accountId, 133 actionType: updateType, 134 tableID: tableID, 135 col: col, 136 minValue: minValue, 137 applyUpdate: fn, 138 } 139 <-c 140 return err 141 } 142 143 func (a *allocator) run(ctx context.Context) { 144 for { 145 select { 146 case <-ctx.Done(): 147 return 148 case act := <-a.c: 149 switch act.actionType { 150 case allocType: 151 a.doAllocate(act) 152 case updateType: 153 a.doUpdate(act) 154 } 155 } 156 } 157 } 158 159 func (a *allocator) doAllocate(act action) { 160 ctx := defines.AttachAccountId(context.Background(), act.accountID) 161 ctx, cancel := context.WithTimeout(ctx, time.Second*10) 162 defer cancel() 163 164 from, to, err := a.store.Allocate( 165 ctx, 166 act.tableID, 167 act.col, 168 act.count, 169 act.txnOp) 170 if a.logger.Enabled(zap.DebugLevel) { 171 a.logger.Debug( 172 "allocate new range", 173 zap.String("key", act.col), 174 zap.Int("count", act.count), 175 zap.Uint64("value", from), 176 zap.Uint64("next", to), 177 zap.Error(err)) 178 } 179 180 act.applyAllocate(from, to, err) 181 } 182 183 func (a *allocator) doUpdate(act action) { 184 ctx := defines.AttachAccountId(context.Background(), act.accountID) 185 ctx, cancel := context.WithTimeout(ctx, time.Second*10) 186 defer cancel() 187 188 err := a.store.UpdateMinValue( 189 ctx, 190 act.tableID, 191 act.col, 192 act.minValue, 193 act.txnOp) 194 if a.logger.Enabled(zap.DebugLevel) { 195 a.logger.Debug( 196 "update range min value", 197 zap.String("key", act.col), 198 zap.Int("count", act.count), 199 zap.Uint64("min-value", act.minValue), 200 zap.Error(err)) 201 } 202 act.applyUpdate(err) 203 } 204 205 func (a *allocator) close() { 206 a.stopper.Stop() 207 close(a.c) 208 } 209 210 var ( 211 allocType = 0 212 updateType = 1 213 ) 214 215 type action struct { 216 txnOp client.TxnOperator 217 accountID uint32 218 actionType int 219 tableID uint64 220 col string 221 count int 222 minValue uint64 223 applyAllocate func(uint64, uint64, error) 224 applyUpdate func(error) 225 } 226 227 func getAccountID(ctx context.Context) (uint32, error) { 228 return defines.GetAccountId(ctx) 229 }