【初心者】Cloud Spanner With Laravelのローカル環境構築

# 追記
【2022-03-06】
2022-03-06現在PHP8.1系の場合、readonlyが予約語であるため、暗黙的なトランザクション（Read transaction）は、エラーになるのでご注意願います。
https://github.com/googleapis/google-cloud-php/issues/5129

下記の例のように明示的にトランザクションブロックで囲えば問題ありません。
（ただしRead-write transactionになってしまいますが）

PHP8.0系以前であれば、readonlyが予約語ではないので問題ありません。

# 概要

自分のメモ用にローカルにCloud Spannerを立て、PHP（Laravel）から操作する手順をまとめました。
なるべくそのまま使えるように記載したつもりですが不明な点があれば、コメントいただければと。
Spannerは1ノードでもお高いと聞きますし、とりあえずPHPで検証してみたいっていう方のお役にも立てたら嬉しいです！
※今回の環境構築は、Docker Desktop for Mac上で行っています。

# ローカルのSpanner環境の構築

以下ブログの通り、githubからcloneしてきて、`docker-compose up -d`するのみｗ
ローカル環境用のdocker-composeを公開してくれている方と、wrenchを公開しているメルカリの方に感謝です！！

[Cloud Spannerのローカル開発環境をdocker-composeでサクッと立ち上げる | フューチャー技術ブログ](https://future-architect.github.io/articles/20210323/)

# Laravel環境構築

今回は普段良く利用しているLaravelを利用したいと思います。

またコロプラさんがLaravel用のspannerのドライバを用意してくれているので、そちらでアクセスしてみます。

## 手順

1. PHP環境構築
2. Laravelインストール
3. Laravel用のspannerドライバのインストール
4. Spanner操作のサンプルコマンド作成＆実行

## ファイル構成

```bash
docker-compose.yml
app/
settings/
  php/
    Dockerfile
```

## PHP環境構築

以下docker-compose.ymlとDockerfileを用意

```docker
version: '3'
services:
  php:
    build:
      context: ./settings/php
    volumes:
      - ./app:/var/www
```

```docker
FROM php:8.1-fpm-buster

ENV TZ=Asia/Tokyo
ENV COMPOSER_ALLOW_SUPERUSER 1
ENV COMPOSER_INSTALL_VERSION=2.1.3

WORKDIR /var/www
RUN apt-get update && apt-get install -y git curl less autoconf zlib1g-dev
RUN pecl install grpc

RUN echo "extension=grpc.so" >> /usr/local/etc/php/php.ini

RUN cd /usr/bin \
    && php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
    && php composer-setup.php --version=${COMPOSER_INSTALL_VERSION} \
    && ln -s /usr/bin/composer.phar /usr/bin/composer \
    && php -r "unlink('composer-setup.php');" \
    && mkdir -p /var/www
```

以下コマンドを実行

```bash
docker-compose up -d --build
```

Spannerのドライバを利用するためにgrpcのインストールが必要なのですが、かなり長いのでご注意ください。。

またprotobufのインストールには、composerで簡単にインストール方法と、peclでC実装をインストールする方法があります。いわずもがな、C実装の方が高速なので、本運用する場合はC実装を使うと良いと思います。

今回はとりあえずSpannerとの接続テストのため、C実装のprotobufはインストールしていません。


[Install gRPC for PHP | Google Cloud](https://cloud.google.com/php/grpc#installing_the_protobuf_runtime_library)

## Laravelインストール

コンテナ内で以下コマンドを実行し、Laravelをインストール

```bash
docker-compose exec php bash
composer create-project laravel/laravel spanner-app
```

そしてこれまたLaravelのインストールも長いです。。

## Laravel用のSpannerのドライバのインストール

コンテナ内で以下コマンドを実行し、Spannerのドライバをインストール

```bash
docker-compose exec php bash
composer require colopl/laravel-spanner
```

※上記ドライバはコロプラさんの[OSS](https://github.com/colopl/laravel-spanner)です。コロプラさんに感謝！！

## Spanner操作のサンプルコマンド作成＆実行

以下コマンドを実行し、テスト用のコマンド（適当にspanner-testとした）を作成

```bash
docker-compose exec php bash
php artisan make:command SpannerTest
```

以下コマンドの中身です。

注意点としては、Spannerのエミュレータの場合は、環境変数「SPANNER_EMULATOR_HOST」を設定してあげる必要がある点です。

通常のGCP上のSpannerに接続する場合は、DefaultServiceAccountなりCredential情報を元に、GCP上のSpannerに接続に行きますが、ローカルのエミュレータの場合は接続先の指定が必要となります。

```bash
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use DB;

class SpannerTest extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'spanner-test';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Command description';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        putenv('SPANNER_EMULATOR_HOST=host.docker.internal:9010');
        $conn = DB::connection('spanner');
        $conn->transaction(function() use ($conn) {
            $result = $conn->select('select * from Singers;');
            var_dump($result);
        });

    }
}
```

以上、Cloud Spanner With Laravelの検証用のローカル環境構築ができました。

# まとめ

何をインストールする必要があるかさえ分かれば、結構簡単に環境構築できることが分かったと思います。
これですぐにSpannerを試すことができますね！！

Spannerは完全にスケールしてくれるので、DBを複数台用意してアプリ側に水平分割の仕組みを導入する必要がありませんし、イベント時や徐々に人気が出て現在のDB台数では足りないから、DB台数を増やしてデータ移行するといったことが発生しません。
あの辛い作業（大抵そういった作業は急にやってきます）から解放してくれる夢のようなDBです。

もちろんこれまでのRDSと比べて注意すべき点があると思いますが、それはこれから確認・検証し、ぜひデータ移行や水平分割から解放された運用にできればと考えています。