55 "fmt"
66 "log"
77 "sync"
8+ "time"
89
910 "github.com/bitfinexcom/bitfinex-api-go/pkg/models/event"
1011 "github.com/bitfinexcom/bitfinex-api-go/pkg/mux/client"
@@ -16,25 +17,32 @@ import (
1617// to all incomming client messages and reconnect client with all its subscriptions
1718// in case of a failure
1819type Mux struct {
19- cid int
20- dms int
21- publicChan chan msg.Msg
22- publicClients map [int ]* client.Client
23- privateChan chan msg.Msg
24- closeChan chan bool
25- privateClient * client.Client
26- mtx * sync.RWMutex
27- Err error
28- transform bool
29- apikey string
30- apisec string
31- subInfo map [int64 ]event.Info
32- authenticated bool
33- publicURL string
34- authURL string
35- online bool
20+ cid int
21+ dms int
22+ publicChan chan msg.Msg
23+ publicClients map [int ]* client.Client
24+ privateChan chan msg.Msg
25+ closeChan chan bool
26+ privateClient * client.Client
27+ mtx * sync.RWMutex
28+ Err error
29+ transform bool
30+ apikey string
31+ apisec string
32+ subInfo map [int64 ]event.Info
33+ authenticated bool
34+ publicURL string
35+ authURL string
36+ online bool
37+ rateLimitQueueSize int
3638}
3739
40+ // api rate limit is 20 calls per minute. 1x3s, 20x1min
41+ const (
42+ rateLimitDuration = 3 * time .Second
43+ maxRateLimitQueueSize = 20
44+ )
45+
3846// New returns pointer to instance of mux
3947func New () * Mux {
4048 return & Mux {
@@ -68,7 +76,7 @@ func (m *Mux) WithDeadManSwitch() *Mux {
6876 return m
6977}
7078
71- // WithAPISEC accepts and persists api sec
79+ // WithAPISEC accepts and persists api secret
7280func (m * Mux ) WithAPISEC (sec string ) * Mux {
7381 m .apisec = sec
7482 return m
@@ -95,17 +103,23 @@ func (m *Mux) Close() bool {
95103 return true
96104}
97105
98- // Subscribe - given the details in form of event.Subscribe,
99- // subscribes client to public channels
106+ // Subscribe - given the details in form of event.Subscribe, subscribes client to public
107+ // channels. If rate limit is reached, calls itself recursively after 1s with same params
100108func (m * Mux ) Subscribe (sub event.Subscribe ) * Mux {
101109 if m .Err != nil {
102110 return m
103111 }
104112
105- m .mtx .Lock ()
106- defer m .mtx .Unlock ()
113+ // if limit is reached, wait 1 second and recuresively
114+ // call Subscribe again with same subscription details
115+ if m .rateLimitQueueSize == maxRateLimitQueueSize {
116+ time .Sleep (1 * time .Second )
117+ return m .Subscribe (sub )
118+ }
107119
108- if subscribed := m .publicClients [m .cid ].SubAdded (sub ); subscribed {
120+ m .mtx .RLock ()
121+ defer m .mtx .RUnlock ()
122+ if m .publicClients [m .cid ].SubAdded (sub ) {
109123 return m
110124 }
111125
@@ -117,6 +131,8 @@ func (m *Mux) Subscribe(sub event.Subscribe) *Mux {
117131 log .Printf ("subs limit is reached on cid: %d, spawning new conn\n " , m .cid )
118132 m .addPublicClient ()
119133 }
134+
135+ m .rateLimitQueueSize ++
120136 return m
121137}
122138
@@ -126,7 +142,9 @@ func (m *Mux) Start() *Mux {
126142 m .addPrivateClient ()
127143 }
128144
129- return m .addPublicClient ()
145+ m .watchRateLimit ()
146+ m .addPublicClient ()
147+ return m
130148}
131149
132150// Listen accepts a callback func that will get called each time mux
@@ -138,12 +156,11 @@ func (m *Mux) Listen(cb func(interface{}, error)) error {
138156 }
139157
140158 m .online = true
141-
142159 for {
143160 select {
144161 case ms , ok := <- m .publicChan :
145162 if ! ok {
146- return errors .New ("channel has closed unexpectedly" )
163+ return errors .New ("public channel has closed unexpectedly" )
147164 }
148165 if ms .Err != nil {
149166 cb (nil , fmt .Errorf ("conn:%d has failed | err:%s | reconnecting" , ms .CID , ms .Err ))
@@ -179,7 +196,7 @@ func (m *Mux) Listen(cb func(interface{}, error)) error {
179196 cb (nil , fmt .Errorf ("unrecognized msg signature: %s" , ms .Data ))
180197 case ms , ok := <- m .privateChan :
181198 if ! ok {
182- return errors .New ("channel has closed unexpectedly" )
199+ return errors .New ("private channel has closed unexpectedly" )
183200 }
184201 if ms .Err != nil {
185202 cb (nil , fmt .Errorf ("err: %s | reconnecting" , ms .Err ))
@@ -286,7 +303,7 @@ func (m *Mux) addPublicClient() *Mux {
286303 c , err := client .
287304 New ().
288305 WithID (m .cid ).
289- WithSubsLimit (20 ).
306+ WithSubsLimit (30 ).
290307 Public (m .publicURL )
291308 if err != nil {
292309 m .Err = err
@@ -311,3 +328,17 @@ func (m *Mux) addPrivateClient() *Mux {
311328 go c .Read (m .privateChan )
312329 return m
313330}
331+
332+ // watchRateLimit will run once every rateLimitDuration
333+ // and free up the queue
334+ func (m * Mux ) watchRateLimit () {
335+ go func () {
336+ for {
337+ if m .rateLimitQueueSize > 0 {
338+ m .rateLimitQueueSize --
339+ }
340+
341+ time .Sleep (rateLimitDuration )
342+ }
343+ }()
344+ }
0 commit comments