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  }