Skip to content

Commit 74815e9

Browse files
committed
searcher: make indexing of repos concurrent
This patch attempts to improve the startup time of hound vastly in cases when we have huge number of repositories and hound would generally take long time to start because of its sequential nature of indexing. Now, we have the startup indexing in a concurrent way while respecting the config.MaxConcurrentIndexers parameter set by users in config.json. Fixes hound-search#250
1 parent f73af2c commit 74815e9

File tree

1 file changed

+84
-8
lines changed

1 file changed

+84
-8
lines changed

searcher/searcher.go

Lines changed: 84 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,24 @@ type Searcher struct {
3434
doneCh chan empty
3535
}
3636

37+
// Wrapper around searcher - used for sending the searcher struct to channel once
38+
// successfully returned from `newSearcher` function.
39+
// We need this wrapper because we need to have the `name` of a repo which a
40+
// particular searcher belongs to.
41+
type searcherWrapper struct {
42+
searcher *Searcher
43+
name string
44+
}
45+
46+
// Wrapper around error - used for sending error to channel once returned from
47+
// `newSearcher` function.
48+
// We need this wrapper because we need to have the `name` of a repo which a
49+
// particular error belongs to.
50+
type errorWrapper struct {
51+
error error
52+
name string
53+
}
54+
3755
type empty struct{}
3856
type limiter chan bool
3957

@@ -277,21 +295,49 @@ func MakeAll(cfg *config.Config) (map[string]*Searcher, map[string]error, error)
277295

278296
lim := makeLimiter(cfg.MaxConcurrentIndexers)
279297

280-
for name, repo := range cfg.Repos {
281-
s, err := newSearcher(cfg.DbPath, name, repo, refs, lim)
282-
if err != nil {
283-
log.Print(err)
284-
errs[name] = err
285-
continue
286-
}
298+
// Channel to receive the successfully created searchers.
299+
searcherCh := make(chan searcherWrapper, 1)
300+
// Channel to receive the errors in creation of searchers.
301+
errorCh := make(chan errorWrapper, 1)
302+
// Channel to quit the go routine listening for new successful searchers or errors.
303+
quitCh := make(chan struct{}, 1)
304+
305+
// Create a wait group for total number of repos so that we can proceed once the
306+
// go routines to create searchers for all repos have returned.
307+
var wg sync.WaitGroup
308+
wg.Add(len(cfg.Repos))
287309

288-
searchers[name] = s
310+
for name, repo := range cfg.Repos {
311+
go newSearcherConcurrent(cfg.DbPath, name, repo, refs, lim, searcherCh, errorCh)
289312
}
290313

314+
// Create a listener on searcherCh and errorCh.
315+
// It also listens on quitCh to decide when to return.
316+
go func() {
317+
for {
318+
select {
319+
case sw := <-searcherCh:
320+
searchers[sw.name] = sw.searcher
321+
wg.Done()
322+
case ew := <-errorCh:
323+
log.Print(ew.error)
324+
errs[ew.name] = ew.error
325+
wg.Done()
326+
case <-quitCh:
327+
return
328+
}
329+
}
330+
}()
331+
291332
if err := refs.removeUnclaimed(); err != nil {
292333
return nil, nil, err
293334
}
294335

336+
// Wait for all go routines to finish.
337+
wg.Wait()
338+
// Send close signal to quitCh to return the listening go routine.
339+
close(quitCh)
340+
295341
// after all the repos are in good shape, we start their polling
296342
for _, s := range searchers {
297343
s.begin()
@@ -464,3 +510,33 @@ func newSearcher(
464510

465511
return s, nil
466512
}
513+
514+
// This function is a wrapper around `newSearcher` function.
515+
// It respects the parameter `cfg.MaxConcurrentIndexers` while making the
516+
// creation of searchers for various repositories concurrent.
517+
func newSearcherConcurrent(
518+
dbpath, name string,
519+
repo *config.Repo,
520+
refs *foundRefs,
521+
lim limiter,
522+
searcherCh chan searcherWrapper,
523+
errorCh chan errorWrapper) {
524+
525+
// acquire a token from the rate limiter
526+
lim.Acquire()
527+
defer lim.Release()
528+
529+
s, err := newSearcher(dbpath, name, repo, refs, lim)
530+
if err != nil {
531+
errorCh <- errorWrapper{
532+
error: err,
533+
name: name,
534+
}
535+
return
536+
}
537+
538+
searcherCh <- searcherWrapper{
539+
searcher: s,
540+
name: name,
541+
}
542+
}

0 commit comments

Comments
 (0)