naoki86star

インターネットの片隅でなにかしら書いてみる

VoltDBのclientをgoで書く

GitHub - VoltDB/voltdb-client-go: VoltDB Golang Client Library

 Voltdbはclientプログラムをgoでも書けるようになってます。*1ガチで構築するときのベンダーの推奨言語は、多分サーバを構成する言語と同じjavaだと思います。そのとき開発環境を構築するにあたりjarクラス周りを整えるのは結構気合が必要と思ってます。*2pythonでclientを書く限りは、voltdbclient.pyがあればサーバと接続できてSQLとかprocedureとか飛ばして結果が得られるようになるので、ちょいと試す分だけなら非常に敷居が低くなります。

 go-langに関しても、上記gitからmoduleをgetしておけば、それで済み、環境構築面での利便性ではpythonと比べても遜色ないです。go-langの持つ、簡潔な記述でnativeモジュールを構築できるメリットは、javaを置き換えていくのに十分な動機になりそうです。ただ、どうしても型のある言語で、SQLの結果を取り扱うコードはpythonに比べるとどうしても冗長になってしまうようです。このへん私にはあまり正確な説明はできませんが。

 なかなか、javaとpythonの両者の存在があるときにgoでclientを書こうという選択肢はまだ少ないですけど、作ろうとするplatformがgo-friendlyであれば十分選択してみたいところです。思いつくところでは、NetDataのプラグインはgo-langで作りやすかったはずです。*3

javaのclientだと、接続制御周りの機能が充実していて、冗長化した複数サーバにつないでおけるとか、再接続制御をやってくれるとか、あったりするのを、go-clientだと今のところ自分で必要に応じて非要件機能的なものを追加していかねばならないです。


# carbonに送信するような実例をprintするだけにしたexample

go get -v -u github.com/VoltDB/voltdb-client-go/voltdbclient
go build

でexeができる。

# example.go

package main

import (
    "database/sql"
    "time"
    "log"
    "fmt"
    _ "github.com/VoltDB/voltdb-client-go/voltdbclient"
)

const INTERVAL = 1

func readRowsLatency(rows *sql.Rows) []string {
    ts := time.Now().Unix()
    ret := make([]string, 0)
    for rows.Next() {
        var timestamp int64
        var host_id int64
        var hostname string
        var interval int64
        var count int64
        var tps int64
        var p50 int64
        var p95 int64
        var p99 int64
        var p99_9 int64
        var p99_99 int64
        var p99_999 int64
        var max int64
        err := rows.Scan(&timestamp, &host_id, &hostname, &interval, &count, &tps, &p50, &p95, &p99, &p99_9, &p99_99, &p99_999, &max)
        if err != nil {
            log.Fatal(err)
            break
        }
        ret = append(ret, fmt.Sprintf("voltdb.tps.%s %d %d\n", hostname, tps, ts ))
    }
    return ret
}

func watch(db *sql.DB, notifyer func([]string)) {
    counter := 0
    for {
        counter += 1
        time.Sleep(INTERVAL * time.Second)
        rows, err := db.Query("@Statistics", "Latency", 0)
        if err != nil {
            log.Fatal(err)
        } else {
            notifyer(readRowsLatency(rows))
        }
    }
    log.Fatal("reach to end of loop", counter)
}

func main() {
    db, err := sql.Open("voltdb", "127.0.0.1:21212" )
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()
    watch(db, func(r []string) {
        fmt.Println(r)
    })
}
db.Query("@Statistics", "Latency", 0)

のところで (procedure Statistics#Latencyの説明はこれ)

db.Query("@AdHoc", "select * from status")

とadhocなSQLを指定することもできたりします。

(2020/08/18 append)
procedureで複数テーブルを返してくるような場合には、voltdbclient.OpenConn() のほうを使ってQuery/Execの結果をイテレートして取り出していくようなことが必要みたいです。voltdbclient.VoltRowsまで落ちるとカラム名指定取得メソッドが使える。

*1:c/c++でも書けます

*2:aptとかで便利になりましたけどclassパスを決めてbuild.xmlとかにきっちり書き残していくのがイマドキでなくてもキツイ

*3:netdataのプラグインpythonでもさらに作りやすいんですけどね