Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions carbon/datapoint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package carbon

import "strconv"

const (
nextPart = " "
nextNode = "."
nextTag = ";"
nextTagValue = "="
nextDatapoint = "\n"
)

// Datapoint is a value stored at a timestamp bucket.
// If no value is recorded at a particular timestamp bucket in a series,
// the value will be None (null).
// Doc: https://graphite.readthedocs.io/en/latest/terminology.html
type Datapoint struct {
path string
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest to use []bytes or bytes.Buffer here instead. Main reason is that currently WithTag, WithPrefix methods would cause a lot of unneeded allocations (strings in golang are immutable).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, you're right! The optimization won't be premature this time

value float64
timestamp int64
}

// NewDatapoint is a datapoint building function
func NewDatapoint(name string, value float64, timestamp int64) *Datapoint {
return &Datapoint{
path: name,
value: value,
timestamp: timestamp,
}
}

// WithPrefix adds provided prefix to datapoint
// Every function call extends datapoint path from start
// For example WithPrefix("second").WithPrefix("first") is
// equal to WithPrefix("first.second")
func (d *Datapoint) WithPrefix(prefix string) *Datapoint {
d.path = prefix + nextNode + d.path
return d
}

// WithTag adds provided tag=value pair to datapoint
// Every function call extends datapoint path
// For example WithTag("tag1","tag1Value").WithTag("tag2","tag2Value") adds ";tag1=tag1Value;tag2=tag2Value"
// While WithTag("tag2","tag2Value").WithTag("tag1","tag1Value") adds ";tag2=tag2Value;tag1=tag1Value"
func (d *Datapoint) WithTag(name, value string) *Datapoint {
d.path += nextTag + name + nextTagValue + value
return d
}

// Plaintext returns Graphite Plaintext record form of datapoint
func (d *Datapoint) Plaintext() string {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better to call method String as that's more go-way of doing things.

Copy link
Author

@kamaev kamaev Jul 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed it, but i need to think about this method, looks bit frustrating to me now

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed path to buffer to make things more clear

var (
value = strconv.FormatFloat(d.value, 'f', -1, 64)
timestamp = strconv.FormatInt(d.timestamp, 10)
)
return d.path + nextPart + value + nextPart + timestamp + nextDatapoint
}
100 changes: 100 additions & 0 deletions carbon/datapoint_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package carbon

import "testing"

func Test_NewDatapoint(t *testing.T) {
const (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be cleaner to use table driven tests:
https://github.com/golang/go/wiki/TableDrivenTests

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

testName = "name"
testPrefixParentNode = "parent"
testPrefixChildNode = "child"
testFirstTagName = "firstTag"
testFirstTagValue = "firstTagValue"
testSecondTagName = "secondTag"
testSecondTagValue = "secondTagValue"
testValue = 3.14159265359
testTimestamp = 1552503600
)

t.Run("simple", func(t *testing.T) {
var (
actual = NewDatapoint(testName, testValue, testTimestamp).Plaintext()
expected = "name 3.14159265359 1552503600\n"
)
if actual != expected {
t.Fatalf("expected: %s\nactual: %s", expected, actual)
}
})

t.Run("with prefix", func(t *testing.T) {
var (
actual = NewDatapoint(testName, testValue, testTimestamp).
WithPrefix(testPrefixParentNode).
Plaintext()
expected = "parent.name 3.14159265359 1552503600\n"
)
if actual != expected {
t.Fatalf("expected: %s\nactual: %s", expected, actual)
}
})

t.Run("with nested prefix", func(t *testing.T) {
var (
actual = NewDatapoint(testName, testValue, testTimestamp).
WithPrefix(testPrefixChildNode).
WithPrefix(testPrefixParentNode).
Plaintext()
expected = "parent.child.name 3.14159265359 1552503600\n"
)
if actual != expected {
t.Fatalf("expected: %s\nactual: %s", expected, actual)
}
})

t.Run("with nested prefix and tag", func(t *testing.T) {
var (
recordForms = []string{
NewDatapoint(testName, testValue, testTimestamp).
WithPrefix(testPrefixChildNode).
WithPrefix(testPrefixParentNode).
WithTag(testFirstTagName, testFirstTagValue).
Plaintext(),
NewDatapoint(testName, testValue, testTimestamp).
WithTag(testFirstTagName, testFirstTagValue).
WithPrefix(testPrefixChildNode).
WithPrefix(testPrefixParentNode).
Plaintext(),
}
expected = "parent.child.name;firstTag=firstTagValue 3.14159265359 1552503600\n"
)
for _, actual := range recordForms {
if actual != expected {
t.Fatalf("expected: %s\nactual: %s", expected, actual)
}
}
})

t.Run("with nested prefix and tags", func(t *testing.T) {
var (
recordForms = []string{
NewDatapoint(testName, testValue, testTimestamp).
WithPrefix(testPrefixChildNode).
WithPrefix(testPrefixParentNode).
WithTag(testFirstTagName, testFirstTagValue).
WithTag(testSecondTagName, testSecondTagValue).
Plaintext(),
NewDatapoint(testName, testValue, testTimestamp).
WithTag(testFirstTagName, testFirstTagValue).
WithTag(testSecondTagName, testSecondTagValue).
WithPrefix(testPrefixChildNode).
WithPrefix(testPrefixParentNode).
Plaintext(),
}
expected = "parent.child.name;firstTag=firstTagValue;secondTag=secondTagValue 3.14159265359 1552503600\n"
)
for _, actual := range recordForms {
if actual != expected {
t.Fatalf("expected: %s\nactual: %s", expected, actual)
}
}
})
}