2021/07/03
ローカル環境でyugabyteDBを立ててRustでアクセスしてみました。
Postgresql互換のあるk8sで展開できるDBとしてyugabyteDBがありますが、商用環境で使ってみたいなと思い、調査も兼ねてローカルでDBを立ててRustで書いたプログラムからアクセスするところまでやってみました。
docker-composeでシングルクラスターをローカルに構築
version: '2'
services:
yb-master:
image: yugabytedb/yugabyte:latest
container_name: yb-master-n1
volumes:
- .docker-data/yb-master:/mnt/master
command: [ "/home/yugabyte/bin/yb-master",
"--fs_data_dirs=/mnt/master",
"--master_addresses=yb-master-n1:7100",
"--rpc_bind_addresses=yb-master-n1:7100",
"--replication_factor=1"]
ports:
- "7000:7000"
environment:
SERVICE_7000_NAME: yb-master
yb-tserver:
image: yugabytedb/yugabyte:latest
container_name: yb-tserver-n1
volumes:
- .docker-data/yb-tserver:/mnt/tserver
command: [ "/home/yugabyte/bin/yb-tserver",
"--fs_data_dirs=/mnt/tserver",
"--start_pgsql_proxy",
"--rpc_bind_addresses=yb-tserver-n1:9100",
"--tserver_master_addrs=yb-master-n1:7100"]
ports:
- "9042:9042"
- "5433:5433"
- "9000:9000"
environment:
SERVICE_5433_NAME: ysql
SERVICE_9042_NAME: ycql
SERVICE_6379_NAME: yedis
SERVICE_9000_NAME: yb-tserver
depends_on:
- yb-master
こちらのファイルを作成して、docker-compose up
します。直下に作った.docker-data
ディレクトリにわらわらとファイルが書き出されてきます。
docker container内にログインしてアクセス
% docker exec -it yb-tserver-n1 bash
# ysqlsh
ysqlsh (11.2-YB-2.7.1.1-b0)
Type "help" for help.
yugabyte=#
host側からアクセス
psql
clientは事前にインストールする必要あります。
% psql -h localhost -p 5433 -d yugabyte -U yugabyte
rustからアクセス
- Getting Startedを参考にしました。
- ここも参考にさせてもらいました。
rustのバージョンは以下を使いました。
cat <<EOF > rust-toolchain
nightly-2021-07-02
EOF
Diesel Migrationを設定
まずは、diesel_cli
ツールをPCにインストール
% cargo install diesel_cli
.envに以下を記載
DATABASE_URL=postgres://yugabyte@localhost:5433/yugabyte
migrationを実行
% diesel setup
% diesel migration generate create_posts
# テーブル作成を確認
% cat <<EOF > ./2021-07-03-043211_create_posts/up.sql
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
title VARCHAR NOT NULL,
body TEXT NOT NULL,
published BOOLEAN NOT NULL DEFAULT 'f'
)
EOF
% diesel migration run
% psql -h localhost -p 5433 -d yugabyte -U yugabyte
# \dt
List of relations
Schema | Name | Type | Owner
--------+----------------------------+-------+----------
public | __diesel_schema_migrations | table | yugabyte
public | posts | table | yugabyte
(2 rows)
# \q
# テーブル削除を確認
% cat <<EOF > ./2021-07-03-043211_create_posts/down.sql
DROP TABLE posts
EOF
% diesel migration revert
# \dt
List of relations
Schema | Name | Type | Owner
--------+----------------------------+-------+----------
public | __diesel_schema_migrations | table | yugabyte
(1 row)
ちなみに、SAVEPOINT
はまだサポートされていないらしく、以下のコマンドを実行するとエラーになりました。
% diesel migration redo
Failed with: SAVEPOINT <transaction> not supported yet
Rustのコードから接続
Cargo.tomlに以下の依存関係を追加
[dependencies]
diesel = { version = "1.4.4", features = ["postgres", "r2d2"] }
dotenv = "0.15.0"
use diesel::prelude::*;
use diesel::pg::PgConnection;
use dotenv::dotenv;
use std::env;
pub fn establish_connection() -> PgConnection {
dotenv().ok();
let database_url = env::var("DATABASE_URL")
.expect("DATABASE_URL must be set");
PgConnection::establish(&database_url)
.expect(&format!("Error connecting to {}", database_url))
}
table! {
posts (id) {
id -> Integer,
title -> Text,
body -> Text,
published -> Bool,
}
}
#[derive(Queryable)]
pub struct Post {
pub id: i32,
pub title: String,
pub body: String,
pub published: bool,
}
#[derive(Insertable)]
#[table_name="posts"]
pub struct NewPost<'a> {
pub title: &'a str,
pub body: &'a str,
}
use diesel;
use diesel::{PgConnection, RunQueryDsl};
use diesel::prelude::*;
use crate::entities::{Post, NewPost};
use crate::scheme::posts;
pub struct PostAdapter { }
impl PostAdapter {
pub fn create<'a>(conn: &PgConnection, title: &'a str, body: &'a str) -> Post {
let new_post = NewPost {
title,
body,
};
diesel::insert_into(posts::table)
.values(&new_post)
.get_result(conn)
.expect("Error saving new post")
}
pub fn read_all(conn: &PgConnection) -> Result<Vec<Post>, String> {
posts::table
.order(posts::id)
.load::<Post>(conn)
.map_err(|err| err.to_string())
}
}
最後に、これらのソースコードをテストを実装して確認しました。
#[cfg(test)]
mod test {
use super::*;
use crate::conn::establish_connection;
use dotenv::dotenv;
#[test]
fn test_insert_and_read() {
dotenv().ok();
let connection = establish_connection();
let results = PostAdapter::read_all(&connection)
.expect("must be read from db");
assert_eq!(results.len(), 0);
let title = "new post";
let body = "test body";
PostAdapter::create(&connection, title, body);
let results = PostAdapter::read_all(&connection)
.expect("must be read from db");
assert_eq!(results.len(), 1);
}
}
ysqlからも確認できました。
yugabyte=# select * from posts;
id | title | body | published
----+----------+-----------+-----------
1 | new post | test body | f
(1 row)
感想
yugabytedbはpostgresqlと互換性があり、アプリケーション側もpostgresqlのつもりで実装すればOKのような感じでした。
ただ、完全に互換性はなくサポートされているものは限られているようです。
ただ、これだけ互換性があれば(私の場合は)そこまで悩まされることはないんじゃないかなと楽観視しています。
(私はアプリケーションを作るときに、「なるべく複雑なSQLやDBの機能は使わない」ようにしているので)
RookをベースにしたK8sのyugabyteDB用のOperatorなるものもあり、開発環境構築にはこちらを使いたいなと思っています。
関連する記事
Concordiumノードをローカルで動かしてみた
Concordiumの調査のために、ローカルでソースコードをビルドしてノードを動かしてみました
[Rust]axumとdragonflyを使ったWebsocket Chatのサンプル実装
redis互換のdragonflyをPUBSUBとして利用して、Websocket Chatアプリのサンプル実装を行いました。
[Rust]TiDBを使ったサンプルアプリケーションの実装
RustからTiDBを使ったアプリケーションの実装を行いました。
[Rust]Google Cloud Storageを利用する
GCSやNFSのファイルを扱えるpackageをRustで実装しました。