Skip to content

Commit 6dd120e

Browse files
authored
try to search optimistically from newer data (#165)
### TL;DR Optimized transaction search performance by implementing time-based search strategies. ### What changed? - Added time-based filtering for function signature searches, prioritizing transactions from the last 30 days - Split transaction hash searches into three parallel time ranges (0-5 days, 5-30 days, and 30+ days ago) - Enhanced address-based transaction searches with time-based optimizations - Introduced new helper function `searchTransactionsByTimeRange` to handle time-based transaction queries ### How to test? 1. Search for a transaction hash and verify results are returned quickly 2. Test function signature searches and confirm recent transactions (< 30 days) appear first 3. Search for an address and verify transactions are properly filtered by time ranges 4. Verify older transactions are still accessible when recent ones aren't found ### Why make this change? To improve search performance by prioritizing recent transactions and implementing parallel search strategies. This approach reduces query load on the database and provides faster results for common use cases where users are typically looking for recent transactions.
2 parents 0d06d2f + 8a8a551 commit 6dd120e

File tree

1 file changed

+116
-21
lines changed

1 file changed

+116
-21
lines changed

internal/handlers/search_handlers.go

Lines changed: 116 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ import (
44
"encoding/hex"
55
"fmt"
66
"math/big"
7+
"strconv"
78
"strings"
89
"sync"
10+
"time"
911

1012
"github.com/gin-gonic/gin"
1113
"github.com/rs/zerolog/log"
@@ -135,7 +137,7 @@ func executeSearch(storage storage.IMainStorage, chainId *big.Int, input SearchI
135137
return searchByAddress(storage, chainId, input.Address)
136138

137139
case input.FunctionSignature != "":
138-
transactions, err := searchByFunctionSelector(storage, chainId, input.FunctionSignature)
140+
transactions, err := searchByFunctionSelectorOptimistically(storage, chainId, input.FunctionSignature)
139141
return SearchResultModel{Transactions: transactions, Type: SearchResultTypeFunctionSignature}, err
140142

141143
default:
@@ -160,10 +162,16 @@ func searchByBlockNumber(mainStorage storage.IMainStorage, chainId *big.Int, blo
160162
return &block, nil
161163
}
162164

163-
func searchByFunctionSelector(mainStorage storage.IMainStorage, chainId *big.Int, functionSelector string) ([]common.TransactionModel, error) {
165+
func searchByFunctionSelectorOptimistically(mainStorage storage.IMainStorage, chainId *big.Int, functionSelector string) ([]common.TransactionModel, error) {
166+
now := time.Now()
167+
thirtyDaysAgo := now.AddDate(0, 0, -30)
168+
164169
result, err := mainStorage.GetTransactions(storage.QueryFilter{
165170
ChainId: chainId,
166171
Signature: functionSelector,
172+
FilterParams: map[string]string{
173+
"block_timestamp_gte": strconv.FormatInt(thirtyDaysAgo.Unix(), 10),
174+
},
167175
SortBy: "block_number",
168176
SortOrder: "desc",
169177
Limit: 20,
@@ -172,7 +180,19 @@ func searchByFunctionSelector(mainStorage storage.IMainStorage, chainId *big.Int
172180
return nil, err
173181
}
174182
if len(result.Data) == 0 {
175-
return []common.TransactionModel{}, nil
183+
result, err = mainStorage.GetTransactions(storage.QueryFilter{
184+
ChainId: chainId,
185+
Signature: functionSelector,
186+
FilterParams: map[string]string{
187+
"block_timestamp_lte": strconv.FormatInt(thirtyDaysAgo.Unix(), 10),
188+
},
189+
SortBy: "block_number",
190+
SortOrder: "desc",
191+
Limit: 20,
192+
})
193+
if err != nil {
194+
return nil, err
195+
}
176196
}
177197

178198
transactions := make([]common.TransactionModel, len(result.Data))
@@ -189,25 +209,50 @@ func searchByHash(mainStorage storage.IMainStorage, chainId *big.Int, hash strin
189209
doneChan := make(chan struct{})
190210
errChan := make(chan error)
191211

192-
// Try as transaction hash
193-
wg.Add(1)
212+
wg.Add(3)
213+
// Try as transaction hash past 5 days
194214
go func() {
195215
defer wg.Done()
196-
txResult, err := mainStorage.GetTransactions(storage.QueryFilter{
197-
ChainId: chainId,
198-
FilterParams: map[string]string{
199-
"hash": hash,
200-
},
201-
Limit: 1,
202-
})
216+
txs, err := searchTransactionsByTimeRange(mainStorage, chainId, hash, 5, 0)
217+
if err != nil {
218+
errChan <- err
219+
return
220+
}
221+
if len(txs) > 0 {
222+
select {
223+
case resultChan <- SearchResultModel{Transactions: []common.TransactionModel{txs[0]}, Type: SearchResultTypeTransaction}:
224+
case <-doneChan:
225+
}
226+
}
227+
}()
228+
229+
// Try as transaction hash past 5-30 days
230+
go func() {
231+
defer wg.Done()
232+
txs, err := searchTransactionsByTimeRange(mainStorage, chainId, hash, 30, 5)
233+
if err != nil {
234+
errChan <- err
235+
return
236+
}
237+
if len(txs) > 0 {
238+
select {
239+
case resultChan <- SearchResultModel{Transactions: []common.TransactionModel{txs[0]}, Type: SearchResultTypeTransaction}:
240+
case <-doneChan:
241+
}
242+
}
243+
}()
244+
245+
// Try as transaction hash more than 30 days ago
246+
go func() {
247+
defer wg.Done()
248+
txs, err := searchTransactionsByTimeRange(mainStorage, chainId, hash, 0, 30)
203249
if err != nil {
204250
errChan <- err
205251
return
206252
}
207-
if len(txResult.Data) > 0 {
208-
txModel := txResult.Data[0].Serialize()
253+
if len(txs) > 0 {
209254
select {
210-
case resultChan <- SearchResultModel{Transactions: []common.TransactionModel{txModel}, Type: SearchResultTypeTransaction}:
255+
case resultChan <- SearchResultModel{Transactions: []common.TransactionModel{txs[0]}, Type: SearchResultTypeTransaction}:
211256
case <-doneChan:
212257
}
213258
}
@@ -299,7 +344,7 @@ func searchByAddress(mainStorage storage.IMainStorage, chainId *big.Int, address
299344
}
300345
return searchResult, err
301346
} else if contractCode == ContractCodeDoesNotExist {
302-
txs, err := findLatestTransactionsFromAddress(mainStorage, chainId, address)
347+
txs, err := findLatestTransactionsFromAddressOptimistically(mainStorage, chainId, address)
303348
if err == nil {
304349
searchResult.Transactions = txs
305350
return searchResult, nil
@@ -318,7 +363,7 @@ func searchByAddress(mainStorage storage.IMainStorage, chainId *big.Int, address
318363
return searchResult, nil
319364
}
320365
}
321-
transactionsFrom, err := findLatestTransactionsFromAddress(mainStorage, chainId, address)
366+
transactionsFrom, err := findLatestTransactionsFromAddressOptimistically(mainStorage, chainId, address)
322367
if err != nil {
323368
return searchResult, err
324369
}
@@ -345,17 +390,38 @@ func findLatestTransactionsToAddress(mainStorage storage.IMainStorage, chainId *
345390
return transactions, nil
346391
}
347392

348-
func findLatestTransactionsFromAddress(mainStorage storage.IMainStorage, chainId *big.Int, address string) ([]common.TransactionModel, error) {
393+
func findLatestTransactionsFromAddressOptimistically(mainStorage storage.IMainStorage, chainId *big.Int, address string) ([]common.TransactionModel, error) {
394+
now := time.Now()
395+
thirtyDaysAgo := now.AddDate(0, 0, -30)
396+
349397
result, err := mainStorage.GetTransactions(storage.QueryFilter{
350398
ChainId: chainId,
351399
FromAddress: address,
352-
Limit: 20,
353-
SortBy: "block_number",
354-
SortOrder: "desc",
400+
FilterParams: map[string]string{
401+
"block_timestamp_gte": strconv.FormatInt(thirtyDaysAgo.Unix(), 10),
402+
},
403+
Limit: 20,
404+
SortBy: "block_number",
405+
SortOrder: "desc",
355406
})
356407
if err != nil {
357408
return nil, err
358409
}
410+
if len(result.Data) == 0 {
411+
result, err = mainStorage.GetTransactions(storage.QueryFilter{
412+
ChainId: chainId,
413+
FromAddress: address,
414+
FilterParams: map[string]string{
415+
"block_timestamp_lte": strconv.FormatInt(thirtyDaysAgo.Unix(), 10),
416+
},
417+
Limit: 20,
418+
SortBy: "block_number",
419+
SortOrder: "desc",
420+
})
421+
if err != nil {
422+
return nil, err
423+
}
424+
}
359425
transactions := make([]common.TransactionModel, len(result.Data))
360426
for i, transaction := range result.Data {
361427
transactions[i] = transaction.Serialize()
@@ -389,3 +455,32 @@ func checkIfContractHasCode(chainId *big.Int, address string) (ContractCodeState
389455
}
390456
return ContractCodeUnknown, nil
391457
}
458+
459+
func searchTransactionsByTimeRange(mainStorage storage.IMainStorage, chainId *big.Int, hash string, startOffsetDays, endOffsetDays int) ([]common.TransactionModel, error) {
460+
now := time.Now()
461+
filters := map[string]string{
462+
"hash": hash,
463+
}
464+
if startOffsetDays > 0 {
465+
startTime := now.AddDate(0, 0, -startOffsetDays)
466+
filters["block_timestamp_gte"] = strconv.FormatInt(startTime.Unix(), 10)
467+
}
468+
if endOffsetDays > 0 {
469+
endTime := now.AddDate(0, 0, -endOffsetDays)
470+
filters["block_timestamp_lte"] = strconv.FormatInt(endTime.Unix(), 10)
471+
}
472+
473+
txResult, err := mainStorage.GetTransactions(storage.QueryFilter{
474+
ChainId: chainId,
475+
FilterParams: filters,
476+
Limit: 1,
477+
})
478+
if err != nil {
479+
return nil, err
480+
}
481+
serialized := make([]common.TransactionModel, len(txResult.Data))
482+
for i, tx := range txResult.Data {
483+
serialized[i] = tx.Serialize()
484+
}
485+
return serialized, nil
486+
}

0 commit comments

Comments
 (0)