Closed14

【成功】Drizzle ORMのPlanetScaleをTiDB Serverlessに移行したい

ピン留めされたアイテム
WhyKWhyK

よって、Drizzle ORMでTiDB Serverlessを使いたい場合は、以下のような実装で利用できる

import { drizzle } from "drizzle-orm/mysql2";
import { createConnection } from "mysql2";

const connection = createConnection({
  host: Deno.env.get("DB_HOST"),
  user: Deno.env.get("DB_USERNAME"),
  password: Deno.env.get("DB_PASSWORD"),
  database: "cinelog",
  /**
   * NOTE: mysql2内の`node:tls`のデフォルトCAを使うための設定
   * @see https://zenn.dev/link/comments/378474ec5af4e7
   */
  ssl: {},
});

export const db = drizzle(connection);

PingCAP社のbohnenさん、ありがとうございました

WhyKWhyK

移行対象はシネログという映画館での鑑賞作品記録個人サイト
https://github.com/windchime-yk/cinelog
前々から不意にDB吹き飛ばした用にローカルにSQLファイルは置いているので、それを元にTiDB上に同じデータを展開する

WhyKWhyK

最初、Chat2QueryというページでCREATE TABLEをすればテーブルが作成できるのかと思ったがそうではなかった
あくまでデータ参照用っぽい

これは誤りで、そもそもCREATE DATABASEをしていなかった私のアホの所業だった

TiDB公式に聞いたところ、なんとQiitaの記事を教えてもらった
https://qiita.com/bohnen/items/24e357b02bde14d1df95
やり取りは英語でやってるけど、実はこれ日本法人の方が返してるんじゃないかって思った

WhyKWhyK

割と公式が潤沢に情報を提供しているようなので、ここからDrizzle ORMでの移行パターンを記す

WhyKWhyK

Drizzle ORMとTiDB双方に公式資料は存在しない模様
なので、TiDBをMySQLだとしてDrizzle ORMのMySQLスタートガイドを利用する
https://orm.drizzle.team/docs/get-started-mysql

なおDenoで書いているが、Node.jsと基本的に変わらないと思っている

接続の実装は以下

import { drizzle } from "drizzle-orm/mysql2";
import { createConnection } from "mysql2";

const connection = createConnection({
  host: Deno.env.get("DB_HOST"),
  user: Deno.env.get("DB_USERNAME"),
  password: Deno.env.get("DB_PASSWORD"),
  database: "cinelog",
});

export const db = drizzle(connection);
WhyKWhyK

すると以下のようなエラーが出た

Error: Connections using insecure transport are prohibited. See https://docs.pingcap.com/tidbcloud/secure-connections-to-serverless-tier-clusters
    at PromiseConnection.query (file:///home/windchime-yk/.cache/deno/npm/registry.npmjs.org/mysql2/3.9.2/promise.js:94:22)
    at MySql2PreparedQuery.execute (file:///home/windchime-yk/.cache/deno/npm/registry.npmjs.org/drizzle-orm/0.27.2/mysql2/index.mjs:51:37)
    at QueryPromise.execute (file:///home/windchime-yk/.cache/deno/npm/registry.npmjs.org/drizzle-orm/0.27.2/session-9628aea0.mjs:1150:31)
    at QueryPromise.then (file:///home/windchime-yk/.cache/deno/npm/registry.npmjs.org/drizzle-orm/0.27.2/alias-3e926a50.mjs:636:21)
    at eventLoopTick (ext:core/01_core.js:169:7) {
  message: "Connections using insecure transport are prohibited. See https://docs.pingcap.com/tidbcloud/secure-c"... 38 more characters,
  code: "ER_UNKNOWN_ERROR",
  errno: 1105,
  sql: undefined,
  sqlState: "HY000",
  sqlMessage: "Connections using insecure transport are prohibited. See https://docs.pingcap.com/tidbcloud/secure-c"... 38 more characters
}

接続が安全ではない、というエラーが表示されURLが案内されるので、以下を読む
https://docs.pingcap.com/tidbcloud/secure-connections-to-serverless-clusters

WhyKWhyK

話は逸れるが現在、Drizzle ORMはTiDB Serverless Driverに対応していないようで、Issueがいくつか立っている
https://github.com/drizzle-team/drizzle-orm/issues/1159
https://github.com/drizzle-team/drizzle-orm/issues/1381
また別場だが、Issueのコメントで共同創業者がTiDB対応について言及していたので、どこかのタイミングで追加はされるはず……
https://github.com/aelew/devterms/issues/51#issuecomment-1992549384

WhyKWhyK

なおMySQL2を使った場合に出るのはcreateConnectionのsslに設定すれば直るらしいのだが、ファイルを読み込まない

Uncaught (in promise) TypeError: Unknown SSL profile '~/isrgrootx1.pem'

そこで、そもそも実装を読み間違えている可能性を考え、実装を確認したところ読み間違えていた
https://github.com/sidorares/node-mysql2/blob/v3.9.2/lib/connection_config.js#L249-L258
そもそも、内部の./constants/ssl_profiles.jsを参照しているらしい

WhyKWhyK

./constants/ssl_profiles.jsの内部を見たところ、Amazon RDSの証明書らしきものが直書きされている
https://github.com/sidorares/node-mysql2/blob/v3.9.2/lib/constants/ssl_profiles.js
実装を見る限り、sslの部分にAmazon RDSと書けば、当該エラーは回避できるらしい

const connection = createConnection({
  host: Deno.env.get("DB_HOST"),
  user: Deno.env.get("DB_USERNAME"),
  password: Deno.env.get("DB_PASSWORD"),
  database: "cinelog",
  ssl: "Amazon RDS"
});

なので、次のエラーを確認するために記載してみたところ……

なんか通ってしまった
あれぇ……???
TiDBは内部的にAWSを使っているので、それが起因してたりするのだろうか

WhyKWhyK

ちなみに、しばらくすると以下のエラーが出る

error: Uncaught Error: read UNKNOWN
    at __node_internal_captureLargerStackTrace (ext:deno_node/internal/errors.ts:91:9)
    at __node_internal_errnoException (ext:deno_node/internal/errors.ts:139:10)
    at TCP.onStreamRead [as onread] (ext:deno_node/internal/stream_base_commons.ts:209:20)
    at TCP.#read (ext:deno_node/internal_binding/stream_wrap.ts:245:12)
    at eventLoopTick (ext:core/01_core.js:169:7)
WhyKWhyK

挙動としては想定されていないものだったようで、PingCAP社のbohnenさんがTiDBのmysql2サンプルで試したところ、動作しなかった
Zenn Scrapsにもまとめてくださった
https://zenn.dev/bohnen/scraps/1fdba46798cfae
私の方は引き続き繋がっていて謎だが、これは想定外挙動として無視したほうが良さそう

このスクラップは1ヶ月前にクローズされました