@@ -3,9 +3,9 @@ package schema
3
3
import (
4
4
"bytes"
5
5
"crypto/md5"
6
- "encoding/binary"
7
6
"errors"
8
7
"fmt"
8
+ "io"
9
9
"sort"
10
10
"strings"
11
11
)
@@ -15,18 +15,13 @@ var ErrInvalidOrgIdzero = errors.New("org-id cannot be 0")
15
15
var ErrInvalidEmptyName = errors .New ("name cannot be empty" )
16
16
var ErrInvalidMtype = errors .New ("invalid mtype" )
17
17
var ErrInvalidTagFormat = errors .New ("invalid tag format" )
18
+ var ErrUnknownPartitionMethod = errors .New ("unknown partition method" )
18
19
19
20
type PartitionedMetric interface {
20
21
Validate () error
21
22
SetId ()
22
- // return a []byte key comprised of the metric's OrgId
23
- // accepts an input []byte to allow callers to re-use
24
- // buffers to reduce memory allocations
25
- KeyByOrgId ([]byte ) []byte
26
- // return a []byte key comprised of the metric's Name
27
- // accepts an input []byte to allow callers to re-use
28
- // buffers to reduce memory allocations
29
- KeyBySeries ([]byte ) []byte
23
+ // PartitionID returns the partition id that should be used for this metric.
24
+ PartitionID (method PartitionByMethod , partitions int32 ) (int32 , error )
30
25
}
31
26
32
27
//go:generate msgp
@@ -63,25 +58,6 @@ func (m *MetricData) Validate() error {
63
58
return nil
64
59
}
65
60
66
- func (m * MetricData ) KeyByOrgId (b []byte ) []byte {
67
- if cap (b )- len (b ) < 4 {
68
- // not enough unused space in the slice so we need to grow it.
69
- newBuf := make ([]byte , len (b ), len (b )+ 4 )
70
- copy (newBuf , b )
71
- b = newBuf
72
- }
73
- // PutUint32 writes directly to the slice rather then appending.
74
- // so we need to set the length to 4 more bytes then it currently is.
75
- b = b [:len (b )+ 4 ]
76
- binary .LittleEndian .PutUint32 (b [len (b )- 4 :], uint32 (m .OrgId ))
77
- return b
78
- }
79
-
80
- func (m * MetricData ) KeyBySeries (b []byte ) []byte {
81
- b = append (b , []byte (m .Name )... )
82
- return b
83
- }
84
-
85
61
// returns a id (hash key) in the format OrgId.md5Sum
86
62
// the md5sum is a hash of the the concatination of the
87
63
// metric + each tag key:value pair (in metrics2.0 sense, so also fields), sorted alphabetically.
@@ -124,7 +100,7 @@ type MetricDefinition struct {
124
100
125
101
// this is a special attribute that does not need to be set, it is only used
126
102
// to keep the state of NameWithTags()
127
- nameWithTags string `json:"-"`
103
+ nameWithTags string
128
104
}
129
105
130
106
// NameWithTags deduplicates the name and tags strings by storing their content
@@ -137,27 +113,26 @@ func (m *MetricDefinition) NameWithTags() string {
137
113
return m .nameWithTags
138
114
}
139
115
140
- sort .Strings (m .Tags )
116
+ nameWithTagsBuffer := & bytes.Buffer {}
117
+ _ = writeSortedTagString (nameWithTagsBuffer , m .Name , m .Tags )
118
+ m .nameWithTags = nameWithTagsBuffer .String ()
141
119
142
- nameWithTagsBuffer := bytes .NewBufferString (m .Name )
143
- tagPositions := make ([]int , 0 , len (m .Tags )* 2 )
120
+ var i int
121
+ cursor := len (m .Name )
122
+ m .Name = m .nameWithTags [:cursor ]
144
123
for _ , t := range m .Tags {
145
- if len (t ) >= 5 && t [:5 ] == "name=" {
124
+ if len (t ) > 5 && t [:5 ] == "name=" {
146
125
continue
147
126
}
148
-
149
- nameWithTagsBuffer .WriteString (";" )
150
- tagPositions = append (tagPositions , nameWithTagsBuffer .Len ())
151
- nameWithTagsBuffer .WriteString (t )
152
- tagPositions = append (tagPositions , nameWithTagsBuffer .Len ())
127
+ m .Tags [i ] = m .nameWithTags [cursor + 1 : cursor + 1 + len (t )]
128
+ cursor += len (t ) + 1
129
+ i ++
153
130
}
154
131
155
- m .nameWithTags = nameWithTagsBuffer .String ()
156
- m .Tags = make ([]string , len (tagPositions )/ 2 )
157
- for i := 0 ; i < len (m .Tags ); i ++ {
158
- m .Tags [i ] = m .nameWithTags [tagPositions [i * 2 ]:tagPositions [i * 2 + 1 ]]
132
+ // if a "name" tag existed, then we have to shorten the slice
133
+ if i < len (m .Tags ) {
134
+ m .Tags = m .Tags [:i ]
159
135
}
160
- m .Name = m .nameWithTags [:len (m .Name )]
161
136
162
137
return m .nameWithTags
163
138
}
@@ -178,7 +153,7 @@ func (m *MetricDefinition) SetId() {
178
153
fmt .Fprintf (buffer , "%d" , m .Interval )
179
154
180
155
for _ , t := range m .Tags {
181
- if len (t ) >= 5 && t [:5 ] == "name=" {
156
+ if len (t ) > 5 && t [:5 ] == "name=" {
182
157
continue
183
158
}
184
159
@@ -211,25 +186,6 @@ func (m *MetricDefinition) Validate() error {
211
186
return nil
212
187
}
213
188
214
- func (m * MetricDefinition ) KeyByOrgId (b []byte ) []byte {
215
- if cap (b )- len (b ) < 4 {
216
- // not enough unused space in the slice so we need to grow it.
217
- newBuf := make ([]byte , len (b ), len (b )+ 4 )
218
- copy (newBuf , b )
219
- b = newBuf
220
- }
221
- // PutUint32 writes directly to the slice rather then appending.
222
- // so we need to set the length to 4 more bytes then it currently is.
223
- b = b [:len (b )+ 4 ]
224
- binary .LittleEndian .PutUint32 (b [len (b )- 4 :], uint32 (m .OrgId ))
225
- return b
226
- }
227
-
228
- func (m * MetricDefinition ) KeyBySeries (b []byte ) []byte {
229
- b = append (b , []byte (m .Name )... )
230
- return b
231
- }
232
-
233
189
// MetricDefinitionFromMetricData yields a MetricDefinition that has no references
234
190
// to the original MetricData
235
191
func MetricDefinitionFromMetricData (d * MetricData ) * MetricDefinition {
@@ -327,3 +283,30 @@ func ValidateTagValue(value string) bool {
327
283
328
284
return ! strings .ContainsRune (value , ';' )
329
285
}
286
+
287
+ func writeSortedTagString (w io.Writer , name string , tags []string ) error {
288
+ sort .Strings (tags )
289
+
290
+ _ , err := io .WriteString (w , name )
291
+ if err != nil {
292
+ return err
293
+ }
294
+
295
+ for _ , t := range tags {
296
+ if len (t ) > 5 && t [:5 ] == "name=" {
297
+ continue
298
+ }
299
+
300
+ _ , err = io .WriteString (w , ";" )
301
+ if err != nil {
302
+ return err
303
+ }
304
+
305
+ _ , err = io .WriteString (w , t )
306
+ if err != nil {
307
+ return err
308
+ }
309
+ }
310
+
311
+ return nil
312
+ }
0 commit comments