Skip to content

Commit bc847ce

Browse files
committed
use one single string to back name & tags
1 parent 7e20ecf commit bc847ce

File tree

2 files changed

+94
-62
lines changed

2 files changed

+94
-62
lines changed

metric.go

+41-55
Original file line numberDiff line numberDiff line change
@@ -113,34 +113,54 @@ type MetricDataArray []*MetricData
113113

114114
// for ES
115115
type MetricDefinition struct {
116-
Id string `json:"id"`
117-
OrgId int `json:"org_id"`
118-
Name string `json:"name" elastic:"type:string,index:not_analyzed"` // graphite format
119-
Metric string `json:"metric"` // kairosdb format (like graphite, but not including some tags)
120-
Interval int `json:"interval"` // minimum 10
121-
Unit string `json:"unit"`
122-
Mtype string `json:"mtype"`
123-
Tags []string `json:"tags" elastic:"type:string,index:not_analyzed"`
124-
LastUpdate int64 `json:"lastUpdate"` // unix timestamp
125-
Partition int32 `json:"partition"`
116+
Id string `json:"id"`
117+
OrgId int `json:"org_id"`
118+
Name string `json:"name" elastic:"type:string,index:not_analyzed"` // graphite format
119+
Metric string `json:"metric"` // kairosdb format (like graphite, but not including some tags)
120+
Interval int `json:"interval"` // minimum 10
121+
Unit string `json:"unit"`
122+
Mtype string `json:"mtype"`
123+
Tags []string `json:"tags" elastic:"type:string,index:not_analyzed"`
124+
LastUpdate int64 `json:"lastUpdate"` // unix timestamp
125+
Partition int32 `json:"partition"`
126+
NameWithTags string `json:"nameWithTags"`
126127
}
127128

128129
func (m *MetricDefinition) SetId() {
129130
sort.Strings(m.Tags)
130131

131-
buffer := bytes.NewBufferString(m.Metric)
132-
buffer.WriteByte(0)
133-
buffer.WriteString(m.Unit)
134-
buffer.WriteByte(0)
135-
buffer.WriteString(m.Mtype)
136-
buffer.WriteByte(0)
137-
fmt.Fprintf(buffer, "%d", m.Interval)
132+
nameWithTagsBuffer := bytes.NewBufferString(m.Name)
138133

139-
for _, k := range m.Tags {
140-
buffer.WriteByte(0)
141-
buffer.WriteString(k)
134+
idBuffer := bytes.NewBufferString(m.Metric)
135+
idBuffer.WriteByte(0)
136+
idBuffer.WriteString(m.Unit)
137+
idBuffer.WriteByte(0)
138+
idBuffer.WriteString(m.Mtype)
139+
idBuffer.WriteByte(0)
140+
fmt.Fprintf(idBuffer, "%d", m.Interval)
141+
142+
tagPositions := make([]int, 0, len(m.Tags)*2)
143+
for _, t := range m.Tags {
144+
if len(t) >= 5 && t[:5] == "name=" {
145+
continue
146+
}
147+
148+
nameWithTagsBuffer.WriteString(";")
149+
tagPositions = append(tagPositions, nameWithTagsBuffer.Len())
150+
nameWithTagsBuffer.WriteString(t)
151+
tagPositions = append(tagPositions, nameWithTagsBuffer.Len())
152+
153+
idBuffer.WriteByte(0)
154+
idBuffer.WriteString(t)
142155
}
143-
m.Id = fmt.Sprintf("%d.%x", m.OrgId, md5.Sum(buffer.Bytes()))
156+
157+
m.NameWithTags = fmt.Sprintf("%s", nameWithTagsBuffer)
158+
m.Tags = make([]string, len(tagPositions)/2)
159+
for i := 0; i < len(m.Tags); i++ {
160+
m.Tags[i] = m.NameWithTags[tagPositions[i*2]:tagPositions[i*2+1]]
161+
}
162+
m.Name = m.NameWithTags[:len(m.Name)]
163+
m.Id = fmt.Sprintf("%d.%x", m.OrgId, md5.Sum(idBuffer.Bytes()))
144164
}
145165

146166
func (m *MetricDefinition) Validate() error {
@@ -184,40 +204,6 @@ func (m *MetricDefinition) KeyBySeries(b []byte) []byte {
184204
return b
185205
}
186206

187-
// NameWithTags returns the full metric name, including tags
188-
// the special tag "name" is ignored
189-
//
190-
// it is assumed that SetId() is called before this method, this ensures
191-
// that the tags are sorted
192-
//
193-
// example:
194-
// name: a.b.c
195-
// tags: c=c, b=b, a=a, name=a.b.c
196-
// becomes: a.b.c;a=a;b=b;c=c
197-
//
198-
func (m *MetricDefinition) NameWithTags() string {
199-
nameLen := len(m.Name)
200-
count := 0
201-
for _, tag := range m.Tags {
202-
if len(tag) >= 5 && tag[:5] == "name=" {
203-
continue
204-
}
205-
count++
206-
nameLen += len(tag)
207-
}
208-
nameLen += count // accounting for all the ";" between tags
209-
b := make([]byte, nameLen)
210-
pos := copy(b, m.Name)
211-
for _, tag := range m.Tags {
212-
if len(tag) >= 5 && tag[:5] == "name=" {
213-
continue
214-
}
215-
pos += copy(b[pos:], ";")
216-
pos += copy(b[pos:], tag)
217-
}
218-
return string(b)
219-
}
220-
221207
func MetricDefinitionFromJSON(b []byte) (*MetricDefinition, error) {
222208
def := new(MetricDefinition)
223209
if err := json.Unmarshal(b, &def); err != nil {

metric_test.go

+53-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package schema
22

33
import (
4+
"reflect"
45
"testing"
6+
"unsafe"
57
)
68

79
func BenchmarkSetId(b *testing.B) {
@@ -50,34 +52,78 @@ func TestTagValidation(t *testing.T) {
5052

5153
func TestNameWithTags(t *testing.T) {
5254
type testCase struct {
53-
expectedName string
54-
md MetricDefinition
55+
expectedName string
56+
expectedNameWithTags string
57+
expectedTags []string
58+
md MetricDefinition
5559
}
5660

5761
testCases := []testCase{
5862
{
63+
"a.b.c",
5964
"a.b.c;tag1=value1",
65+
[]string{"tag1=value1"},
6066
MetricDefinition{Name: "a.b.c", Tags: []string{"tag1=value1", "name=ccc"}},
6167
}, {
68+
"a.b.c",
6269
"a.b.c;a=a;b=b;c=c",
70+
[]string{"a=a", "b=b", "c=c"},
6371
MetricDefinition{Name: "a.b.c", Tags: []string{"name=a.b.c", "c=c", "b=b", "a=a"}},
6472
}, {
6573
"a.b.c",
74+
"a.b.c",
75+
[]string{},
6676
MetricDefinition{Name: "a.b.c", Tags: []string{"name=a.b.c"}},
6777
}, {
6878
"a.b.c",
79+
"a.b.c",
80+
[]string{},
6981
MetricDefinition{Name: "a.b.c", Tags: []string{}},
7082
}, {
71-
"a.b.c;a=a;b=b;c=c",
72-
MetricDefinition{Name: "a.b.c", Tags: []string{"c=c", "a=a", "b=b"}},
83+
"c",
84+
"c;a=a;b=b;c=c",
85+
[]string{"a=a", "b=b", "c=c"},
86+
MetricDefinition{Name: "c", Tags: []string{"c=c", "a=a", "b=b"}},
7387
},
7488
}
7589

7690
for _, tc := range testCases {
7791
tc.md.SetId()
78-
fullName := tc.md.NameWithTags()
79-
if tc.expectedName != fullName {
80-
t.Fatalf("Expected name %s, but got %s", tc.expectedName, fullName)
92+
if tc.expectedName != tc.md.Name {
93+
t.Fatalf("Expected name %s, but got %s", tc.expectedName, tc.md.Name)
94+
}
95+
96+
if tc.expectedNameWithTags != tc.md.NameWithTags {
97+
t.Fatalf("Expected name with tags %s, but got %s", tc.expectedNameWithTags, tc.md.NameWithTags)
98+
}
99+
100+
if len(tc.expectedTags) != len(tc.md.Tags) {
101+
t.Fatalf("Expected tags %+v, but got %+v", tc.expectedTags, tc.md.Tags)
102+
}
103+
104+
for i := range tc.expectedTags {
105+
if len(tc.expectedTags[i]) != len(tc.md.Tags[i]) {
106+
t.Fatalf("Expected tags %+v, but got %+v", tc.expectedTags, tc.md.Tags)
107+
}
108+
}
109+
110+
getAddress := func(s string) uint {
111+
return uint((*reflect.StringHeader)(unsafe.Pointer(&s)).Data)
112+
}
113+
114+
nameWithTagsAddr := getAddress(tc.md.NameWithTags)
115+
116+
nameAddr := getAddress(tc.md.Name)
117+
if nameAddr != nameWithTagsAddr {
118+
t.Fatalf("Name slice does not appear to be slice of base string, %d != %d", nameAddr, nameWithTagsAddr)
119+
}
120+
121+
for i := range tc.md.Tags {
122+
tagAddr := getAddress(tc.md.Tags[i])
123+
124+
if tagAddr < nameWithTagsAddr || tagAddr > nameWithTagsAddr+uint(len(tc.md.NameWithTags)) {
125+
t.Fatalf("Tag slice does not appear to be slice of base string, %d != %d", tagAddr, nameWithTagsAddr)
126+
}
81127
}
82128
}
83129
}

0 commit comments

Comments
 (0)