@@ -2,13 +2,16 @@ package plugin
22
33import (
44 "bytes"
5+ "context"
56 "encoding/json"
67 "fmt"
78 "io"
89 "net/http"
910 "net/url"
1011 "time"
1112
13+ "golang.org/x/time/rate"
14+
1215 "github.com/grafana/grafana-plugin-sdk-go/backend/log"
1316)
1417
@@ -20,27 +23,38 @@ type FacetRequest struct {
2023}
2124
2225type DataSetClient struct {
23- dataSetUrl string
24- apiKey string
25- netClient * http.Client
26+ dataSetUrl string
27+ apiKey string
28+ netClient * http.Client
29+ rateLimiter * rate.Limiter
2630}
2731
2832func NewDataSetClient (dataSetUrl string , apiKey string ) * DataSetClient {
2933 // Consider using the backend.httpclient package provided by the Grafana SDK.
3034 // This would allow a per-instance configurable timeout, rather than the hardcoded value here.
31- var netClient = & http.Client {
35+ netClient : = & http.Client {
3236 Timeout : time .Second * 10 ,
3337 }
3438
39+ // TODO Are there alternate approaches to implementing rate limits via the Grafana SDK?
40+ // Consult with Grafana support about this, potentially there's a simplier option.
41+ rateLimiter := rate .NewLimiter (rate .Every (1 * time .Minute ), 100 ) // 100 requests / minute
42+
3543 return & DataSetClient {
36- dataSetUrl : dataSetUrl ,
37- apiKey : apiKey ,
38- netClient : netClient ,
44+ dataSetUrl : dataSetUrl ,
45+ apiKey : apiKey ,
46+ netClient : netClient ,
47+ rateLimiter : rateLimiter ,
3948 }
4049}
4150
4251func (d * DataSetClient ) newRequest (method , url string , body io.Reader ) (* http.Request , error ) {
43- const VERSION = "3.0.5"
52+ const VERSION = "3.0.6"
53+
54+ if err := d .rateLimiter .Wait (context .Background ()); err != nil {
55+ log .DefaultLogger .Error ("error applying rate limiter" , "err" , err )
56+ return nil , err
57+ }
4458
4559 request , err := http .NewRequest (method , url , body )
4660 if err != nil {
@@ -83,6 +97,10 @@ func (d *DataSetClient) doPingRequest(req interface{}) (*LRQResult, error) {
8397
8498 var respBody LRQResult
8599 var token string
100+
101+ delay := 250 * time .Millisecond
102+ const maxDelay = 1 * time .Second
103+
86104 for i := 0 ; ; i ++ {
87105 resp , err := d .netClient .Do (request )
88106 if err != nil {
@@ -115,7 +133,10 @@ func (d *DataSetClient) doPingRequest(req interface{}) (*LRQResult, error) {
115133 token = resp .Header .Get (TOKEN_HEADER )
116134 }
117135
118- time .Sleep (100 * time .Millisecond )
136+ time .Sleep (delay )
137+ if delay < maxDelay {
138+ delay *= 2
139+ }
119140
120141 u := fmt .Sprintf ("%s/v2/api/queries/%s?lastStepSeen=%d" , d .dataSetUrl , respBody .Id , respBody .StepsCompleted )
121142 request , err = d .newRequest ("GET" , u , nil )
0 commit comments