···129129130130// Acquire implements scheduler.Acquire.
131131func (s *multiScheduler) Acquire(ctx context.Context) (*process, error) {
132132- // Start in interactive. yieldFunc will switch us to batch. sem can be nil
133133- // if we fail while switching to batch. nil value prevents us releasing
134134- // twice.
132132+ // There are two stages, interactive and batch. We first start by acquiring the interactive mode semaphore.
133133+ // At some point in the future (if this search request is expensive enough),
134134+ // yieldFunc will switch us to the batch mode semaphore.
135135+ //
136136+ // It's possible for "sem" to be nil if we fail while switching to batch. In this scenario,
137137+ // the nil value will prevent us from releasing twice.
138138+135139 sem := s.semInteractive
136140137141 if err := sem.Acquire(ctx); err != nil {
···227231// The only error it will return is a context error if ctx expires. In that
228232// case the process should stop running and call Release.
229233func (p *process) Yield(ctx context.Context) error {
234234+ // Return immediately if we have already yielded or if we haven't used up our full timeslice
235235+ // (represented via yieldTimer).
230236 if p.yieldTimer == nil || !p.yieldTimer.Exceeded() {
231237 return nil
232238 }
233239234234- // Try to yield. This can return an error if our context expired.
240240+ // We've just exceeded our timeslice.
241241+242242+ // First, try to yield. This can return an error if our context expired.
235243 err := p.yieldFunc(ctx)
236244 if err != nil {
237245 return err
238246 }
239247240240- // Successfully yielded. Stop our timer and mark it nil so we don't call
248248+ // We've successfully yielded. Second, stop our timer and mark it nil so we don't call
241249 // yieldFunc again.
242250 p.yieldTimer.Stop()
243251 p.yieldTimer = nil
+17-7
shards/shards.go
···618618 }()
619619620620 tr.LazyPrintf("before selectRepoSet shards:%d", len(shards))
621621+ // Select the subset of shards that we will search over for the given query.
621622 shards, q = selectRepoSet(shards, q)
622623 tr.LazyPrintf("after selectRepoSet shards:%d %s", len(shards), q)
623624···634635635636 defer cancel()
636637638638+ // We set the number of workers to GOMAXPROCS, or the number of shards,
639639+ // whichever is smaller.
637640 workers := runtime.GOMAXPROCS(0)
638641 if workers > len(shards) {
639642 workers = len(shards)
···653656 wg sync.WaitGroup
654657 )
655658656656- // Since searching is mostly CPU bound, we limit the number
657657- // of parallel shard searches which also reduces the peak working set
658658-659659+ // Start workers that receive shards from the search channel, search them,
660660+ // and send the results to the results channel. This process is repeated
661661+ // until the search channel is closed.
662662+ //
663663+ // Note: Making "search" a buffered channel has the effect of limiting the number of parallel shard searches.
664664+ // Since searching is mostly CPU bound, limiting parallel shard searches also reduces the peak working set.
659665 wg.Add(workers)
660666 for i := 0; i < workers; i++ {
661667 go func() {
···697703698704search:
699705 for {
700700- _ = proc.Yield(ctx) // We let searchOneShard handle context errors.
706706+ // At the top of each iteration, have the proc associated with this search yield its won "timeslice"
707707+ // to possibly allow other searches to make progress
708708+ _ = proc.Yield(ctx) // Note: we let searchOneShard handle context errors
701709702710 select {
703703- case work <- next:
711711+ case work <- next: // is there a worker available to search the next shard?
704712 pending.append(next.priority)
705713706714 shard++
···709717 } else {
710718 next = shards[shard]
711719 }
712712- case r, ok := <-results:
720720+ case r, ok := <-results: // is there a result to send back?
713721 if !ok {
714722 break search
715723 }
···725733 continue
726734 }
727735736736+ // Update the match count statistics and stop searching new shards if we've
737737+ // reached the limit set in the options.
728738 totalMatchCount += r.SearchResult.Stats.MatchCount
729739 if opts.TotalMaxMatchCount > 0 && totalMatchCount > opts.TotalMaxMatchCount {
730740 stop()
···735745 r.Priority = r.priority
736746 r.MaxPendingPriority = pending.max()
737747738738- sendByRepository(r.SearchResult, opts, sender)
748748+ sendByRepository(r.SearchResult, opts, sender) // send the result back to the client
739749 }
740750 }
741751