FFFT

主にwebプロダクト開発に関連する話をつらつらと

【2020年7月】Google AdSenseは新型コロナウイルスの影響で審査をストップ


f:id:keyama4:20200418210941p:plain

ブログの投稿記事数がある程度溜まったので、Google AdSenseに申請してみました。
ちなみに申請日は2020年4月18日(土)

翌日には審査結果が返ってきました。
タイトルのとおり、Google AdSenseの審査は新型コロナウイルスの影響で全面ストップしているようです。

[追記]
2020年7月も引き続きストップ。

新型コロナウイルス感染症(COVID-19)の世界的流行の影響で、Google では現在、一部のサービスで一時的に遅延が発生しています。そのため、現時点ではお客様のサイトを審査することができません。
ご不便をおかけして誠に申し訳ございませんが、ご理解とご協力のほど、よろしくお願い申し上げます。

f:id:keyama4:20200419185755p:plain

IT企業の最大手、Googleでも一部の業務を停止させる事態。
審査担当者の業務に、感染リスクのある制約があるのか(審査フローにVPNでも接続できないネットワーク制限のかかったシステムを介した作業が組まれてたり?)、または、ウイルスの影響で広告主が激減したため、一時的に審査自体を止める判断になったのか。
ウイルスの影響で世界的に余暇の時間の過ごし方に制約がついたので、インターネットでの情報発信量が増え、掲載側の申請が増えたからという理由も考えられそうですね。

いずれにせよ、影響を受けないだろうと思っていたサービスもこういった対応をする必要が出てくる状況になっているんですね...
世界的にウイルスの終息が進むことを祈るばかり。

審査の返信は自動化しているとは思うものの、あまり迷惑をかけないよう再申請は間隔を広げて行っていこうと思います。

ブラウザ側のデータ管理はWeb Storageで良いよねって思ってたけど容量問題にぶつかった

どんな背景からどんな問題が起きたか

あるアプリケーションの開発。
サーバサイドの都合で、一時保存APIが用意できない状況。
ただ、それなりに入力量が多いフォームでユーザーが途中まで入力しても離脱してまた再入力するケースを救いたい。

上記の状況を解決するべく、擬似的な一時保存機能の実現にWeb Storage(localStorage)を使おうとしたんですが...
容量問題にぶつかる。

単純なテキスト入力などのフォームの値であれば、奇想天外な量でない限り、全く問題なくlocalStorageの容量内に収まる。
しかし、今回のフォームは10MB以下の任意のファイルを最大6つアップロードできるようにする必要が。
アップロードされたファイルをbase64エンコードしてlocalStorageに入れたら容量不足に。(そりゃそうだ)

localStorageとのやりとりは王道のこちらのライブラリを使用。クロスブラウザで動作します。
github.com

こんなエラーが出ました。

Failed to execute 'setItem' on 'Storage': Setting the value of 'KEY_NAME' exceeded the quota.

そもそもWeb Storageとは

Web StorageHTML5から追加された仕組みで、ユーザーのブラウザにデータを管理(追加・上書き・取得・削除)させることができます。
localStorageとsessionStorageの2種類があり、違いはデータのライフサイクル(保存期間)。

・localStorageは永続(明示的にプログラムまたはブラウザのコンソールから消して初めて消える)
・sessionStorageはブラウザのタブが閉じられるまで

localStorageはサイトに何度訪れても最後に入力、または取得したデータを再現させたいシーンなどに利用されます。
ほとんどの場合、サーバサイドで永続化された情報を使えることが多い気がするので、
「このデータ、入力補助のためだけにしか使わないよね。サーバサイドで永続化させるの違うね。localStorage使うか。」みたいな場合に使っている気がする。後は本当は良くないけど、今回のように一時保存APIなどサーバサイドで用意するべきAPIがない場合など。

sessionStorageは主に入力内容を次の画面(確認画面とか)に引き継がせる役割で使われていることが多いです。
その名の通り、セッション単位で保持しておきたいデータを管理するのに最適という感じでしょうか。

どちらともオリジン単位でデータが管理されます。
key-value型のデータ保存形式でAPIもシンプル。
keyを指定してset, remove, getができ、stringで値が保存されます。

Web Storageにはどれだけデータを入れられるか

cookieとは比べ物にならないぐらい大容量だぜ!」っていうのは、よく聞いていたので何も考えずに作ってエラー出てから気づくという...
cookieが4KB(20個までなのでトータル80KB)だからたとえ比べ物にならなくても、今の時代のファイルじゃ容量越えるよねと。
Web Storageはデバイスのブラウザごとに容量の上限が変わるのですが、5MBを見ておけばメジャーなデバイスのブラウザでは動作するようです。
Androidだけ、やけに低く2MBちょっとらしい。Androidのブラウザも推奨ブラウザに含めるなら2MBを見ておくと良いかも。

どうしたか

諦めました。
具体的には、
・ファイルアップロード以外の入力情報をlocalStorageで管理し、擬似一時保存を実現させる
・ファイルはreactのcontextで管理し、ユーザーがリロードやタブ、ブラウザを閉じたらデータがなくなるのを許容する
にしました。

今回のアプリケーションの与件が「サーバサイドで一時保存APIを用意できない」だったので上記の対応にしましたが、本来的にはサーバサイドでAPIを用意する、ですね。

bashからzshに3分移行 ~ MacOS catalinaからデフォルトシェルがbashからzshに ~

Macを使っている方は、catalinaからデフォルトシェルがbashからzshに変わりましたね。(今更感ありますが...)
後述しますが、訳あってmojaveからcatalinaにアップグレードを数ヶ月前に行いました。
ターミナルアプリ(自分はiTerm2)を起動する度に The default interactive shell is now zsh. To update your account to use zsh, please run chsh -s /bin/zsh と警告が出るのでいい加減やるかと思い、zshに移行したので簡単に最低限の手順を残しておきます。
まだ移行されてない方が少しでも時間をかけずに移行作業を行うための参考にしていただけたら幸いです。

なお、zshへの移行はもちろんマストではないので、「俺はそのままbash使ってくんだよ。警告うぜぇな。」という場合は下記のコマンドを叩くと次回の起動から警告表示がされなくなります。

$ echo "export BASH_SILENCE_DEPRECATION_WARNING=1" >> ~/.bash_profile

ターミナルアプリを一度閉じて、再起動して確認してみてください。

zshに移行するぜ」という方は以下の手順を実行しましょう。

警告文に記載のとおり、chsh -s /bin/zsh を実行

これでシェルがbashからzshに変わります。
次にbashの設定ファイルの情報をzshに引き継がせます。

デフォルト環境変数などの情報をまとめたbashログイン時に実行されるファイル群をzsh用に変換(と言いつつただのコピー)

$ cat ~/.bash_profile >> ~/.zprofile
$ cat ~/.bashrc >> ~/.zshrc
$ source ~/.zprofile
$ source ~/.zshrc

上記は最低限であり、その他bashで使っていた設定ファイルがあれば、移行・反映してください。

zshはそのままだと補完機能がかなり弱いため、補完機能を強化する専用のライブラリを入れる

github.com

$ brew install zsh-completions

これを入れると「bashより補完機能全然良いやん。」ってなりました。
ここまでやれば自分は一旦十分という感じ。

最後に、自分は業務の中でzipファイルを解凍することが週何度かあるので、「catalinaのzipファイルを解凍するバグが解消されるまでアップグレードしない!」と思ってたんですが、あまりにもこのバグのFIXに時間がかかっているので「もうzipファイルの解凍はサードパーティのソフト使うか」となりました。10.15.3でも直ってなのですが、本日10.15.4にアップデートしたら直った!
f:id:keyama4:20200415094349p:plain
ついに上記のエラーを見ることがなくなりました。嬉しい。

既存のリソースをterraform管理にする手順

簡易に手順とハマりどころをまとめておく。

過去に手動で作ってしまったGCPのCloud SQLインスタンスやDB設定、ユーザー設定があったのでそれをterraform管理下にした話。

まず、公式にあるとおり、現在のバージョン(記事執筆時点でのlatestはv0.11.13)ではstateへの反映のみで構成(*.tf)ファイルを自動で生成してくれるところまではサポートしてないとのこと。

www.terraform.io

The current implementation of Terraform import can only import resources into the state. It does not generate configuration. A future version of Terraform will also generate configuration.

今後のバージョンアップで設定ファイルも自動で出力してくれるようにするみたいだが熱望。
この後の流れでさらっとまとめるがすでに動いている環境のリソース定義を手動で構成ファイルにまとめていくのは心臓によろしくないし、俺何やってるんだろう状態になる。
とりあえずplanを叩くことがやたら多かった。
terraformで管理されていない環境をまるっとterraform管理に寄せていく流れを加速させるにはマジで早いところ対応するべきところだと思う。

というわけで実際の作業の流れは下記のようになる。

  1. 新しくterraformに管理させたい既存のリソース定義を空で追加
  2. terraform import コマンドを使って1つずつ、terraform管理に入れていく
  3. 管理対象に入れたリソースの定義を頑張って手動で追加
  4. terraform plan で差分確認
  5. terraform apply

新しくterraformに管理させたい既存のリソース定義を空で追加

公式より。

Because of this, prior to running terraform import it is necessary to write manually a resource configuration block for the resource, to which the imported object will be mapped.

とのこと。
空で追加していく。

Cloud SQLインスタンスとDB, ユーザーをterraform管理下にしたい。
下記を構成ファイルに追加する。

resource "google_sql_database_instance" "instance-name" {
}

resource "google_sql_database" "db-name" {
}

resource "google_sql_user" "user-name" {
}

terraform import コマンドを使って1つずつ、terraform管理に入れていく

terraform importコマンドを使う。
www.terraform.io


残念ながら一括でリソース指定はできないよう。
terraformをstaging, productionそれぞれで使っている人が多い気がするので対象のリソースが多ければスクリプトを組んでまとめた方が安全な気がします。

実際のコマンドの使い方は下記。

terraform import ${リソースタイプ} ${ID}

ここで言うIDはすでに環境で稼働しているリソースを一意に識別するもの。
IDはリソースタイプによって指定方法が異なるので、公式で一つ一つ指定方法を確認していく必要がある。

それぞれのリソースタイプのリファレンスの下部にimportの指定方法が記載されている。(見つけにくかった。)

https://www.terraform.io/docs/providers/google/r/sql_database_instance.html#import
https://www.terraform.io/docs/providers/google/r/sql_database.html#import
https://www.terraform.io/docs/providers/google/r/sql_user.html#import

今回はそれぞれ下記のようになった。

terraform import google_sql_database_instance.instance-name ${プロジェクト名}/${インスタンス名}
terraform import google_sql_user.user-name ${インスタンス名}/${ホスト名}/${ユーザー名}
terraform import google_sql_database.db-name ${インスタンス名}/${データベース名}

実行。

が、下記のようなエラーが。。。

Error: Provider "kubernetes" depends on non-var "google_container_cluster.hoge_cluster.0/google_container_cluster.hoge_cluster.N". Providers for import can currently
only depend on variables or must be hardcoded. You can stop import
from loading configurations by specifying `-config=""`.

解決策はこちら。
keyama.hatenablog.com

管理対象に入れたリソースの定義を頑張って手動で追加

頑張る。

後はplanを叩きつつ、修正しながら差分チェックしてオッケーであればapply。

ここで自分はterraform importを実行した環境のバージョンとterraform plan, terraform applyを実行したバージョンのパッチバージョンが異なることで怒られた。

Error: 
Terraform doesn't allow running any operations against a state
that was written by a future Terraform version. The state is
reporting it is written by Terraform '0.11.10'

Please run at least that version of Terraform to continue.

パッチバージョンのズレも許さない徹底感。
stateいじる系はやっぱりセンシティブなんだなと。

バージョン合わせることで無事できました。

terraformでリソースをimportしようとしたらproviderの設定が理由で怒られた

GCP環境に手動で作ってしまったリソースをterraform管理に入れようと、terraform importしてぶつかったエラー。
今回いじったterraformのproviderには `google`, `kubernetes` を入れている。

k8sの設定が理由で何やら怒られる。

Error: Provider "kubernetes" depends on non-var "google_container_cluster.hoge_cluster.0/google_container_cluster.hoge_cluster.N". Providers for import can currently
only depend on variables or must be hardcoded. You can stop import
from loading configurations by specifying `-config=""`.

import時にproviderの設定はハードコードされてる必要があるか、変数にのみ依存できるよ

とのこと。

今回importしたいリソースにk8sは関係なかったのでコメントアウトしてimportを再度実行。

でいけた。

なんでこんなんなってるのだろうか。

ここらへんを見る。
provider configurations must not depend on resources during import · Issue #17847 · hashicorp/terraform · GitHub

nginxから「413 Request Entity Too Large」が返却された際の対応

ちょっと大きめのファイルをアップロードしようとすると413が返却されることがあります。
nginxはデフォルトでアップロードサイズが制限されていて、1Mがデフォルトになっています。
(ちょっとサイズのあるPDFは1Mを超えるのでデフォルト厳しすぎないかと思ってしまう。)

対応はクライアントからアップロードするファイルサイズを1M以下になるように圧縮するか、nginxの設定を変更します。

confファイルに

client_max_body_size 10m;

を1行追加すればおっけー。
今回は10Mで設定。

後はnginxを再起動すればconfの設定が読み込まれて反映されます。

「このランディングページ、ちゃんとユーザーに読まれてる?」を知りたい

Webサービスを運営しているとサービス紹介などが載っているランディングページがちゃんとユーザーに読まれているか、気になることがあると思います。

 

コンテンツページの先に遷移させたい画面があるのなら遷移率で見ればそのコンテンツページの貢献度がわかりそうですが一概にそうとは言えません。

 

多くのサイトの目的は「クリックさせて次のページに遷移させること」ではなく、さらにその先にある「コンバージョン」のはずです。

 

となるとコンテンツページの役割は「なんとなく良さそうなサイトだから次に進んでみよう」と思ってもらうことではなく、「なるほど、こういう価値を提供してくれるのか、使ってみよう」と思ってもらうことになるはずです。

 

つまり、「次ページの遷移率」と合わせて、「ユーザーがちゃんとコンテンツを読んでいるか、サービスの提供価値を理解してくれているか」の2つがそのページのサービス貢献度を見るにあたって重要になりそうです。

 

次ページへの遷移率はgoogle analyticsから見ることができますね。

では、コンテンツがちゃんと読まれているかどうかはどうやって見ればよいのでしょうか。

 

2010年の文献ですが、Microsoft Researchのレポートで「ユーザーはそのページに訪問して10秒以内にそのサイトに留まり続けるかを決める」そうです。http://susandumais.com/sigir2010-dwelltimemodel.pdf

 

そのページのユーザーの滞在時間が10秒を超えたらそこからさらに数秒、数十秒は見て回ってもらえるということです。

そこでサービスの提供価値がユーザーにとって嬉しいものであれば次のページに進んでくれるわけです。

 

この10秒がランディングページにとって審判の時間となるわけですね。

 

つまり、そのページの平均滞在時間が10秒を切っているようであればサービス理解が浅いユーザーが一定数いて、そのまま離脱、もしくは先に進んでしまっているということになりそうです。

 

平均滞在時間が10秒以内の場合、集客しているユーザーのニーズにコンテンツが合っていない、そもそもサービスの提供価値が弱過ぎる、など原因はいろいろと考えられそうです。

 

10秒以上だったらちゃんと読んでもらえてるのかよ、というともちろんそれは一概にそうとは言えなさそうです。

最低限の数字ぐらいに考えるのが良さそうです。

 

ユーザーに正しく提供価値を理解してもらえるランディングページを作りたいものです。

 

最後に...

もちろんそのサイトのコンバージョンポイントが無料なのか有料なのか、エンドユーザー向けなのか法人向けなのか、でこの話は変わってくるかと思います。

無料のエンドユーザー向けならランディングページの目的が「なんとなく面白そうと思ってもらってとにかくカジュアルに使い始めてもらう!」になるものもありそうです。

 

そのサービスのターゲットユーザーの特性やニーズを考えてサービスの顔になるランディングページの役割を決めて良いものを提供したいですね。