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

{telemetry,remoteconfig}: support fraction of second intervals #2364

Merged
merged 1 commit into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 15 additions & 0 deletions internal/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,18 @@ func ParseTagString(str string) map[string]string {
ForEachStringTag(str, func(key, val string) { res[key] = val })
return res
}

// FloatEnv returns the parsed float64 value of an environment variable,
// or def otherwise.
func FloatEnv(key string, def float64) float64 {
env, ok := os.LookupEnv(key)
if !ok {
return def
}
v, err := strconv.ParseFloat(env, 64)
if err != nil {
log.Warn("Non-float value for env var %s, defaulting to %f. Parse failed with error: %v", key, def, err)
return def
}
return v
}
9 changes: 4 additions & 5 deletions internal/remoteconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,13 @@ func DefaultClientConfig() ClientConfig {
}

func pollIntervalFromEnv() time.Duration {
interval := internal.IntEnv(envPollIntervalSec, 5)
interval := internal.FloatEnv(envPollIntervalSec, 5.0)
if interval < 0 {
log.Debug("Remote config: cannot use a negative poll interval: %s = %d. Defaulting to 5s.", envPollIntervalSec, interval)
return 5 * time.Second
log.Debug("Remote config: cannot use a negative poll interval: %s = %f. Defaulting to 5s.", envPollIntervalSec, interval)
interval = 5.0
} else if interval == 0 {
log.Debug("Remote config: poll interval set to 0. Polling will be continuous.")
return time.Nanosecond
}

return time.Duration(interval) * time.Second
return time.Duration(interval * float64(time.Second))
}
54 changes: 54 additions & 0 deletions internal/remoteconfig/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2023 Datadog, Inc.

package remoteconfig

import (
"testing"
"time"

"github.com/stretchr/testify/assert"
)

func Test_pollIntervalFromEnv(t *testing.T) {
defaultInterval := time.Second * time.Duration(5.0)
tests := []struct {
name string
setup func(t *testing.T)
want time.Duration
}{
{
name: "default",
setup: func(t *testing.T) {},
want: defaultInterval,
},
{
name: "float",
setup: func(t *testing.T) { t.Setenv("DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS", "0.2") },
want: time.Millisecond * 200,
},
{
name: "integer",
setup: func(t *testing.T) { t.Setenv("DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS", "2") },
want: time.Second * 2,
},
{
name: "negative",
setup: func(t *testing.T) { t.Setenv("DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS", "-1") },
want: defaultInterval,
},
{
name: "zero",
setup: func(t *testing.T) { t.Setenv("DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS", "0") },
want: time.Nanosecond,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.setup(t)
assert.Equal(t, tt.want, pollIntervalFromEnv())
})
}
}
15 changes: 9 additions & 6 deletions internal/telemetry/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ var (
// also the default URL in case connecting to the agent URL fails.
agentlessURL = "https://instrumentation-telemetry-intake.datadoghq.com/api/v2/apmtelemetry"

defaultHeartbeatInterval = 60 // seconds
defaultHeartbeatInterval = 60.0 // seconds

// LogPrefix specifies the prefix for all telemetry logging
LogPrefix = "Instrumentation telemetry: "
Expand Down Expand Up @@ -222,14 +222,17 @@ func (c *client) start(configuration []Configuration, namespace Namespace) {
}

c.flush()
c.heartbeatInterval = heartbeatInterval()
c.heartbeatT = time.AfterFunc(c.heartbeatInterval, c.backgroundHeartbeat)
}

heartbeat := internal.IntEnv("DD_TELEMETRY_HEARTBEAT_INTERVAL", defaultHeartbeatInterval)
if heartbeat < 1 || heartbeat > 3600 {
log("DD_TELEMETRY_HEARTBEAT_INTERVAL=%d not in [1,3600] range, setting to default of %d", heartbeat, defaultHeartbeatInterval)
func heartbeatInterval() time.Duration {
heartbeat := internal.FloatEnv("DD_TELEMETRY_HEARTBEAT_INTERVAL", defaultHeartbeatInterval)
if heartbeat <= 0 || heartbeat > 3600 {
log("DD_TELEMETRY_HEARTBEAT_INTERVAL=%d not in [1,3600] range, setting to default of %f", heartbeat, defaultHeartbeatInterval)
heartbeat = defaultHeartbeatInterval
}
c.heartbeatInterval = time.Duration(heartbeat) * time.Second
c.heartbeatT = time.AfterFunc(c.heartbeatInterval, c.backgroundHeartbeat)
return time.Duration(heartbeat * float64(time.Second))
}

// Stop notifies the telemetry endpoint that the app is closing. All outstanding
Expand Down
48 changes: 48 additions & 0 deletions internal/telemetry/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import (
"sync"
"testing"
"time"

"github.com/stretchr/testify/assert"
)

func TestClient(t *testing.T) {
Expand Down Expand Up @@ -387,3 +389,49 @@ func TestCollectDependencies(t *testing.T) {
t.Fatalf("Timed out waiting for dependency payload")
}
}

func Test_heartbeatInterval(t *testing.T) {
defaultInterval := time.Second * time.Duration(defaultHeartbeatInterval)
tests := []struct {
name string
setup func(t *testing.T)
want time.Duration
}{
{
name: "default",
setup: func(t *testing.T) {},
want: defaultInterval,
},
{
name: "float",
setup: func(t *testing.T) { t.Setenv("DD_TELEMETRY_HEARTBEAT_INTERVAL", "0.2") },
want: time.Millisecond * 200,
},
{
name: "integer",
setup: func(t *testing.T) { t.Setenv("DD_TELEMETRY_HEARTBEAT_INTERVAL", "2") },
want: time.Second * 2,
},
{
name: "negative",
setup: func(t *testing.T) { t.Setenv("DD_TELEMETRY_HEARTBEAT_INTERVAL", "-1") },
want: defaultInterval,
},
{
name: "zero",
setup: func(t *testing.T) { t.Setenv("DD_TELEMETRY_HEARTBEAT_INTERVAL", "0") },
want: defaultInterval,
},
{
name: "long",
setup: func(t *testing.T) { t.Setenv("DD_TELEMETRY_HEARTBEAT_INTERVAL", "4000") },
want: defaultInterval,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.setup(t)
assert.Equal(t, tt.want, heartbeatInterval())
})
}
}