go.mercari.io/datastore@v1.8.2/tx_batch.go (about) 1 package datastore 2 3 import ( 4 "sync" 5 ) 6 7 // TransactionBatch provides Batch operation under Transaction. 8 // TransactionBatch does nothing until you call Exec(). 9 // This helps to reduce the number of RPCs. 10 type TransactionBatch struct { 11 Transaction Transaction 12 13 put txBatchPut 14 get txBatchGet 15 delete txBatchDelete 16 } 17 18 // TxBatchPutHandler represents Entity's individual callback when batching Put with transaction processing. 19 type TxBatchPutHandler func(pKey PendingKey, err error) error 20 21 type txBatchPut struct { 22 m sync.Mutex 23 keys []Key 24 srcs []interface{} 25 hs []TxBatchPutHandler 26 } 27 28 type txBatchGet struct { 29 m sync.Mutex 30 keys []Key 31 dsts []interface{} 32 hs []BatchErrHandler 33 } 34 35 type txBatchDelete struct { 36 m sync.Mutex 37 keys []Key 38 hs []BatchErrHandler 39 } 40 41 // Put Entity operation into the queue. 42 // This operation doesn't Put to Datastore immediately. 43 // If a h is provided, it passes the processing result to the handler, and treats the return value as the value of the result of Putting. 44 func (b *TransactionBatch) Put(key Key, src interface{}, h TxBatchPutHandler) { 45 b.put.Put(key, src, h) 46 } 47 48 // Get Entity operation into the queue. 49 func (b *TransactionBatch) Get(key Key, dst interface{}, h BatchErrHandler) { 50 b.get.Get(key, dst, h) 51 } 52 53 // Delete Entity operation into the queue. 54 func (b *TransactionBatch) Delete(key Key, h BatchErrHandler) { 55 b.delete.Delete(key, h) 56 } 57 58 // Exec will perform all the processing that was queued. 59 // This process is done recursively until the queue is empty. 60 // The return value may be MultiError, but the order of contents is not guaranteed. 61 // Exec() doesn't call Commit() or Rollback(), You should call that manually. 62 func (b *TransactionBatch) Exec() error { 63 var wg sync.WaitGroup 64 var errors []error 65 var m sync.Mutex 66 wg.Add(3) 67 68 go func() { 69 defer wg.Done() 70 errs := b.put.Exec(b.Transaction) 71 if len(errs) != 0 { 72 m.Lock() 73 errors = append(errors, errs...) 74 m.Unlock() 75 } 76 }() 77 go func() { 78 defer wg.Done() 79 errs := b.get.Exec(b.Transaction) 80 if len(errs) != 0 { 81 m.Lock() 82 errors = append(errors, errs...) 83 m.Unlock() 84 } 85 }() 86 go func() { 87 defer wg.Done() 88 errs := b.delete.Exec(b.Transaction) 89 if len(errs) != 0 { 90 m.Lock() 91 errors = append(errors, errs...) 92 m.Unlock() 93 } 94 }() 95 96 wg.Wait() 97 98 if len(errors) != 0 { 99 return MultiError(errors) 100 } 101 102 // Batch操作した後PropertyLoadSaverなどで追加のBatch操作が積まれたらそれがなくなるまで処理する 103 if len(b.put.keys) != 0 || len(b.get.keys) != 0 || len(b.delete.keys) != 0 { 104 return b.Exec() 105 } 106 107 return nil 108 } 109 110 func (b *txBatchPut) Put(key Key, src interface{}, h TxBatchPutHandler) { 111 b.m.Lock() 112 defer b.m.Unlock() 113 114 b.keys = append(b.keys, key) 115 b.srcs = append(b.srcs, src) 116 b.hs = append(b.hs, h) 117 } 118 119 func (b *txBatchPut) Exec(tx Transaction) []error { 120 if len(b.keys) == 0 { 121 return nil 122 } 123 124 b.m.Lock() 125 defer func() { 126 b.keys = nil 127 b.srcs = nil 128 b.hs = nil 129 }() 130 defer b.m.Unlock() 131 132 newPendingKeys, err := tx.PutMulti(b.keys, b.srcs) 133 134 if merr, ok := err.(MultiError); ok { 135 trimmedError := make([]error, 0, len(merr)) 136 for idx, err := range merr { 137 h := b.hs[idx] 138 if h != nil { 139 err = h(newPendingKeys[idx], err) 140 } 141 if err != nil { 142 trimmedError = append(trimmedError, err) 143 } 144 } 145 return trimmedError 146 } else if err != nil { 147 for _, h := range b.hs { 148 if h != nil { 149 h(nil, err) 150 } 151 } 152 return []error{err} 153 } 154 155 errs := make([]error, 0, len(newPendingKeys)) 156 for idx, newKey := range newPendingKeys { 157 h := b.hs[idx] 158 if h != nil { 159 err := h(newKey, nil) 160 if err != nil { 161 errs = append(errs, err) 162 } 163 } 164 } 165 166 if len(errs) != 0 { 167 return errs 168 } 169 170 return nil 171 } 172 173 func (b *txBatchGet) Get(key Key, dst interface{}, h BatchErrHandler) { 174 b.m.Lock() 175 defer b.m.Unlock() 176 177 b.keys = append(b.keys, key) 178 b.dsts = append(b.dsts, dst) 179 b.hs = append(b.hs, h) 180 } 181 182 func (b *txBatchGet) Exec(tx Transaction) []error { 183 if len(b.keys) == 0 { 184 return nil 185 } 186 187 b.m.Lock() 188 defer func() { 189 b.keys = nil 190 b.dsts = nil 191 b.hs = nil 192 }() 193 defer b.m.Unlock() 194 195 err := tx.GetMulti(b.keys, b.dsts) 196 197 if merr, ok := err.(MultiError); ok { 198 trimmedError := make([]error, 0, len(merr)) 199 for idx, err := range merr { 200 h := b.hs[idx] 201 if h != nil { 202 err = h(err) 203 } 204 if err != nil { 205 trimmedError = append(trimmedError, err) 206 } 207 } 208 return trimmedError 209 } else if err != nil { 210 for _, h := range b.hs { 211 if h != nil { 212 h(err) 213 } 214 } 215 return []error{err} 216 } 217 218 errs := make([]error, 0, len(b.hs)) 219 for _, h := range b.hs { 220 if h != nil { 221 err := h(nil) 222 if err != nil { 223 errs = append(errs, err) 224 } 225 } 226 } 227 228 if len(errs) != 0 { 229 return errs 230 } 231 232 return nil 233 } 234 235 func (b *txBatchDelete) Delete(key Key, h BatchErrHandler) { 236 b.m.Lock() 237 defer b.m.Unlock() 238 239 b.keys = append(b.keys, key) 240 b.hs = append(b.hs, h) 241 } 242 243 func (b *txBatchDelete) Exec(tx Transaction) []error { 244 if len(b.keys) == 0 { 245 return nil 246 } 247 248 b.m.Lock() 249 defer func() { 250 b.keys = nil 251 b.hs = nil 252 }() 253 defer b.m.Unlock() 254 255 err := tx.DeleteMulti(b.keys) 256 257 if merr, ok := err.(MultiError); ok { 258 trimmedError := make([]error, 0, len(merr)) 259 for idx, err := range merr { 260 h := b.hs[idx] 261 if h != nil { 262 err = h(err) 263 } 264 if err != nil { 265 trimmedError = append(trimmedError, err) 266 } 267 } 268 return trimmedError 269 } else if err != nil { 270 for _, h := range b.hs { 271 if h != nil { 272 h(err) 273 } 274 } 275 return []error{err} 276 } 277 278 errs := make([]error, 0, len(b.hs)) 279 for _, h := range b.hs { 280 if h != nil { 281 err := h(nil) 282 if err != nil { 283 errs = append(errs, err) 284 } 285 } 286 } 287 288 if len(errs) != 0 { 289 return errs 290 } 291 292 return nil 293 }