go.mercari.io/datastore@v1.8.2/boom/transaction.go (about) 1 package boom 2 3 import ( 4 "reflect" 5 "sync" 6 7 "go.mercari.io/datastore" 8 ) 9 10 // Transaction represents a set of datastore operations to be committed atomically. 11 // 12 // Operations are enqueued by calling the Put and Delete methods on Transaction 13 // (or their Multi-equivalents). These operations are only committed when the 14 // Commit method is invoked. To ensure consistency, reads must be performed by 15 // using Transaction's Get method or by using the Transaction method when 16 // building a query. 17 // 18 // A Transaction must be committed or rolled back exactly once. 19 type Transaction struct { 20 m sync.Mutex 21 bm *Boom 22 tx datastore.Transaction 23 pendingKeysLater []*setKeyLater 24 } 25 26 // DatastoreTransaction returns datastore.Transaction that contains in boom's Transaction. 27 // This function should not be used unless you have a special reason. 28 func DatastoreTransaction(tx *Transaction) datastore.Transaction { 29 return tx.tx 30 } 31 32 type setKeyLater struct { 33 pendingKey datastore.PendingKey 34 src interface{} 35 } 36 37 // Boom object that is the source of the Batch object is returned. 38 func (tx *Transaction) Boom() *Boom { 39 return tx.bm 40 } 41 42 // Kind retrieves kind name from struct. 43 func (tx *Transaction) Kind(src interface{}) string { 44 return tx.bm.Kind(src) 45 } 46 47 // Key retrieves datastore key from struct without error occurred. 48 func (tx *Transaction) Key(src interface{}) datastore.Key { 49 return tx.bm.Key(src) 50 } 51 52 // KeyError retrieves datastore key from struct with error occurred. 53 func (tx *Transaction) KeyError(src interface{}) (datastore.Key, error) { 54 return tx.bm.KeyError(src) 55 } 56 57 // Get loads the entity stored for key into dst, which must be a struct pointer or implement PropertyLoadSaver. 58 // key will be extracted from dst. 59 // 60 // If there is no such entity for the key, Get returns ErrNoSuchEntity. 61 // The values of dst's unmatched struct fields are not modified, and matching slice-typed fields are not reset before appending to them. 62 // In particular, it is recommended to pass a pointer to a zero valued struct on each Get call. 63 func (tx *Transaction) Get(dst interface{}) error { 64 dsts := []interface{}{dst} 65 err := tx.GetMulti(dsts) 66 if merr, ok := err.(datastore.MultiError); ok { 67 return merr[0] 68 } else if err != nil { 69 return err 70 } 71 72 return nil 73 } 74 75 // GetMulti is a batch version of Get. 76 // key will be extracted from each struct of dst. 77 // 78 // dst must be a []S, []*S, []I or []P, for some struct type S, some interface type I, or some non-interface non-pointer type P such that P or *P implements PropertyLoadSaver. 79 // If an []I, each element must be a valid dst for Get: it must be a struct pointer or implement PropertyLoadSaver. 80 func (tx *Transaction) GetMulti(dst interface{}) error { 81 keys, err := tx.bm.extractKeys(dst) 82 if err != nil { 83 return err 84 } 85 86 return tx.tx.GetMulti(keys, dst) 87 } 88 89 // Put saves the entity src into the datastore. 90 // key will be extract from src struct. 91 // src must be a struct pointer or implement PropertyLoadSaver; if a struct pointer then any unexported fields of that struct will be skipped. 92 // If k is an incomplete key, the returned key will be a unique key generated by the datastore, 93 // and inject key to src struct. 94 func (tx *Transaction) Put(src interface{}) (datastore.PendingKey, error) { 95 srcs := []interface{}{src} 96 keys, err := tx.PutMulti(srcs) 97 if merr, ok := err.(datastore.MultiError); ok { 98 return nil, merr[0] 99 } else if err != nil { 100 return nil, err 101 } 102 103 return keys[0], nil 104 } 105 106 // PutMulti is a batch version of Put. 107 // 108 // src must satisfy the same conditions as the dst argument to GetMulti. 109 func (tx *Transaction) PutMulti(src interface{}) ([]datastore.PendingKey, error) { 110 keys, err := tx.bm.extractKeys(src) 111 if err != nil { 112 return nil, err 113 } 114 115 pKeys, err := tx.tx.PutMulti(keys, src) 116 if err != nil { 117 return nil, err 118 } 119 120 v := reflect.Indirect(reflect.ValueOf(src)) 121 tx.m.Lock() 122 defer tx.m.Unlock() 123 for idx, pKey := range pKeys { 124 if !keys[idx].Incomplete() { 125 continue 126 } 127 tx.pendingKeysLater = append(tx.pendingKeysLater, &setKeyLater{ 128 pendingKey: pKey, 129 src: v.Index(idx).Interface(), 130 }) 131 } 132 133 return pKeys, nil 134 } 135 136 // Delete deletes the entity. 137 // key will be extract from src struct. 138 func (tx *Transaction) Delete(src interface{}) error { 139 srcs := []interface{}{src} 140 err := tx.DeleteMulti(srcs) 141 if merr, ok := err.(datastore.MultiError); ok { 142 return merr[0] 143 } else if err != nil { 144 return err 145 } 146 147 return nil 148 } 149 150 // DeleteMulti is a batch version of Delete. 151 func (tx *Transaction) DeleteMulti(src interface{}) error { 152 keys, err := tx.bm.extractKeys(src) 153 if err != nil { 154 return err 155 } 156 157 return tx.tx.DeleteMulti(keys) 158 } 159 160 // Commit applies the enqueued operations atomically. 161 func (tx *Transaction) Commit() (datastore.Commit, error) { 162 commit, err := tx.tx.Commit() 163 if err != nil { 164 return nil, err 165 } 166 167 tx.m.Lock() 168 defer tx.m.Unlock() 169 170 for _, s := range tx.pendingKeysLater { 171 key := commit.Key(s.pendingKey) 172 err = tx.bm.setStructKey(s.src, key) 173 if err != nil { 174 return nil, err 175 } 176 } 177 tx.pendingKeysLater = nil 178 179 return commit, nil 180 } 181 182 // Rollback abandons a pending transaction. 183 func (tx *Transaction) Rollback() error { 184 return tx.tx.Rollback() 185 } 186 187 // Batch creates batch mode objects. 188 func (tx *Transaction) Batch() *TransactionBatch { 189 b := tx.tx.Batch() 190 return &TransactionBatch{bm: tx.bm, tx: tx, b: b} 191 }