2017-04-10 18:54:12 +02:00
|
|
|
package influxdb
|
|
|
|
|
|
|
|
import (
|
|
|
|
"log"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/influxdata/influxdb/client/v2"
|
|
|
|
"github.com/influxdata/influxdb/models"
|
|
|
|
|
|
|
|
"github.com/FreifunkBremen/yanic/database"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
MeasurementNode = "node" // Measurement for per-node statistics
|
|
|
|
MeasurementGlobal = "global" // Measurement for summarized global statistics
|
|
|
|
CounterMeasurementFirmware = "firmware" // Measurement for firmware statistics
|
|
|
|
CounterMeasurementModel = "model" // Measurement for model statistics
|
|
|
|
batchMaxSize = 500
|
|
|
|
batchTimeout = 5 * time.Second
|
|
|
|
)
|
|
|
|
|
|
|
|
type Connection struct {
|
|
|
|
database.Connection
|
|
|
|
config Config
|
|
|
|
client client.Client
|
|
|
|
points chan *client.Point
|
|
|
|
wg sync.WaitGroup
|
|
|
|
}
|
|
|
|
|
|
|
|
type Config map[string]interface{}
|
|
|
|
|
|
|
|
func (c Config) Enable() bool {
|
|
|
|
return c["enable"].(bool)
|
|
|
|
}
|
|
|
|
func (c Config) Address() string {
|
|
|
|
return c["address"].(string)
|
|
|
|
}
|
|
|
|
func (c Config) Database() string {
|
|
|
|
return c["database"].(string)
|
|
|
|
}
|
|
|
|
func (c Config) Username() string {
|
|
|
|
return c["username"].(string)
|
|
|
|
}
|
|
|
|
func (c Config) Password() string {
|
|
|
|
return c["password"].(string)
|
|
|
|
}
|
2017-06-01 18:17:32 +02:00
|
|
|
func (c Config) Tags() map[string]interface{} {
|
|
|
|
if c["tags"] != nil {
|
|
|
|
return c["tags"].(map[string]interface{})
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2017-04-10 18:54:12 +02:00
|
|
|
|
|
|
|
func init() {
|
2017-04-17 20:42:06 +02:00
|
|
|
database.RegisterAdapter("influxdb", Connect)
|
2017-04-10 18:54:12 +02:00
|
|
|
}
|
|
|
|
func Connect(configuration interface{}) (database.Connection, error) {
|
|
|
|
var config Config
|
|
|
|
config = configuration.(map[string]interface{})
|
|
|
|
if !config.Enable() {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
// Make client
|
|
|
|
c, err := client.NewHTTPClient(client.HTTPConfig{
|
|
|
|
Addr: config.Address(),
|
|
|
|
Username: config.Username(),
|
|
|
|
Password: config.Password(),
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
db := &Connection{
|
|
|
|
config: config,
|
|
|
|
client: c,
|
|
|
|
points: make(chan *client.Point, 1000),
|
|
|
|
}
|
|
|
|
|
|
|
|
db.wg.Add(1)
|
|
|
|
go db.addWorker()
|
|
|
|
|
|
|
|
return db, nil
|
|
|
|
}
|
|
|
|
|
2017-04-18 03:10:16 +02:00
|
|
|
func (conn *Connection) addPoint(name string, tags models.Tags, fields models.Fields, t ...time.Time) {
|
2017-06-01 18:17:32 +02:00
|
|
|
if configTags := conn.config.Tags(); configTags != nil {
|
|
|
|
for tag, valueInterface := range configTags {
|
|
|
|
if value, ok := valueInterface.(string); ok && tags.Get([]byte(tag)) == nil {
|
|
|
|
tags.SetString(tag, value)
|
|
|
|
} else {
|
|
|
|
log.Println(name, "could not saved configured value of tag", tag)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-04-18 03:10:16 +02:00
|
|
|
point, err := client.NewPoint(name, tags.Map(), fields, t...)
|
2017-04-10 18:54:12 +02:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
conn.points <- point
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close all connection and clean up
|
|
|
|
func (conn *Connection) Close() {
|
|
|
|
close(conn.points)
|
|
|
|
conn.wg.Wait()
|
|
|
|
conn.client.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
// stores data points in batches into the influxdb
|
|
|
|
func (conn *Connection) addWorker() {
|
|
|
|
bpConfig := client.BatchPointsConfig{
|
|
|
|
Database: conn.config.Database(),
|
|
|
|
Precision: "m",
|
|
|
|
}
|
|
|
|
|
|
|
|
var bp client.BatchPoints
|
|
|
|
var err error
|
|
|
|
var writeNow, closed bool
|
|
|
|
timer := time.NewTimer(batchTimeout)
|
|
|
|
|
|
|
|
for !closed {
|
|
|
|
// wait for new points
|
|
|
|
select {
|
|
|
|
case point, ok := <-conn.points:
|
|
|
|
if ok {
|
|
|
|
if bp == nil {
|
|
|
|
// create new batch
|
|
|
|
timer.Reset(batchTimeout)
|
|
|
|
if bp, err = client.NewBatchPoints(bpConfig); err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bp.AddPoint(point)
|
|
|
|
} else {
|
|
|
|
closed = true
|
|
|
|
}
|
|
|
|
case <-timer.C:
|
|
|
|
if bp == nil {
|
|
|
|
timer.Reset(batchTimeout)
|
|
|
|
} else {
|
|
|
|
writeNow = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// write batch now?
|
|
|
|
if bp != nil && (writeNow || closed || len(bp.Points()) >= batchMaxSize) {
|
|
|
|
log.Println("saving", len(bp.Points()), "points")
|
|
|
|
|
|
|
|
if err = conn.client.Write(bp); err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
writeNow = false
|
|
|
|
bp = nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
timer.Stop()
|
|
|
|
conn.wg.Done()
|
|
|
|
}
|