GCP BigQueryチュートリアルお触りメモ
GCPのData Engineer認定試験に向けて、データウェアハウスに分類されるBigQueryのチュートリアルを一周したのとドキュメントを読み漁った時のメモです。最近メモ続きで今回はコードは一行も出てきませんが、試験直前に見直すために書き残します。そろそろパイプラインとか自前で作ってみないと。。。
データロード方法
データを流し込むのは以下の方法がある。
- GCSのファイルを読み込ませる (CSV, JSON, Avro, Parquet, ORC)
- Googleのサービス経由で流し込む
- ローカル(GCP以外の場所)からファイルをアップロードして読み込む
- DMLで一括挿入
- プログラムからストリーミングインサート
- Dataflowでストリーミングで流し込む
- (ロードじゃないけど) 外部のデータソースの参照
データロードのTips
- 直接ファイルをBQに入れると Upload -> Importの二段階が走って途中でこけると悲しい。GCS経由だと10倍程度早いようだ。
- 圧縮データでロードする場合は、Avroが最適、次点がPurquet。圧縮率が高いのに、並列読み込みできるから。
- CSVとJSONファイルは、gzip圧縮よりも圧縮なしの方が早い。
- 基本は文字コード UTF-8で入れること。ISO-8859-1でも読み込み可能だが、UTF-8に変換しながら読み込むため遅い。
テーブル構造
- 構造化データを入れる
ストリーム処理
- ストリーミングの上限10.000行 / sec
- ストリーミングインサートするときは、上記の上限に引っかからないようにDataflowを噛ませた方が良さそう。
- DataflowならPub/Sub, GCS, JDBC(アプリ経由)のテンプレートが用意されているから使うハードルは低い。
- バッチは強力な整合性、ストリーミングは結果整合性と整合性の性質が異なる。(チュートリアルでFluentd -> BQの場合は1分くらいラグがあった)
課金の話
- 課金対象は、読み込んだ行数 で決まるので出力結果だけ絞っても金額は絞らない場合と変わらない。(よって、LIMIT句を加えても課金額は変わらない!!)
権限設定周り
模擬試験でも出てきて焦ったところだけれども、この人にはこのデータセットは見せたくないとか権限設定は大事。
JavaScriptの2次元配列の展開・重複削除・集約をLodashを簡潔に書く
2018年末に書いたSlack Botを少しリファクタリングした時の話。2次元配列と配列要素の重複削除ってよく使いそうで意外とベストなやり方ってよく分からないですよね。自分の場合は2次元配列は map
と foreach
、重複削除は Set
オブジェクトで実装していましたが、後からぱっと見て処理が訳わからんコードになりがちです。JSって flatMap
ってないので気合いで書いちゃう。
以前書いたTrello/Slack/OpenWhiskで作ったBotでも同じような処理を実装しなければいけなくて、やっぱり読みづらいコードが爆誕してしまいました。正直コード書いてから1週間経った今でさえ分からんw
さて、こんなダメコードを書いていてはいけないと解決策を模索したところ辿り着いたのがLodash。
ライブラリではよく中の実装で使われている印象があり今更かよ感が出ていますが、Lodashで関数を組み合わせて実装してみると思った以上に読みやすくなったのでBefore/Afterで見比べながら書き残します。
解決策
入力データ(Trello APIを呼び出した時のレスポンス)は以下のようなJSONです。
[ { id: "dfhajsdfhalsdfhasrber", name: "task1", labels: [ { id: "aaa", name: "hoge" }, { id: "bbb", name: "fuga" } ] }, { id: "ruweqioryqweryqweyr", name: "task2", labels: [ { id: "aaa", name: "hoge" }, { id: "ccc", name: "piyo" } ] } ]
この関数のアウトプットとしては、{name: "hoge", count: n}
というオブジェクトの配列にしたい。
改善前
まずは年末の働かない頭で書いた以下のコードを見て欲しい。
function summarizeLabels(cards) { // 二次元配列でラベルを取り出して展開し、シンプルな文字列のみの配列にする ... (1) let labelLists = []; cards.map(card => { card.labels.forEach(label => labelLists.push(label.name)); }); // 配列内のラベル一覧を重複削除で取り出す ... (2) const labelSet = new Set(labelLists); // ラベルごとの件数を配列とラベル一覧から導出 ... (3) let labelResults = []; for (let targetLabel of labelSet) { const labelCount = labelLists.filter(cardLabel => cardLabel == targetLabel).length || 0; labelResults.push({ name: targetLabel, count: labelCount }); } return labelResults; }
やっていることとしては、以下3つ。文字に起こすと長いですね。
- まず
label
プロパティを取り出すと配列の中に配列が入れ子になった2次元配列の形になる。これは扱いづらいから展開しておく(1)。 - 次にキーを取り出すために(1)の配列を重複削除してキーのリストを取り出す(2)。
- 最後に(2)のキーのそれぞれに対して(1)の中で合致するラベルの数をカウントしてオブジェクトに突っ込む(3)。
いやー、、、 よ゛み゛つ゛ら゛い゛よ゛お゛
正直後で直したくないコードが見事に爆誕です。理解しづらい理由としては、2次元配列を展開していることを中から外に向かって処理を追わないと理解できない、Setってなんだっけ、filter関数と突っ込む先の配列定義が離れすぎて何の配列か分からない。こんなところでしょうか。
改善後
Lodashで関数チェーンを作り上げて直感的に書くことを試みます。最終的なアウトプットに値を集計する関数のみ自分で作成し、これ以外はLodashで定義された関数を使います。
const _ = require("lodash"); /** * 名称ごとの出現数を集計する. * @param {Object} stat 集計値 * @param {String} name 集計キー ... 配列内の個々の値が入る */ const gatherNames = (stat, name) => { if (_.isUndefined(stat[name])) { stat[name] = { name: name, count: 0 }; } stat[name].count++; return stat; }; function summarizeLabels(cards) { const labelResults = _.chain(cards) .flatMap(_.property("labels")) // ... (1) .map(label => label.name) .reduce(gatherNames, {}) // ... (2)/(3) .values() .value(); // ... これを呼び出して初めて上記の関数チェーンが実行される return labelResults; }
以前よりもコードが美しくなった気がします!
改善前のコードで分かりづらかった2次元配列の展開は _.flatMap()
で一発で書けます。Lodashのメソッドもmap
, reduce
といった比較的聞き覚えのある名称なので理解しやすい。for文回さずシンプルに書けるところもポイント高いですね。
一番読みやすくなっているポイントは、処理を関数を組み合わせて関数チェーンで表現できていることでしょうか。
Lodashくん、、、君はすごいライブラリだったのか。。。色んなライブラリで多用される理由も納得です。
JavaScriptのクロージャって結局なんだよ
JavaScriptでは初歩的なコードでは現れないけれどふとした拍子に出てくる単語っていくつかいるような気がします。 特にカリー化、高階関数、クロージャ、ファンクターあたり。関数型プログラミングを進めていたら最初の方に出てきて戸惑ったのが クロージャ ってやつです。走り書きでn番煎じですが調べて噛み砕いた結果の備忘録です。
結論
要は 関数 です。("スコープ" と書いてあることが多いけど、実際に指しているのは関数の模様)
関数の中でも、以下の特徴を持つ関数のようですね。
- 未実行の関数を返す関数である。
- 子の関数からアクセスできる変数を保持する。
ただの関数とは何が違うわけ?
上の(1)だけ見てもJavaで言うところのファクトリメソッド, JavaScriptだと関数ファクトリ(?)っぽいです。 半分合っていそうですが、違いはソースコードを元に見た方が早い。
let outerVar = "outerVar"; function makeInner(params) { const innerVar = "InnerVar"; function inner() { console.log(`I can see: ${outerVar}, ${innerVar}, ${params}`); } return inner; // 未実行の関数を返す } const inner = makeInner("params"); inner(); // --> I can see: outerVar, innerVar, params
上の例だと、makeInner
関数がクロージャになります。inner
関数を返していますね。この makeInner
実行時には inner()
としていないから未実行です。(定義1)
また、クロージャ内のローカル変数は inner
宣言時には関数のスコープ外になっていますが消えません。(定義2)
こうやって親の関数から返却される子の関数から、子&親の関数内/関数の引数/グローバルスコープの変数にアクセスできるところが他の関数とは違うところ。 ただ、変数は残ってはいるもののあくまで 参照のみが残っているだけ であって値がそのまま保持されるわけではなさそう。
(let outerVar ~ makeInnerクロージャの宣言まで同じ) // クロージャから内部の関数を引数を渡して生成する前でも後でも参照する値は変更可能 // outerVar = "Outer2"; // inner() --> outerVar=Outer2 const inner = makeInner("params"); // outerVar = "Outer3"; // inner() --> outerVar=Outer3 inner(); // --> I can see: outerVar, innerVar, params
2番目の例の通り、途中で書き変わると最終的にクロージャから返却される関数の実行結果は変わるようですね。
参考
JavaScript関数型プログラミング 複雑性を抑える発想と実践法を学ぶ (impress top gear)
- 作者: Luis Atencio,株式会社イディオマコムニカ加藤大雄
- 出版社/メーカー: インプレス
- 発売日: 2017/06/09
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
Gradleを使ってIBM Cloud Functions with Javaの関数を実装する
あけましておめでとうございます。新年一発目はサーバーレスです。 昨年はNode.jsしかほとんど触らなかったIBM Cloud Functions(OpenWhisk)をJavaで実装します。
ただし、公式のチュートリアルだと自力でgsonにCLASSPATH通してデプロイしてねと雑にしか書いていないので、こちらのブログのチュートリアルを使います。
とはいっても、作り方は2ステップで簡単です。 ブログ記事そのままですが、書き初めとうことで基本を押さえに行きます。
Javaの関数を書く
ポイントは2つ押さえておけばOKです。
- 実行する関数(厳密にはエントリーポイントとなるメソッドを指します)の名前は
main
固定です。(別名にしてもいいけど仕組みに乗っかった方が楽) - どうやらリクエストとレスポンスのJSONパーサーはgsonを使うことが推奨されています。(これ以外のJSONパーサーは使えないのかは未検証です)
簡単なHello worldは以下のようになります。
package com.example; import com.google.gson.JsonObject; public class Hello { public static JsonObject main(JsonObject args) { // リクエストを受け取る String name = args.getAsJsonPrimitive("name").getAsString(); // レスポンスを返す JsonObject response = new JsonObject(); response.addProperty("greeting", "Hello " + name + "!"); return response; } }
JARに固めてデプロイする
gsonはIBM Cloud側で提供されているので依存関係にJARとして含めなくても良いのですが、これ以外にもライブラリを含めたいとかありますよね?DIするならgoogle-guiceとか、HTTPクライアントにOkHttp使いたいとか諸々。そんな時は公式のチュートリアルのようにいちいちクラスパスを通すのは時間の無駄なのでGradleで一気にまとめます。(Jarに固めればいいので、Mavenでもできるはず)
上のHello worldの例だと最小限の記述で以下のように書けます。単純に依存関係のJARの取得先と何を入れるのかを書くだけ。
apply plugin: 'java' version = '1.0' repositories { mavenCentral() } dependencies { compile 'com.google.code.gson:gson:2.8.5' }
あとは ibmcloud fn action create hello-wsk-java build/libs/hello-wsk-java-1.0.jar --main com.example.Hello
でデプロイするだけ!
ちょっとした関数を書くにはJavaを使うのは億劫ですが、かなり簡単にできるんですね。
2018年振り返りと2019年の抱負
今年の年末エントリーです。
2018年は部署異動があったおかげで、仕事環境も私生活もガラッと変わった1年でした。 心のゆとりができたこともあってジムに通い始めたり技術書読んだりと社会人1〜2年目の時にはできなかったことにも手を出せて初めて社会人楽しいと思えた。ただ、色んなことに手を出し始めて手をつけられないことも多かったので、供養も兼ねて振り返りと来年の抱負を書き留めておきます。
2018年
[業務内]
上半期はテストフェイズ最後の期間なのにトラブルシューティングの嵐、嵐、嵐。 LinuxとETLツールとDBのログをひたすら読み解いてました。ログを読む力と問題の切り分け方法 + PMとのやり取りにある程度自信が結果的にはつきました。Jenkinsを使ったCICDも含めてインフラとミドルウェア周りに触れたいい経験だったと思いますよ。3月に一度倒れたので二度とやりたくない。
下半期は今どきのMicro ServicesでAPI開発、しかもAWSに取り組めたのは部門異動して本当に良かったと思える仕事でした。この期間内にAWSデベロッパーアソシエイトの認定が取れたことも大きな自信になったし、Javaの開発は1年半ぶりだったけど1年目に負けないように食らいついた。やっぱりロジックをひたすら考えてコーディングするのはめちゃくちゃ楽しい。 恥ずかしながらJUnitの使い方も1年目の新人よりも分かってなくてすごい焦ったのは今でも覚えていて、Udemyのコース1本をお盆休みかけてがっつりやったのは後ですごく役立った。 APIだけじゃなくて初物としてやらせてもらえたAWS Batchが一番楽しかった気がする。 それにとあるアプリ用にPythonのAPI開発, 入力チェックツールとしてNode.js使えて多言語だったのが飽きない理由の一つだったかもしれない。Node.jsバリバリのツール開発をやらせてもらえたのは感謝しかない。
[業務外 (社内)]
- 全社イベント向けアプリ(認証基盤連携 + Kubernetes + ログ基盤連携) 3〜4月
- Kubernetes @AWS 勉強会 (k8s + Codeシリーズ + Lambda) 6〜9月
- マネージャー向け案件管理アプリ (React/Redux ガッツリ + Carbon Design System) 10〜12月
業務外の社内開発も結構やってました。期間を見ると途切れることなくやってましたね、、、 上2つはk8s触れたし、最後のはReactガッツリで満足。インフラからフロントエンドまでまさにフルスタックで開発してました。
[個人開発]
- Spring Boot
- JAX-RS(Jersey)
- GraphQL
- 勉強会に参加したりしたけど, 案件の開発フェイズに入って時間が合わず途中で断念。来年こそは本格的に取り組む!
- Serverless(Lambda/OpenWhisk)
[気になったけど本格的に取り組めなかった技術たち]
来年はここからピックアップして取り組みたい。毎月テーマを決めていくつか取り組めば良かったなと反省です。
- GraphQL
- gRPC
- Go
- Scala
- クリーンアーキテクチャ
- 関数型プログラミング
- Serverless全般(OpenWhisk / Lambda / Spring Function)
- BlockChain (Ethereum / HyperLedger Fabric)
- Pupetter
- X Platform (Electoron / Flutter)
- MicroProfile
- Knative
- Istio
[書籍]
心に余裕ができてから少しずつ書籍を読めるようになった1年でもありました。読んでてよかった本をピックアップします。
- コンテナベース・オーケストレーション
- 入門Kubernetes
- DeploymentやService, Ingressだけじゃなく、Holizontal Pod Autoscalerを知れたのはサービスのスケーリングを考える上で助かった。
- リーダブルコード
- 話すように書き、無関係な細かい処理は外に出し、数週間後の自分がみて分かるコードを書くことは設計開発時の心構えになった。
- 2週間後の自分が見てわかるコードを書くが自分の戒めになった。
- React開発 現場の教科書
- 社内ワークの時に, アプリの構成の参考になった。DesignSystemやAtomicデザインは必見。
- JavaScript本格入門
- String/Number/Arrayといった基本型やPromiseのリファレンスとして最適。
- 漫画
- 100冊以上買って読んでましたw
- これはこれでまとめておけばよかったな。
2019年の抱負
技術面
1〜2ヶ月に1テーマ決めて取り組んだことをプログにまとめます。今のところ考えているテーマは、サーバレス(Knative/Istio/FaaS)、マイクロサービス(Micro Profile)、関数型プログラミング(JS/Scala)/クリーンアーキテクチャ、ブロックチェーン(Hyper Ledger)、自動化(Puppeteer/CICDプロダクト)あたりです。
あとはアウトプットとして
- ブログ記事
- 日本語記事を年間24本
- Mediumに英語記事6本
- 書籍まとめ
- 年間5本
生活面
技術面だけじゃなくて生活面も改善させます。疲れにくい身体を作るのは継続したい。12月はジム通いは後半サボっちゃったから今年は具体的に目標決めます。
- 体重
- 6月までに58kg
- 12月末までに60kgを目指す。
来年はもっと頑張れる一年にしたいと思います。それでは良いお年を。
Trello APIとSlackで今週の頑張りをめちゃくちゃ褒めるボットを作る
モチベーション
仕事のタスク管理って基本めんどくさいですよね。僕はTrelloで仕事(+一部の業務外のToDo)のタスクを管理しているんですが、Trelloは期限やチェックリスト, コメント残しにカレンダーと機能が豊富でとても便利です。 ただ、年末の自分の評価シートを書いている時に、この案件に入ってから何かしら自分の作業効率って上がっているのかしらとふと思いました。その時は結局分からずじまいで悲しくなったので、できれば定量的に(週ごとに捌いたタスク数とか、修正にかかった時間とか)改善が把握できるBotが欲しいと思いつきました。あわよくば、モチベーションもあげたい。。。じゃあ今週頑張ったかを褒めちぎるBotを作ろう!!
実現したいこと
今回実現したいことを列挙するとこんな感じです。シンプルに2つだけにします。
- 今週の完了したタスク数, タスク名をTrelloの完了リストから取得する
- 完了済みタスク数と褒め言葉をSlackで自分宛に投げる
自分のスケジュール(毎週の進捗ミーティングやら検証環境へのリリースタイミング)や面倒くさがりの性格を考えて、自分でSlackコマンドを打つんじゃなくてスケジュール起動でSlack Botから通知を飛ばすようにしました。
Botの構成
構成図は割とシンプルになりました。
上の構成図の技術要素も並べるとこんな感じ。 極力労力をかけず/コードを書かずにサービスを組み合わせて実現させることを第一に実装します。
- Node.js
- スピード重視で慣れている言語で作ります。TypeScriptは使いません。
- Trello API
- 公式から出ているAPIでタスクを取得します。
- Slack
- 業務でも使ってるコミュニケーションツール。特定のタイミングで頑張った成果と褒め言葉を自分に投げる先です。
- 一番手軽なWebhook経由でメッセージを投稿させます。
- IBM Cloud Functions
- こちらもスピード重視でCloudFoundryやIBM Kubernetes Serviceよりも手軽に上記の要素を実現できる方法として採用。
- Slackにメッセージを飛ばすパッケージが事前に用意されていたので、
- Serverless Framework
- Continuous Delivery @IBM Cloud
できたもの
金曜22時になったら頑張りを通知してくれるようになりました!!
ソースコードはこちらに置いていますので、ご参考まで。 github.com
実装のポイント
実際にコードを書いたのは、Trelloから完了済みカードを取ってきて整形するところだけです。Trelloの特定のリストからカードを一式取ってくるだけなので、難しくはありません。以下実際のコードです。
("use strict"); const axios = require("axios"); const qs = require("querystring"); /** * Trelloのカードを操作する. */ class TrelloClient { constructor(params) { this.baseUrl = params.TRELLO_URL || "https://api.trello.com/1"; this.apiKey = params.TRELLO_API_KEY || "API KEY"; this.token = params.TRELLO_TOKEN || "TOKEN"; this.listId = params.TRELLO_LIST || "LIST_ID"; } /** * 完了済みタスクの一覧を取得する. * @param {String} listId 完了リストのID * @returns {Promise} 完了済みタスクのJSON配列 */ getCompletedTasks() { const reqParams = qs.stringify({ key: this.apiKey, token: this.token, fields: "id,name,labels" }); const TRELLO_URL_GET_COMPLETED_TASKS = `${this.baseUrl}/lists/${ this.listId }/cards?${reqParams}`; return axios.get(TRELLO_URL_GET_COMPLETED_TASKS).then(response => { return response.data || {}; }); } } module.exports = TrelloClient;
params
オブジェクトの各パラメータは serverless.yml に定義した値が渡ってきます。後は呼び元のメインの関数内でこのクラスをnewして使えば終わりです。全体で100行ちょっとしかないので軽いですね。
TrelloのAPIを叩く部分しか書か必要がない理由は、Slackへメッセージを投稿する部分は事前定義済みSlackパッケージを使うからです。atachmentsを使ったカラフルな凝ったメッセージ使えないですが、文字列連結すればいける範囲だったので無理せずそのまま使いました。
ハマりポイント
ContinuousDeliveryでServerless Frameworkを使ったデプロイするときは、CDのバージョンに注意
NPMの環境を指定してもNode.jsはv0.10系です。IBM SDKだとv6系しか入ってないのでなるべくv8系以上にしたいところ。v0.10系だと serverless
パッケージがpostinstall時にでこけます。以下の記事を参考にしてNVMを使えば良さそうとのことだったので、v8系をNVM経由でインストールして解決。
developer.ibm.com
Serverless Frameworkからデプロイするときに認証エラーになる
Serverless Frameworkは .wskprops
ファイルを参照してIBM Cloud Functionsにアクセスするようなのですが、どうも ibmcloud login
でログインしても上手く生成されないようです。以下のようにして .wskprops
を生成したらデプロイできるようになりました。
Deploy shellscrpit to work with IBM Cloud Function ...
これで来年の改善具合が見える化できるので楽しみです :)
AWS認定 デベロッパーアソシエイトに合格してきました
TL; DR
遅ればせながら、9月末にAWS認定 デベロッパーアソシエイト試験(2018/6リリース)に合格しました!
認定取るためにとにかくサービスを弄り舐め回しましたし、普段は触らないであろうサービスに色々触るいい機会になりました。 実案件の設計と開発どっちにも役立ちそうな内容を学べたので受ける価値ありです。
AWS認定って何?
AWSが公式にやってる認定試験。(関東近郊だと開催日平日なのが辛いです笑)
役割別認定
専門知識認定
- 高度なネットワーキング
- ビッグデータ
- セキュリティ
デベロッパーアソシエイトは認定されると, 認定試験公式ページ曰く以下の能力を持っていることを証明できるみたいですね。
AWS 認定デベロッパー – アソシエイト試験は AWS プラットフォームでのアプリケーションの開発と保守についての技術的な専門知識を認定します。 この試験では以下について理解していることが求められます。
試験ではどんなこと聞かれたの?
テーマ
AWSのサービスの知識,各サービスの使い方,ベストプラクティスについて聞かれます。 ほとんどがセキュリティと可用性を高めるには?という設問です。単純な知識問題の比率はかなり低め。
また、突然Pythonのコードが出てきたりするので、一度はチュートリアルに沿ってコードを書いておくと吉でしたね。 むしろドキュメントを見ながら実装できることを前提にしている節が見受けられます。
設問例
出題パターンはある程度決まっているので、脊髄反射で解けるやつを求めて分からんやつは飛ばして次に行く。 2時間あっという間に終わります。。。
EC2からS3にアクセスしたいけど、権限がなくて処理がアベンドした。問題の解決策のうち素早くかつ最も安全なものは?
API Gateway, Lambda, RDSからなるAPIを作ったけど、人気が出たからRDSへの負荷が高くなってしまった。ほとんどが読み取りだけど、処理がハングしないための対策は?
CodeCommitに変更がプッシュされる度にツール通知を受け取りたい。どうする? (SNS, SESと絡めて)
…
出題内容
めちゃくちゃ広いです。出題されたテーマ的にはセキュリティ,コンピューティング,データストアあたりが多め。
ただし、先にも書いたように出題範囲が上記の通りセキュリティと可用性の設問が多かったので、主要なサービス+セキュリティ/可用性向上に関係しようなサービスを押さえておけばいいなと思いました。
セキュリティ ★
- アクセス制御: IAM
- 暗号化; KMS
コンピューティング ★
- EC2
- Auto Scaling
- ECS
- Lambda
- AWS Batch
ネットワーク
- VPC
- Elastic Load Balancer ★
- CloudFront ★
- Route53
ストリーミング
データストア ★
- RDS
- DynamoDB
- S3
- Elasticache
アプリケーション統合
メッセージング ★
- SQS
- SNS
開発ツール / CDCI ★
実際どんな対策したの?
AWSのドキュメント見ながらやるのが7割。 - とにかくサービスを使う. - どうなったらサービスがエラーを返すか、使う側がコケるかというエラー系を押さえておくとなお良し。
知識系の問題はオンラインの動画見て補完するので3割。
- ホワイトペーパーを読もう! とありますが、1年以上前のものであれば日本語化されているものの最近のやつだと英語版しかありません。英語読むのが辛い人は、字面だけ追ってもすぐ夢の世界に行くことは間違いありません。できれば動画を見つつ手を動かせるコースが望ましいです。
- 費用はかかりましたが、A CLOUD GURUのコースは受けけて正解でした。
まとめ
認定試験を取るまで私の場合は3週間ほどかかりましたが、2~4weekくらいあれば十分かなと思います。 ミソはとにかくサービスを触ること。これに尽きます。