func (c *Client) Store(ctx context.Context, headers map[string]string, req []byte) error {
	var (
		wg       sync.WaitGroup
		mu       sync.Mutex
		multiErr xerrors.MultiError
	)
	for _, url := range c.urls {
		url := url
		reqCloned := append(make([]byte, 0, len(req)), req...)
		httpReq, err := http.NewRequest("POST", url, bytes.NewReader(reqCloned))
		if err != nil {
			return err
		}
		httpReq.Header.Add("Content-Encoding", "snappy")
		httpReq.Header.Set("Content-Type", "application/x-protobuf")
		httpReq.Header.Set("User-Agent", userAgent)
		httpReq.Header.Set("X-Prometheus-Remote-Write-Version", "0.1.0")
		for k, v := range headers {
			httpReq.Header.Set(k, v)
		}
		httpReq = httpReq.WithContext(ctx)
		ctx, cancel := context.WithTimeout(context.Background(), c.timeout)
		wg.Add(1)
		metrics := c.metrics[url]
		go func() {
			if err := write(ctx, httpReq, c.client); err != nil {
				mu.Lock()
				multiErr = multiErr.Add(err)
				mu.Unlock()
				metrics.errors.Inc(1)
			} else {
				metrics.success.Inc(1)
			}
			cancel()
			wg.Done()
		}()
	}
	wg.Wait()
	return multiErr.FinalError()
}
Store sends a batch of samples to the HTTP endpoint, the request is the proto marshalled
and encoded bytes from codec.go.
package main
import (
	"bufio"
	"bytes"
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"math"
	"net/http"
	"sync"
	"time"
	"promremotebench/pkg/generators"
	"github.com/golang/protobuf/proto"
	"github.com/golang/snappy"
	xerrors "github.com/m3db/m3/src/x/errors"
	"github.com/pkg/errors"
	"github.com/prometheus/common/config"
	"github.com/prometheus/common/version"
	"github.com/prometheus/prometheus/prompb"
	"github.com/uber-go/tally"
	"go.uber.org/zap"
)
func writeLoop(
	generator generators.HostsSimulator,
	scrapeDuration time.Duration,
	progressBy time.Duration,
	newSeriesPercent float64,
	remotePromClient *Client,
	remotePromBatchSize int,
	headers map[string]string,
	logger *zap.Logger,
	checker Checker,
) {
	numWorkers := maxNumScrapesActive *
		int(math.Ceil(float64(scrapeDuration)/float64(progressBy)))
	workers := make(chan struct{}, numWorkers)
	for i := 0; i < numWorkers; i++ {
		workers <- struct{}{}
	}
	ticker := time.NewTicker(progressBy)
	defer ticker.Stop()
	for range ticker.C {
		series, err := generator.Generate(progressBy, scrapeDuration,
			newSeriesPercent)
		if err != nil {
			logger.Fatal("error generating load", zap.Error(err))
		}
		select {
		case token := <-workers:
			go func() {
				checker.Store(series)
				for _, s := range series {
					remoteWrite(s, remotePromClient, remotePromBatchSize,
						headers, logger)
				}
				workers <- token
			}()
		default:
		}
	}
}
func remoteWrite(
	series []prompb.TimeSeries,
	remotePromClient *Client,
	remotePromBatchSize int,
	headers map[string]string,
	logger *zap.Logger,
) {
	i := 0
	for ; i < len(series)-remotePromBatchSize; i += remotePromBatchSize {
		remoteWriteBatch(series[i:i+remotePromBatchSize], remotePromClient,
			headers, logger)
	}
	remoteWriteBatch(series[i:], remotePromClient,
		headers, logger)
}
func remoteWriteBatch(
	series []prompb.TimeSeries,
	remotePromClient *Client,
	headers map[string]string,
	logger *zap.Logger,
) {
	ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
	defer cancel()
	req := &prompb.WriteRequest{
		Timeseries: series,
	}
	data, err := proto.Marshal(req)
	if err != nil {
		logger.Error("error marshalling prompb write request", zap.Error(err))
		return
	}
	encoded := snappy.Encode(nil, data)
	err = remotePromClient.Store(ctx, headers, encoded)
	if err != nil {
		logger.Error("error writing to remote prom store", zap.Error(err))
		return
	}
}
const maxErrMsgLen = 256
var userAgent = fmt.Sprintf("Prometheus/%s", version.Version)
type Client struct {
	urls    []string
	client  *http.Client
	timeout time.Duration
	metrics map[string]clientMetrics
}
type clientMetrics struct {
	success tally.Counter
	errors  tally.Counter
}
type recoverableError struct {
	error
}
func NewClient(
	urls []string,
	timeout time.Duration,
	scope tally.Scope,
) (*Client, error) {
	httpClient, err := config.NewClientFromConfig(config.HTTPClientConfig{}, "remote_storage", false)
	if err != nil {
		return nil, err
	}
	metrics := make(map[string]clientMetrics, len(urls))
	for _, url := range urls {
		scope := scope.Tagged(map[string]string{
			"url": url,
		})
		metrics[url] = clientMetrics{
			success: scope.Counter("success"),
			errors:  scope.Counter("errors"),
		}
	}
	return &Client{
		urls:    urls,
		client:  httpClient,
		timeout: time.Duration(timeout),
		metrics: metrics,
	}, nil
}