Turso を本格的に Deno Deploy で使っていくために確認をしていたら、いくつか思い違い(ドキュメント全部読め)があったので、まとめておきたい。
速度回りの部分は、少し有用な情報だと信じてる。
参考
問題 - 速度がずいぶん遅い -
以下のソースコードを用意し、Deno Deploy から Turso に問い合わせをさせて、速度を確認した。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import "https://deno.land/std@0.185.0/dotenv/load.ts"; import { serve } from "https://deno.land/std@0.185.0/http/server.ts"; import { type Config, createClient } from "https://esm.sh/@libsql/client";
const config = { url: Deno.env.get("TURSO_DB_URL"), authToken: Deno.env.get("TURSO_AUTH_TOKEN"), } as Config;
const db = createClient(config);
await serve( async (request) => { console.time('select'); const selectResult = await db.execute("SELECT * FROM items"); console.timeEnd('select');
console.time('insert'); await db.execute({ sql: "INSERT INTO items(name) VALUES(?)", args: [`USER${selectResult.rows.length}`], }); console.timeEnd('insert');
return new Response(JSON.stringify({ items: selectResult.rows }), { headers: { "content-type": "application/json" }, }); }, { port: 8000 } );
|
これを Deno Deploy にて動かす。
select とinsert の速度はそれぞれ Deno Deploy の Logs で確認できる。
手元のブラウザで、アクセスすると次のような結果になった。
1 2
| select: 20ms insert: 11ms
|
悪くはなさそう。
続けて、アメリカ バージニア州のリージョンでEC2にサーバーを立てて、curlで呼び出してみる。
1 2
| select: 658ms insert: 162ms
|
ずいぶん遅いと感じたので、調査開始。
フォーラムで質問
質問を投げたらすぐ応答があった。
要約すると「レプリカの作成が不足しているのでは」ということだった。
ここは思い違いをしていて、Turso が各エッジに自動的に展開をしてくれていると認識していたが、それは誤りでレプリカの作成操作が必要だった。
というわけでレプリカを作成する。
1 2
| $ turso db replicate [プライマリのデータベース名] iad
|
作成できたので、再度バージニアに立てたサーバーから curl でリクエストを送ってみる。
結果は次の通り。
1 2
| select: 7ms insert: 184ms
|
select が高速化、日本でリクエストしているのと大差ない速度になった。
思い違い
Turso はプライマリが、負荷基準で切り替わるものと思い込んでいたが、実際は異なっていた。
ざっと並べると次のようになっている。
- プライマリインスタンスは、CLIで作成した時に一番近いところを設定される。
- CLIで作成するとき、任意の場所にプライマリインスタンスを作成できる。
- プライマリインスタンスは移動できない。
- レプリカは手動で作成する必要がある。
- 読み込みは、レプリカに接続することで待ち時間を最小化する。
- 書き込みは、プライマリインスタンスにホップ(ドキュメントではホップと書いてある)する。
- プライマリインスタンスに書き込まれた内容は、レプリカに反映される。
日本からだけアクセスを試しいたら、ずっと思い違いしていた可能性が高い。
質問してわかったこと
Tursoにアクセスするとき、URLに libsql:
を使うと WebSocket 接続が使われ、https:
を使うとステートレスなHTTP通信になる。
このため、insert => select の順で操作すると、最初の insert の接続を基準として通信がプライマリに飛んでしまうため、遅くなる可能性があるとのこと。
httpsで接続するとステートレスなので、そういった事象は無いとのこと。
この点を考慮して次の2つが、ベストのパフォーマンスが出そうに感じた。
- 「書き込みだけ」「読み込みだけ」なら
libsql:
を優先使用する。
- いくつかの書き込みの途上で、読み込みを要する場合、そこだけ別のクライアントのインスタンスで
https:
を使う。
insertを100件呼び出せば、ステートレスな、https:
より、接続が流用される libsql:
の方が早そうにも思える。
ものは試し、やってみる。
次のソースコードを用意して、動作させてみる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import "https://deno.land/std@0.185.0/dotenv/load.ts"; import { serve } from "https://deno.land/std@0.185.0/http/server.ts"; import { type Config, createClient } from "https://esm.sh/@libsql/client";
const config = { url: Deno.env.get("TURSO_DB_URL"), authToken: Deno.env.get("TURSO_AUTH_TOKEN"), } as Config;
const db = createClient(config);
await serve( async (request) => { console.time("insert"); for await (const i of Array(100).keys()) { await db.execute({ sql: "INSERT INTO items(name) VALUES(?)", args: [`USER${i}`], }); } console.timeEnd("insert");
return new Response(JSON.stringify({ items: [1, 2, 3] }), { headers: { "content-type": "application/json" }, }); }, { port: 8000 } );
|
環境変数で、https://~
と libsql://~
を切り替える。
1 2 3 4 5 6
| # 6 回試した # https: 1088ms ~ 1306ms
# libsql: 2487ms ~ 3282ms
|
結果 https:
の方が早かったので、単純な話ではないらしい。
現状積極的に、libsql
で接続を試みたいとは感じられない結果が出た。
ただし、libsqlの記述で transaction() が https
では効かないという記述があるので、そのケースでは使えるはず。
該当の記述
最後にtransaction を使って試してみる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| import "https://deno.land/std@0.185.0/dotenv/load.ts"; import { serve } from "https://deno.land/std@0.185.0/http/server.ts"; import { type Config, createClient } from "https://esm.sh/@libsql/client";
const config = { url: Deno.env.get("TURSO_DB_URL"), authToken: Deno.env.get("TURSO_AUTH_TOKEN"), } as Config;
const db = createClient(config);
await serve( async (request) => { console.time("insert"); const tran = await db.transaction();
for await (const i of Array(100).keys()) { await tran.execute({ sql: "INSERT INTO items(name) VALUES(?)", args: [`USER${i}`], }); } await tran.commit(); console.timeEnd("insert");
return new Response(JSON.stringify({ items: [1, 2, 3] }), { headers: { "content-type": "application/json" }, }); }, { port: 8000 } );
|
実行結果は次の通り。
速度がかなり改善した。
select も同じように複数回投げて速度を確認してみる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import "https://deno.land/std@0.185.0/dotenv/load.ts"; import { serve } from "https://deno.land/std@0.185.0/http/server.ts"; import { type Config, createClient } from "https://esm.sh/@libsql/client";
const config = { url: Deno.env.get("TURSO_DB_URL"), authToken: Deno.env.get("TURSO_AUTH_TOKEN"), } as Config;
const db = createClient(config);
await serve( async (request) => { console.time("select"); for await (const _ of Array(100).keys()) { await db.execute("SELECT * FROM items"); }
console.timeEnd("select");
return new Response(JSON.stringify({ items: [1, 2, 3] }), { headers: { "content-type": "application/json" }, }); }, { port: 8000 } );
|
結果は次の通り。こちらは、libsql:
の方が早かった。
1 2 3 4 5 6 7
| # 6 回試した # データは6000件程 # https: 6532ms ~ 10197ms
# libsql: 4960ms ~ 6836ms
|
最適化するとき、接続を切り替えるのは1つの手法として検討に値すると考えられる。
Turso の動作検証をする中で勘違いをしていたことをまとめて、追加で調査をした。
Turso の discord は、質問投げた時のレスポンスがとても良かった。ありがたい。
質問投げて10分くらいで、最初のレスポンスがあったくらい。
では。