Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

statsd: support sending metrics with timestamp. #262

Merged
merged 8 commits into from Oct 31, 2022
Merged

Conversation

remeh
Copy link
Contributor

@remeh remeh commented Jul 28, 2022

Adds two methods GaugeWithTimestamp and CountWithTimestamp in the ClientInterface to support sending gauge and count with timestamp, for users aggregating metrics on their side and who can provide a timestamp for a given metric.

Metrics sent using GaugeWithTimestamp and CountWithTimestamp to the DogStatsD server will be directly sent to the intake with no aggregation done server-side.

If the client is created using WithClientSideAggregation(), no aggregation will be performed on metrics submitted with timestamp, they will be written in the serialization buffer as-is.

Adds two methods in the `ClientInterface` to support sending gauge and count with
timestamp `GaugeWithTimestamp` and `CountWithTimestamp`, for users aggregating metrics
on their side and who can provide a timestamp for a given metric.
…ization buffer.

They are not going through the client aggregator.
statsd/aggregator.go Outdated Show resolved Hide resolved
statsd/buffer.go Outdated Show resolved Hide resolved
statsd/statsd.go Outdated
Comment on lines 163 to 167
// Gauge measures the value of a metric at a given time.
// Even with client side aggregation enabled, there is no aggregation done on a metric
// sent using GaugeWithTimestamp: it is written as is in the serialization buffer.
GaugeWithTimestamp(name string, value float64, tags []string, rate float64, timestamp time.Time) error

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why only gauge and count ? rate/set doesn't make send but we might want to send timing, histogram and distribution with timestamp too, no ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now, timed metrics ingestion will only be supported for Gauge and Count (DogStatsD server side and backend side).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should explain that somewhere, maybe the README or the Go doc string.

statsd/statsd.go Outdated Show resolved Hide resolved
statsd/statsd.go Outdated Show resolved Hide resolved
statsd/statsd.go Outdated Show resolved Hide resolved
statsd/statsd.go Show resolved Hide resolved
statsd/statsd.go Outdated Show resolved Hide resolved
statsd/statsd.go Show resolved Hide resolved
// Gauge measures the value of a metric at a given time.
// Even with client side aggregation enabled, there is no aggregation done on a metric
// sent using GaugeWithTimestamp: it is written as is in the serialization buffer.
func (c *Client) GaugeWithTimestamp(name string, value float64, tags []string, rate float64, timestamp time.Time) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should return an error if timestamp is 0 (same for count).

Copy link
Contributor Author

@remeh remeh Jul 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure, that's not an invalid timestamp. A weird one, but not an invalid one. For instance, 1 is also a weird one, you see what I mean?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's an error since go has default value. This means an non-initialized timestamp.
Sending a time.Time at 0 will produce a Unix timestamp of -62135596800.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A gotcha... Also linked to your other question about time.Time vs uint64...
You are right I'll add a test if timestamp.Unix() < 0, WDYT?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm trying to understand why 0 (jan 1970) is the limit here.

0 is a valid timestamp that can be represented, separate from the go zero-value.
There is a t.IsZero() that differentiates between the two:
https://go.dev/play/p/njBnvlej3Rf

Dogstatsd parsing correctly handles negative timestamps. src

I think the limiting factor here is the intake according to this comment:

    // timestamp for this value in seconds since the UNIX epoch

So my suggestion here is to add a comment about why this is the minimum supported epoch.

BTW - I noticed that the public metrics api says:

Note: Metric timestamps cannot be more than ten minutes in the future or more than one hour in the past.

I assume this doesn't apply to us? Or should we be enforcing that condition here by rejecting timestamps older than 1hr?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah.. We have to draw a limit somewhere in the clients and since the intake has a "minimum timestamp" limit and we already have other places limiting to the unix epoch in the Agent, I think having int(0) as the minimal possible value is speaking to everyone in a client. Let me know if you think I'm wrong, really open to discussion with these topics.

I assume this doesn't apply to us?

It doesn't indeed, a future PR will update the public documentation, with things more related about the "features" than the technical chunks in the client.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm trying to understand why 0 (jan 1970) is the limit here.

The idea is to provide a hard-coded limit instead of no limit, even if it has no actual feature meaning here.
I added a mention in the comment to redirect to the feature documentation.

Please report to the Datadog documentation for the maximum age of a metric.

@remeh remeh requested a review from hush-hush August 26, 2022 07:52
statsd/statsd.go Outdated Show resolved Hide resolved
// Gauge measures the value of a metric at a given time.
// Even with client side aggregation enabled, there is no aggregation done on a metric
// sent using GaugeWithTimestamp: it is written as is in the serialization buffer.
func (c *Client) GaugeWithTimestamp(name string, value float64, tags []string, rate float64, timestamp time.Time) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm trying to understand why 0 (jan 1970) is the limit here.

0 is a valid timestamp that can be represented, separate from the go zero-value.
There is a t.IsZero() that differentiates between the two:
https://go.dev/play/p/njBnvlej3Rf

Dogstatsd parsing correctly handles negative timestamps. src

I think the limiting factor here is the intake according to this comment:

    // timestamp for this value in seconds since the UNIX epoch

So my suggestion here is to add a comment about why this is the minimum supported epoch.

BTW - I noticed that the public metrics api says:

Note: Metric timestamps cannot be more than ten minutes in the future or more than one hour in the past.

I assume this doesn't apply to us? Or should we be enforcing that condition here by rejecting timestamps older than 1hr?

statsd/statsd.go Outdated
return ErrNoClient
}

if timestamp.Unix() < 0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if timestamp.Unix() < 0 {
if timestamp.Unix() < minSupportedEpoch {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I first introduced a minValidTimestamp const but it was only used here so I've decided to re-use noTimestamp instead, WDYT?

statsd/statsd.go Outdated Show resolved Hide resolved
@@ -126,6 +126,9 @@ const (
writerWindowsPipe string = "pipe"
)

// noTimestamp is used as a value for metric without a given timestamp.
const noTimestamp = int64(0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small edge case here:
In GaugeWithTimestamp, we are rejecting timestamps older than epoch 0, which implies that an epoch == 0 is valid.

But here we say that 0 represents a metric without a timestamp, which makes it impossible to submit with a timestamp of 0. GaugeWithTimestamp will happily pass it along, but then appendTimestamp will be a no-op as it matches the noTimestamp value.

Suggested change
const noTimestamp = int64(0)
const noTimestamp = int64(minSupportedEpoch - 1)

Copy link
Contributor Author

@remeh remeh Sep 19, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched to: timestamp must be > 0 to be appended to a message or to be considered valid by GaugeWithTimestamp and CountWithTimestamp.

Copy link

@ogaca-dd ogaca-dd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM (Nothing to add compare to existing comments).

@remeh remeh merged commit 6391269 into master Oct 31, 2022
@remeh remeh deleted the remeh/timed-sample branch October 31, 2022 13:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants