Ruby on RailsでCloud Spannerエミュレータを使ってみる

[PONOS Advent Calendar 2022](https://qiita.com/advent-calendar/2022/ponos) の20日目の記事です。
昨日は@tequila0725さんの[BitbucketでPR作成時に表示されるコミットメッセージリストをPR説明テンプレート有りでも表示する](https://qiita.com/tequila0725/items/2074958ee693dc58bac6)でした。

# はじめに

Cloud Spannerは計画的ダウンタイムがなく、無制限のスケーリングなど、他のデータベースにはない特徴を持ったデータベースです。

https://cloud.google.com/spanner?hl=ja

標準のActiveRecordはSpannerには対応していませんが、Googleからactiverecord-spanner-adapterが提供されていますので、そのGemを使って「Ruby on Rails」 + 「Cloud Spanner」の開発することできます。
この記事では、Cloud Spannerエミュレータを使用し、Ruby on Railsでアプリを作り、activerecord-spanner-adapterを使ってテーブルを作成するまでを紹介させていただきます。

# Cloud SpannerエミュレータをDockerで起動する

(1)新しいディレクトリを作成する

```shell
mkdir ~/rails_spanner_test
cd ~/rails_spanner_test
```

(2)docker-compose.ymlを作成する
```yaml
version: '3'

services:

  spanner:
    container_name: spanner
    image: gcr.io/cloud-spanner-emulator/emulator:1.4.3
    ports:
      - 9010:9010
      - 9020:9020
    networks:
      - test-network

  spanner-init:
    container_name: spanner-init
    image: gcr.io/google.com/cloudsdktool/cloud-sdk:400.0.0-slim
    command: >
      bash -c 'gcloud config configurations create emulator &&
              gcloud config set auth/disable_credentials true &&
              gcloud config set project $${PROJECT_ID} &&
              gcloud config set api_endpoint_overrides/spanner $${SPANNER_EMULATOR_URL} &&
              gcloud spanner instances create $${INSTANCE_NAME} --config=emulator-config --description=Emulator --nodes=1 &&
              echo this container keep running && tail -f /dev/null'
    environment:
      PROJECT_ID: test-project
      SPANNER_EMULATOR_URL: http://spanner:9020/
      INSTANCE_NAME: test-instance
    networks:
      - test-network

  spanner-cli:
    container_name: spanner-cli
    image: sjdaws/spanner-cli:latest
    environment:
      SPANNER_EMULATOR_HOST: spanner:9010
    command: ['sh', '-c', 'echo this container keep running && tail -f /dev/null']
    networks:
      - test-network

networks:
  test-network:
    driver: bridge
``` 

(3)コンテナを起動する

```shell
docker-compose up -d
```

# Railsアプリを作成する

以下の環境で実施しました。
+ OS: macOS Monterey version 12.6
+ Rails: 3.1.1
+ rbenv: 1.2.0
+ Rails: 7.0.2

(1)Railsのバージョンを3.1.1に設定する

```shell
cd ~/rails_spanner_test
rbenv local 3.1.1
```

(2)新しいRailsアプリを作成する

```shell
rails new .
```

(3)Gemfileにactiverecord-spanner-adapterのGemを追加する

https://rubygems.org/gems/activerecord-spanner-adapter

```ruby
gem 'activerecord-spanner-adapter'
```

(4)Gemをインストールする

```shell
bundle install
```

※M1 Macを使う場合以下のようなエラーがでると思います。

```shell
In Gemfile:
  activerecord-spanner-adapter was resolved to 1.2.2, which depends on
    google-cloud-spanner was resolved to 2.16.1, which depends on
      google-cloud-spanner-admin-database-v1 was resolved to 0.11.0, which depends on
        gapic-common was resolved to 0.16.0, which depends on
          googleapis-common-protos was resolved to 1.4.0, which depends on
            grpc
```

その場合、少し古いGemとなりますが、Rubygem.orgのアーカイブから、universal-darwinのバージョンを使ってください。

```shell
gem "grpc", "~> 1.45.0"
```

(5)config/database.ymlを修正する

```yaml
default: &default
  adapter: spanner
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 10 } %>
  project: test-project
  instance: test-instance

development:
  <<: *default
  database: devdb

test:
  <<: *default
  database: testdb

production:
  <<: *default
  database: proddb
```

(6)動作確認用にモデルを作成する

```shell
bin/rails g model user name:string
```

(7)SPANNER_EMULATOR_HOST環境変数を設定する
アプリケーションが起動すると、クライアント ライブラリは自動的に SPANNER_EMULATOR_HOST をチェックし、エミュレータが実行されている場合は接続します。

```shell
export SPANNER_EMULATOR_HOST=localhost:9010
```

(8)データベースを作成する

```shell
bin/rails db:create
```

(9)マイグレートしてテーブルを作成する

```shell
bin/rails db:migrate
```

# Spanner CLIで確認する

usersテーブルが作成されていることが確認できました！

```shell
% docker-compose exec spanner-cli spanner-cli -p test-project -i test-instance -d devdb
Connected.
spanner> show tables;
+----------------------+
| Tables_in_devdb      |
+----------------------+
| schema_migrations    |
| ar_internal_metadata |
| users                |
+----------------------+
3 rows in set (0.06 sec)
```

# まとめ

エミュレータと実際のCloud Spannerと異なる部分もありますが、大きな問題はなくローカル環境で開発することができます。
今回、Rails開発者がSpannerを使い始めるきっかけになればと簡単ではありますが記事にさせていただきました。

実際に開発を進める際には、例えば
+ ReadOnlyトランザクションを使う場合とRead/Writeトランザクションを使う場合の方法
+ バルクインサート
+ ミューテーション
+ インターリーブ
などの必要な情報がありますので、まず以下のExamplesやLimitationsを読むことをお勧めします。

https://github.com/googleapis/ruby-spanner-activerecord

明日は@MilayYadokariさんです！