github.com/hoffie/larasync@v0.0.0-20151025221940-0384d2bddcef/repository/transactionManager.go (about)

     1  package repository
     2  
     3  import (
     4  	"sync"
     5  
     6  	"github.com/hoffie/larasync/helpers/lock"
     7  	"github.com/hoffie/larasync/repository/content"
     8  )
     9  
    10  const transactionsInContainer int = 100
    11  
    12  func reverseTransactionSlice(slice []*Transaction) []*Transaction {
    13  	for i := 0; i < len(slice)/2; i++ {
    14  		slice[i], slice[len(slice)-1-i] = slice[len(slice)-1-i], slice[i]
    15  	}
    16  	return slice
    17  }
    18  
    19  // TransactionManager is used to query and add data written
    20  // in the server transaction log.
    21  type TransactionManager struct {
    22  	manager *TransactionContainerManager
    23  	lock    sync.Locker
    24  }
    25  
    26  // newTransactionManager initializes a new transaction manager
    27  // with the given storage as a backend.
    28  func newTransactionManager(storage content.Storage, lockingPath string) *TransactionManager {
    29  	manager := newTransactionContainerManager(storage, lockingPath)
    30  	return &TransactionManager{
    31  		manager: manager,
    32  		lock: lock.CurrentManager().Get(
    33  			lockingPath,
    34  			"transaction_manager",
    35  		),
    36  	}
    37  }
    38  
    39  // CurrentTransactionID returns the most recent ID stored in the
    40  // backend.
    41  func (tm *TransactionManager) CurrentTransactionID() (int64, error) {
    42  	newestTransaction, err := tm.CurrentTransaction()
    43  	if err != nil {
    44  		return 0, err
    45  	}
    46  	return newestTransaction.ID, nil
    47  }
    48  
    49  // CurrentTransaction returns the most recent Transaction which is stored
    50  // in the TransactionLog.
    51  func (tm *TransactionManager) CurrentTransaction() (*Transaction, error) {
    52  	currentTransactionContainer, err := tm.manager.CurrentTransactionContainer()
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  
    57  	transactionsLength := len(currentTransactionContainer.Transactions)
    58  	if transactionsLength == 0 {
    59  		// Empty transactions. No current UUID.
    60  		return nil, ErrTransactionNotExists
    61  	}
    62  
    63  	transactions := currentTransactionContainer.Transactions
    64  	newestTransaction := transactions[transactionsLength-1]
    65  	return newestTransaction, nil
    66  }
    67  
    68  // Add adds the given transaction to the storage.
    69  func (tm *TransactionManager) Add(transaction *Transaction) error {
    70  	locker := tm.lock
    71  
    72  	locker.Lock()
    73  	err := func() error {
    74  		manager := tm.manager
    75  		transactionContainer, err := manager.CurrentTransactionContainer()
    76  		if err != nil {
    77  			return err
    78  		}
    79  
    80  		var previousID int64
    81  		if len(transactionContainer.Transactions) > 0 {
    82  			latestIndex := len(transactionContainer.Transactions) - 1
    83  			previousID = transactionContainer.Transactions[latestIndex].ID
    84  		}
    85  
    86  		if len(transactionContainer.Transactions) >= transactionsInContainer {
    87  			transactionContainer, err = manager.NewContainer()
    88  			if err != nil {
    89  				return err
    90  			}
    91  		}
    92  
    93  		transaction.PreviousID = previousID
    94  		if transaction.ID == 0 {
    95  			transaction.ID = previousID + 1
    96  		}
    97  
    98  		transactionContainer.Transactions = append(
    99  			transactionContainer.Transactions,
   100  			transaction,
   101  		)
   102  		return manager.Set(transactionContainer)
   103  	}()
   104  	locker.Unlock()
   105  	return err
   106  }
   107  
   108  // Get returns the transaction with the given UUID.
   109  func (tm *TransactionManager) Get(transactionID int64) (*Transaction, error) {
   110  	manager := tm.manager
   111  	currentTransactionContainer, err := manager.CurrentTransactionContainer()
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  
   116  	transactionContainer := currentTransactionContainer
   117  	for transactionContainer != nil {
   118  		for _, transaction := range transactionContainer.Transactions {
   119  			if transaction.ID == transactionID {
   120  				return transaction, nil
   121  			}
   122  		}
   123  
   124  		if transactionContainer.PreviousUUID == "" {
   125  			transactionContainer = nil
   126  		} else {
   127  			transactionContainer, err = manager.Get(
   128  				transactionContainer.PreviousUUID)
   129  			if err != nil {
   130  				return nil, err
   131  			}
   132  		}
   133  	}
   134  	return nil, ErrTransactionNotExists
   135  
   136  }
   137  
   138  // From returns all transactions from the given transactionUUID. It does not include
   139  // the transaction of the given transactionUUID.
   140  func (tm *TransactionManager) From(transactionID int64) ([]*Transaction, error) {
   141  	manager := tm.manager
   142  	currentTransactionContainer, err := manager.CurrentTransactionContainer()
   143  	if err != nil {
   144  		return nil, err
   145  	}
   146  
   147  	transactions := [][]*Transaction{}
   148  	transactionContainer := currentTransactionContainer
   149  	found := false
   150  	for transactionContainer != nil {
   151  		foundTransactions := []*Transaction{}
   152  		for _, transaction := range transactionContainer.Transactions {
   153  			if found {
   154  				foundTransactions = append(foundTransactions, transaction)
   155  			}
   156  
   157  			if transaction.ID == transactionID {
   158  				found = true
   159  			}
   160  		}
   161  		if found {
   162  			transactions = append(transactions, foundTransactions)
   163  			break
   164  		}
   165  
   166  		transactions = append(transactions, transactionContainer.Transactions)
   167  
   168  		if transactionContainer.PreviousUUID == "" {
   169  			transactionContainer = nil
   170  		} else {
   171  			transactionContainer, err = manager.Get(
   172  				transactionContainer.PreviousUUID)
   173  			if err != nil {
   174  				return nil, err
   175  			}
   176  		}
   177  	}
   178  
   179  	returnTransactions := []*Transaction{}
   180  	for i := len(transactions) - 1; i >= 0; i-- {
   181  		returnTransactions = append(returnTransactions, transactions[i]...)
   182  	}
   183  
   184  	return returnTransactions, nil
   185  }
   186  
   187  // All returns all transactions in the system.
   188  func (tm *TransactionManager) All() ([]*Transaction, error) {
   189  	return tm.From(0)
   190  }
   191  
   192  // Exists checks if the given Transaction UUID exists in this repository.
   193  func (tm *TransactionManager) Exists(transactionID int64) bool {
   194  	_, err := tm.Get(transactionID)
   195  	if err != nil {
   196  		return false
   197  	}
   198  	return true
   199  }