Skip to content

Commit 6e9ac3a

Browse files
authored
Merge pull request #29 from getamis/subscription_id
multiclient: assign client an unique id
2 parents da953f1 + b260752 commit 6e9ac3a

5 files changed

Lines changed: 142 additions & 70 deletions

File tree

multiclient/client.go

Lines changed: 61 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import (
2424
"time"
2525

2626
"github.com/cskr/pubsub"
27-
"github.com/ethereum/go-ethereum"
27+
ethereum "github.com/ethereum/go-ethereum"
2828
"github.com/ethereum/go-ethereum/common"
2929
"github.com/ethereum/go-ethereum/core/types"
3030
"github.com/ethereum/go-ethereum/ethclient"
@@ -619,30 +619,30 @@ type Header struct {
619619
// SubscribeNewHead subscribes to notifications about the current blockchain head
620620
// on the given channel.
621621
func (mc *Client) SubscribeNewHead(ctx context.Context, ch chan<- *Header) (ethereum.Subscription, error) {
622-
clientsMap := mc.rpcClientMap.Map()
623-
lens := len(clientsMap)
622+
ids := mc.rpcClientMap.Ids()
623+
lens := len(ids)
624624
if lens == 0 {
625625
return nil, ErrNoEthClient
626626
}
627627

628628
var subscribeNewHeadWg sync.WaitGroup
629629

630630
cctx, cancel := context.WithCancel(ctx)
631-
for url := range clientsMap {
631+
for _, id := range ids {
632632
subscribeNewHeadWg.Add(1)
633-
go mc.subscribeNewHead(cctx, &subscribeNewHeadWg, url, ch)
633+
go mc.subscribeNewHead(cctx, &subscribeNewHeadWg, id, ch)
634634
}
635635

636636
newClientCh := mc.pubSub.Sub(newAvailableClientTopic)
637-
// handle new clients comes
637+
// handle new clients come
638638
go func() {
639639
defer mc.pubSub.Unsub(newClientCh, newAvailableClientTopic)
640640
for {
641641
select {
642642
case newC := <-newClientCh:
643-
url := newC.(string)
643+
id := newC.(uint64)
644644
subscribeNewHeadWg.Add(1)
645-
go mc.subscribeNewHead(cctx, &subscribeNewHeadWg, url, ch)
645+
go mc.subscribeNewHead(cctx, &subscribeNewHeadWg, id, ch)
646646
case <-cctx.Done():
647647
return
648648
}
@@ -657,46 +657,66 @@ func (mc *Client) SubscribeNewHead(ctx context.Context, ch chan<- *Header) (ethe
657657
}), nil
658658
}
659659

660-
func (mc *Client) subscribeNewHead(ctx context.Context, wg *sync.WaitGroup, url string, ch chan<- *Header) error {
660+
func (mc *Client) subscribeNewHead(ctx context.Context, wg *sync.WaitGroup, id uint64, ch chan<- *Header) error {
661+
logger := log.New("id", id)
661662
defer wg.Done()
662663

664+
retryTimer := time.NewTimer(0)
665+
defer retryTimer.Stop()
666+
663667
for {
664-
rc := mc.rpcClientMap.Get(url)
668+
url, rc := mc.rpcClientMap.GetById(id)
665669
if rc == nil {
666-
log.Trace("EthClient has been removed", "url", url)
670+
logger.Trace("EthClient has been removed")
671+
return nil
672+
}
673+
subLogger := logger.New("url", url)
674+
// If we have error, we need to retry
675+
err := doSubscribe(ctx, subLogger, rc, ch)
676+
if err == nil {
667677
return nil
668678
}
669679

670-
headerCh := make(chan *types.Header)
671-
c := ethclient.NewClient(rc)
672-
sub, err := c.SubscribeNewHead(ctx, headerCh)
673-
if err != nil {
674-
log.Warn("Failed to subscribe new head", "url", url, "err", err)
675-
} else {
676-
WAIT_NEW_HEADER:
677-
for {
678-
select {
679-
case header := <-headerCh:
680-
h := &Header{
681-
Client: rc,
682-
Header: header,
683-
}
684-
select {
685-
case ch <- h:
686-
case <-ctx.Done():
687-
return nil
688-
}
689-
case err := <-sub.Err():
690-
log.Warn("Failed during subscription", "url", url, "err", err)
691-
break WAIT_NEW_HEADER
692-
case <-ctx.Done():
693-
return nil
694-
}
680+
// reset timer with retryPeriod
681+
retryTimer.Reset(retryPeriod)
682+
select {
683+
case <-retryTimer.C:
684+
case <-ctx.Done():
685+
return nil
686+
}
687+
subLogger.Trace("Retry to subscribe new head")
688+
}
689+
}
690+
691+
func doSubscribe(ctx context.Context, logger log.Logger, rc *rpc.Client, ch chan<- *Header) error {
692+
headerCh := make(chan *types.Header)
693+
c := ethclient.NewClient(rc)
694+
subCtx, cancel := context.WithCancel(ctx)
695+
defer cancel()
696+
697+
sub, err := c.SubscribeNewHead(subCtx, headerCh)
698+
if err != nil {
699+
logger.Warn("Failed to subscribe new head", "err", err)
700+
return err
701+
}
702+
for {
703+
select {
704+
case header := <-headerCh:
705+
h := &Header{
706+
Client: rc,
707+
Header: header,
695708
}
709+
select {
710+
case ch <- h:
711+
case <-subCtx.Done():
712+
return nil
713+
}
714+
case err := <-sub.Err():
715+
logger.Warn("Failed during subscription", "err", err)
716+
return err
717+
case <-subCtx.Done():
718+
return nil
696719
}
697-
// retry subscribe after retryPeriod
698-
time.Sleep(retryPeriod)
699-
log.Trace("Retry to subscribe new head", "url", url)
700720
}
701721
}
702722

@@ -788,8 +808,8 @@ func (mc *Client) DialClients(ctx context.Context) {
788808
for i := 0; i < len(urls); i++ {
789809
dialed := <-dialCh
790810
if dialed.client != nil {
791-
mc.rpcClientMap.Replace(dialed.url, dialed.client)
792-
mc.pubSub.Pub(dialed.url, newAvailableClientTopic)
811+
id := mc.rpcClientMap.Replace(dialed.url, dialed.client)
812+
mc.pubSub.Pub(id, newAvailableClientTopic)
793813
}
794814
}
795815
}

multiclient/consul.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func ConsulDiscovery(rawURL, serviceID, serviceScheme string) Option {
3838
}
3939
log.Info("EthClients from consul", "urls", urls)
4040
for _, url := range urls {
41-
mc.ClientMap().Set(url, nil)
41+
mc.ClientMap().Add(url, nil)
4242
}
4343
return nil
4444
}

multiclient/k8s.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import (
2121
"sync"
2222

2323
"github.com/getamis/sirius/log"
24-
"k8s.io/api/core/v1"
24+
v1 "k8s.io/api/core/v1"
2525
"k8s.io/apimachinery/pkg/api/meta"
2626
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2727
"k8s.io/apimachinery/pkg/fields"
@@ -180,7 +180,7 @@ func (s *endpointStore) Add(obj interface{}) error {
180180
defer s.mutex.Unlock()
181181

182182
for _, url := range urls {
183-
s.rpcClientMap.Set(url, nil)
183+
s.rpcClientMap.Add(url, nil)
184184
}
185185

186186
s.endpoints[o.GetUID()] = urls
@@ -202,7 +202,7 @@ func (s *endpointStore) Update(obj interface{}) error {
202202
// Add new urls
203203
for _, url := range news {
204204
if c := s.rpcClientMap.Get(url); c == nil {
205-
s.rpcClientMap.Set(url, nil)
205+
s.rpcClientMap.Add(url, nil)
206206
}
207207
}
208208

multiclient/map.go

Lines changed: 76 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,26 @@ import (
2424
)
2525

2626
type Map struct {
27-
m map[string]*rpc.Client
27+
// the mapping form url to client
28+
clientMap map[string]*client
29+
// the mapping subscription id to url
30+
idMap map[uint64]string
31+
idCounter uint64
2832
newClientCh chan<- string
2933

3034
lock sync.RWMutex
3135
}
3236

37+
type client struct {
38+
*rpc.Client
39+
Id uint64
40+
}
41+
3342
func NewMap(newClientCh chan<- string) *Map {
3443
return &Map{
35-
m: make(map[string]*rpc.Client),
44+
clientMap: make(map[string]*client),
45+
idMap: make(map[uint64]string),
46+
idCounter: 0,
3647
newClientCh: newClientCh,
3748
}
3849
}
@@ -41,48 +52,76 @@ func (m *Map) Delete(key string) {
4152
m.lock.Lock()
4253
defer m.lock.Unlock()
4354

44-
if c := m.m[key]; c != nil {
45-
c.Close()
55+
c, ok := m.clientMap[key]
56+
if !ok {
57+
return
58+
}
59+
if c.Client != nil {
60+
c.Client.Close()
4661
}
47-
delete(m.m, key)
48-
log.Trace("Eth client removed", "url", key)
62+
delete(m.idMap, c.Id)
63+
delete(m.clientMap, key)
64+
log.Trace("Eth client removed", "c.Id", c.Id, "url", key)
4965
}
5066

51-
func (m *Map) Set(key string, value *rpc.Client) {
67+
func (m *Map) Add(key string, value *rpc.Client) {
5268
m.lock.Lock()
5369
defer m.lock.Unlock()
5470

55-
m.m[key] = value
71+
m.idCounter++
72+
m.clientMap[key] = &client{
73+
Id: m.idCounter,
74+
Client: value,
75+
}
76+
m.idMap[m.idCounter] = key
77+
5678
if m.newClientCh != nil {
5779
select {
5880
case m.newClientCh <- key:
5981
default:
6082
}
6183
}
62-
log.Trace("Eth client added", "url", key)
84+
log.Trace("Eth client added", "id", m.idCounter, "url", key)
6385
}
6486

65-
func (m *Map) Replace(key string, value *rpc.Client) {
87+
func (m *Map) Replace(key string, value *rpc.Client) uint64 {
6688
m.lock.Lock()
6789
defer m.lock.Unlock()
6890

69-
if _, ok := m.m[key]; ok {
70-
m.m[key] = value
91+
if _, ok := m.clientMap[key]; ok {
92+
m.clientMap[key].Client = value
7193
}
94+
95+
return m.clientMap[key].Id
7296
}
7397

7498
func (m *Map) Get(key string) *rpc.Client {
7599
m.lock.RLock()
76100
defer m.lock.RUnlock()
77101

78-
return m.m[key]
102+
return m.clientMap[key].Client
103+
}
104+
105+
func (m *Map) GetById(id uint64) (string, *rpc.Client) {
106+
m.lock.RLock()
107+
defer m.lock.RUnlock()
108+
109+
key, ok := m.idMap[id]
110+
if !ok {
111+
return "", nil
112+
}
113+
c, ok := m.clientMap[key]
114+
if !ok {
115+
return "", nil
116+
}
117+
return key, c.Client
79118
}
80119

81120
func (m *Map) Len() int {
82121
m.lock.RLock()
83122
defer m.lock.RUnlock()
84123

85-
return len(m.m)
124+
return len(m.clientMap)
86125
}
87126

88127
// List returns a deep copy of client list
@@ -91,9 +130,9 @@ func (m *Map) List() []*rpc.Client {
91130
defer m.lock.RUnlock()
92131

93132
l := []*rpc.Client{}
94-
for _, v := range m.m {
95-
if v != nil {
96-
l = append(l, v)
133+
for _, v := range m.clientMap {
134+
if v.Client != nil {
135+
l = append(l, v.Client)
97136
}
98137
}
99138
return l
@@ -105,21 +144,34 @@ func (m *Map) Map() map[string]*rpc.Client {
105144
defer m.lock.RUnlock()
106145

107146
newMap := map[string]*rpc.Client{}
108-
for k, v := range m.m {
109-
if v != nil {
110-
newMap[k] = v
147+
for k, v := range m.clientMap {
148+
if v.Client != nil {
149+
newMap[k] = v.Client
111150
}
112151
}
113152
return newMap
114153
}
115154

155+
func (m *Map) Ids() []uint64 {
156+
m.lock.RLock()
157+
defer m.lock.RUnlock()
158+
159+
ids := []uint64{}
160+
for _, c := range m.clientMap {
161+
if c.Client != nil {
162+
ids = append(ids, c.Id)
163+
}
164+
}
165+
return ids
166+
}
167+
116168
func (m *Map) Keys() []string {
117169
m.lock.RLock()
118170
defer m.lock.RUnlock()
119171

120-
urls := make([]string, len(m.m))
172+
urls := make([]string, len(m.clientMap))
121173
index := 0
122-
for k := range m.m {
174+
for k := range m.clientMap {
123175
urls[index] = k
124176
index++
125177
}
@@ -131,8 +183,8 @@ func (m *Map) NilClients() []string {
131183
defer m.lock.RUnlock()
132184

133185
urls := make([]string, 0)
134-
for k, v := range m.m {
135-
if v == nil {
186+
for k, v := range m.clientMap {
187+
if v.Client == nil {
136188
urls = append(urls, k)
137189
}
138190
}

multiclient/option.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ func EthURLs(urls []string) Option {
2828
return func(mc *Client) error {
2929
log.Info("EthClients from static list", "urls", urls)
3030
for _, url := range urls {
31-
mc.ClientMap().Set(url, nil)
31+
mc.ClientMap().Add(url, nil)
3232
}
3333
return nil
3434
}

0 commit comments

Comments
 (0)