func NewClient(remoteName string, clientConfig ClientConfig, httpConfig promConfig.HTTPClientConfig) (*Client, error) {
	parsedUrl, err := url.Parse(clientConfig.URL)
	if err != nil {
		return nil, fmt.Errorf("parsing-%s-url: %w", remoteName, err)
	}
	httpClient, err := promConfig.NewClientFromConfig(httpConfig, fmt.Sprintf("%s_client", remoteName), promConfig.WithHTTP2Disabled())
	if err != nil {
		return nil, err
	}
	t := httpClient.Transport
	httpClient.Transport = &nethttp.Transport{
		RoundTripper: t,
	}
	return &Client{
		remoteName:   remoteName,
		url:          &promConfig.URL{URL: parsedUrl},
		cHTTP:        httpClient,
		clientConfig: clientConfig,
	}, nil
}
NewClient creates a new read or write client. The `clientType` should be either `read` or `write`. The client type
is used to get the auth from the auth store. If the `clientType` is other than the ones specified, then auth may not work.
package utils
import (
	"bufio"
	"bytes"
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"
	"github.com/gogo/protobuf/proto"
	"github.com/golang/snappy"
	"github.com/opentracing-contrib/go-stdlib/nethttp"
	"github.com/pkg/errors"
	promConfig "github.com/prometheus/common/config"
	"github.com/prometheus/common/version"
	"github.com/prometheus/prometheus/prompb"
	"github.com/schollz/progressbar/v3"
	"github.com/timescale/promscale/pkg/log"
)
var userAgent = fmt.Sprintf("Prometheus/%s", version.Version)
const (
	maxErrMsgLen = 512
	Read = iota
	Write
)
type (
	RecoveryAction uint8
	eventType      uint8
)
const (
	Retry RecoveryAction = iota
	Skip
	Abort
)
const (
	onTimeout eventType = iota
	onError
)
type ClientConfig struct {
	URL          string
	Timeout      time.Duration
	OnTimeout    RecoveryAction
	OnTimeoutStr string
	OnErr        RecoveryAction
	OnErrStr     string
	MaxRetry     int
	Delay time.Duration
}
func (c ClientConfig) shouldRetry(on eventType, retries int, message string) (retry bool) {
	makeDecision := func(r RecoveryAction) bool {
		switch r {
		case Retry:
			if c.MaxRetry != 0 {
				if retries > c.MaxRetry {
					log.Info("msg", "exceeded retrying limit in "+message+". Erring out.")
					return false
				}
			}
			message += "."
			log.Info("msg", fmt.Sprintf("%s Retrying again after delay", message), "delay", c.Delay)
			time.Sleep(c.Delay)
			return true
		case Abort:
		}
		return false
	}
	switch on {
	case onTimeout:
		return makeDecision(RecoveryAction(c.OnTimeout))
	case onError:
		return makeDecision(RecoveryAction(c.OnErr))
	default:
		panic("invalid 'on'. Should be 'error' or 'timeout'")
	}
}
func RecoveryActionEnum(typ string) (RecoveryAction, error) {
	switch typ {
	case "retry":
		return Retry, nil
	case "skip":
		return Skip, nil
	case "abort":
		return Abort, nil
	default:
		return 0, fmt.Errorf("invalid slab enums: expected from ['retry', 'skip', 'abort']")
	}
}
func ParseClientInfo(conf *ClientConfig) error {
	wrapErr := func(typ string, err error) error {
		return fmt.Errorf("enum conversion '%s': %w", typ, err)
	}
	enum, err := RecoveryActionEnum(conf.OnTimeoutStr)
	if err != nil {
		return wrapErr("on-timeout", err)
	}
	conf.OnTimeout = enum
	enum, err = RecoveryActionEnum(conf.OnErrStr)
	if err != nil {
		return wrapErr("on-error", err)
	}
	conf.OnErr = enum
	return nil
}
type Client struct {
	remoteName   string
	url          *promConfig.URL
	cHTTP        *http.Client
	clientConfig ClientConfig
}
func (c *Client) Config() ClientConfig {
	return c.clientConfig
}