feat(clients): add nim-allographer client with BaraDB driver support
CI / test (push) Has been cancelled
CI / verify (push) Has been cancelled
Clients CI / build-server (push) Has been cancelled
Clients CI / test-python (push) Has been cancelled
Clients CI / test-javascript (push) Has been cancelled
Clients CI / test-nim (push) Has been cancelled
Clients CI / test-rust (push) Has been cancelled
CI / test (push) Has been cancelled
CI / verify (push) Has been cancelled
Clients CI / build-server (push) Has been cancelled
Clients CI / test-python (push) Has been cancelled
Clients CI / test-javascript (push) Has been cancelled
Clients CI / test-nim (push) Has been cancelled
Clients CI / test-rust (push) Has been cancelled
- Pure Nim wire protocol client (async + sync, no C FFI) - Query Builder: fluent API, SQL generator, execution, transactions - Schema Builder: create/alter/drop tables, column operations - Connection pool with aging and timeout handling - ORM integration via compile-time DB_BARADB switch - Tests: test_open, test_query - Documentation: README.md, PLAN_BARADB.md
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
# nim c -r --threads:off -d:reset example/mariadb/benchmark.nim
|
||||
|
||||
import std/asyncdispatch
|
||||
import std/json
|
||||
import std/os
|
||||
import std/random
|
||||
import std/strutils
|
||||
import std/monotimes
|
||||
import std/times
|
||||
import ../../src/allographer/connection
|
||||
import ../../src/allographer/query_builder
|
||||
import ../../src/allographer/schema_builder
|
||||
|
||||
|
||||
randomize()
|
||||
|
||||
const
|
||||
range1_10000 = 1..10000
|
||||
countNum = 500
|
||||
shouldDisplayLog = false
|
||||
selectSql = "SELECT `index` as id, `randomNumber` FROM `World` WHERE `index` = ?"
|
||||
updateSql = "UPDATE `World` SET `randomNumber` = ? WHERE `index` = ?"
|
||||
|
||||
|
||||
let
|
||||
maxConnections = getEnv("DB_MAX_CONNECTION", "95").parseInt
|
||||
timeout = getEnv("DB_TIMEOUT", "30").parseInt
|
||||
|
||||
mariaUrl = getEnv("MARIA_URL")
|
||||
|
||||
|
||||
proc timeProcess(name: string, cb: proc(): Future[void]) {.async.} =
|
||||
var eachTime = 0.0
|
||||
var sumTime = 0.0
|
||||
const repeatCount = 5
|
||||
var resultStr = ""
|
||||
|
||||
for i in 1..repeatCount:
|
||||
sleep(100)
|
||||
let start = getMonoTime()
|
||||
await cb()
|
||||
eachTime = float64((getMonoTime() - start).inMilliseconds) / 1000.0
|
||||
sumTime += eachTime
|
||||
if i > 1: resultStr.add("\n")
|
||||
resultStr.add("|" & $i & "|" & $eachTime & "|")
|
||||
|
||||
echo name
|
||||
echo "|num|time|"
|
||||
echo "|---|---|"
|
||||
echo resultStr
|
||||
echo "|Avg|" & $(sumTime / repeatCount) & "|"
|
||||
echo ""
|
||||
|
||||
|
||||
proc mariaBenchmarkScenario(rdb: MariadbConnections) {.async.} =
|
||||
rdb.create(
|
||||
table("World", [
|
||||
Column.increments("index"),
|
||||
Column.integer("randomNumber").default(0)
|
||||
])
|
||||
)
|
||||
seeder(rdb, "World"):
|
||||
var data = newSeq[JsonNode]()
|
||||
for i in range1_10000:
|
||||
data.add(
|
||||
%*{"randomNumber": rand(range1_10000)}
|
||||
)
|
||||
await rdb.table("World").insert(data)
|
||||
|
||||
proc benchUpdate() {.async.} =
|
||||
var futures = newSeq[Future[void]](countNum)
|
||||
for i in 1..countNum:
|
||||
let index = rand(range1_10000)
|
||||
let number = rand(range1_10000)
|
||||
futures[i - 1] = (proc(): Future[void] {.async.} =
|
||||
discard rdb.select("index as id", "randomNumber").table("World").where("index", "=", index).first().await
|
||||
rdb.table("World").where("index", "=", index).update(%*{"randomNumber": number}).await
|
||||
)()
|
||||
await all(futures)
|
||||
|
||||
proc benchUpdatePreparedCold() {.async.} =
|
||||
let selectStmt = rdb.prepare(selectSql)
|
||||
let updateStmt = rdb.prepare(updateSql)
|
||||
defer:
|
||||
selectStmt.close().await
|
||||
updateStmt.close().await
|
||||
var futures = newSeq[Future[void]](countNum)
|
||||
for i in 1..countNum:
|
||||
let index = rand(range1_10000)
|
||||
let number = rand(range1_10000)
|
||||
futures[i - 1] = (proc(): Future[void] {.async.} =
|
||||
rdb.withConn(
|
||||
proc(ctx: MariadbPreparedContext): Future[void] {.async.} =
|
||||
discard await selectStmt.first(ctx, @[$index])
|
||||
await updateStmt.exec(ctx, @[$number, $index])
|
||||
)
|
||||
)()
|
||||
await all(futures)
|
||||
|
||||
let selectStmtWarm = rdb.prepare(selectSql)
|
||||
let updateStmtWarm = rdb.prepare(updateSql)
|
||||
|
||||
proc benchUpdatePreparedWarm() {.async.} =
|
||||
var futures = newSeq[Future[void]](countNum)
|
||||
for i in 1..countNum:
|
||||
let index = rand(range1_10000)
|
||||
let number = rand(range1_10000)
|
||||
futures[i - 1] = (proc(): Future[void] {.async.} =
|
||||
rdb.withConn(
|
||||
proc(ctx: MariadbPreparedContext): Future[void] {.async.} =
|
||||
discard await selectStmtWarm.first(ctx, @[$index])
|
||||
await updateStmtWarm.exec(ctx, @[$number, $index])
|
||||
)
|
||||
)()
|
||||
await all(futures)
|
||||
|
||||
await timeProcess("mariadb benchmark", benchUpdate)
|
||||
await timeProcess("mariadb benchmark prepared cold", benchUpdatePreparedCold)
|
||||
await timeProcess("mariadb benchmark prepared warm", benchUpdatePreparedWarm)
|
||||
|
||||
|
||||
proc main() =
|
||||
let rdb = dbOpen(MariaDB, mariaUrl, maxConnections, timeout, shouldDisplayLog=shouldDisplayLog)
|
||||
mariaBenchmarkScenario(rdb).waitFor
|
||||
|
||||
main()
|
||||
@@ -0,0 +1,164 @@
|
||||
// apt install -y libcurl4-openssl-dev
|
||||
// gcc test.c -o test -lmariadb -lcurl && ./test
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <mariadb/mysql.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
struct MemoryStruct {
|
||||
char *memory;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) {
|
||||
size_t realsize = size * nmemb;
|
||||
struct MemoryStruct *mem = (struct MemoryStruct *)userp;
|
||||
|
||||
mem->memory = realloc(mem->memory, mem->size + realsize + 1);
|
||||
if (mem->memory == NULL) {
|
||||
fprintf(stderr, "Not enough memory (realloc returned NULL)\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
memcpy(&(mem->memory[mem->size]), contents, realsize);
|
||||
mem->size += realsize;
|
||||
mem->memory[mem->size] = 0;
|
||||
|
||||
return realsize;
|
||||
}
|
||||
|
||||
int main() {
|
||||
MYSQL *conn;
|
||||
MYSQL_STMT *stmt;
|
||||
MYSQL_BIND bind[5];
|
||||
my_bool is_null[5] = {0};
|
||||
unsigned long length[5];
|
||||
|
||||
// MariaDBに接続するための設定
|
||||
const char *host = "mariadb";
|
||||
const char *user = "user";
|
||||
const char *password = "pass";
|
||||
const char *database = "database";
|
||||
|
||||
conn = mysql_init(NULL);
|
||||
if (conn == NULL) {
|
||||
fprintf(stderr, "mysql_init() failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (mysql_real_connect(conn, host, user, password, database, 0, NULL, 0) == NULL) {
|
||||
fprintf(stderr, "mysql_real_connect() failed\n");
|
||||
mysql_close(conn);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// テーブルを削除
|
||||
if (mysql_query(conn, "DROP TABLE IF EXISTS `test`")) {
|
||||
fprintf(stderr, "DROP TABLE failed. Error: %s\n", mysql_error(conn));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// テーブルを作成
|
||||
if (mysql_query(conn, "CREATE TABLE IF NOT EXISTS `test` ("
|
||||
"`bool` BOOLEAN,"
|
||||
"`int` INT,"
|
||||
"`float` DOUBLE,"
|
||||
"`str` VARCHAR(255),"
|
||||
"`data` BLOB)")) {
|
||||
fprintf(stderr, "CREATE TABLE failed. Error: %s\n", mysql_error(conn));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
CURL *curl;
|
||||
CURLcode res;
|
||||
struct MemoryStruct chunk;
|
||||
chunk.memory = malloc(1);
|
||||
chunk.size = 0;
|
||||
|
||||
curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||
curl = curl_easy_init();
|
||||
if(curl) {
|
||||
curl_easy_setopt(curl, CURLOPT_URL, "https://nim-lang.org/assets/img/twitter_banner.png");
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
|
||||
|
||||
res = curl_easy_perform(curl);
|
||||
if(res != CURLE_OK) {
|
||||
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
curl_global_cleanup();
|
||||
|
||||
// データを挿入
|
||||
stmt = mysql_stmt_init(conn);
|
||||
if (!stmt) {
|
||||
fprintf(stderr, "mysql_stmt_init() failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
const char *insert_query = "INSERT INTO `test` (`bool`, `int`, `float`, `str`, `data`) VALUES (?, ?, ?, ?, ?)";
|
||||
if (mysql_stmt_prepare(stmt, insert_query, strlen(insert_query))) {
|
||||
fprintf(stderr, "mysql_stmt_prepare() failed. Error: %s\n", mysql_stmt_error(stmt));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
memset(bind, 0, sizeof(bind));
|
||||
|
||||
my_bool bool_value = 1;
|
||||
int int_value = 1;
|
||||
double float_value = 1.1;
|
||||
char str_value[255] = "alice";
|
||||
length[0] = sizeof(bool_value);
|
||||
length[1] = sizeof(int_value);
|
||||
length[2] = sizeof(float_value);
|
||||
length[3] = strlen(str_value);
|
||||
length[4] = chunk.size;
|
||||
|
||||
bind[0].buffer_type = MYSQL_TYPE_TINY;
|
||||
bind[0].buffer = (char *)&bool_value;
|
||||
bind[0].is_null = &is_null[0];
|
||||
bind[0].length = &length[0];
|
||||
|
||||
bind[1].buffer_type = MYSQL_TYPE_LONG;
|
||||
bind[1].buffer = (char *)&int_value;
|
||||
bind[1].is_null = &is_null[1];
|
||||
bind[1].length = &length[1];
|
||||
|
||||
bind[2].buffer_type = MYSQL_TYPE_DOUBLE;
|
||||
bind[2].buffer = (char *)&float_value;
|
||||
bind[2].is_null = &is_null[2];
|
||||
bind[2].length = &length[2];
|
||||
|
||||
bind[3].buffer_type = MYSQL_TYPE_STRING;
|
||||
bind[3].buffer = (char *)str_value;
|
||||
bind[3].buffer_length = sizeof(str_value);
|
||||
bind[3].is_null = &is_null[3];
|
||||
bind[3].length = &length[3];
|
||||
|
||||
bind[4].buffer_type = MYSQL_TYPE_BLOB;
|
||||
bind[4].buffer = (char *)chunk.memory;
|
||||
bind[4].buffer_length = chunk.size;
|
||||
bind[4].is_null = &is_null[4];
|
||||
bind[4].length = &length[4];
|
||||
|
||||
if (mysql_stmt_bind_param(stmt, bind)) {
|
||||
fprintf(stderr, "mysql_stmt_bind_param() failed. Error: %s\n", mysql_stmt_error(stmt));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (mysql_stmt_execute(stmt)) {
|
||||
fprintf(stderr, "mysql_stmt_execute() failed. Error: %s\n", mysql_stmt_error(stmt));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
mysql_stmt_close(stmt);
|
||||
mysql_close(conn);
|
||||
free(chunk.memory);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user