Client Design

openGeminiAbout 7 minAbout 1959 words

Background

Due to the InfluxDB 1.X client largely being in maintenance mode, and openGemini continuously evolving, the community has decided to develop its own client SDK for openGemini to better support it. This includes functionalities like supporting multiple server addresses and the Apache Arrow Flight protocol.

Client SDK Planned Features

  • Support for connecting to multiple server addresses
  • Support for the Apache Arrow Flight protocol
  • Capabilities for SQL queries, structured queries, writing, and batch writing are detailed in the UML diagram below
  • Default timeouts, with a connection timeout of 10 seconds and read/write timeout of 30 seconds

The methods described in this document assume that the programming language does not support overloading. If overloading is supported by the programming language, some optimizations and adjustments can be made to the method names.

Client constructor params design

Database & RetentionPolicy management design

Write point design

Execute interface design

The Execute interface provides a unified SQL execution interface that automatically routes different types of statements to appropriate underlying methods. This design supports SQL-like statements including INSERT, SELECT, CREATE, DROP, and other database operations with parameter support and type safety.

Statement routing logic

Parameter support

The Execute interface supports parameterized statements with automatic type conversion:

Usage examples

Basic usage

result, err := client.Execute(opengemini.Statement{
    Database: "mydb",
    Command:  "SELECT * FROM weather LIMIT 10",
})

Parameterized query

result, err := client.Execute(opengemini.Statement{
    Database: "mydb", 
    Command:  "SELECT * FROM weather WHERE location=$loc AND temp>$temp",
    Params: map[string]any{
        "loc":  "beijing",
        "temp": 25.0,
    },
})

Parameterized insert

result, err := client.Execute(opengemini.Statement{
    Database: "mydb",
    Command:  "INSERT weather,location=$location temperature=$temp,humidity=$hum",
    Params: map[string]any{
        "location": "shanghai", 
        "temp":     30.2,
        "hum":      70,
    },
})

Query design

OpenTelemetry integration design

To enhance the observability of the OpenGemini client and facilitate tracking of performance metrics, errors, and other information related to query and write operations, this solution adopts the interceptor pattern to integrate OpenTelemetry, enabling full-link tracing. The design supports non-intrusive extensions, allowing coexistence with other interceptors (such as logging and authentication interceptors) while minimizing modifications to the original client.

Interceptor design

The interceptor pattern defines a standardized interface to hook into client operations (query/write) and inject telemetry logic.

Define the base client class,associated with the Interceptor interface

The base  Client  class manages a collection of interceptors, allowing dynamic registration and execution of interceptor logic during client operations.

Define the interceptor implementation class integrating OpenTelemetry,implementing the Interceptor interface

The OtelClient class implements the Interceptor interface, embedding OpenTelemetry logic to capture traces, metrics, and logs for client operations.

Tracing system core module

Usage Example(Go language examples)

func main() {
    var ctx = context.Background()
    shutdown, err := setupOtelSDK(ctx)
    if err != nil {
        return
    }
    defer func() {
        err = errors.Join(err, shutdown(ctx))
    }()

    config := &opengemini.Config{
        Addresses: []opengemini.Address{{
            Host: "127.0.0.1",
            Port: 8086,
        }},
    }
    client, err := opengemini.NewClient(config)
    if err != nil {
        fmt.Println(err)
        return
    }

    client.Interceptors(opengemini.NewOtelInterceptor())

    err = client.CreateDatabase("db0")
    if err != nil {
    }
}

QueryBuilder design

Ping design

Inner Http client design

Using a design similar to InnerHttpClient, encapsulate authentication, load balancing, retry logic, and more within the internal structure, providing a simple interface to the client. This enhances modularity and code clarity.

Error handling

Error message

Scene1 http request failed

$operation request failed, error: $error_details

Scene2 http response code is not 200~300

$operation error resp, code: $code, body: $body

Scene3 other error

$operation failed, error: $error_details
# example:
writePoint failed, unmarshall response body error: json: cannot unmarshal number ...