@@ -7,8 +7,10 @@ package code
7
7
import (
8
8
"bufio"
9
9
"context"
10
+ "errors"
10
11
"fmt"
11
12
"io"
13
+ "net"
12
14
"strconv"
13
15
"strings"
14
16
"time"
@@ -39,8 +41,11 @@ var _ Indexer = &ElasticSearchIndexer{}
39
41
40
42
// ElasticSearchIndexer implements Indexer interface
41
43
type 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 {}
44
49
}
45
50
46
51
type elasticLogger struct {
@@ -78,7 +83,23 @@ func NewElasticSearchIndexer(url, indexerName string) (*ElasticSearchIndexer, bo
78
83
indexer := & ElasticSearchIndexer {
79
84
client : client ,
80
85
indexerAliasName : indexerName ,
86
+ available : true ,
87
+ stopTimer : make (chan struct {}),
81
88
}
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
+
82
103
exists , err := indexer .init ()
83
104
if err != nil {
84
105
indexer .Close ()
@@ -126,14 +147,14 @@ func (b *ElasticSearchIndexer) init() (bool, error) {
126
147
ctx := context .Background ()
127
148
exists , err := b .client .IndexExists (b .realIndexerName ()).Do (ctx )
128
149
if err != nil {
129
- return false , err
150
+ return false , b . checkError ( err )
130
151
}
131
152
if ! exists {
132
153
mapping := defaultMapping
133
154
134
155
createIndex , err := b .client .CreateIndex (b .realIndexerName ()).BodyString (mapping ).Do (ctx )
135
156
if err != nil {
136
- return false , err
157
+ return false , b . checkError ( err )
137
158
}
138
159
if ! createIndex .Acknowledged {
139
160
return false , fmt .Errorf ("create index %s with %s failed" , b .realIndexerName (), mapping )
@@ -143,7 +164,7 @@ func (b *ElasticSearchIndexer) init() (bool, error) {
143
164
// check version
144
165
r , err := b .client .Aliases ().Do (ctx )
145
166
if err != nil {
146
- return false , err
167
+ return false , b . checkError ( err )
147
168
}
148
169
149
170
realIndexerNames := r .IndicesByAlias (b .indexerAliasName )
@@ -152,10 +173,10 @@ func (b *ElasticSearchIndexer) init() (bool, error) {
152
173
Add (b .realIndexerName (), b .indexerAliasName ).
153
174
Do (ctx )
154
175
if err != nil {
155
- return false , err
176
+ return false , b . checkError ( err )
156
177
}
157
178
if ! res .Acknowledged {
158
- return false , fmt .Errorf ("" )
179
+ return false , fmt .Errorf ("create alias %s to index %s failed" , b . indexerAliasName , b . realIndexerName () )
159
180
}
160
181
} else if len (realIndexerNames ) >= 1 && realIndexerNames [0 ] < b .realIndexerName () {
161
182
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) {
165
186
Add (b .realIndexerName (), b .indexerAliasName ).
166
187
Do (ctx )
167
188
if err != nil {
168
- return false , err
189
+ return false , b . checkError ( err )
169
190
}
170
191
if ! res .Acknowledged {
171
- return false , fmt .Errorf ("" )
192
+ return false , fmt .Errorf ("change alias %s to index %s failed" , b . indexerAliasName , b . realIndexerName () )
172
193
}
173
194
}
174
195
175
196
return exists , nil
176
197
}
177
198
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
+
178
209
func (b * ElasticSearchIndexer ) addUpdate (ctx context.Context , batchWriter git.WriteCloserError , batchReader * bufio.Reader , sha string , update fileUpdate , repo * repo_model.Repository ) ([]elastic.BulkableRequest , error ) {
179
210
// Ignore vendored files in code search
180
211
if setting .Indexer .ExcludeVendored && analyze .IsVendor (update .Filename ) {
@@ -190,7 +221,7 @@ func (b *ElasticSearchIndexer) addUpdate(ctx context.Context, batchWriter git.Wr
190
221
return nil , err
191
222
}
192
223
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 )
194
225
}
195
226
}
196
227
@@ -275,7 +306,7 @@ func (b *ElasticSearchIndexer) Index(ctx context.Context, repo *repo_model.Repos
275
306
Index (b .indexerAliasName ).
276
307
Add (reqs ... ).
277
308
Do (context .Background ())
278
- return err
309
+ return b . checkError ( err )
279
310
}
280
311
return nil
281
312
}
@@ -285,7 +316,7 @@ func (b *ElasticSearchIndexer) Delete(repoID int64) error {
285
316
_ , err := b .client .DeleteByQuery (b .indexerAliasName ).
286
317
Query (elastic .NewTermsQuery ("repo_id" , repoID )).
287
318
Do (context .Background ())
288
- return err
319
+ return b . checkError ( err )
289
320
}
290
321
291
322
// 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,
409
440
From (start ).Size (pageSize ).
410
441
Do (context .Background ())
411
442
if err != nil {
412
- return 0 , nil , nil , err
443
+ return 0 , nil , nil , b . checkError ( err )
413
444
}
414
445
415
446
return convertResult (searchResult , kw , pageSize )
@@ -423,7 +454,7 @@ func (b *ElasticSearchIndexer) Search(repoIDs []int64, language, keyword string,
423
454
Size (0 ). // We only needs stats information
424
455
Do (context .Background ())
425
456
if err != nil {
426
- return 0 , nil , nil , err
457
+ return 0 , nil , nil , b . checkError ( err )
427
458
}
428
459
429
460
query = query .Must (langQuery )
@@ -440,7 +471,7 @@ func (b *ElasticSearchIndexer) Search(repoIDs []int64, language, keyword string,
440
471
From (start ).Size (pageSize ).
441
472
Do (context .Background ())
442
473
if err != nil {
443
- return 0 , nil , nil , err
474
+ return 0 , nil , nil , b . checkError ( err )
444
475
}
445
476
446
477
total , hits , _ , err := convertResult (searchResult , kw , pageSize )
@@ -449,4 +480,33 @@ func (b *ElasticSearchIndexer) Search(repoIDs []int64, language, keyword string,
449
480
}
450
481
451
482
// 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