Skip to content

Commit fdde90e

Browse files
authored
Merge pull request #13 from vitwit/anil/fix_concurrency
fix concurency
2 parents 07ecbaa + 7b79367 commit fdde90e

1 file changed

Lines changed: 71 additions & 54 deletions

File tree

internal/cosmos/client.go

Lines changed: 71 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"io"
99
"net/http"
1010
"strings"
11+
"sync"
1112
"time"
1213

1314
"github.com/anilcse/cosmoscope/internal/portfolio"
@@ -21,61 +22,15 @@ var (
2122
chainInfoCache = make(map[string]*ChainInfo)
2223
assetListCache = make(map[string]AssetList)
2324
registryBaseURL = "https://raw.githubusercontent.com/cosmos/chain-registry/master"
25+
cacheMutex sync.RWMutex
2426
)
2527

26-
// getActiveEndpoint tries each REST endpoint until it finds one that responds
27-
func getActiveEndpoint(endpoints []RestEndpoint) string {
28-
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
29-
defer cancel()
30-
31-
type result struct {
32-
endpoint string
33-
err error
34-
}
35-
resultChan := make(chan result)
36-
37-
// Try all endpoints concurrently
38-
for _, endpoint := range endpoints {
39-
go func(addr string) {
40-
client := &http.Client{Timeout: 2 * time.Second}
41-
req, err := http.NewRequestWithContext(ctx, "GET", addr+"/cosmos/base/tendermint/v1beta1/node_info", nil)
42-
if err != nil {
43-
resultChan <- result{endpoint: addr, err: err}
44-
return
45-
}
46-
47-
resp, err := client.Do(req)
48-
if err != nil {
49-
resultChan <- result{endpoint: addr, err: err}
50-
return
51-
}
52-
defer resp.Body.Close()
53-
54-
if resp.StatusCode == http.StatusOK {
55-
resultChan <- result{endpoint: addr, err: nil}
56-
} else {
57-
resultChan <- result{endpoint: addr, err: fmt.Errorf("endpoint returned status %d", resp.StatusCode)}
58-
}
59-
}(endpoint.Address)
60-
}
61-
62-
// Return the first successful endpoint
63-
for range endpoints {
64-
select {
65-
case r := <-resultChan:
66-
if r.err == nil {
67-
return r.endpoint
68-
}
69-
case <-ctx.Done():
70-
return ""
71-
}
72-
}
73-
74-
return ""
75-
}
76-
7728
func FetchChainInfo(network string) (*ChainInfo, error) {
78-
if info, exists := chainInfoCache[network]; exists {
29+
// Try to read from cache first
30+
cacheMutex.RLock()
31+
info, exists := chainInfoCache[network]
32+
cacheMutex.RUnlock()
33+
if exists {
7934
return info, nil
8035
}
8136

@@ -93,12 +48,20 @@ func FetchChainInfo(network string) (*ChainInfo, error) {
9348
return nil, fmt.Errorf("error decoding chain info: %v", err)
9449
}
9550

51+
// Store in cache with write lock
52+
cacheMutex.Lock()
9653
chainInfoCache[network] = &chainInfo
54+
cacheMutex.Unlock()
55+
9756
return &chainInfo, nil
9857
}
9958

10059
func fetchAssetList(network string) (*AssetList, error) {
101-
if assetList, exists := assetListCache[network]; exists {
60+
// Try to read from cache first
61+
cacheMutex.RLock()
62+
assetList, exists := assetListCache[network]
63+
cacheMutex.RUnlock()
64+
if exists {
10265
return &assetList, nil
10366
}
10467

@@ -111,12 +74,15 @@ func fetchAssetList(network string) (*AssetList, error) {
11174
}
11275
defer resp.Body.Close()
11376

114-
var assetList AssetList
11577
if err := json.NewDecoder(resp.Body).Decode(&assetList); err != nil {
11678
return nil, fmt.Errorf("error decoding asset list: %v", err)
11779
}
11880

81+
// Store in cache with write lock
82+
cacheMutex.Lock()
11983
assetListCache[network] = assetList
84+
cacheMutex.Unlock()
85+
12086
return &assetList, nil
12187
}
12288

@@ -331,3 +297,54 @@ func getHexAddress(address string) string {
331297
}
332298
return hex.EncodeToString(bz)
333299
}
300+
301+
// getActiveEndpoint tries each REST endpoint until it finds one that responds
302+
func getActiveEndpoint(endpoints []RestEndpoint) string {
303+
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
304+
defer cancel()
305+
306+
type result struct {
307+
endpoint string
308+
err error
309+
}
310+
resultChan := make(chan result)
311+
312+
// Try all endpoints concurrently
313+
for _, endpoint := range endpoints {
314+
go func(addr string) {
315+
client := &http.Client{Timeout: 2 * time.Second}
316+
req, err := http.NewRequestWithContext(ctx, "GET", addr+"/cosmos/base/tendermint/v1beta1/node_info", nil)
317+
if err != nil {
318+
resultChan <- result{endpoint: addr, err: err}
319+
return
320+
}
321+
322+
resp, err := client.Do(req)
323+
if err != nil {
324+
resultChan <- result{endpoint: addr, err: err}
325+
return
326+
}
327+
defer resp.Body.Close()
328+
329+
if resp.StatusCode == http.StatusOK {
330+
resultChan <- result{endpoint: addr, err: nil}
331+
} else {
332+
resultChan <- result{endpoint: addr, err: fmt.Errorf("endpoint returned status %d", resp.StatusCode)}
333+
}
334+
}(endpoint.Address)
335+
}
336+
337+
// Return the first successful endpoint
338+
for range endpoints {
339+
select {
340+
case r := <-resultChan:
341+
if r.err == nil {
342+
return r.endpoint
343+
}
344+
case <-ctx.Done():
345+
return ""
346+
}
347+
}
348+
349+
return ""
350+
}

0 commit comments

Comments
 (0)