BigQuery の課金額で泣かないための UserScript
テックリード @ 株式会社カケハシ
医療SaaSの共通基盤を開発。TypeScriptと関数型プログラミングで堅牢なシステム設計を実践。
はじめに
BigQuery を利用する上で、うっかり高額なクエリを投げてしまったことはありませんか?また、「BigQuery を利用したいけれど課金額が分からないと破産しそうで怖い」という方もいるのではないかと思います。
https://qiita.com/itkr/items/745d54c781badc148bb9
そこで、BigQuery のエクスプローラ画面で、入力したクエリの課金額を実行前にその場で表示してくれる UserScript を作成しました。

解決策
要件
そこで、私は以下のような機能を実現したいと考えました。
-
そのクエリで処理されるバイト数を取得する
-
バイト数から課金額を計算し表示する
UserScript にした理由
ブラウザで表示しているページを手軽に書き換える方法として、たとえば以下があります。 今回は、「手軽に複数のブラウザに向けて公開したい」「ユーザが一度導入したら意識せずに機能が働いて欲しい」「ユーザのリテラシは高いと考えて良い」という要件のため、UserScript として開発しました。
-
各ブラウザの拡張機能
-
ユーザが簡単に機能を導入できる
-
開発者は提供したい各ブラウザの拡張機能の仕様に合わせる必要がある
-
UserScript
-
開発者は簡単に複数のブラウザに向けて機能を公開できる
-
ブックマークレット
-
ユーザが能動的にアクションしないと実行されない
処理されるバイト数の取得
また、処理されるバイト数を取得する方法としてすぐ思いつく方法は「DOM からテキストを取得してパースする」があるかと思います。BigQuery ではクエリを書き換えるたびに処理されるバイト数を表示してくれるので、この DOM を MutationObserver で監視すれば取得できるでしょう。しかし、10 MB のように整形されたテキストよりも生のバイト数の方が扱いやすいため、この方法は採用しませんでした。
代わりに、以下の方法で実現しました。
まず、XMLHttpRequest.prototype.send をオーバーライドして XHR による通信を監視します。
(function() {
const globalSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function(){
this.addEventListener("readystatechange", function() {
// XHR による通信のレスポンスが取得できる
console.log(this.responseText)
}, false)
globalSend.apply(this, arguments)
}
}
次に、レスポンステキストに totalBytesProcessed が含まれている場合は、レスポンステキストを JSON 形式としてパースし、処理されるバイト数を取得します。
if (this.responseText.includes("totalBytesProcessed")) {
const totalBytesProcessed = JSON.parse(this.responseText)[0]?.data?.response?.totalBytesProcessed
}
この方法はあまり行儀の良いやり方ではないため、場合によっては利用者を不安にさせるかもしれませんが、今回の UserScript は全体で 50 行に満たない非常に小規模なスクリプトであるため、このスクリプトに危険性が無いことをご理解頂いた上で利用していただけると考えました。
スクリプト
以下に該当のスクリプトを公開しています。
今回のスクリプトでは、米国リージョンを基準にした料金を表示するため、他リージョンでの金額を表示したい方はよしなにスクリプトを書き換えていただければと思います。
おわりに
課金額に気をつけながら、快適な BigQuery ライフを送りましょう。
免責事項
私は当ページに掲載した情報について可能な限り安全かつ正確であるように努めておりますが、安全性または正確性などについて責任を負うものでは有りません。 当ページに掲載された情報によって生じたあらゆる損害等について、理由の如何に関わらず、私は一切責任を負いません。