Firebase・GCP(BigQuery)を活用したリソースのPV数のカウントについて

ネイティブアプリ開発のプロジェクトで、自分のプロフィールが閲覧された回数などを記録して、ユーザーにそれを表示してあげると言う機構を作成しました。
その際に、Firebase、GCPを活用して作ったので、その記録を書きたいと思います！
主にバックエンド(Rails)の視点で書いていますが、基本は仕組みの解説なので、言語はそれほど関係ないかなと思います。

# 作った機構の全体像
まずは作った機構の全体像を載せておきます。
- アプリ側のコードにGAのタグを埋め込み
- ユーザーがイベントを踏むと発火しデータをGAへ送信
- GAで保存→定期的にGAからBigQueryにデータをエクスポート
- BigQueryでデータを永続化→BigQueryで保存データに対してSQLを実行してデータを加工して取得
- そのデータをアプリで表示

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2367608/9daefa56-908c-47fc-7c49-6b6201b19bf1.png)




# GCPとFirebaseの違い
- GCPとはGoogle Cloud Platformの略で、Googleが提供するサービスをクラウド上（インターネット）で利用できるもの
    - 語弊はかなりあるかもだが、すごく簡単に言うとAmzonが提供しているAWSと同じようなもので、それのGoogle版
- 「Firebase」は Google Cloud Platformの一部サービス
    - Webサービスなどを開発する際に用いる、バックエンドサービスがFirebase
    - Firebase を開発に用いることによって、バックエンドの開発・管理をする必要がなくなるので、サーバレスアーキテクチャと呼ばれていたりする
    - GoogleのFirebase、AWSのAmplifyがその中でもよく上がる選択肢
    - 今回はFirebaseを介してBigQueryとGoogleAnalyticsを統合して連携させるためにFirebaseを使った
    - 語弊はあると思うが、今回の理解としてはGCPの中の１サービスとしてFirebaseが存在しているという理解でも大丈夫そう（間違ってたら教えてください）

# GCPの管理単位

## 組織(例: relic.co.jp)
- googleアカウントが所属する組織が存在していれば、そのアカウントでログインすると組織に紐づく

### プロジェクト
- 組織に紐づいているgoogleアカウントがプロジェクトを作成すればそのプロジェクトは組織に紐づいて作成される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2367608/d4343198-2a6e-3c60-4a00-d91ba7519374.png)


- 紐づいている組織がないgoogleアカウントがプロジェクトを作成すればそのプロジェクトは組織に紐づかず作成される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2367608/fba753b2-2b55-9c59-1f20-2dee8c412b11.png)


- 組織に紐づいていないアカウントが作ったプロジェクトに招待されて入ると組織に紐づかないプロジェクトの一覧に表示される

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2367608/8bb88b06-401a-f741-d860-4ee2ff210588.png)


- プロジェクトにはIDが振られる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2367608/fe05f3f1-2628-8f56-8ec2-e55a348b8821.png)

# GoogleAnalytics(FirebaseAnalytics)とFirebaseの連携
- https://firebase.google.com/docs/analytics/get-started?hl=ja&platform=web
- 新しい Firebase プロジェクトを作成する場合は、プロジェクトの作成ワークフローで Google アナリティクスを有効にする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2367608/511b2c20-42e0-90d5-5407-e9ea0442cf30.png)


- 既存の Firebase プロジェクトで Google アナリティクスが有効になっていない場合は、settings > [プロジェクトの設定] の [統合] タブに移動して Google アナリティクスを有効にする。
- firebaseと紐づけてGAを設定すると連携されたアカウントが作られる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2367608/8cebdef4-1afa-5b97-a07f-a2600d549e61.png)


# FireBaseとGCP(BigQuery)の連携

-  Firebaseはプロジェクト単位で管理する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2367608/3d67f276-4ed2-79c8-672d-98f31cc6d12c.png)

- firebaseでプロジェクトを作成するとGCPのコンソールでもプロジェクトが同期して作成されている
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2367608/1580c48c-7a06-2d27-0b18-1474ea81f7a2.png)


- firebase管理画面からプロジェクトの設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2367608/8f4396d2-0c0d-c923-6753-17be3917067b.png)


- 統合タブから各サービスの紐付けを行う
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2367608/c6edac8a-de1f-6c3c-e0e0-ed9c935d6746.png)


## サービスアカウントの作成
### 認証
- BigQueryのAPIを使う際には認証が必要
- BigQueryだけでなくGCPのサービスのAPIを叩く際には、共通して必要な作業
- Service Accountsという方式を使った認証方法を活用
- サービスアカウントにはリソースの操作権限を付与（AWSのIAMポリシー的なもの）
- サービス固有のjson形式のキーを発行してそれをアプリに環境変数とかで置き、APIを叩くという方式

#### IAMと管理
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2367608/464bee74-e29b-6f76-c712-78c2a9f58f01.png)


#### サービスアカウントの作成（AWSのIAMのような感じ）
- サービスアカウントを作成し、BigQueryを操作する権限を付与する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2367608/31a3be3a-1caa-23aa-a740-d35f73c96374.png)



 #### 作成後、キーを追加するとクレデンシャルが発行される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2367608/9585e833-a159-2899-550b-9cca439d6fb5.png)


#### キー(jsonファイル)をサーバーのルートにおいて環境変数で呼び出す
 - 環境変数名は決まっていて以下のような感じでファイル指定で呼び出すようにする
 
 ```yaml
 GOOGLE_APPLICATION_CREDENTIALS: ./gcp-credentials.json
 ```
- 本番等はAWSのsystem managerに環境変数としてjsonのキーをそのまま入れて呼びすようにした

#### projectのIAMの変更権限を有効化する
https://cloud.google.com/iam/docs/granting-changing-revoking-access?_ga=2.88702043.-1113265949.1642144611

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2367608/6582df4b-b800-c8fe-6211-f309ed301aa1.png)


 #### プロジェクトでのIAMの管理権限を付与する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2367608/94915b7a-8314-5222-7751-b615c221263f.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2367608/5df40f29-7538-b80f-bd65-0e6ca3efcd15.png)


### BigQueryにアクセスするサービスアカウントに権限をアタッチする
- 最初何も設定していなければデフォルトでGCPの全てのリソースにフル権限でアクセスできる「オーナー」権限になる。
- IAM→権限の編集から権限を変更する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2367608/54a832d4-080e-5bdf-d38a-b299042c2b76.png)



## APIとサービス

### 有効なAPIサービスとサービス
- 有効なAPIサービス一覧でbigqueryを選ぶとサービスアカウントが紐づいていることを確認できる


## 請求アカウント紐付け
- BigQueryを有効化するにはプロジェクトに請求アカウントを紐づける必要がある
https://cloud.google.com/bigquery/docs/quickstarts/load-data-console?authuser=1&_ga=2.60872684.-1113265949.1642144611

https://cloud.google.com/billing/docs/how-to/modify-project?authuser=1#enable_billing_for_a_project
- 「お支払い」から

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2367608/5ef2c28c-ade9-6136-dfd8-274f4bf4ee3e.png)


- GCPはプロジェクト単位で請求アカウントを紐付けることができる（請求アカウントの新規発行はアカウント自身が作成でき、そのアカウントは何個でもプロジェクトに紐づけることができる）

- 「請求先アカウントを管理」から管理している請求アカウントの状況を確認できる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2367608/69643ae9-c3d4-214b-8b6e-d568bb17e74c.png)

- 紐付け後も「リンクされた請求先アカウント」から請求状況（レポートを確認できる）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2367608/69643ae9-c3d4-214b-8b6e-d568bb17e74c.png)


# 作った機構の全体像（再掲）
- アプリ側のコードにGAのタグを埋め込み
- ユーザーがイベントを踏むと発火しデータをGAへ送信
- GAで保存→定期的にGAからBigQueryにデータをエクスポート
- BigQueryでデータを永続化→BigQueryで保存データに対してSQLを実行してデータを加工して取得
- そのデータをアプリで表示

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2367608/cd2c18eb-a6ae-e72a-cb47-d2199d505359.png)



## GAから BigQueryへのエクスポート処理の設定手順
https://support.google.com/analytics/answer/3437618?hl=ja

## アプリケーションの設定
- gem 導入
```Gemfile
gem 'google-api-client'
gem 'google-cloud-bigquery'
```

## BigQuery概念
上から順に粒度が細かくなるイメージ

- プロジェクト
- データセット
    - データセットとは、BigQueryで扱うテーブルの集合（データベースのようなもの）
- テーブル
    - データセットの中のテーブル

## 発行するSQL

### BigQueryで使うsqlの基本
 #### 基本構文
- SELECT句にカラムをそのまま指定して、FROMにはプロジェクト名.データセット名.テーブル名
```sql
SELECT 
  event_name
FROM 
  `{プロジェクト名}.{データセット名}.*`
```

#### カラムのデータがネストしている形式のものを取り出したい時
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2367608/d209efc6-c6ee-d09c-0f04-c205e09ff97e.png)


https://ichi.pro/bigquery-no-unnest-kansu-o-shiyoshite-analytics-no-ibento-parame-ta-o-bunsekisuru-276538172443458

- jsonのネストしているデータを全部取り出す場合
```sql
SELECT param
FROM `#{@settings.project}.#{@settings.data_set}.*`,
UNNEST(event_params) AS param
```

- 特定のカラムの値だけ取り出したい場合
```sql
SELECT
  ( SELECT params.value.int_value
    FROM UNNEST(event_params) params WHERE params.key = 'post_id' ) 
    AS post_id
FROM `#{@settings.project}.#{@settings.data_set}.*`
```
- FROM句に``UNNEST``関数を用いてjsonをオブジェクト形式っぽく取り出せるようにして``params``という別名に渡す（別名はなんでも良い。asは省略されている）
    - UNNEST関数の使い方( https://ichi.pro/bigquery-no-unnest-kansu-o-shiyoshite-analytics-no-ibento-parame-ta-o-bunsekisuru-276538172443458 )
- SELECT句では別名からparams.value.データ型で型指定をする。
- WHERE句では取り出したいカラムを指定する。


``( SELECT params.value.int_value
    FROM UNNEST(event_params) params WHERE params.key = 'ga_session_id' ) 
    AS ga_session_id``の部分は単に、カラム名を出力しているのと同じ
以下と同義だが、 BigQueryではそう書けないだけ。
```sql
SELECT 
  post_id
FROM 
  `#{@settings.project}.#{@settings.data_set}.*`
```

### sqlをパラメータにして実行するメソッド
https://www.rubydoc.info/gems/google-cloud-bigquery/0.23.0/Google%2FCloud%2FBigquery%2FProject:query
```
Google::Cloud::Bigquery.new.query(sql)
```

## データセット、テーブルのデータ保存期間設定
- デフォルトは60日間になっている
- 保存期間なしにして永久保存に変更
- データセット内で今後作成するテーブルのデフォルトのデータ有効期限を設定
    - BigQueryダッシュボード→データセット→詳細を編集→デフォルトテーブルの有効期限
<img width="1136" alt="image.png (102.1 kB)" src="https://img.esa.io/uploads/production/attachments/10655/2022/02/28/78280/e1a3e32a-9cec-4973-82cf-9abcec34c710.png">

- テーブル自体のデータ有効期限を決める場合
    - BigQueryダッシュボード→テーブル→詳細を編集→有効期限
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2367608/4838545b-b6f4-1142-23ed-a52ecf8da66e.png)

# BigQuery使用料金試算

基本は以下の単価に実際どれくらいアクセスがあるかを掛けて算出した。

[Google公式の料金(ストレージ)](https://cloud.google.com/bigquery/pricing?hl=ja#storage)
[Google公式の料金(分析/クエリ)](https://cloud.google.com/bigquery/pricing?hl=ja#on_demand_pricing)

## 料金まとめ
### 分析料金
- クエリ（オンデマンド） $5.00 per TB 毎月 1 TB まで無料
- クエリが参照するテーブルあたりのデータ最小処理容量は 10 MB、クエリあたりのデータ最小処理容量は 10 MB
- 作ったクエリが一回の実行でどれくらいのバイト数を消費するのかは、BigQueryのコンソールから手動実行すれば表示されるのでそれを参考値として算出
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2367608/c9db4352-a9fb-a3a7-0a13-15d71820d173.png)


### ストレージ料金
- アクティブ ストレージ``$0.020 per GB``、 毎月10 GB まで無料
- 長期保存 ``$0.010 per GB``、毎月 10 GB まで無料


## コスト削減
- キャッシュをつかえば毎回アクセスする必要がなくなるので料金も安くなる

# 請求管理
## BigQuery利用時のカスタム割り当て（Custom Quota, 日次の使用量制限）
https://cloud.google.com/bigquery/docs/custom-quotas
- BigQueryのプロジェクト毎およびユーザアカウント（ServiceAccount）毎に 「1日あたりの上限使用量」を設定
- 上限を超えるとクエリ実行時にエラーとなり、それ以上費用が発生しなくなる
- 設定
https://cloud.google.com/docs/quota#requesting_higher_quota
「割り当てを編集」から
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2367608/b79e1de7-8e06-ed54-cfb1-4a5a7f626c7d.png)

- プロジェクト全体: max 10TB/日 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2367608/bd7c1992-99f8-fecc-281c-7d7895830157.png)

 - 各ユーザ: max  1TB/日
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2367608/fe107c04-742f-a5d7-2039-3d030c2bc3e1.png)


- 設定値
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2367608/62089a15-ab33-8d4a-bd16-be5aa8043753.png)



こちらを設定しておけばBigQuery破産は防げます。



いかがでしたでしょうか？？実は、この後の続きとして、閲覧数のデータをRDSのデータとがっちゃんこして、AWS QuickSightで表示して分析するというタスクがあり、そちらの機構をAWS Glueなどを使って構築した記事も執筆中です。乞うご期待。