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 }