Skip to content

Commit 1682023

Browse files
authored
Ignore duplicate transactions that occur at same time (#31)
1 parent 20d2ab8 commit 1682023

File tree

3 files changed

+46
-4
lines changed

3 files changed

+46
-4
lines changed

models/transactions.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"time"
88

99
ledgerError "github.com/RealImage/QLedger/errors"
10+
"github.com/lib/pq"
1011
"github.com/pkg/errors"
1112
)
1213

@@ -132,6 +133,15 @@ func (t *TransactionDB) Transact(txn *Transaction) bool {
132133

133134
_, err = tx.Exec("INSERT INTO transactions (id, timestamp, data) VALUES ($1, $2, $3)", txn.ID, txn.Timestamp, transactionData)
134135
if err != nil {
136+
// Ignore duplicate transactions and return success response
137+
if err.(*pq.Error).Code.Name() == "unique_violation" {
138+
log.Println("Ignoring duplicate transaction of id:", txn.ID)
139+
err = tx.Rollback()
140+
if err != nil {
141+
log.Println("Error rolling back transaction:", err)
142+
}
143+
return true
144+
}
135145
return handleTransactionError(tx, errors.Wrap(err, "insert transaction failed"))
136146
}
137147

models/transactions_test.go

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"database/sql"
55
"log"
66
"os"
7+
"sync"
78
"testing"
89

910
_ "github.com/lib/pq"
@@ -168,9 +169,40 @@ func (ts *TransactionsModelSuite) TestTransact() {
168169
exists, err := transactionDB.IsExists("t003")
169170
assert.Equal(t, err, nil, "Error while checking for existing transaction")
170171
assert.Equal(t, exists, true, "Transaction should exist")
172+
}
173+
174+
func (ts *TransactionsModelSuite) TestDuplicateTransactions() {
175+
t := ts.T()
171176

172-
done = transactionDB.Transact(transaction)
173-
assert.Equal(t, done, false, "Transaction should not be created")
177+
transactionDB := NewTransactionDB(ts.db)
178+
transaction := &Transaction{
179+
ID: "t005",
180+
Lines: []*TransactionLine{
181+
&TransactionLine{
182+
AccountID: "a1",
183+
Delta: 100,
184+
},
185+
&TransactionLine{
186+
AccountID: "a2",
187+
Delta: -100,
188+
},
189+
},
190+
}
191+
192+
var wg sync.WaitGroup
193+
wg.Add(5)
194+
for i := 1; i <= 5; i++ {
195+
go func(txn *Transaction) {
196+
done := transactionDB.Transact(transaction)
197+
assert.Equal(t, done, true, "Transaction creation should be success")
198+
wg.Done()
199+
}(transaction)
200+
}
201+
wg.Wait()
202+
203+
exists, err := transactionDB.IsExists("t005")
204+
assert.Equal(t, err, nil, "Error while checking for existing transaction")
205+
assert.Equal(t, exists, true, "Transaction should exist")
174206
}
175207

176208
func (ts *TransactionsModelSuite) TestTransactWithBoundaryValues() {

tests/transactions_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,8 @@ func RunCSVTests(accountsEndpoint string, transactionsEndpoint string, filename
9393
localwg.Done()
9494
}()
9595
localwg.Wait()
96-
if status1 == http.StatusCreated && status2 == http.StatusCreated {
97-
log.Fatalf("Parallel repeated transactions with same ID %v are accepted", t["id"])
96+
if (status1 != http.StatusCreated && status1 != http.StatusAccepted) || (status2 != http.StatusCreated && status2 != http.StatusAccepted) {
97+
log.Fatalf("Parallel repeated transactions with same ID %v are not accepted", t["id"])
9898
} else if status1 >= 400 && status2 >= 400 {
9999
log.Fatalf("Both parallel repeated transactions with same ID %v are failed", t["id"])
100100
}

0 commit comments

Comments
 (0)