@@ -7,8 +7,10 @@ package code
77import (
88 "bufio"
99 "context"
10+ "errors"
1011 "fmt"
1112 "io"
13+ "net"
1214 "strconv"
1315 "strings"
1416 "time"
@@ -39,8 +41,11 @@ var _ Indexer = &ElasticSearchIndexer{}
3941
4042// ElasticSearchIndexer implements Indexer interface
4143type ElasticSearchIndexer struct {
42- client * elastic.Client
43- indexerAliasName string
44+ client * elastic.Client
45+ indexerAliasName string
46+ available bool
47+ availabilityCallback func (bool )
48+ stopTimer chan struct {}
4449}
4550
4651type elasticLogger struct {
@@ -78,7 +83,23 @@ func NewElasticSearchIndexer(url, indexerName string) (*ElasticSearchIndexer, bo
7883 indexer := & ElasticSearchIndexer {
7984 client : client ,
8085 indexerAliasName : indexerName ,
86+ available : true ,
87+ stopTimer : make (chan struct {}),
8188 }
89+
90+ ticker := time .NewTicker (10 * time .Second )
91+ go func () {
92+ for {
93+ select {
94+ case <- ticker .C :
95+ indexer .checkAvailability ()
96+ case <- indexer .stopTimer :
97+ ticker .Stop ()
98+ return
99+ }
100+ }
101+ }()
102+
82103 exists , err := indexer .init ()
83104 if err != nil {
84105 indexer .Close ()
@@ -126,14 +147,14 @@ func (b *ElasticSearchIndexer) init() (bool, error) {
126147 ctx := context .Background ()
127148 exists , err := b .client .IndexExists (b .realIndexerName ()).Do (ctx )
128149 if err != nil {
129- return false , err
150+ return false , b . checkError ( err )
130151 }
131152 if ! exists {
132153 mapping := defaultMapping
133154
134155 createIndex , err := b .client .CreateIndex (b .realIndexerName ()).BodyString (mapping ).Do (ctx )
135156 if err != nil {
136- return false , err
157+ return false , b . checkError ( err )
137158 }
138159 if ! createIndex .Acknowledged {
139160 return false , fmt .Errorf ("create index %s with %s failed" , b .realIndexerName (), mapping )
@@ -143,7 +164,7 @@ func (b *ElasticSearchIndexer) init() (bool, error) {
143164 // check version
144165 r , err := b .client .Aliases ().Do (ctx )
145166 if err != nil {
146- return false , err
167+ return false , b . checkError ( err )
147168 }
148169
149170 realIndexerNames := r .IndicesByAlias (b .indexerAliasName )
@@ -152,10 +173,10 @@ func (b *ElasticSearchIndexer) init() (bool, error) {
152173 Add (b .realIndexerName (), b .indexerAliasName ).
153174 Do (ctx )
154175 if err != nil {
155- return false , err
176+ return false , b . checkError ( err )
156177 }
157178 if ! res .Acknowledged {
158- return false , fmt .Errorf ("" )
179+ return false , fmt .Errorf ("create alias %s to index %s failed" , b . indexerAliasName , b . realIndexerName () )
159180 }
160181 } else if len (realIndexerNames ) >= 1 && realIndexerNames [0 ] < b .realIndexerName () {
161182 log .Warn ("Found older gitea indexer named %s, but we will create a new one %s and keep the old NOT DELETED. You can delete the old version after the upgrade succeed." ,
@@ -165,16 +186,26 @@ func (b *ElasticSearchIndexer) init() (bool, error) {
165186 Add (b .realIndexerName (), b .indexerAliasName ).
166187 Do (ctx )
167188 if err != nil {
168- return false , err
189+ return false , b . checkError ( err )
169190 }
170191 if ! res .Acknowledged {
171- return false , fmt .Errorf ("" )
192+ return false , fmt .Errorf ("change alias %s to index %s failed" , b . indexerAliasName , b . realIndexerName () )
172193 }
173194 }
174195
175196 return exists , nil
176197}
177198
199+ // SetAvailabilityChangeCallback sets callback that will be triggered when availability changes
200+ func (b * ElasticSearchIndexer ) SetAvailabilityChangeCallback (callback func (bool )) {
201+ b .availabilityCallback = callback
202+ }
203+
204+ // Ping checks if elastic is available
205+ func (b * ElasticSearchIndexer ) Ping () bool {
206+ return b .available
207+ }
208+
178209func (b * ElasticSearchIndexer ) addUpdate (ctx context.Context , batchWriter git.WriteCloserError , batchReader * bufio.Reader , sha string , update fileUpdate , repo * repo_model.Repository ) ([]elastic.BulkableRequest , error ) {
179210 // Ignore vendored files in code search
180211 if setting .Indexer .ExcludeVendored && analyze .IsVendor (update .Filename ) {
@@ -190,7 +221,7 @@ func (b *ElasticSearchIndexer) addUpdate(ctx context.Context, batchWriter git.Wr
190221 return nil , err
191222 }
192223 if size , err = strconv .ParseInt (strings .TrimSpace (stdout ), 10 , 64 ); err != nil {
193- return nil , fmt .Errorf ("Misformatted git cat-file output: %v" , err )
224+ return nil , fmt .Errorf ("misformatted git cat-file output: %v" , err )
194225 }
195226 }
196227
@@ -275,7 +306,7 @@ func (b *ElasticSearchIndexer) Index(ctx context.Context, repo *repo_model.Repos
275306 Index (b .indexerAliasName ).
276307 Add (reqs ... ).
277308 Do (context .Background ())
278- return err
309+ return b . checkError ( err )
279310 }
280311 return nil
281312}
@@ -285,7 +316,7 @@ func (b *ElasticSearchIndexer) Delete(repoID int64) error {
285316 _ , err := b .client .DeleteByQuery (b .indexerAliasName ).
286317 Query (elastic .NewTermsQuery ("repo_id" , repoID )).
287318 Do (context .Background ())
288- return err
319+ return b . checkError ( err )
289320}
290321
291322// indexPos find words positions for start and the following end on content. It will
@@ -409,7 +440,7 @@ func (b *ElasticSearchIndexer) Search(repoIDs []int64, language, keyword string,
409440 From (start ).Size (pageSize ).
410441 Do (context .Background ())
411442 if err != nil {
412- return 0 , nil , nil , err
443+ return 0 , nil , nil , b . checkError ( err )
413444 }
414445
415446 return convertResult (searchResult , kw , pageSize )
@@ -423,7 +454,7 @@ func (b *ElasticSearchIndexer) Search(repoIDs []int64, language, keyword string,
423454 Size (0 ). // We only needs stats information
424455 Do (context .Background ())
425456 if err != nil {
426- return 0 , nil , nil , err
457+ return 0 , nil , nil , b . checkError ( err )
427458 }
428459
429460 query = query .Must (langQuery )
@@ -440,7 +471,7 @@ func (b *ElasticSearchIndexer) Search(repoIDs []int64, language, keyword string,
440471 From (start ).Size (pageSize ).
441472 Do (context .Background ())
442473 if err != nil {
443- return 0 , nil , nil , err
474+ return 0 , nil , nil , b . checkError ( err )
444475 }
445476
446477 total , hits , _ , err := convertResult (searchResult , kw , pageSize )
@@ -449,4 +480,33 @@ func (b *ElasticSearchIndexer) Search(repoIDs []int64, language, keyword string,
449480}
450481
451482// Close implements indexer
452- func (b * ElasticSearchIndexer ) Close () {}
483+ func (b * ElasticSearchIndexer ) Close () {
484+ close (b .stopTimer )
485+ }
486+
487+ func (b * ElasticSearchIndexer ) checkError (err error ) error {
488+ var opErr * net.OpError
489+ if b .available && (elastic .IsConnErr (err ) || (errors .As (err , & opErr ) && (opErr .Op == "dial" || opErr .Op == "read" ))) {
490+ b .available = false
491+ if b .availabilityCallback != nil {
492+ b .availabilityCallback (b .available )
493+ }
494+ }
495+ return err
496+ }
497+
498+ func (b * ElasticSearchIndexer ) checkAvailability () {
499+ if b .available {
500+ return
501+ }
502+
503+ // Request cluster state to check if elastic is available again
504+ _ , err := b .client .ClusterState ().Do (context .Background ())
505+ if err != nil {
506+ return
507+ }
508+ b .available = true
509+ if b .availabilityCallback != nil {
510+ b .availabilityCallback (b .available )
511+ }
512+ }
0 commit comments