@@ -104,46 +104,62 @@ func NewQuickwitDatasource(settings backend.DataSourceInstanceSettings) (instanc
104
104
Database : index ,
105
105
MaxConcurrentShardRequests : int64 (maxConcurrentShardRequests ),
106
106
ConfiguredFields : configuredFields ,
107
- IsReady : false ,
108
- }
109
- return & QuickwitDatasource {dsInfo : model }, nil
110
- }
111
-
112
- // Network dependent datasource initialization.
113
- // This is not done in the "constructor" function to allow saving the ds
114
- // even if the server is not responsive.
115
- func (ds * QuickwitDatasource ) initDatasource (force bool ) error {
116
- if ds .dsInfo .IsReady && ! force {
117
- return nil
118
- }
119
-
120
- indexMetadataList , err := GetIndexesMetadata (ds .dsInfo .Database , ds .dsInfo .URL , ds .dsInfo .HTTPClient )
121
- if err != nil {
122
- return fmt .Errorf ("failed to get index metadata : %w" , err )
123
- }
124
-
125
- if len (indexMetadataList ) == 0 {
126
- return fmt .Errorf ("no index found for %s" , ds .dsInfo .Database )
127
- }
128
-
129
- timeField , timeOutputFormat , err := GetTimestampFieldInfos (indexMetadataList )
130
- if nil != err {
131
- return err
107
+ ReadyStatus : make (chan es.ReadyStatus , 1 ),
108
+ ShouldInit : true ,
132
109
}
133
110
134
- ds .dsInfo .ConfiguredFields .TimeField = timeField
135
- ds .dsInfo .ConfiguredFields .TimeOutputFormat = timeOutputFormat
111
+ ds := & QuickwitDatasource {dsInfo : model }
136
112
137
- ds .dsInfo .IsReady = true
138
- return nil
113
+ // Create an initialization goroutine
114
+ go func (ds * QuickwitDatasource , readyStatus chan <- es.ReadyStatus ) {
115
+ var status es.ReadyStatus = es.ReadyStatus {
116
+ IsReady : false ,
117
+ Err : nil ,
118
+ }
119
+ for {
120
+ // Will retry init everytime the channel is consumed until ready
121
+ if ! status .IsReady || ds .dsInfo .ShouldInit {
122
+ qwlog .Debug ("Initializing Datasource" )
123
+ status .IsReady = true
124
+ status .Err = nil
125
+
126
+ indexMetadataList , err := GetIndexesMetadata (ds .dsInfo .Database , ds .dsInfo .URL , ds .dsInfo .HTTPClient )
127
+ if err != nil {
128
+ status .IsReady = false
129
+ status .Err = fmt .Errorf ("failed to get index metadata : %w" , err )
130
+ } else if len (indexMetadataList ) == 0 {
131
+ status .IsReady = false
132
+ status .Err = fmt .Errorf ("no index found for %s" , ds .dsInfo .Database )
133
+ } else {
134
+ timeField , timeOutputFormat , err := GetTimestampFieldInfos (indexMetadataList )
135
+ if nil != err {
136
+ status .IsReady = false
137
+ status .Err = err
138
+ } else if "" == timeField {
139
+ status .IsReady = false
140
+ status .Err = fmt .Errorf ("timefield is empty for %s" , ds .dsInfo .Database )
141
+ } else if "" == timeOutputFormat {
142
+ status .Err = fmt .Errorf ("timefield's output_format is empty, logs timestamps will not be parsed correctly for %s" , ds .dsInfo .Database )
143
+ }
144
+
145
+ ds .dsInfo .ConfiguredFields .TimeField = timeField
146
+ ds .dsInfo .ConfiguredFields .TimeOutputFormat = timeOutputFormat
147
+ ds .dsInfo .ShouldInit = false
148
+ }
149
+ }
150
+ readyStatus <- status
151
+ }
152
+ }(ds , model .ReadyStatus )
153
+ return ds , nil
139
154
}
140
155
141
156
// Dispose here tells plugin SDK that plugin wants to clean up resources when a new instance
142
157
// created. As soon as datasource settings change detected by SDK old datasource instance will
143
158
// be disposed and a new one will be created using NewSampleDatasource factory function.
144
159
func (ds * QuickwitDatasource ) Dispose () {
145
- // Clean up datasource instance resources.
146
- // TODO
160
+ // FIXME: The ReadyStatus channel should probably be closed here, but doing it
161
+ // causes odd calls to healthcheck to fail. Needs investigation
162
+ // close(ds.dsInfo.ReadyStatus)
147
163
}
148
164
149
165
// CheckHealth handles health checks sent from Grafana to the plugin.
@@ -152,28 +168,37 @@ func (ds *QuickwitDatasource) Dispose() {
152
168
// a datasource is working as expected.
153
169
func (ds * QuickwitDatasource ) CheckHealth (ctx context.Context , req * backend.CheckHealthRequest ) (* backend.CheckHealthResult , error ) {
154
170
res := & backend.CheckHealthResult {}
171
+ res .Status = backend .HealthStatusOk
172
+ res .Message = "plugin is running"
155
173
156
- if err := ds .initDatasource (true ); err != nil {
157
- res .Status = backend .HealthStatusError
158
- res .Message = fmt .Errorf ("Failed to initialize datasource: %w" , err ).Error ()
159
- return res , nil
160
- }
174
+ ds .dsInfo .ShouldInit = true
175
+ status := <- ds .dsInfo .ReadyStatus
161
176
162
- if ds .dsInfo .ConfiguredFields .TimeField == "" || ds .dsInfo .ConfiguredFields .TimeOutputFormat == "" {
177
+ if nil != status .Err {
178
+ res .Status = backend .HealthStatusError
179
+ res .Message = fmt .Errorf ("Failed to initialize datasource: %w" , status .Err ).Error ()
180
+ } else if "" == ds .dsInfo .ConfiguredFields .TimeField {
163
181
res .Status = backend .HealthStatusError
164
182
res .Message = fmt .Sprintf ("timefield is missing from index config \" %s\" " , ds .dsInfo .Database )
165
- return res , nil
183
+ } else if "" == ds .dsInfo .ConfiguredFields .TimeOutputFormat {
184
+ res .Status = backend .HealthStatusError
185
+ res .Message = fmt .Sprintf ("timefield's output_format is missing from index config \" %s\" " , ds .dsInfo .Database )
166
186
}
187
+ qwlog .Debug (res .Message )
167
188
168
- res .Status = backend .HealthStatusOk
169
- res .Message = "plugin is running"
170
189
return res , nil
171
190
}
172
191
173
192
func (ds * QuickwitDatasource ) QueryData (ctx context.Context , req * backend.QueryDataRequest ) (* backend.QueryDataResponse , error ) {
174
193
// Ensure ds is initialized, we need timestamp infos
175
- if err := ds .initDatasource (false ); err != nil {
176
- return & backend.QueryDataResponse {}, fmt .Errorf ("Failed to initialize datasource" )
194
+ status := <- ds .dsInfo .ReadyStatus
195
+ if ! status .IsReady {
196
+ qwlog .Debug (fmt .Errorf ("Datasource initialization failed: %w" , status .Err ).Error ())
197
+ response := & backend.QueryDataResponse {
198
+ Responses : backend.Responses {},
199
+ }
200
+ response .Responses ["__qwQueryDataError" ] = backend .ErrDataResponse (backend .StatusInternal , "Datasource initialization failed" )
201
+ return response , nil
177
202
}
178
203
179
204
return queryData (ctx , req .Queries , & ds .dsInfo )
0 commit comments