func NewWindowQueryRecorderGetters(
	knower Knower, windows []time.Duration,
) (QueryRecorder, WindowQueryGetters) {
	recorders := WindowQueryRecorders{}
	getters := WindowQueryGetters{}
	for _, window := range windows {
		rg := NewWindowRecorderGetter(knower, window)
		recorders[window] = rg
		getters[window] = rg
	}
	return recorders, getters
}
NewWindowQueryRecorderGetters returns a single QueryRecorder wrapper a set of individual
QueryRecorderGetters, one for each time window. It also returns WindowQueryGetters containing
QueryGetters for each time window.
package comm
import (
	"sync"
	"time"
	"github.com/drausin/libri/libri/common/id"
	"github.com/drausin/libri/libri/librarian/api"
	prom "github.com/prometheus/client_golang/prometheus"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)
const (
	selfPeerLabel  = "self"
	otherPeerLabel = "other"
	endpointLabel  = "endpoint"
	queryTypeLabel = "query_type"
	outcomeLabel   = "outcome"
	counterNamespace = "libri"
	counterSubsystem = "routing"
	counterName      = "peer_query_count"
)
var (
	healthyErrStatusCodes = map[codes.Code]struct{}{
		codes.InvalidArgument:   {},
		codes.PermissionDenied:  {},
		codes.ResourceExhausted: {},
	}
)
type QueryRecorder interface {
	Record(peerID id.ID, endpoint api.Endpoint, qt QueryType, o Outcome)
}
func MaybeRecordRpErr(r QueryRecorder, peerID id.ID, endpoint api.Endpoint, err error) {
	if peerID == nil {
		return
	}
	if errSt, ok := status.FromError(err); ok {
		if _, in := healthyErrStatusCodes[errSt.Code()]; in {
			r.Record(peerID, endpoint, Response, Success)
			return
		}
	}
	r.Record(peerID, endpoint, Response, Error)
}
type QueryGetter interface {
	Get(peerID id.ID, endpoint api.Endpoint) QueryOutcomes
	CountPeers(endpoint api.Endpoint, qt QueryType, known bool) int
}
type QueryRecorderGetter interface {
	QueryRecorder
	QueryGetter
}
type scalarRG struct {
	peers              map[string]endpointQueryOutcomes
	endpointQueryPeers endpointQueryPeers
	knower             Knower
	mu                 sync.Mutex
}
func NewQueryRecorderGetter(knower Knower) QueryRecorderGetter {
	return &scalarRG{
		peers:              make(map[string]endpointQueryOutcomes),
		endpointQueryPeers: newEndpointQueryPeers(),
		knower:             knower,
	}
}
func (r *scalarRG) Record(peerID id.ID, endpoint api.Endpoint, qt QueryType, o Outcome) {
	if endpoint == api.All {
		panic("cannot record outcome for all endpoints")
	}
	r.mu.Lock()
	idStr := peerID.String()
	if _, in := r.peers[idStr]; !in {
		r.peers[idStr] = newEndpointQueryOutcomes()
	}
	r.mu.Unlock()
	r.record(peerID, endpoint, qt, o)
	r.record(peerID, api.All, qt, o)
}
func (r *scalarRG) record(peerID id.ID, endpoint api.Endpoint, qt QueryType, o Outcome) {
	idStr := peerID.String()
	known := r.knower.Know(peerID)
	r.mu.Lock()
	m := r.peers[idStr][endpoint][qt][o]
	r.endpointQueryPeers[endpoint][qt][known][idStr] = struct{}{}
	r.mu.Unlock()
	m.Record()
}
func (r *scalarRG) Get(peerID id.ID, endpoint api.Endpoint) QueryOutcomes {
	r.mu.Lock()
	po, in := r.peers[peerID.String()]
	r.mu.Unlock()
	if !in {
		return newQueryOutcomes()  
	}
	return po[endpoint]
}
func (r *scalarRG) CountPeers(endpoint api.Endpoint, qt QueryType, known bool) int {
	r.mu.Lock()
	defer r.mu.Unlock()
	return len(r.endpointQueryPeers[endpoint][qt][known])
}
type WindowQueryRecorders map[time.Duration]QueryRecorder
type WindowQueryGetters map[time.Duration]QueryGetter
func (rs WindowQueryRecorders) Record(
	peerID id.ID, endpoint api.Endpoint, qt QueryType, o Outcome,
) {
	for _, r := range rs {
		r.Record(peerID, endpoint, qt, o)
	}
}