InfluxCommunity / influxdb3-go

The go package that provides a simple and convenient way to interact with InfluxDB 3.
https://pkg.go.dev/github.com/InfluxCommunity/influxdb3-go
MIT License
21 stars 11 forks source link

Type-safe conversion for line-protocol Addfield #43

Closed arukiidou closed 8 months ago

arukiidou commented 9 months ago

Use Case

Expected behavior

func example() {
    p := NewPoint("measurement", map[string]string{}, map[string]interface{}{"measurement": "air"}, time.Now())
        // unsupported type.
    p.AddFieldFromValue("✅ Compile error.", cmplx.Inf())
        // supported type.
    p.AddFieldFromValue("✅ This is safe.", NewValueFromInt(255))
}

Actual behavior

func example() {
    p := NewPoint("measurement", map[string]string{}, map[string]interface{}{"measurement": "air"}, time.Now())
        // unsupported type.
    p.AddField("❌ Will panic.", cmplx.Inf())
        // supported type.
    p.AddField("❓This is OK, but really?", 255)
}

Additional info

NewValueFrom

func (m *Point) AddFieldFromValue(k string, v lineprotocol.Value) *Point {}
func NewValueFromInt[I Integer](v I) lineprotocol.Value {}

(Retracted)NewFieldFrom

func (m *Point) AddFieldRaw(k string, v Field) *Point {}
func NewFieldFromInt[I Integer](v I) Field {}

Why not line-protocol?

bednar commented 9 months ago

Hi @arukiidou,

Thank you for raising the issue and submitting your PR. I have a preference for Variant A.

Best regards

arukiidou commented 9 months ago

benchmarking

✅ This is not only type safe, lower memory allocation and performance improvement.

go test -bench . -benchmem
cpu: Intel(R) Core(TM) i5-10400 CPU @ 2.90GHz
// p.AddField("float64", f)
BenchmarkAddField-12                     3476425               344.5 ns/op           152 B/op          9 allocs/op
// p.AddFieldFromValue("float64", NewValueFromFloat(f))
BenchmarkNewValueFromTyped-12            9434280               121.2 ns/op            48 B/op          2 allocs/op
// p.AddFieldFromValue("float64", NewValueFromNative(f))
BenchmarkNewValueFromNative-12           4411726               269.3 ns/op           112 B/op          7 allocs/op
func BenchmarkAddField(b *testing.B) {
    // cpu: Intel(R) Core(TM) i5-10400 CPU @ 2.90GHz
    // BenchmarkAddPoint-12                  3410827           345.9 ns/op       152 B/op          9 allocs/op
    // BenchmarkNewValueFromTyped-12         9870652           122.6 ns/op        48 B/op          2 allocs/op
    // BenchmarkNewValueFromNative-12        4241845           282.0 ns/op       112 B/op          7 allocs/op
    p := NewPoint(
        "bench",
        map[string]string{
            "id":        "10ad=",
            "ven=dor":   "AWS",
            `host"name`: `ho\st "a"`,
            `x\" x`:     "a b",
        },
        map[string]interface{}{},
        time.Unix(60, 70))

    f := float64(80.1234567)
    ints := int64(-1234567890)
    u := uint64(12345677890)
    s := string(`six, "seven", eight`)
    bt := []byte(`six=seven\, eight`)
    bl := bool(false)

    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        p.AddField("float64", f)
        p.AddField("int64", ints)
        p.AddField("uint64", u)
        p.AddField("string", s)
        p.AddField("bytes", bt)
        p.AddField("bool", bl)
    }
}

func BenchmarkNewValueFromTyped(b *testing.B) {
    // BenchmarkNewValueFromTyped-12         9870652           122.6 ns/op        48 B/op          2 allocs/op
    // BenchmarkNewValueFromNative-12        4241845           282.0 ns/op       112 B/op          7 allocs/op
    p := NewPoint(
        "bench",
        map[string]string{
            "id":        "10ad=",
            "ven=dor":   "AWS",
            `host"name`: `ho\st "a"`,
            `x\" x`:     "a b",
        },
        map[string]interface{}{},
        time.Unix(60, 70))

    f := float64(80.1234567)
    ints := int64(-1234567890)
    u := uint64(12345677890)
    s := string(`six, "seven", eight`)
    bt := []byte(`six=seven\, eight`)
    bl := bool(false)

    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        p.AddFieldFromValue("float64", NewValueFromFloat(f))
        p.AddFieldFromValue("int64", NewValueFromInt(ints))
        p.AddFieldFromValue("uint64", NewValueFromUInt(u))
        p.AddFieldFromValue("string", NewValueFromString(s))
        p.AddFieldFromValue("bytes", NewValueFromString(bt))
        p.AddFieldFromValue("bool", NewValueFromBoolean(bl))
    }
}

func BenchmarkNewValueFromNative(b *testing.B) {
    // BenchmarkNewValueFromNative-12        4241845           282.0 ns/op       112 B/op          7 allocs/op
    p := NewPoint(
        "bench",
        map[string]string{
            "id":        "10ad=",
            "ven=dor":   "AWS",
            `host"name`: `ho\st "a"`,
            `x\" x`:     "a b",
        },
        map[string]interface{}{},
        time.Unix(60, 70))

    f := float64(80.1234567)
    ints := int64(-1234567890)
    u := uint64(12345677890)
    s := string(`six, "seven", eight`)
    bt := []byte(`six=seven\, eight`)
    bl := bool(false)

    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        p.AddFieldFromValue("float64", NewValueFromNative(f))
        p.AddFieldFromValue("int64", NewValueFromNative(ints))
        p.AddFieldFromValue("uint64", NewValueFromNative(u))
        p.AddFieldFromValue("string", NewValueFromNative(s))
        p.AddFieldFromValue("bytes", NewValueFromNative(bt))
        p.AddFieldFromValue("bool", NewValueFromNative(bl))
    }
}

AddField

image

NewValueFrom-Typed

image

NewValueFrom-Native

image