func (s *GraphStore) insert(e *spb.Entry) { i := sort.Search(len(s.entries), func(i int) bool { return compare.Entries(e, s.entries[i]) == compare.LT }) if i == len(s.entries) { s.entries = append(s.entries, e) } else if i < len(s.entries) && compare.EntriesEqual(e, s.entries[i]) { s.entries[i] = e } else if i == 0 { s.entries = append([]*spb.Entry{e}, s.entries...) } else { s.entries = append(s.entries[:i], append([]*spb.Entry{e}, s.entries[i:]...)...) } }
// invoke calls req concurrently for each delegated service in p, merges the // results, and delivers them to f. func (p *proxyService) invoke(req func(graphstore.Service, graphstore.EntryFunc) error, f graphstore.EntryFunc) error { stop := make(chan struct{}) // Closed to signal cancellation // Create a channel for each delegated request, and a callback that // delivers results to that channel. The callback will handle cancellation // signaled by a close of the stop channel, and exit early. rcv := make([]graphstore.EntryFunc, len(p.stores)) // callbacks chs := make([]chan *spb.Entry, len(p.stores)) // channels for i := range p.stores { ch := make(chan *spb.Entry) chs[i] = ch rcv[i] = func(e *spb.Entry) error { select { case <-stop: // cancellation has been signalled return nil case ch <- e: return nil } } } // Invoke the requests for each service, using the corresponding callback. errc := p.foreach(func(i int, s graphstore.Service) error { err := req(s, rcv[i]) close(chs[i]) return err }) // Accumulate and merge the results. This is a straightforward round-robin // n-finger merge of the values from the delegated requests. var h compare.ByEntries // used to preserve stream order var last *spb.Entry // used to deduplicate entries var perr error // error while accumulating go func() { defer close(stop) for { hit := false // are any requests still pending? // Give each channel a chance to produce a value, round-robin to // preserve the global ordering. for _, ch := range chs { if e, ok := <-ch; ok { hit = true heap.Push(&h, e) } } // If there are any values pending, deliver one to the consumer. // If not, and there are no more values coming, we're finished. if h.Len() != 0 { entry := heap.Pop(&h).(*spb.Entry) if last == nil || !compare.EntriesEqual(last, entry) { last = entry if err := f(entry); err != nil { if err != io.EOF { perr = err } return } } } else if !hit { return // no more work to do } } }() err := waitErr(errc) // wait for all receives to complete <-stop // wait for all sends to complete if perr != nil { return perr } return err }