Skip to content

Commit 9fcd686

Browse files
datadog: add fast path for serializer
In the common case where metric names/tags are all ASCII, we can copy bytes much more quickly. Update some unrelated code to fix some formatting concerns and also use the newest syntax.
1 parent d6e7f85 commit 9fcd686

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+230
-192
lines changed

.github/workflows/golangci-lint.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
steps:
2121
- uses: actions/setup-go@v5
2222
with:
23-
go-version: '>=1.20'
23+
go-version: '>=1.24'
2424

2525
- uses: actions/checkout@v4
2626

buckets.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,5 @@ func splitMeasureField(s string) (measure, field string) {
3939
} else {
4040
field = s
4141
}
42-
return
42+
return measure, field
4343
}

cmd/dogstatsd/main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ func split(args []string, sep string) (head, tail []string) {
148148
} else {
149149
head, tail = args[:i], args[i+1:]
150150
}
151-
return
151+
return head, tail
152152
}
153153

154154
func indexOf(args []string, s string) int {
@@ -187,5 +187,5 @@ func (tags *tags) Set(s string) (err error) {
187187
}
188188
*tags = append(*tags, tag)
189189
}
190-
return
190+
return err
191191
}

datadog/append_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ func BenchmarkAppendMetric(b *testing.B) {
1717

1818
for _, test := range testMetrics {
1919
b.Run(test.m.Name, func(b *testing.B) {
20-
for i := 0; i != b.N; i++ {
20+
for b.Loop() {
2121
appendMetric(buffer[:0], test.m)
2222
}
2323
})

datadog/client.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ func bufSizeFromFD(f *os.File, sizehint int) (bufsize int, err error) {
162162
// to accept larger datagrams, or fallback to the default socket buffer size
163163
// if it failed.
164164
if bufsize, err = unix.GetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_SNDBUF); err != nil {
165-
return
165+
return bufsize, err
166166
}
167167

168168
// The kernel applies a 2x factor on the socket buffer size, only half of it
@@ -197,7 +197,7 @@ func bufSizeFromFD(f *os.File, sizehint int) (bufsize int, err error) {
197197

198198
// Creating the file put the socket in blocking mode, reverting.
199199
_ = unix.SetNonblock(fd, true)
200-
return
200+
return bufsize, err
201201
}
202202

203203
type ddWriter interface {

datadog/parse.go

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func parseEvent(s string) (e Event, err error) {
2020
header, next = nextToken(next, ':')
2121
if len(header) < 7 {
2222
err = fmt.Errorf("datadog: %#v has a malformed event header", s)
23-
return
23+
return e, err
2424
}
2525

2626
header = header[3 : len(header)-1] // Strip off '_e{' and '}'
@@ -30,13 +30,13 @@ func parseEvent(s string) (e Event, err error) {
3030
titleLen, err = strconv.ParseInt(rawTitleLen, 10, 64)
3131
if err != nil {
3232
err = fmt.Errorf("datadog: %#v has a malformed title length", s)
33-
return
33+
return e, err
3434
}
3535

3636
textLen, err = strconv.ParseInt(rawTextLen, 10, 64)
3737
if err != nil {
3838
err = fmt.Errorf("datadog: %#v has a malformed text length", s)
39-
return
39+
return e, err
4040
}
4141

4242
rawTitle := next[:titleLen]
@@ -45,12 +45,12 @@ func parseEvent(s string) (e Event, err error) {
4545

4646
if len(rawTitle) == 0 {
4747
err = fmt.Errorf("datadog: %#v has a malformed title", s)
48-
return
48+
return e, err
4949
}
5050

5151
if len(rawText) == 0 {
5252
err = fmt.Errorf("datadog: %#v has malformed text", s)
53-
return
53+
return e, err
5454
}
5555

5656
e = Event{
@@ -72,7 +72,7 @@ func parseEvent(s string) (e Event, err error) {
7272
ts, err = strconv.ParseInt(rawMetadataFields[i][2:], 10, 64)
7373
if err != nil {
7474
err = fmt.Errorf("datadog: %#v has a malformed timestamp", s)
75-
return
75+
return e, err
7676
}
7777
e.Ts = ts
7878
case 'p':
@@ -89,7 +89,7 @@ func parseEvent(s string) (e Event, err error) {
8989
tags = rawMetadataFields[i][1:]
9090
default:
9191
err = fmt.Errorf("datadog: %#v has unexpected metadata field", s)
92-
return
92+
return e, err
9393
}
9494
}
9595
}
@@ -107,7 +107,7 @@ func parseEvent(s string) (e Event, err error) {
107107
}
108108
}
109109

110-
return
110+
return e, err
111111
}
112112

113113
func parseMetric(s string) (m Metric, err error) {
@@ -125,17 +125,17 @@ func parseMetric(s string) (m Metric, err error) {
125125

126126
if len(name) == 0 {
127127
err = fmt.Errorf("datadog: %#v is missing a metric name", s)
128-
return
128+
return m, err
129129
}
130130

131131
if len(val) == 0 {
132132
err = fmt.Errorf("datadog: %#v is missing a metric value", s)
133-
return
133+
return m, err
134134
}
135135

136136
if len(typ) == 0 {
137137
err = fmt.Errorf("datadog: %#v is missing a metric type", s)
138-
return
138+
return m, err
139139
}
140140

141141
if len(rate) != 0 {
@@ -146,7 +146,7 @@ func parseMetric(s string) (m Metric, err error) {
146146
rate = rate[1:]
147147
default:
148148
err = fmt.Errorf("datadog: %#v has a malformed sample rate", s)
149-
return
149+
return m, err
150150
}
151151
}
152152

@@ -156,7 +156,7 @@ func parseMetric(s string) (m Metric, err error) {
156156
tags = tags[1:]
157157
default:
158158
err = fmt.Errorf("datadog: %#v has malformed tags", s)
159-
return
159+
return m, err
160160
}
161161
}
162162

@@ -165,13 +165,13 @@ func parseMetric(s string) (m Metric, err error) {
165165

166166
if value, err = strconv.ParseFloat(val, 64); err != nil {
167167
err = fmt.Errorf("datadog: %#v has a malformed value", s)
168-
return
168+
return m, err
169169
}
170170

171171
if len(rate) != 0 {
172172
if sampleRate, err = strconv.ParseFloat(rate, 64); err != nil {
173173
err = fmt.Errorf("datadog: %#v has a malformed sample rate", s)
174-
return
174+
return m, err
175175
}
176176
}
177177

@@ -199,7 +199,7 @@ func parseMetric(s string) (m Metric, err error) {
199199
}
200200
}
201201

202-
return
202+
return m, err
203203
}
204204

205205
func nextToken(s string, b byte) (token, next string) {
@@ -208,7 +208,7 @@ func nextToken(s string, b byte) (token, next string) {
208208
} else {
209209
token = s
210210
}
211-
return
211+
return token, next
212212
}
213213

214214
func split(s string, b byte) (head, tail string) {
@@ -217,7 +217,7 @@ func split(s string, b byte) (head, tail string) {
217217
} else {
218218
head = s
219219
}
220-
return
220+
return head, tail
221221
}
222222

223223
func count(s string, b byte) (n int) {
@@ -231,5 +231,5 @@ func count(s string, b byte) (n int) {
231231
s = s[off+1:]
232232
}
233233

234-
return
234+
return n
235235
}

datadog/parse_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func TestParseMetricFailure(t *testing.T) {
4242
func BenchmarkParseMetric(b *testing.B) {
4343
for _, test := range testMetrics {
4444
b.Run(test.m.Name, func(b *testing.B) {
45-
for i := 0; i != b.N; i++ {
45+
for b.Loop() {
4646
parseMetric(test.s)
4747
}
4848
})
@@ -64,7 +64,7 @@ func TestParseEventSuccess(t *testing.T) {
6464
func BenchmarkParseEvent(b *testing.B) {
6565
for _, test := range testEvents {
6666
b.Run(test.e.Title, func(b *testing.B) {
67-
for i := 0; i != b.N; i++ {
67+
for b.Loop() {
6868
parseEvent(test.s)
6969
}
7070
})

datadog/serializer.go

Lines changed: 24 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -223,87 +223,47 @@ func isTrim(b byte) bool { return b == '.' || b == '_' || b == '-' }
223223
// appendSanitizedMetricName converts *any* string into something that StatsD / Graphite
224224
// accepts without complaints.
225225
func appendSanitizedMetricName(dst []byte, raw string) []byte {
226-
nameLen := 0
227-
orig := len(dst)
226+
origLen := len(dst)
228227
if raw == "" {
229228
if len(dst) == 0 {
230229
return append(dst, "_unnamed_"...)
231230
}
232231
return dst
233232
}
234-
// ── 1. accent folding (creates one temporary ↴)
235-
// tmp := stripUnicodeAccents([]byte(raw))
236233

237-
// ── 2. run the same ASCII sanitizer, but write into dst
234+
// Simple transformation: iterate through runes and convert/replace as needed
238235
lastWasRepl := false
239-
for i := 0; i < len(raw); i++ {
240-
c := byte(raw[i])
236+
for i, r := range raw {
237+
if i >= maxLen {
238+
break
239+
}
241240

242-
if c < 128 && valid[c] {
243-
// ASCII valid chars
244-
dst = append(dst, c)
245-
nameLen++
241+
if r < utf8.RuneSelf && valid[byte(r)] {
242+
// Valid ASCII character
243+
dst = append(dst, byte(r))
246244
lastWasRepl = false
247-
} else if c >= 0xC2 && c <= 0xC3 && i+1 < len(raw) {
248-
// Check for 2-byte UTF-8 sequences that are common accented letters
249-
c2 := byte(raw[i+1])
250-
if c2 >= 0x80 && c2 <= 0xBF { // Valid second byte
251-
// Decode the 2-byte sequence
252-
codepoint := uint16(c&0x1F)<<6 | uint16(c2&0x3F)
253-
254-
// Map common accented characters (U+00C0-U+00FF range)
255-
if codepoint >= 0xC0 && codepoint <= 0xFF {
256-
mapped := latin1SupplementMap[codepoint]
257-
if valid[mapped] {
258-
dst = append(dst, mapped)
259-
nameLen++
260-
lastWasRepl = false
261-
i++ // Skip the second byte
262-
continue
263-
}
264-
}
265-
}
266-
// If we get here, treat as invalid
267-
if !lastWasRepl {
268-
dst = append(dst, replacement)
269-
nameLen++
270-
lastWasRepl = true
271-
}
272-
} else {
273-
// Everything else (3-byte, 4-byte sequences, invalid chars)
274-
// Skip continuation bytes (0x80-0xBF) to avoid creating invalid UTF-8
275-
for i+1 < len(raw) && (raw[i+1]&0xC0) == 0x80 {
276-
i++
277-
}
278-
if !lastWasRepl {
245+
} else if r >= 0xC0 && r <= 0xFF {
246+
// Latin-1 Supplement block (common accented characters like À, É, ñ)
247+
mapped := latin1SupplementMap[r]
248+
if valid[mapped] {
249+
dst = append(dst, mapped)
250+
lastWasRepl = false
251+
} else if !lastWasRepl {
279252
dst = append(dst, replacement)
280-
nameLen++
281253
lastWasRepl = true
282254
}
283-
}
284-
285-
if nameLen >= maxLen {
286-
break
255+
} else if !lastWasRepl {
256+
// Invalid or unsupported character - only append if we didn't just add a replacement
257+
dst = append(dst, replacement)
258+
lastWasRepl = true
287259
}
288260
}
289261

290-
// 3. trim leading / trailing '.', '_' or '-'
291-
start, end := orig, len(dst)
292-
for start < end && isTrim(dst[start]) {
293-
start++
294-
}
295-
for end > start && isTrim(dst[end-1]) {
296-
end--
297-
}
298-
299-
// 4. compact if we trimmed something
300-
if start > orig || end < len(dst) {
301-
copy(dst[orig:], dst[start:end])
302-
dst = dst[:orig+(end-start)]
303-
}
262+
// Trim leading/trailing '.', '_' or '-'
263+
trimmed := bytes.Trim(dst[origLen:], "._-")
264+
dst = append(dst[:origLen], trimmed...)
304265

305-
// 5. fallback if everything vanished
306-
if len(dst) == orig {
266+
if len(dst) == origLen {
307267
return append(dst, "_truncated_"...)
308268
}
309269
return dst

0 commit comments

Comments
 (0)