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
Conversation
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.
597553e
to
97e39ef
Compare
…ization buffer. They are not going through the client aggregator.
a64f8dc
to
3529331
Compare
statsd/statsd.go
Outdated
// 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 | ||
|
There was a problem hiding this comment.
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 ?
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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.
// 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 { |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
f895d2f
to
87572b0
Compare
// 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 { |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if timestamp.Unix() < 0 { | |
if timestamp.Unix() < minSupportedEpoch { |
There was a problem hiding this comment.
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?
@@ -126,6 +126,9 @@ const ( | |||
writerWindowsPipe string = "pipe" | |||
) | |||
|
|||
// noTimestamp is used as a value for metric without a given timestamp. | |||
const noTimestamp = int64(0) |
There was a problem hiding this comment.
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.
const noTimestamp = int64(0) | |
const noTimestamp = int64(minSupportedEpoch - 1) |
There was a problem hiding this comment.
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
.
There was a problem hiding this 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).
795a20f
to
04fc6a2
Compare
04fc6a2
to
da9d9c6
Compare
Adds two methods
GaugeWithTimestamp
andCountWithTimestamp
in theClientInterface
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
andCountWithTimestamp
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.