github.com/status-im/status-go@v1.1.0/services/wallet/activity/benchmarks_test.go (about)

     1  package activity
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"testing"
     7  
     8  	eth "github.com/ethereum/go-ethereum/common"
     9  	"github.com/status-im/status-go/services/wallet/common"
    10  	"github.com/status-im/status-go/services/wallet/testutils"
    11  	"github.com/status-im/status-go/services/wallet/transfer"
    12  )
    13  
    14  func setupBenchmark(b *testing.B, accountsCount int, inMemory bool) (deps FilterDependencies, close func(), accounts []eth.Address) {
    15  	deps, close = setupTestActivityDBStorageChoice(b, inMemory)
    16  
    17  	const transactionCount = 100000
    18  	const mtSendRatio = 0.2   // 20%
    19  	const mtSwapRatio = 0.1   // 10%
    20  	const mtBridgeRatio = 0.1 // 10%
    21  	const pendingCount = 10
    22  	const mtSendCount = int(float64(transactionCount) * mtSendRatio)
    23  	const mtSwapCount = int(float64(transactionCount) * mtSwapRatio)
    24  	// Bridge requires two transactions
    25  	const mtBridgeCount = int(float64(transactionCount) * (mtBridgeRatio / 2))
    26  
    27  	trs, _, _ := transfer.GenerateTestTransfers(b, deps.db, 0, transactionCount)
    28  
    29  	accounts = []eth.Address{}
    30  	for i := 0; i < accountsCount; i++ {
    31  		if i%2 == 0 {
    32  			accounts = append(accounts, trs[i].From)
    33  		} else {
    34  			accounts = append(accounts, trs[i].To)
    35  		}
    36  	}
    37  
    38  	i := 0
    39  	multiTxs := make([]transfer.MultiTransaction, mtSendCount+mtSwapCount+mtBridgeCount)
    40  	for ; i < mtSendCount; i++ {
    41  		multiTxs[i] = transfer.GenerateTestSendMultiTransaction(trs[i])
    42  		trs[i].From = accounts[i%len(accounts)]
    43  		multiTxs[i].FromAddress = trs[i].From
    44  		// Currently the network ID is not filled in for send transactions
    45  		multiTxs[i].FromNetworkID = 0
    46  		multiTxs[i].ToNetworkID = 0
    47  
    48  		multiTxs[i].ID = transfer.InsertTestMultiTransaction(b, deps.db, &multiTxs[i])
    49  		trs[i].MultiTransactionID = multiTxs[i].ID
    50  	}
    51  
    52  	for j := 0; j < mtSwapCount; i, j = i+1, j+1 {
    53  		multiTxs[i] = transfer.GenerateTestSwapMultiTransaction(trs[i], testutils.SntSymbol, int64(i))
    54  		trs[i].From = accounts[i%len(accounts)]
    55  		multiTxs[i].FromAddress = trs[i].From
    56  
    57  		multiTxs[i].ID = transfer.InsertTestMultiTransaction(b, deps.db, &multiTxs[i])
    58  		trs[i].MultiTransactionID = multiTxs[i].ID
    59  	}
    60  
    61  	for mtIdx := 0; mtIdx < mtBridgeCount; i, mtIdx = i+2, mtIdx+1 {
    62  		firstTrIdx := i
    63  		secondTrIdx := i + 1
    64  		multiTxs[mtIdx] = transfer.GenerateTestBridgeMultiTransaction(trs[firstTrIdx], trs[secondTrIdx])
    65  		trs[firstTrIdx].From = accounts[i%len(accounts)]
    66  		trs[secondTrIdx].To = accounts[(i+3)%len(accounts)]
    67  		multiTxs[mtIdx].FromAddress = trs[firstTrIdx].From
    68  		multiTxs[mtIdx].ToAddress = trs[secondTrIdx].To
    69  		multiTxs[mtIdx].FromAddress = trs[i].From
    70  
    71  		multiTxs[mtIdx].ID = transfer.InsertTestMultiTransaction(b, deps.db, &multiTxs[mtIdx])
    72  		trs[firstTrIdx].MultiTransactionID = multiTxs[mtIdx].ID
    73  		trs[secondTrIdx].MultiTransactionID = multiTxs[mtIdx].ID
    74  	}
    75  
    76  	for i = 0; i < transactionCount-pendingCount; i++ {
    77  		trs[i].From = accounts[i%len(accounts)]
    78  		transfer.InsertTestTransfer(b, deps.db, trs[i].From, &trs[i])
    79  	}
    80  
    81  	for ; i < transactionCount; i++ {
    82  		trs[i].From = accounts[i%len(accounts)]
    83  		transfer.InsertTestPendingTransaction(b, deps.db, &trs[i])
    84  	}
    85  
    86  	return
    87  }
    88  
    89  var allNetEnabled = []common.ChainID(nil)
    90  
    91  func BenchmarkGetActivityEntries(bArg *testing.B) {
    92  	deps, closeFn, accounts := setupBenchmark(bArg, 6, true)
    93  	defer closeFn()
    94  
    95  	type params struct {
    96  		inMemory bool
    97  		// resultCount must be nil to expect as many requested
    98  		resultCount            *int
    99  		generateTestParameters func() (addresses []eth.Address, allAddresses bool, networks []common.ChainID, filter *Filter, startIndex int)
   100  	}
   101  	testCases := []struct {
   102  		name   string
   103  		params params
   104  	}{
   105  		{
   106  			"RAM_NoFilter",
   107  			params{
   108  				true,
   109  				nil,
   110  				func() ([]eth.Address, bool, []common.ChainID, *Filter, int) {
   111  					return accounts, true, allNetEnabled, &Filter{}, 0
   112  				},
   113  			},
   114  		},
   115  		{
   116  			"SSD_NoFilter",
   117  			params{
   118  				false,
   119  				nil,
   120  				func() ([]eth.Address, bool, []common.ChainID, *Filter, int) {
   121  					return accounts, true, allNetEnabled, &Filter{}, 0
   122  				},
   123  			},
   124  		},
   125  		{
   126  			"SSD_MovingWindow",
   127  			params{
   128  				false,
   129  				nil,
   130  				func() ([]eth.Address, bool, []common.ChainID, *Filter, int) {
   131  					return accounts, true, allNetEnabled, &Filter{}, 200
   132  				},
   133  			},
   134  		},
   135  		{
   136  			"SSD_AllAddr_AllTos",
   137  			params{
   138  				false,
   139  				nil,
   140  				func() ([]eth.Address, bool, []common.ChainID, *Filter, int) {
   141  					return accounts, true, allNetEnabled, &Filter{CounterpartyAddresses: accounts[3:]}, 0
   142  				},
   143  			},
   144  		},
   145  		{
   146  			"SSD_OneAddress",
   147  			params{
   148  				false,
   149  				nil,
   150  				func() ([]eth.Address, bool, []common.ChainID, *Filter, int) {
   151  					return accounts[0:1], false, allNetEnabled, &Filter{}, 0
   152  				},
   153  			},
   154  		},
   155  		// All memory from here
   156  		{
   157  			"FilterSend_AllAddr",
   158  			params{
   159  				true,
   160  				nil,
   161  				func() ([]eth.Address, bool, []common.ChainID, *Filter, int) {
   162  					return accounts, true, allNetEnabled, &Filter{
   163  						Types: []Type{SendAT},
   164  					}, 0
   165  				},
   166  			},
   167  		},
   168  		{
   169  			"FilterSend_6Addr",
   170  			params{
   171  				true,
   172  				nil,
   173  				func() ([]eth.Address, bool, []common.ChainID, *Filter, int) {
   174  					return accounts[len(accounts)-6:], false, allNetEnabled, &Filter{
   175  						Types: []Type{SendAT},
   176  					}, 0
   177  				},
   178  			},
   179  		},
   180  		{
   181  			"FilterThreeNetworks",
   182  			params{
   183  				true,
   184  				nil,
   185  				func() ([]eth.Address, bool, []common.ChainID, *Filter, int) {
   186  					return accounts, true, []common.ChainID{}, &Filter{}, 0
   187  				},
   188  			},
   189  		},
   190  	}
   191  
   192  	const resultCount = 100
   193  	for _, tc := range testCases {
   194  		addresses, allAddresses, nets, filter, startIndex := tc.params.generateTestParameters()
   195  		networks := allNetworksFilter()
   196  		if len(nets) > 0 {
   197  			networks = nets
   198  		}
   199  
   200  		bArg.Run(tc.name, func(b *testing.B) {
   201  			// Reset timer after setup
   202  			b.ResetTimer()
   203  
   204  			// Run benchmark
   205  			for i := 0; i < b.N; i++ {
   206  				res, err := getActivityEntries(context.Background(), deps, addresses, allAddresses, networks, *filter, startIndex, resultCount)
   207  				if err != nil {
   208  					b.Error(err)
   209  				} else if tc.params.resultCount != nil {
   210  					if len(res) != *tc.params.resultCount {
   211  						b.Error("Got less then expected")
   212  					}
   213  				} else if len(res) != resultCount {
   214  					b.Error("Got less than requested")
   215  				}
   216  			}
   217  		})
   218  	}
   219  }
   220  
   221  func BenchmarkSQLQuery(b *testing.B) {
   222  	type params struct {
   223  		query            string
   224  		args             []interface{}
   225  		expectedResCount int
   226  	}
   227  
   228  	deps, closeFn, accounts := setupBenchmark(b, 10000, true)
   229  	defer closeFn()
   230  
   231  	var addrValuesStr, insertAddrValuesStr, addrPlaceholdersStr string
   232  	var refAccounts []interface{}
   233  	for _, acc := range accounts {
   234  		addrValuesStr += fmt.Sprintf("X'%s',", acc.Hex()[2:])
   235  		insertAddrValuesStr += fmt.Sprintf("(X'%s'),", acc.Hex()[2:])
   236  		addrPlaceholdersStr += "?,"
   237  		refAccounts = append(refAccounts, acc)
   238  	}
   239  	addrValuesStr = addrValuesStr[:len(addrValuesStr)-1]
   240  	insertAddrValuesStr = insertAddrValuesStr[:len(insertAddrValuesStr)-1]
   241  	addrPlaceholdersStr = addrPlaceholdersStr[:len(addrPlaceholdersStr)-1]
   242  
   243  	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 {
   244  		b.Fatal("failed to create temporary table", err)
   245  	}
   246  
   247  	testCases := []struct {
   248  		name string
   249  		args params
   250  		res  testing.BenchmarkResult
   251  	}{
   252  		{
   253  			name: "JoinQuery",
   254  			args: params{
   255  				query:            "SELECT COUNT(*) FROM transfers JOIN filter_addresses_table ON transfers.tx_from_address = filter_addresses_table.address",
   256  				expectedResCount: 99990,
   257  			},
   258  		},
   259  		{
   260  			name: "LiteralQuery",
   261  			args: params{
   262  				query:            fmt.Sprintf("SELECT COUNT(*) FROM transfers WHERE tx_from_address IN (%s)", addrValuesStr),
   263  				expectedResCount: 99990,
   264  			},
   265  		},
   266  		{
   267  			name: "ParamQuery",
   268  			args: params{
   269  				query:            fmt.Sprintf("SELECT COUNT(*) FROM transfers WHERE tx_from_address IN (%s)", addrPlaceholdersStr),
   270  				args:             refAccounts,
   271  				expectedResCount: 99990,
   272  			},
   273  		},
   274  	}
   275  
   276  	for _, tc := range testCases {
   277  		b.Run(tc.name, func(b *testing.B) {
   278  			b.ResetTimer()
   279  
   280  			for i := 0; i < b.N; i++ {
   281  				b.ResetTimer()
   282  
   283  				for i := 0; i < b.N; i++ {
   284  					res, err := deps.db.Query(tc.args.query, tc.args.args...)
   285  					if err != nil {
   286  						b.Fatal("failed to query db", err)
   287  					}
   288  					res.Next()
   289  
   290  					var count int
   291  					if err := res.Scan(&count); err != nil {
   292  						b.Fatal("failed to scan db result", err)
   293  					}
   294  					if count != tc.args.expectedResCount {
   295  						b.Fatalf("unexpected result count: %d, expected: %d", count, tc.args.expectedResCount)
   296  					}
   297  
   298  					res.Close()
   299  				}
   300  			}
   301  		})
   302  	}
   303  }