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  }