envoyproxy / envoyproxy/ratelimit

DogStatsD sink appends STATSD_PORT to unix:// addresses, breaking Unix Domain Socket support

Open Beginner friendly
#1,223 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Go
Stars
2.7k
Forks
535
PR merge metrics
No merged PRs in 30d

Description

## Description

The DogStatsD sink (`USE_DOG_STATSD=true`) does not support Unix Domain Socket addresses for `STATSD_HOST`. When `STATSD_HOST` is set to a `unix://` address (as documented/expected by the underlying `DataDog/datadog-go` client), the sink unconditionally appends `STATSD_PORT` to it, producing an invalid address that never resolves to the actual socket file. All stats are silently dropped — no error is logged anywhere.

## Root cause

In [`src/godogstats/dogstatsd_sink.go`](https://github.com/envoyproxy/ratelimit/blob/main/src/godogstats/dogstatsd_sink.go), `NewSink()` builds the address as:

```go
client, err := statsd.New(sink.config.host+":"+strconv.Itoa(sink.config.port), statsd.WithoutClientSideAggregation())
```

This always concatenates `host + ":" + port`, with no check for whether `host` is already a complete `unix://`, `unixgram://`, or `unixstream://` address (all of which are documented, supported formats for `datadog-go`'s `statsd.New`).

`StatsdPort` (`STATSD_PORT`) also always defaults to `8125` ([`src/settings/settings.go`](https://github.com/envoyproxy/ratelimit/blob/main/src/settings/settings.go)), so even if a user doesn't set `STATSD_PORT` at all, it's still appended.

Example: with
```
STATSD_HOST=unix:///var/run/datadog/dsd.socket
```
(and `STATSD_PORT` left at its default), the resulting address passed to `statsd.New` is:
```
unix:///var/run/datadog/dsd.socket:8125
```

`datadog-go`'s `resolveAddr`/`createWriter` (in `statsdex.go`) recognize the `unix://` prefix and pass the *rest of the string verbatim* as the socket file path — so the writer ends up trying to connect to a socket literally named `/var/run/datadog/dsd.socket:8125`, which does not exist (the real file is `/var/run/datadog/dsd.socket`, without a port suffix).

Because `datadog-go`'s UDS writer defers the actual `connect()` until the first `Write()` call (see `uds.go`, "Defer connection to first Write"), `NewSink()` returns no error and the service logs `"Stats initialized for dogstatsd"` successfully at startup. Every subsequent `Write()` then fails with `ENOENT`/"no such file or directory", but `FlushCounter`/`FlushGauge`/`FlushTimer` in `dogstatsd_sink.go` discard the error returned by `client.Count()`/`client.Gauge()`/`client.Timing()`, so the failure is completely silent — no logs, no metrics, no crash, no indication anything is wrong.

## Expected behavior

Setting `STATSD_HOST` to a `unix://`, `unixgram://`, or `unixstream://` address should work for DogStatsD over a Unix Domain Socket, without requiring/appending a port — matching how `datadog-go`'s own `statsd.New` documents its `addr` parameter:

> New returns a pointer to a new Client given an addr in the format "hostname:port" for UDP, "unix:///path/to/socket" for UDS or "\\.\pipe\path\to\pipe" for Windows Named Pipes.

## Actual behavior

`STATSD_PORT` is always appended to `STATSD_HOST`, corrupting any Unix Domain Socket address and causing all metrics to be silently dropped.

## Suggested fix

In `NewSink()`, only append `:port` when `host` does not already look like a complete address (e.g. doesn't have a `unix://`/`unixgram://`/`unixstream://` prefix or a Windows named pipe prefix), similar to how `datadog-go`'s own `resolveAddr` decides whether to append a port:

```go
addr := sink.config.host
if !hasSchemePrefix(addr) { // unix://, unixgram://, unixstream://, \\.\pipe\
addr = addr + ":" + strconv.Itoa(sink.config.port)
}
client, err := statsd.New(addr, statsd.WithoutClientSideAggregation())
```

## Environment

- `envoyproxy/ratelimit` built from a recent `main` (confirmed present as of commit range including #625/#627, June 2024, and still present in `main` as of August 2026)
- Deployed on Kubernetes/AKS with `USE_DOG_STATSD=true`, `STATSD_HOST=unix:///var/run/datadog/dsd.socket` mounted via a `hostPath` volume shared with the Datadog node agent's DogStatsD Unix Domain Socket

Contributor guide

Open the contributing guide

Research direction

Read NewSink in src/godogstats/dogstatsd_sink.go and review StatsdPort in src/settings/settings.go first. Check how the underlying datadog-go client expects unix://, unixgram://, and unixstream:// addresses. Done means socket addresses reach statsd.New unchanged while hostnames still receive the configured port.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
observability-sre
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.