@@ -104,46 +104,62 @@ func NewQuickwitDatasource(settings backend.DataSourceInstanceSettings) (instanc
104104 Database : index ,
105105 MaxConcurrentShardRequests : int64 (maxConcurrentShardRequests ),
106106 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 ,
132109 }
133110
134- ds .dsInfo .ConfiguredFields .TimeField = timeField
135- ds .dsInfo .ConfiguredFields .TimeOutputFormat = timeOutputFormat
111+ ds := & QuickwitDatasource {dsInfo : model }
136112
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
139154}
140155
141156// Dispose here tells plugin SDK that plugin wants to clean up resources when a new instance
142157// created. As soon as datasource settings change detected by SDK old datasource instance will
143158// be disposed and a new one will be created using NewSampleDatasource factory function.
144159func (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)
147163}
148164
149165// CheckHealth handles health checks sent from Grafana to the plugin.
@@ -152,28 +168,37 @@ func (ds *QuickwitDatasource) Dispose() {
152168// a datasource is working as expected.
153169func (ds * QuickwitDatasource ) CheckHealth (ctx context.Context , req * backend.CheckHealthRequest ) (* backend.CheckHealthResult , error ) {
154170 res := & backend.CheckHealthResult {}
171+ res .Status = backend .HealthStatusOk
172+ res .Message = "plugin is running"
155173
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
161176
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 {
163181 res .Status = backend .HealthStatusError
164182 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 )
166186 }
187+ qwlog .Debug (res .Message )
167188
168- res .Status = backend .HealthStatusOk
169- res .Message = "plugin is running"
170189 return res , nil
171190}
172191
173192func (ds * QuickwitDatasource ) QueryData (ctx context.Context , req * backend.QueryDataRequest ) (* backend.QueryDataResponse , error ) {
174193 // 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
177202 }
178203
179204 return queryData (ctx , req .Queries , & ds .dsInfo )
0 commit comments