304 lines
8.4 KiB
Go
304 lines
8.4 KiB
Go
|
package activity
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"fmt"
|
||
|
"testing"
|
||
|
|
||
|
eth "github.com/ethereum/go-ethereum/common"
|
||
|
"github.com/status-im/status-go/services/wallet/common"
|
||
|
"github.com/status-im/status-go/services/wallet/testutils"
|
||
|
"github.com/status-im/status-go/services/wallet/transfer"
|
||
|
)
|
||
|
|
||
|
func setupBenchmark(b *testing.B, accountsCount int, inMemory bool) (deps FilterDependencies, close func(), accounts []eth.Address) {
|
||
|
deps, close = setupTestActivityDBStorageChoice(b, inMemory)
|
||
|
|
||
|
const transactionCount = 100000
|
||
|
const mtSendRatio = 0.2 // 20%
|
||
|
const mtSwapRatio = 0.1 // 10%
|
||
|
const mtBridgeRatio = 0.1 // 10%
|
||
|
const pendingCount = 10
|
||
|
const mtSendCount = int(float64(transactionCount) * mtSendRatio)
|
||
|
const mtSwapCount = int(float64(transactionCount) * mtSwapRatio)
|
||
|
// Bridge requires two transactions
|
||
|
const mtBridgeCount = int(float64(transactionCount) * (mtBridgeRatio / 2))
|
||
|
|
||
|
trs, _, _ := transfer.GenerateTestTransfers(b, deps.db, 0, transactionCount)
|
||
|
|
||
|
accounts = []eth.Address{}
|
||
|
for i := 0; i < accountsCount; i++ {
|
||
|
if i%2 == 0 {
|
||
|
accounts = append(accounts, trs[i].From)
|
||
|
} else {
|
||
|
accounts = append(accounts, trs[i].To)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
i := 0
|
||
|
multiTxs := make([]transfer.TestMultiTransaction, mtSendCount+mtSwapCount+mtBridgeCount)
|
||
|
for ; i < mtSendCount; i++ {
|
||
|
multiTxs[i] = transfer.GenerateTestSendMultiTransaction(trs[i])
|
||
|
trs[i].From = accounts[i%len(accounts)]
|
||
|
multiTxs[i].FromAddress = trs[i].From
|
||
|
// Currently the network ID is not filled in for send transactions
|
||
|
multiTxs[i].FromNetworkID = nil
|
||
|
multiTxs[i].ToNetworkID = nil
|
||
|
|
||
|
multiTxs[i].MultiTransactionID = transfer.InsertTestMultiTransaction(b, deps.db, &multiTxs[i])
|
||
|
trs[i].MultiTransactionID = multiTxs[i].MultiTransactionID
|
||
|
}
|
||
|
|
||
|
for j := 0; j < mtSwapCount; i, j = i+1, j+1 {
|
||
|
multiTxs[i] = transfer.GenerateTestSwapMultiTransaction(trs[i], testutils.SntSymbol, int64(i))
|
||
|
trs[i].From = accounts[i%len(accounts)]
|
||
|
multiTxs[i].FromAddress = trs[i].From
|
||
|
|
||
|
multiTxs[i].MultiTransactionID = transfer.InsertTestMultiTransaction(b, deps.db, &multiTxs[i])
|
||
|
trs[i].MultiTransactionID = multiTxs[i].MultiTransactionID
|
||
|
}
|
||
|
|
||
|
for mtIdx := 0; mtIdx < mtBridgeCount; i, mtIdx = i+2, mtIdx+1 {
|
||
|
firstTrIdx := i
|
||
|
secondTrIdx := i + 1
|
||
|
multiTxs[mtIdx] = transfer.GenerateTestBridgeMultiTransaction(trs[firstTrIdx], trs[secondTrIdx])
|
||
|
trs[firstTrIdx].From = accounts[i%len(accounts)]
|
||
|
trs[secondTrIdx].To = accounts[(i+3)%len(accounts)]
|
||
|
multiTxs[mtIdx].FromAddress = trs[firstTrIdx].From
|
||
|
multiTxs[mtIdx].ToAddress = trs[secondTrIdx].To
|
||
|
multiTxs[mtIdx].FromAddress = trs[i].From
|
||
|
|
||
|
multiTxs[mtIdx].MultiTransactionID = transfer.InsertTestMultiTransaction(b, deps.db, &multiTxs[mtIdx])
|
||
|
trs[firstTrIdx].MultiTransactionID = multiTxs[mtIdx].MultiTransactionID
|
||
|
trs[secondTrIdx].MultiTransactionID = multiTxs[mtIdx].MultiTransactionID
|
||
|
}
|
||
|
|
||
|
for i = 0; i < transactionCount-pendingCount; i++ {
|
||
|
trs[i].From = accounts[i%len(accounts)]
|
||
|
transfer.InsertTestTransfer(b, deps.db, trs[i].From, &trs[i])
|
||
|
}
|
||
|
|
||
|
for ; i < transactionCount; i++ {
|
||
|
trs[i].From = accounts[i%len(accounts)]
|
||
|
transfer.InsertTestPendingTransaction(b, deps.db, &trs[i])
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
var allNetEnabled = []common.ChainID(nil)
|
||
|
|
||
|
func BenchmarkGetActivityEntries(bArg *testing.B) {
|
||
|
deps, closeFn, accounts := setupBenchmark(bArg, 6, true)
|
||
|
defer closeFn()
|
||
|
|
||
|
type params struct {
|
||
|
inMemory bool
|
||
|
// resultCount must be nil to expect as many requested
|
||
|
resultCount *int
|
||
|
generateTestParameters func() (addresses []eth.Address, allAddresses bool, networks []common.ChainID, filter *Filter, startIndex int)
|
||
|
}
|
||
|
testCases := []struct {
|
||
|
name string
|
||
|
params params
|
||
|
}{
|
||
|
{
|
||
|
"RAM_NoFilter",
|
||
|
params{
|
||
|
true,
|
||
|
nil,
|
||
|
func() ([]eth.Address, bool, []common.ChainID, *Filter, int) {
|
||
|
return accounts, true, allNetEnabled, &Filter{}, 0
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
"SSD_NoFilter",
|
||
|
params{
|
||
|
false,
|
||
|
nil,
|
||
|
func() ([]eth.Address, bool, []common.ChainID, *Filter, int) {
|
||
|
return accounts, true, allNetEnabled, &Filter{}, 0
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
"SSD_MovingWindow",
|
||
|
params{
|
||
|
false,
|
||
|
nil,
|
||
|
func() ([]eth.Address, bool, []common.ChainID, *Filter, int) {
|
||
|
return accounts, true, allNetEnabled, &Filter{}, 200
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
"SSD_AllAddr_AllTos",
|
||
|
params{
|
||
|
false,
|
||
|
nil,
|
||
|
func() ([]eth.Address, bool, []common.ChainID, *Filter, int) {
|
||
|
return accounts, true, allNetEnabled, &Filter{CounterpartyAddresses: accounts[3:]}, 0
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
"SSD_OneAddress",
|
||
|
params{
|
||
|
false,
|
||
|
nil,
|
||
|
func() ([]eth.Address, bool, []common.ChainID, *Filter, int) {
|
||
|
return accounts[0:1], false, allNetEnabled, &Filter{}, 0
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
// All memory from here
|
||
|
{
|
||
|
"FilterSend_AllAddr",
|
||
|
params{
|
||
|
true,
|
||
|
nil,
|
||
|
func() ([]eth.Address, bool, []common.ChainID, *Filter, int) {
|
||
|
return accounts, true, allNetEnabled, &Filter{
|
||
|
Types: []Type{SendAT},
|
||
|
}, 0
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
"FilterSend_6Addr",
|
||
|
params{
|
||
|
true,
|
||
|
nil,
|
||
|
func() ([]eth.Address, bool, []common.ChainID, *Filter, int) {
|
||
|
return accounts[len(accounts)-6:], false, allNetEnabled, &Filter{
|
||
|
Types: []Type{SendAT},
|
||
|
}, 0
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
"FilterThreeNetworks",
|
||
|
params{
|
||
|
true,
|
||
|
nil,
|
||
|
func() ([]eth.Address, bool, []common.ChainID, *Filter, int) {
|
||
|
return accounts, true, []common.ChainID{}, &Filter{}, 0
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
const resultCount = 100
|
||
|
for _, tc := range testCases {
|
||
|
addresses, allAddresses, nets, filter, startIndex := tc.params.generateTestParameters()
|
||
|
networks := allNetworksFilter()
|
||
|
if len(nets) > 0 {
|
||
|
networks = nets
|
||
|
}
|
||
|
|
||
|
bArg.Run(tc.name, func(b *testing.B) {
|
||
|
// Reset timer after setup
|
||
|
b.ResetTimer()
|
||
|
|
||
|
// Run benchmark
|
||
|
for i := 0; i < b.N; i++ {
|
||
|
res, err := getActivityEntries(context.Background(), deps, addresses, allAddresses, networks, *filter, startIndex, resultCount)
|
||
|
if err != nil {
|
||
|
b.Error(err)
|
||
|
} else if tc.params.resultCount != nil {
|
||
|
if len(res) != *tc.params.resultCount {
|
||
|
b.Error("Got less then expected")
|
||
|
}
|
||
|
} else if len(res) != resultCount {
|
||
|
b.Error("Got less than requested")
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func BenchmarkSQLQuery(b *testing.B) {
|
||
|
type params struct {
|
||
|
query string
|
||
|
args []interface{}
|
||
|
expectedResCount int
|
||
|
}
|
||
|
|
||
|
deps, closeFn, accounts := setupBenchmark(b, 10000, true)
|
||
|
defer closeFn()
|
||
|
|
||
|
var addrValuesStr, insertAddrValuesStr, addrPlaceholdersStr string
|
||
|
var refAccounts []interface{}
|
||
|
for _, acc := range accounts {
|
||
|
addrValuesStr += fmt.Sprintf("X'%s',", acc.Hex()[2:])
|
||
|
insertAddrValuesStr += fmt.Sprintf("(X'%s'),", acc.Hex()[2:])
|
||
|
addrPlaceholdersStr += "?,"
|
||
|
refAccounts = append(refAccounts, acc)
|
||
|
}
|
||
|
addrValuesStr = addrValuesStr[:len(addrValuesStr)-1]
|
||
|
insertAddrValuesStr = insertAddrValuesStr[:len(insertAddrValuesStr)-1]
|
||
|
addrPlaceholdersStr = addrPlaceholdersStr[:len(addrPlaceholdersStr)-1]
|
||
|
|
||
|
if _, err := deps.db.Exec(fmt.Sprintf("CREATE TEMP TABLE filter_addresses_table (address VARCHAR PRIMARY KEY); INSERT INTO filter_addresses_table (address) VALUES %s;", insertAddrValuesStr)); err != nil {
|
||
|
b.Fatal("failed to create temporary table", err)
|
||
|
}
|
||
|
|
||
|
testCases := []struct {
|
||
|
name string
|
||
|
args params
|
||
|
res testing.BenchmarkResult
|
||
|
}{
|
||
|
{
|
||
|
name: "JoinQuery",
|
||
|
args: params{
|
||
|
query: "SELECT COUNT(*) FROM transfers JOIN filter_addresses_table ON transfers.tx_from_address = filter_addresses_table.address",
|
||
|
expectedResCount: 99990,
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "LiteralQuery",
|
||
|
args: params{
|
||
|
query: fmt.Sprintf("SELECT COUNT(*) FROM transfers WHERE tx_from_address IN (%s)", addrValuesStr),
|
||
|
expectedResCount: 99990,
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "ParamQuery",
|
||
|
args: params{
|
||
|
query: fmt.Sprintf("SELECT COUNT(*) FROM transfers WHERE tx_from_address IN (%s)", addrPlaceholdersStr),
|
||
|
args: refAccounts,
|
||
|
expectedResCount: 99990,
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for _, tc := range testCases {
|
||
|
b.Run(tc.name, func(b *testing.B) {
|
||
|
b.ResetTimer()
|
||
|
|
||
|
for i := 0; i < b.N; i++ {
|
||
|
b.ResetTimer()
|
||
|
|
||
|
for i := 0; i < b.N; i++ {
|
||
|
res, err := deps.db.Query(tc.args.query, tc.args.args...)
|
||
|
if err != nil {
|
||
|
b.Fatal("failed to query db", err)
|
||
|
}
|
||
|
res.Next()
|
||
|
|
||
|
var count int
|
||
|
if err := res.Scan(&count); err != nil {
|
||
|
b.Fatal("failed to scan db result", err)
|
||
|
}
|
||
|
if count != tc.args.expectedResCount {
|
||
|
b.Fatalf("unexpected result count: %d, expected: %d", count, tc.args.expectedResCount)
|
||
|
}
|
||
|
|
||
|
res.Close()
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
}
|