Cloud SQL Private IP で接続できないとき

「接続できない」には VPC の設定が〜〜とかいろいろあると思うんだけど、今回はこのエラーメッセージに対する解決

$ mysql -u root -p --host 127.0.0.1 --port 3306
Enter password: 
ERROR 1045 (28000): Access denied for user 'root'@'cloudsqlproxy~10.0.0.2' (using password: YES)

結論、パスワードに特殊文字がないか確認しよう。入ってたらパスワードを設定しなおそう。

Ensure that the database password does not contain any of the following special characters: [] {}() , ; ? * ! @.

Help And Training Community より

前提

このガイドと同じ構成

Quickstart: Connect to a Cloud SQL for MySQL instance with private IP  |  Google Cloud

つまり、

  • private IP の Cloud SQL
  • 同じ VPC network 内に GCE instance を立てた
  • ssh terminal #1 に Cloud SQL Auth Proxy
  • ssh terminal #2 から mysql コマンドを叩こうとしている (で、 Access denied になる)

お気持ち

ググっても SSL cert の話とかネットワーク周りで、しかしパスワードは 100% あってるから盲点だった。Salesforce ありがとう、というかなんで Salesforce?

やってみた: Spanner Change stream と Dataflow で別プロジェクトの BigQuery にデータを流す

pre-amble

「Spanner のデータを Change stream, Dataflow を使って別プロジェクトにある BigQuery にデータを流してみよう」というお題をもらったのでやってみた。BigQuery 以外触ったことなかったのでまずそれぞれどんなもんだい、ってとこから始まった。

setup

  • project A: Spanner, DataFlow
  • project B: BigQuery

Spanner (Change stream) → Dataflow → BigQuery

Spanner

Spanner とは

何も知らないので、Cloud SQLRDBMS なのに対して Spanner は KV store なのかな? とか思ったけど、そんなことはなく RDBMS みたいに使えて*、PostgreSQLGoogle Cloud SQL の2つの SQL dialect の選択肢がある。

*公式が「Spanner provides a solid foundation for all kinds of applications with its combination of familiar relational database management system (RDBMS) features, such as ... and unique features such as strong external consistency via TrueTime and high availability via native synchronous ...」と言ってるので、 RDBMS と言い切るのも微妙な気がした。RDBMS +α って感じ?

この SO だと、「Cloud SQL で事足りるならそれで OK、それ以上のデータ量とかの要件があるなら Spanner」って言ってた↓

sample DB + backend

「Spanner はコストがえぐいから、とりあえず free instance を使うといいよ」とのことだったので見てみたところ、90日間使える free instance があった。個人的に、90日を過ぎて消し忘れても 30-day grace period の後に勝手に消してくれるのが嬉しいポイント。

Spanner free trial instances overview  |  Google Cloud

で、さらにすごいのがチュートリアルで勝手にデータを流してくれるバックエンド + DB がコマンド2つで用意できるところ... というのがこの辺に書いてある↓

Create a Spanner free trial instance and sample application  |  Google Cloud

30分もあれば上のページにある内容はとりあえず動かすところまではいける (理解はともかく)。

自分用コマンド↓

# in tab 1
gcloud auth application-default login
    
gcloud spanner samples backend \
    finance --instance-id \
        <instance-id>
        
# in tab 2
gcloud auth application-default login

gcloud spanner samples workload \
    finance

outputs

というわけで、

  • trial-instance という Spanner instance
  • の中に、 finance-db というデータベース
  • の中に、 Accounts, TransactionHistory などのテーブル

が作成された状態がここまで

Change stream

Change stream とは

個人的に、

CREATE CHANGE STREAM SingerAlbumStream
FOR Singers, Albums;

という DDL で作成するものですよというのが一番わかりやすい説明かも。

公式の説明だと、

A change stream watches and streams out a Spanner database's data changes—inserts, updates, and deletes—in near real-time.

...

Spanner treats change streams as schema objects, much like tables and indexes. As such, you create, modify, and delete change streams using DDL statements, and you can view a database's change streams just like other DDL-managed schema objects.

とのことで、

  • データベースへの変更を stream するモノ
  • モノ = schema object → table, index と同じように扱える

というのが自分的ポイントだった。

sauce: Change streams overview  |  Spanner  |  Google Cloud

Change stream の作成

Change stream はデータベース全体に対して作成することもできるし、特定のテーブル・カラムに対して作成することもできる。今回は TransactionHistory に対してだけ作ってみたかったので (なんとなく)、↓の DDL を Spanner Studio* の中で実行した。

CREATE CHANGE STREAM TransactionStream
FOR TransactionHistory

(CamelCase なのはサンプルコードに従ったから)

*Spanner Studio は Spanner に対していろいろクエリが書けるエディタで、BigQuery に慣れてれば違和感なく使えると思う。多分。

outputs

trial-instance の中に finance-db.TransactionHistory に対する change stream ができた

Dataflow の作成

作業的にはここまでと同様ボタンぽちぽちで終わるんだけど、Dataflow job の設定にあたっていろいろわからないことがあったので一番大変だった。↓の記事がめちゃ参考になった。

【Cloud Spanner】Change StreamsをBigQueryに保存して利用する

概要としては、

  • Spanner change stream → BigQuery の Dataflow job を作成するには、すでにある「テンプレート」ってやつを使えばすぐできる (テンプレートを選択して Run Job をクリックするだけ)

BigQuery dataset (in other project) の作成

Dataflow job を作成してるときに destination を作ってないことに気がついたので、お題の「別プロジェクトの BigQuery」を設定した。

↓ の画像のようなフィールドなんだけど、(1) dataset 単位で job を作る (2) datasetId だけでなく project.datasetId の形式で設定できるというのがポイントだった。

(1) に関しては、change stream の最大の単位が DB なので (インスタンスではなく) よく考えたら「たしかに」ってなった。 (2) に関しては、前回の Datastream みたいに UI で裏技を使わないと別プロジェクトの BigQuery を指定できない、とかではないので安心した。

Service Agent への権限付与

ベスプラ的には service account を別途つくるべきなのだが、いったん何も指定しないで job を作成することにした。で、このときどの service agent が使われるかなんだけど、結論 Compute Engine と同じ <project number>-compute@developer.gserviceaccount.com になるっぽい (何も指定しなかったらこれになった)。

ただ、 xx-compute@... とは別に Dataflow の service agent はあるようなので、本来は Dataflow の方が使われるのか..? ほんとは作ったあとに権限をつけたので、ここらへんはちゃんと確認できてえいない。

Service agents  |  IAM Documentation  |  Google Cloud

何はともあれ principal がわかったので、別プロジェクトにいって <project number>-compute@developer.gserviceaccount.com に権限をつけにいった。Dataflow Service Agent の権限を見ると、bigquery.capacityCommitments.* など BigQuery Data Editor にない権限もついているので今回は BigQuery Admin をつけた。Data Editor にない権限が今回の job において使われるのかは未確認なので、ほんとは Data Editor でもよかったのかも? BigQuery Admin には bigquery.jobs.* がすでについているので、 BigQuery Job User はつけなかった。

「まじか」ってなったところ

(1) Dataflow job には pause/resume がない

ストリームなのでそれはそう案件なのかもだけど、 Dataflow job は一度止めると「再開」というのがない。代わりに、止めたジョブをクローンすることで再開的な挙動にさせることができるけど、停止〜再開してた間のデータがどうなるのかはよくわかってない

(2) 同じ名前の実行中の Dataflow job は複数作成できない

1 のところで、止めたあとに job をクローンしようとしたんだけど、名前をそのままにしておいたらエラーになった。ただ、job は止まるのに時間がちょっとかかる (体感10分くらい?) ので、止まるまで待てば同じ名前の job は作成できる。

outputs

  • Dataflow job
  • IAM binding for Compute Engine default service account x BIgQuery Admin in the other project

これで Spanner → Change stream → Dataflow job → Bigquery ができて、 BigQuery にデータ入ったことも確認できた。

調べもの

Dataflow job > "metadata instance ID" "metadata database" とは??

いかんせん Dataflow のジョブは設定項目があって、必須項目の中だけでも結構 ??? になった

Create and manage change streams  |  Spanner  |  Google Cloud

ここに書いてあって、 change stream の internal state を管理するためのデータを metadata instance/database が必要。これは実際のデータが入ってる instance/database と同じでもいいけど、↓の理由から分けることがおおすすめ

  • 実データの方を readonly アクセスにできる
  • DB 全体を watch する change stream (FOR ALL) の場合、change stream のメタデータへの変更か実データへの変更かを気にしなくていい

Change stream: exclude TTL based deletes

Spanner のデータには "TTL" (time-to-live) を設定できて、GCS の lifecycle policy のように一定期間後自動的にデータが削除されるようにできる。で、 Change stream でこの TTL 起因の delete を watch しないように設定できる。

やってみた: Cloud SQL for MySQL → Datastream → 別プロジェクトの BigQuery

pre-amble

構成

  • project A: Cloud SQL, DataStream
  • project B: BigQuery

という設定だよ

DB と BigQuery dataset の対応

ここで紹介するやり方は、複数のデータベースを1つの BigQuery dataset に流し込むやり方で、たとえば

  • MySQL 側の db1 DB に tbl1 tbl2 がある
  • db2 DB に tbl1 がある
  • BigQuery dataset は mysql_data を作成

とした場合、 BigQuery 側には

  • mysql_data.db1_tbl1
  • mysql_data.db1_tbl2
  • mysql_data.db2_tbl1

のように、1つの dataset 以下に schema_table の形式のテーブルが作成される。dataset と DB を 1:1 にするやり方は少なくとも UI からだと難しそう。 terraform だとできるのかも? できないかも?

Cloud SQL の準備

資料

[1] Configure a Cloud SQL for MySQL database  |  Datastream  |  Google Cloud

Cloud SQL インスタンスを立てる

ふつうにインスタンスをつくれば OK なんだけど、 Datastream で使うので[1] binlog を有効化しておく (あとからでもできる)

また、Datastream でデータを転送したいので、たとえばこことかからダミーデータを入れておくとよき

tip: 家の回線が IPv6 の場合 gcloud beta sql connect コマンドだと IPv4 じゃなくても接続できる

Datastream 用にユーザを追加

Datastream が MySQL に接続するための DB user をつくる。資料1にある SQL を叩けば OK ↓

CREATE USER 'datastream'@'%' IDENTIFIED BY 'YOUR_PASSWORD';
GRANT REPLICATION SLAVE, SELECT, REPLICATION CLIENT ON *.* TO 'datastream'@'%';
FLUSH PRIVILEGES;

Datastream の設定

全体像はこう↓

  • MySQL につなげる Connection Profile を作成
  • BigQuery につなげる Connection Profile を作成
  • それぞれの profile を source/destination とした Stream を作成

資料

[1] Replicate data to BigQuery in near real-time with Datastream  |  Google Cloud

[2] Create connection profiles  |  Datastream  |  Google Cloud

[3] DatastreamをつかったChange Data Capture環境構築 (MySQL -> Datastream -> Cloud Storage) #GoogleCloud - Qiita

1. MySQL の Connection Profile の作成

tip: ここでは Datastream と Cloud SQL の画面をいったり来たりするよ

tip: 資料1 の QuickStart はわかりやすいけど postgres 用なので、適宜2で保管していくイメージ

まず "Connection settings" のところでは↓のように入力する

  • Hostname or IP: Cloud SQL instance の Public IP Address
  • Username / Password: 前のところで作成した datastream ユーザの情報

"Secure your connection to your source" のところは、 None/Server-only/Server-client の選択肢の順でセキュアになっていく。 Server-client も設定は難しくないのでこれがおすすめ。

Server-client にした場合、

  1. Source CA certificate = Cloud SQL instance の certificate
  2. Source client certificate
  3. Source private key

の3つのファイルが必要になるので、 Cloud SQL のコンソールから証明書たちを発行する

ファイルをローカルに保存したら、 connection profile を作成するページに戻ってアップロードする

最後の Define Connectivity Method のところは、 IP allowlisting でやってみた。この選択肢をクリックすると、 Datastream が使う IP のリストが出てくるので、これらを Cloud SQL 側に追加する。

あとは connection テストが通れば ok

2. BigQuery connection profile の作成

この段階だと「どの BigQuery につなぐ」っていう設定項目はないので、 Quickstart の通りに作成すれば OK

https://cloud.google.com/datastream/docs/quickstart-replication-to-bigquery#destCP4BigQuery

3. Stream の作成

こんな感じの画面で、 step 4 までは資料1を見つつさくさく進めて ok

step 5 になったら、 新しいデータセットを作成する ことで違う project の dataset に接続する

注意点としては、一度選択をはずすと、違う project の dataset は選択肢に出てこない。同じ名前の dataset をもう一度選択したかったら、一回 dataset を削除して改めて同じ手順を踏む必要がある

この段階で Validate stream をしてもエラーになるので、とりあえず CREATE だけする

Datastream service agent に別 project の BigQuery の権限をつける

前提: Datastream が source からデータを読んだり destination にデータを書き込むときは Datastream service agent を使ってて、同じ project 内ならデフォルトで bigquery の権限を持っているけど、違う project だと権限をつけてあげる必要がある。

権限付与には↓の3つのステップがある

  1. service agent の email address の特定
  2. 必要なロールの特定
  3. iam binding の設定

1. service agent の email address の特定

結論 service-PROJECT_NUMBER@gcp-sa-datastream.iam.gserviceaccount.com なんだけど、調べるためには↓を見る

Service agents  |  IAM Documentation  |  Google Cloud

このページにはすべてのサービスの service agent の email が載ってるので、今後も役に立つはず

2. 必要なロールの特定

これも結論

  • BigQuery Data Editor
  • BigQuery Job User

を project level (dataset level ではなく) でつければ OK。dataset を特定してるから Data Editor は dataset だけでいいと思ったんだけど、それだと stream がエラーになったので、project level でつける必要がある (Job User は project でしかつけられないよ)。

なぜ Data Editor かというと、また service agent のページを見ると、 Datastream service agent には "Datastream Service Agent" がついているのがわかる。

で、このロールについてる permission はこう↓なので、これらを持ってる最小権限の pre-defined role の組み合わせは BigQuery Data Editor + BigQuery Job User となる

3. iam binding の設定

principal/email と必要なロールがわかったので、 BigQuery のあるプロジェクト側で 権限を付与する

注意点として、少なくとも自分の環境だと Datastream service agent がコンソールだと BigQuery 側 project の IAM 一覧に出てこなかったという謎がある。下記の gcloud コマンドを叩けば見れるので、なんか UI 側にそういう制限があるのかも?

gcloud projects get-iam-policy <your project> \
        --flatten="bindings[].members" \
        --format='table(bindings.role)' \
        --filter="bindings.members:service-<your project number>@gcp-sa-datastream.iam.gserviceaccount.com"

Stream の実行

最後に Stream をストリームしはじめれば ok。もしエラーになったら Logging を見るといいかも? (解決しないこともある)

トラシュー日記: Django の request.session

problem

  • ちょっとバグい処理があって、本番環境で 500 を出しちゃった
  • 修正して、ローカルでは普通に処理できるのに、本番で同じページを開くと未だに500になる
  • コードを読んだら request.session を読んでた

fix

本番環境からログアウトする

details

ここにある通り、 request.session をクリアするのに一番早い方法はログアウト。このほかにも clearsession を呼んだりもできる。

https://docs.djangoproject.com/en/5.0/topics/http/sessions/#clearing-the-session-store

めも: redux の state mutation デバッグ

こういうエラーが出てた

Invariant failed: A state mutation was detected between dispatches, 
in the path `xxx.xxx.xxx.`. 

This may cause incorrect behavior

で、その下にエラーがおきた dispatch(...)ソースコードも出てたんだけどデバッグちょー時間かかった

ポイント1: 表示された dispatch はあくまで「そこで state mutation が検知されましたよ」というだけで、その dispatch の中の action が原因というわけではない。というかそこで検知されているので、むしろその action が原因の可能性は低そう

ポイント2: path がポイントで、その path で示された state がいじられてそうなところを探すといいかも(それが大変なわけだが)

たとえば、今回は path が user123.posts.0 みたいなもので、「ID が user123 の posts の配列の一番最初がいじられてるよ」ということだった。で、調査したらコンポーネントの中で user.posts.sort() 的なことを書いてた (補足: sort は in-place でソートするので、正しくは [...user.posts].sort() )。

Aurora MySQL → S3 の方法比較めも: parquet or csv?

注: そもそもこういうことを考えなくていいように redshift zero etl とか使った方がよさそう

前提: 別のアカウントから RAM で共有された Aurora cluster をクローンして S3 になんとかしてデータを吐き出したい

(1) Snapshot をエクスポートする

まず共有されたクローンだと、 cluster そのものをエクスポートすることはできない。ドキュメントからは分かりにくい(というか自分はわからんかった)が、サポートに聞いたのでガチ。なので Snapshot を作ってからエクスポートする必要がある。

pros: parquet なので後段の処理でデータが扱いやすい

cons: とにかくおっそい。snapshot をエクスポートするときに裏でクローンを作っているっぽい (by インフラの人) で、 QA 環境の cluster でも 40分くらいかかる。

(2) SELECT INTO OUTFILE S3CSV をエクスポートする

pros: まあシンプルに速い

cons: とにかくめんどくさい。クエリを実行するために、いろんなインフラリソースを作らなきゃいけないし、 DB に繋がるまでが大変。クエリもめんどい。

ぱっと覚えてるだけで

  • RDS にアタッチする S3 bucket へのアクセスが許可された IAM Role をつくる
  • DB への接続を確立するための user/password を secrets manager とかに格納 + それ関連のポリシー
  • aws_default_s3_role を cluster parameter group に設定

とか...

しかもうちは RAM なので、DB のパスワードが terraform で二重管理になるらしい (terraform は触らないので)

とはいえ、 parquet の方もそっちはそっちで待ち時間が長いから、パイプライン全体を見ると管理するリソースは増えたりする (eventbridge はさむとか)