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 }