LoginSignup
3
1

More than 1 year has passed since last update.

RailsとTiDBの統合について

Posted at

shutterstock_1043482054.png
この記事は dev.toで発表されたものです。

このチュートリアルは、RailsとTiDBの統合に関する、おそらくWebで初めてのチュートリアルです。TiDBは、水平的なスケーラビリティ、高可用性、MySQLとの互換性を特徴とする、オープンソースの分散SQLデータベースです。

初心者にとって、ActiveRecordのような複雑なORMをTiDBに統合するのは難しい可能性があります。Web上にはこのトピックを扱っている記事がほとんどないことから、私はRailsユーザーによるTiDB導入を支援するために、このチュートリアルを執筆しました。

TiDB開発環境をローカルで構築する

Railsとの統合に先立って、TiDBクラスタをローカルマシンにデプロイする必要があります。TiDBでは、TiDBエコシステム用のパッケージマネージャーであるTiUPによって、デプロイを円滑に進められるようになっています。

TiUPのインストール

TiUPのインストールは、Darwin、Linuxのどちらのオペレーティングシステムからも簡単に行うことができます。次のコマンドを実行するだけです。

$ curl --proto '=https' --tlsv1.2 -sSf https://tiup-mirrors.pingcap.com/install.sh | sh

これによって$HOME/.tiup/binPATH環境変数に追加され、TiUPを直接使用できるようになります。

ローカルクラスタの確立

一般的にTiDBクラスタは複数のノードを必要とするため、デプロイが複雑で時間がかかる場合があります。そのためTiUPは、TiDBのテスト環境をローカルですばやく構築できる、TiUPコンポーネントのplaygroundを用意しています。

このコマンドは次のようにシンプルなものです。

$ tiup playground

次のように出力されます。

$ tiup playground
Starting component ``playground``: /Users/hooopo/.tiup/components/playground/v1.4.1/tiup-playground
Use the latest stable version: v5.0.0

    Specify version manually: tiup playground <version>
    The stable version: tiup playground v4.0.0
    The nightly version: tiup playground nightly

Playground Bootstrapping...
Start pd instance
Start tikv instance
Start tidb instance
Waiting for tidb instances ready
127.0.0.1:4000 ... Done
Start tiflash instance
Waiting for tiflash instances ready
127.0.0.1:3930 ... Done
CLUSTER START SUCCESSFULLY, Enjoy it ^-^
To connect TiDB: mysql --host 127.0.0.1 --port 4000 -u root -p (no password)
To view the dashboard: http://127.0.0.1:2379/dashboard
To view the Prometheus: http://127.0.0.1:9090
To view the Grafana: http://127.0.0.1:3000

これでローカルクラスタが確立されました。

TiDB Dashboardへのアクセス

TiDBには、クラスタを監視するためのWeb UI、TiDB Dashboardが用意されています。これはすでにPDコンポーネントに組み込まれており、http://127.0.0.1:2379/dashboard から直接アクセスできます。

image.png

TiUPの詳細については、公式ドキュメントをご覧ください。

RailsをTiDB用に設定する

TiDBクラスタを確立したところで、次にこれをRailsと統合します。

Railsプロジェクトの作成

TiDBはMySQLとの互換性があるため、MySQL用に設定されたRailsアプリを作成できます。

$ rails new myapp --database=mysql

database.ymlの設定

database.ymlでは次の2つの重要な設定があります。

  • portを4000に設定します。ローカルのTiDBクラスタは、4000をデフォルトのポートとして使用します。
  • データベース接続変数tidb_enable_noop_functionsをONに設定します。Railsは、TiDBではデフォルトで無効になっているget_lock関数を使用する必要があります。
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  port: 4000
  username: root
  password:
  host: 127.0.0.1
  variables:
    tidb_enable_noop_functions: ON

URIメソッドを使用してデータベース接続を設定する場合も、設定方法は同様です。

default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  url: <%= ENV.fetch("DB_URL") || "mysql2://root:pass@localhost:4000/myapp" %>
  variables:
    tidb_enable_noop_functions: ON

プライマリキー、自動インクリメント、一意インデックスの設定

usersというテーブルを作成します。

class CreateUsers < ActiveRecord::Migration[6.1]
  def change
    create_table :users do |t|
      t.string :email
      t.string :password
      t.string :username

      t.timestamps
    end
  end
end

一意インデックスを追加します。

class AddUniqueIndexForEmail < ActiveRecord::Migration[6.1]
  def change
    add_index :users, :email, unique: true
  end
end

TiDBはMySQLとの互換性があるため、使用方法はスタンドアロンのMySQLデータベースとほとんど同じです。プライマリキー、自動インクリメント、一意インデックスなどの機能について互換性がない他の分散データベースと比較して、追加の処理が不要なTiDBは導入が非常に容易です。

ここまでで、次のようなデータテーブルが生成されます。

mysql> show create table users;
+ -------+------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------+
| Table | Create Table |
+-------+------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------+
| users | CREATE TABLE `users` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `email` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `username` varchar(255) DEFAULT NULL,
  `created_at` datetime(6) NOT NULL,
  `updated_at` datetime(6) NOT NULL,
  PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,
  UNIQUE KEY `index_users_on_email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin AUTO_INCREMENT=30001 |
+-------+------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------+
1 row in set (0.01 sec)

セーブポイントへのパッチの追加

TiDBとActiveRecordを結合するうえで1つだけ障害になるのが、現時点ではTiDBではセーブポイントがサポートされていないことです。そこで、これを解決する簡単なパッチを作成しました。

# https://github.com/rails/rails/blob/6-1-stable/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb#L313
require 'active_record/connection_adapters/abstract/database_statements.rb'

module DisableSavepoint
  def transaction(requires_new: nil, isolation: nil, joinable: true)
    if requires_new
      requires_new = nil
      Rails.logger.warn "savepoint statement was used, but your db not support, ignored savepoint."
      Rails.logger.warn caller
      super(requires_new: requires_new, isolation: isolation, joinable: joinable)
    else
      super(requires_new: requires_new, isolation: isolation, joinable: joinable)
    end
  end
end

ActiveRecord::ConnectionAdapters::DatabaseStatements.send(:prepend, DisableSavepoint)

Railsでセーブポイントを使用するのは、トランザクションがtrueをパラメータrequires_newに渡す場合に限られます。その場合は、このパッチによってrequires_newの値がnilに変更され、移行のためのログが出力されます。

私の経験では、Railsプロジェクトでセーブポイントが頻繁に使用されることはほとんどないため、移行が必要になった場合でも大きな問題はありません。マイグレーションを行う場合はセーブポイントを使用しますが、並行して実行する移行がなければ、セーブポイントを削除しても予期しない結果になることはありません。

参考資料

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1