@@ -12,7 +12,6 @@ import (
12
12
"strings"
13
13
"time"
14
14
15
- "github.com/grafana/grafana-plugin-sdk-go/backend"
16
15
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
17
16
)
18
17
@@ -32,6 +31,7 @@ type DatasourceInfo struct {
32
31
ShouldInit bool
33
32
}
34
33
34
+ // TODO: Move ConfiguredFields closer to handlers, the client layer doesn't need this stuff
35
35
type ConfiguredFields struct {
36
36
TimeField string
37
37
TimeOutputFormat string
@@ -41,85 +41,29 @@ type ConfiguredFields struct {
41
41
42
42
// Client represents a client which can interact with elasticsearch api
43
43
type Client interface {
44
- GetConfiguredFields () ConfiguredFields
45
- ExecuteMultisearch (r * MultiSearchRequest ) (* MultiSearchResponse , error )
46
- MultiSearch () * MultiSearchRequestBuilder
44
+ ExecuteMultisearch (r []* SearchRequest ) ([]* json.RawMessage , error )
47
45
}
48
46
47
+ var logger = log .New ()
48
+
49
49
// NewClient creates a new Quickwit client
50
- var NewClient = func (ctx context.Context , ds * DatasourceInfo , timeRange backend.TimeRange ) (Client , error ) {
51
- logger := log .New ()
52
- logger .Debug ("Creating new client" , "configuredFields" , fmt .Sprintf ("%#v" , ds .ConfiguredFields ), "index" , ds .Database )
50
+ var NewClient = func (ctx context.Context , ds * DatasourceInfo ) (Client , error ) {
51
+ logger .Debug ("Creating new client" , "index" , ds .Database )
53
52
54
53
return & baseClientImpl {
55
- logger : logger ,
56
- ctx : ctx ,
57
- ds : ds ,
58
- configuredFields : ds .ConfiguredFields ,
59
- index : ds .Database ,
60
- timeRange : timeRange ,
54
+ ctx : ctx ,
55
+ ds : ds ,
56
+ index : ds .Database ,
61
57
}, nil
62
58
}
63
59
64
60
type baseClientImpl struct {
65
- ctx context.Context
66
- ds * DatasourceInfo
67
- configuredFields ConfiguredFields
68
- index string
69
- timeRange backend.TimeRange
70
- logger log.Logger
71
- }
72
-
73
- func (c * baseClientImpl ) GetConfiguredFields () ConfiguredFields {
74
- return c .configuredFields
75
- }
76
-
77
- type multiRequest struct {
78
- header map [string ]interface {}
79
- body interface {}
80
- interval time.Duration
81
- }
82
-
83
- func (c * baseClientImpl ) executeBatchRequest (uriPath , uriQuery string , requests []* multiRequest ) (* http.Response , error ) {
84
- bytes , err := c .encodeBatchRequests (requests )
85
- if err != nil {
86
- return nil , err
87
- }
88
- return c .executeRequest (http .MethodPost , uriPath , uriQuery , bytes )
89
- }
90
-
91
- func (c * baseClientImpl ) encodeBatchRequests (requests []* multiRequest ) ([]byte , error ) {
92
- c .logger .Debug ("Encoding batch requests to json" , "batch requests" , len (requests ))
93
- start := time .Now ()
94
-
95
- payload := bytes.Buffer {}
96
- for _ , r := range requests {
97
- reqHeader , err := json .Marshal (r .header )
98
- if err != nil {
99
- return nil , err
100
- }
101
- payload .WriteString (string (reqHeader ) + "\n " )
102
-
103
- reqBody , err := json .Marshal (r .body )
104
-
105
- if err != nil {
106
- return nil , err
107
- }
108
-
109
- body := string (reqBody )
110
- body = strings .ReplaceAll (body , "$__interval_ms" , strconv .FormatInt (r .interval .Milliseconds (), 10 ))
111
- body = strings .ReplaceAll (body , "$__interval" , r .interval .String ())
112
-
113
- payload .WriteString (body + "\n " )
114
- }
115
-
116
- elapsed := time .Since (start )
117
- c .logger .Debug ("Encoded batch requests to json" , "took" , elapsed )
118
-
119
- return payload .Bytes (), nil
61
+ ctx context.Context
62
+ ds * DatasourceInfo
63
+ index string
120
64
}
121
65
122
- func (c * baseClientImpl ) executeRequest (method , uriPath , uriQuery string , body []byte ) (* http.Response , error ) {
66
+ func (c * baseClientImpl ) makeRequest (method , uriPath , uriQuery string , body []byte ) (* http.Request , error ) {
123
67
u , err := url .Parse (c .ds .URL )
124
68
if err != nil {
125
69
return nil , err
@@ -136,59 +80,49 @@ func (c *baseClientImpl) executeRequest(method, uriPath, uriQuery string, body [
136
80
if err != nil {
137
81
return nil , err
138
82
}
139
-
140
- c .logger .Debug ("Executing request" , "url" , req .URL .String (), "method" , method )
141
-
142
83
req .Header .Set ("Content-Type" , "application/x-ndjson" )
84
+ return req , nil
85
+ }
143
86
144
- start := time . Now ()
145
- defer func () {
146
- elapsed := time . Since ( start )
147
- c . logger . Debug ( "Executed request" , "took" , elapsed )
148
- }()
149
- //nolint:bodyclose
150
- resp , err := c .ds . HTTPClient . Do ( req )
87
+ // Multisearch uses a shallow unmarshalled struct to defer the decoding to downstream handlers
88
+ type MultiSearchResponse struct {
89
+ Responses [] * json. RawMessage `json:"responses"`
90
+ }
91
+
92
+ func ( c * baseClientImpl ) ExecuteMultisearch ( requests [] * SearchRequest ) ([] * json. RawMessage , error ) {
93
+ req , err := c .createMultiSearchRequest ( requests , c . index )
151
94
if err != nil {
152
95
return nil , err
153
96
}
154
97
155
- return resp , nil
156
- }
157
-
158
- func (c * baseClientImpl ) ExecuteMultisearch (r * MultiSearchRequest ) (* MultiSearchResponse , error ) {
159
- c .logger .Debug ("Executing multisearch" , "search requests" , r .Requests )
160
-
161
- multiRequests := c .createMultiSearchRequests (r .Requests )
162
- queryParams := c .getMultiSearchQueryParameters ()
163
- clientRes , err := c .executeBatchRequest ("_elastic/_msearch" , queryParams , multiRequests )
98
+ res , err := c .ds .HTTPClient .Do (req )
164
99
if err != nil {
165
100
return nil , err
166
101
}
167
- res := clientRes
168
102
defer func () {
169
103
if err := res .Body .Close (); err != nil {
170
- c . logger .Warn ("Failed to close response body" , "err" , err )
104
+ logger .Warn ("Failed to close response body" , "err" , err )
171
105
}
172
106
}()
173
107
174
- c . logger .Debug ("Received multisearch response" , "code" , res .StatusCode , "status" , res .Status , "content-length" , res .ContentLength )
108
+ logger .Debug ("Received multisearch response" , "code" , res .StatusCode , "status" , res .Status , "content-length" , res .ContentLength )
175
109
176
110
if res .StatusCode >= 400 {
177
111
qe := QuickwitQueryError {
178
112
Status : res .StatusCode ,
179
113
Message : "Error on multisearch" ,
180
114
ResponseBody : res .Body ,
181
- QueryParam : queryParams ,
182
- RequestBody : r . Requests ,
115
+ QueryParam : req . URL . RawQuery ,
116
+ RequestBody : requests ,
183
117
}
184
118
185
119
errorPayload , _ := json .Marshal (qe )
186
- c . logger .Error (string (errorPayload ))
120
+ logger .Error (string (errorPayload ))
187
121
return nil , fmt .Errorf (string (errorPayload ))
188
122
}
189
123
190
124
start := time .Now ()
191
- c . logger .Debug ("Decoding multisearch json response" )
125
+ logger .Debug ("Decoding multisearch json response" )
192
126
193
127
var msr MultiSearchResponse
194
128
dec := json .NewDecoder (res .Body )
@@ -198,43 +132,53 @@ func (c *baseClientImpl) ExecuteMultisearch(r *MultiSearchRequest) (*MultiSearch
198
132
}
199
133
200
134
elapsed := time .Since (start )
201
- c . logger .Debug ("Decoded multisearch json response" , "took" , elapsed )
135
+ logger .Debug ("Decoded multisearch json response" , "took" , elapsed )
202
136
203
- msr .Status = res .StatusCode
204
-
205
- return & msr , nil
137
+ return msr .Responses , nil
206
138
}
207
139
208
- func (c * baseClientImpl ) createMultiSearchRequests (searchRequests []* SearchRequest ) []* multiRequest {
209
- multiRequests := []* multiRequest {}
210
-
211
- for _ , searchReq := range searchRequests {
212
- mr := multiRequest {
213
- header : map [string ]interface {}{
214
- "ignore_unavailable" : true ,
215
- "index" : strings .Split (c .index , "," ),
216
- },
217
- body : searchReq ,
218
- interval : searchReq .Interval ,
140
+ func (c * baseClientImpl ) makeMultiSearchPayload (searchRequests []* SearchRequest , index string ) ([]byte , error ) {
141
+ // Format, marshall and interpolate
142
+ payload := bytes.Buffer {}
143
+ for _ , r := range searchRequests {
144
+ header := map [string ]interface {}{
145
+ "ignore_unavailable" : true ,
146
+ "index" : strings .Split (index , "," ),
219
147
}
148
+ reqHeader , err := json .Marshal (header )
149
+ if err != nil {
150
+ return nil , err
151
+ }
152
+ payload .WriteString (string (reqHeader ) + "\n " )
220
153
221
- multiRequests = append (multiRequests , & mr )
222
- }
154
+ reqBody , err := json .Marshal (r )
155
+
156
+ if err != nil {
157
+ return nil , err
158
+ }
159
+
160
+ body := string (reqBody )
161
+ body = strings .ReplaceAll (body , "$__interval_ms" , strconv .FormatInt (r .Interval .Milliseconds (), 10 ))
162
+ body = strings .ReplaceAll (body , "$__interval" , r .Interval .String ())
223
163
224
- return multiRequests
164
+ payload .WriteString (body + "\n " )
165
+ }
166
+ return payload .Bytes (), nil
225
167
}
226
168
227
- func (c * baseClientImpl ) getMultiSearchQueryParameters () string {
228
- var qs []string
169
+ func (c * baseClientImpl ) createMultiSearchRequest (requests []* SearchRequest , index string ) (* http.Request , error ) {
170
+ body , err := c .makeMultiSearchPayload (requests , index )
171
+ if err != nil {
172
+ return nil , err
173
+ }
229
174
175
+ var qs []string
230
176
maxConcurrentShardRequests := c .ds .MaxConcurrentShardRequests
231
177
if maxConcurrentShardRequests == 0 {
232
178
maxConcurrentShardRequests = 5
233
179
}
234
180
qs = append (qs , fmt .Sprintf ("max_concurrent_shard_requests=%d" , maxConcurrentShardRequests ))
235
- return strings .Join (qs , "&" )
236
- }
181
+ queryParams := strings .Join (qs , "&" )
237
182
238
- func (c * baseClientImpl ) MultiSearch () * MultiSearchRequestBuilder {
239
- return NewMultiSearchRequestBuilder ()
183
+ return c .makeRequest (http .MethodPost , "_elastic/_msearch" , queryParams , body )
240
184
}
0 commit comments