Browse Source

remote.Client: store urlString

During remote write, we call url.String() twice:
- to add the Endpoint() to the span
- to actually know where whe should send the request

This value does not change over time, and it's not really that
lightweight to calculate. I wrote this simple benchmark:

    func BenchmarkURLString(b *testing.B) {
        u, err := url.Parse("https://remote.write.com/api/v1")
        require.NoError(b, err)

        b.Run("string", func(b *testing.B) {
            count := 0
            for i := 0; i < b.N; i++ {
                count += len(u.String())
            }
        })
    }

And the results are ~200ns/op, 80B/op, 3 allocs/op.

Yes, we're going to go to the network here, which is a huge amount of
resources compared to this, but still, on agents that send 500 requests
per second, that is 1500 wasteful allocations per second.

Signed-off-by: Oleg Zaytsev <mail@olegzaytsev.com>
pull/12142/head
Oleg Zaytsev 2 years ago
parent
commit
beb7d3b80f
No known key found for this signature in database
GPG Key ID: 7E9FE9FD48F512EF
  1. 14
      storage/remote/client.go

14
storage/remote/client.go

@ -80,7 +80,7 @@ func init() {
// Client allows reading and writing from/to a remote HTTP endpoint.
type Client struct {
remoteName string // Used to differentiate clients in metrics.
url *config_util.URL
urlString string // url.String()
Client *http.Client
timeout time.Duration
@ -122,7 +122,7 @@ func NewReadClient(name string, conf *ClientConfig) (ReadClient, error) {
return &Client{
remoteName: name,
url: conf.URL,
urlString: conf.URL.String(),
Client: httpClient,
timeout: time.Duration(conf.Timeout),
readQueries: remoteReadQueries.WithLabelValues(name, conf.URL.String()),
@ -154,7 +154,7 @@ func NewWriteClient(name string, conf *ClientConfig) (WriteClient, error) {
return &Client{
remoteName: name,
url: conf.URL,
urlString: conf.URL.String(),
Client: httpClient,
retryOnRateLimit: conf.RetryOnRateLimit,
timeout: time.Duration(conf.Timeout),
@ -187,7 +187,7 @@ type RecoverableError struct {
// Store sends a batch of samples to the HTTP endpoint, the request is the proto marshalled
// and encoded bytes from codec.go.
func (c *Client) Store(ctx context.Context, req []byte) error {
httpReq, err := http.NewRequest("POST", c.url.String(), bytes.NewReader(req))
httpReq, err := http.NewRequest("POST", c.urlString, bytes.NewReader(req))
if err != nil {
// Errors from NewRequest are from unparsable URLs, so are not
// recoverable.
@ -255,7 +255,7 @@ func (c Client) Name() string {
// Endpoint is the remote read or write endpoint.
func (c Client) Endpoint() string {
return c.url.String()
return c.urlString
}
// Read reads from a remote endpoint.
@ -276,7 +276,7 @@ func (c *Client) Read(ctx context.Context, query *prompb.Query) (*prompb.QueryRe
}
compressed := snappy.Encode(nil, data)
httpReq, err := http.NewRequest("POST", c.url.String(), bytes.NewReader(compressed))
httpReq, err := http.NewRequest("POST", c.urlString, bytes.NewReader(compressed))
if err != nil {
return nil, fmt.Errorf("unable to create request: %w", err)
}
@ -310,7 +310,7 @@ func (c *Client) Read(ctx context.Context, query *prompb.Query) (*prompb.QueryRe
}
if httpResp.StatusCode/100 != 2 {
return nil, fmt.Errorf("remote server %s returned HTTP status %s: %s", c.url.String(), httpResp.Status, strings.TrimSpace(string(compressed)))
return nil, fmt.Errorf("remote server %s returned HTTP status %s: %s", c.urlString, httpResp.Status, strings.TrimSpace(string(compressed)))
}
uncompressed, err := snappy.Decode(nil, compressed)

Loading…
Cancel
Save