Hey there, fellow developers! 👋 Today, we're diving into something exciting - building time series applications using GoFrame and TDengine. If you're working with IoT, industrial systems, or any application that deals with time-based data, this tutorial is for you!

What We'll Build 🎯

We'll create a Go application that can:

  • Connect to TDengine (a high-performance time series database)
  • Insert sensor data (both single and batch operations)
  • Query and analyze time series data
  • Handle real-world time series scenarios

Prerequisites 📝

Before we jump in, make sure you have:

  • Go installed on your machine
  • Basic understanding of Go programming
  • GoFrame framework installed
  • TDengine database set up

Don't have these installed? No worries! Just run:

# Install GoFrame if you haven't already
go get -u github.com/gogf/gf/v2

# Install TDengine Go driver
go get -u github.com/taosdata/driver-go/v2

Setting Up Your Project 🔧

First, let's set up our project configuration. Create a tdengine.yaml file:

tdengine:
  host: "127.0.0.1"
  port: 6030
  username: "root"
  password: "taosdata"
  database: "test"

Pro tip: 💡 Always keep your credentials in environment variables for production!

Connecting to TDengine 🔌

Let's write our first piece of code - connecting to TDengine:

package main

import (
    "database/sql"
    "fmt"
    "github.com/gogf/gf/v2/frame/g"
    "github.com/gogf/gf/v2/os/gctx"
    _ "github.com/taosdata/driver-go/v2/taosSql"
)

func main() {
    ctx := gctx.New()

    // Get configuration values
    config := map[string]interface{}{
        "host":     g.Cfg().MustGet(ctx, "tdengine.host").String(),
        "port":     g.Cfg().MustGet(ctx, "tdengine.port").Int(),
        "username": g.Cfg().MustGet(ctx, "tdengine.username").String(),
        "password": g.Cfg().MustGet(ctx, "tdengine.password").String(),
        "database": g.Cfg().MustGet(ctx, "tdengine.database").String(),
    }

    // Build connection string
    connStr := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", 
        config["username"], config["password"], 
        config["host"], config["port"], config["database"])

    // Connect to database
    db, err := sql.Open("taosSql", connStr)
    if err != nil {
        panic(fmt.Sprintf("Failed to connect: %v", err))
    }
    defer db.Close()

    fmt.Println("🎉 Successfully connected to TDengine!")
}

Storing Sensor Data 📊

Now for the fun part - let's store some sensor data! Here's a simple example:

// Create our sensor data table
_, err = db.Exec(`
    CREATE TABLE IF NOT EXISTS sensor_data (
        ts TIMESTAMP,
        temperature FLOAT,
        humidity FLOAT
    )
`)

// Insert some test data
now := time.Now()
_, err = db.Exec(`
    INSERT INTO sensor_data (ts, temperature, humidity) 
    VALUES (?, ?, ?)
`, now, 25.5, 60.8)

Batch Insertion for Better Performance 🚄

When dealing with time series data, you often need to insert multiple records at once. Here's how to do it efficiently:

func insertBatchData(conn *af.Connector) error {
    data := []string{
        `{"metric":"sensor_data","timestamp":1623000000000,"temperature":25.5,"humidity":60.8}`,
        `{"metric":"sensor_data","timestamp":1623000001000,"temperature":26.2,"humidity":59.5}`,
        `{"metric":"sensor_data","timestamp":1623000002000,"temperature":24.8,"humidity":61.2}`,
    }

    return conn.InfluxDBInsertLines(data, "ms")
}

Querying Your Time Series Data 🔍

Need to analyze your data? Here's how to query it:

func querySensorData(db *sql.DB) {
    // Get last hour's data
    startTime := time.Now().Add(-1 * time.Hour)
    rows, err := db.Query(`
        SELECT ts, temperature, humidity 
        FROM sensor_data 
        WHERE ts > ? 
        ORDER BY ts DESC
    `, startTime)
    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close()

    // Process results
    for rows.Next() {
        var ts time.Time
        var temp, humidity float32
        rows.Scan(&ts, &temp, &humidity)
        fmt.Printf("Time: %s, Temp: %.1f°C, Humidity: %.1f%%\n", 
            ts.Format(time.RFC3339), temp, humidity)
    }
}

Best Practices and Tips 💡

  1. Error Handling: Always check for errors and handle them appropriately
  2. Connection Management: Use connection pooling for better performance
  3. Batch Operations: Prefer batch inserts over single inserts for large datasets
  4. Time Precision: Be consistent with your timestamp precision
  5. Query Optimization: Use appropriate indexes and time ranges in your queries

Common Gotchas to Watch Out For ⚠️

  • Remember that TDengine's native driver uses cgo, so you'll need gcc installed
  • Always specify timestamp precision consistently
  • Be careful with timezone handling in timestamps
  • Monitor your connection pool size for optimal performance

What's Next? 🎯

Now that you have the basics down, you could:

  • Add metrics and monitoring
  • Implement data aggregation
  • Create REST APIs for your time series data
  • Build real-time dashboards

Conclusion 🎉

You now have the foundational knowledge to build time series applications with GoFrame and TDengine! This combination provides a robust platform for handling time series data at scale.

Have questions? Drop them in the comments below! And if you found this helpful, don't forget to like and share!

Happy coding! 👩‍💻👨‍💻