Vitess: Introduction, New Features and the Vinted User Story

www.youtube.com

関心のあるところだけ抜粋。

Introduction

  • 4 つのキーワード:
    • Keyspace は MySQL のデータベースに相当する。論理的なデータベースを表すので、実際は複数に horizontal sharding された物理データベースで構成される。
    • Shard は Keyspace の subset。Keyspace は複数の Shard で構成できる。1 Shard は 1 primary と 0 ~ n の replica で構成される。
    • VSchema は特定のテーブルをどう horizontal sharding するかを定義する。
    • VIndex は VSchema 内で利用されるもので、MySQL のインデックスに相当する。

Vinted User Story

  • Vinted は 8000 万ユーザーいる中古ファッションマーケット
  • Vitess を導入している。トラフィックは 2.2 Million QPS、38 TB data、5K VStreams
  • Why Vitess?
    • いくつかあるが、Throttling API はその一つ。バッチで db に負荷のかかる処理をするときに、負荷が上がりすぎたら処理を止めるようなコードが書ける。
    • もともとは application 側でシャーディングしていた
    • latency overhead は 2ms くらいで優秀という評価

New Features (v18, v19)

  • Query Serving
    • Basic CTE support for SELECTs
    • MySQL syntax extension: VEXPLAIN
    • DELETES with JOIN
  • Performance and Reliability
    • Connection pooling がより効率的になった。以前はまず pool が full になるまで新規接続を確立していたが、新規接続はコストがかかるのでなるべく行わない挙動に変わった。

Upcoming (v20+)

  • More CTE support

読書メモ: Prometheus実践ガイド: クラウドネイティブな監視システムの構築

どんな本?

Prometheus の概要から実践的な運用ノウハウまでまとまっていて、それを日本語で読めるのがポイント。 また、雑多になるのでここにはメモしてないが PromQL の解説も詳しいので、クエリの書き方に迷ったときの辞書としても便利そう。

Prometheus のアーキテクチャ

Prometheus のシステムを構成するコンポーネントは主に次の四つ:

  • Prometheus Server: Prometheus 本体。Exporter から受け取ったメトリクスを保存してクエリの実行を担う。
  • Exporter: メトリクスの収集。監視したい対象に合わせて必要なものを導入する。種類は非常に多く、200 以上存在する。
  • AlertManager: アラートの発報
  • Grafana: 収集したメトリクスの可視化

これらのコンポーネントは Go 言語で実装されており、シンプルなバイナリで提供されている。

Why Prometheus?

一つ目の理由は、他の監視システムに比べて構成がシンプルであること。 Web サーバーやデータベースといったミドルウェアが必要ないので、どんな環境でも導入が容易になってる。

二つ目の理由は、クラウドネイティブな監視が可能であること。 ここでいうクラウドネイティブな監視とは、監視対象となるシステムが頻繁に変化しても適切に追従でき、システム全体の正常性が常に把握できること を意味してる。 そのために Prometheus は、監視サーバーがクライアントに対してメトリクスを収集しに行く、 Pull 型のメトリクス収集の仕組みを採用している。 同時に、クラウドシステムではサーバーの IP アドレスもクライアントの数も固定されていないので、Prometheus はサービスディスカバリの仕組みも備えている。 サービスディスカバリは、クラウドサービスの API などを利用して対象の情報を能動的に取得・更新して、構成の変化に動的に追従して監視を継続できるようにする仕組みのこと。

三つ目の理由は、Kubernetes 上での監視のデファクトスタンダードになっているため、エコシステムが充実していること。 結果、エクスポーターが豊富に提供されている。サーバーのメトリクス監視には Node exporter、nginx の監視には nginx-prometheus-exporter、MySQL の監視には mysqld_exporter などが利用できる。 また、Kubernetes とともに CNCF にホスティングされている。Kubernetes 上で Prometheus を構築するための Prometheus Operator というプロジェクトもある。

データ構造とクエリ

Prometheus では、すべてのデータがメトリクスとして表現される。メトリクスは Key-Value 構造になっている。 たとえば、localhost:9090 マシンの CPU 使用率が 50% というメトリクスは、cpu_usage{instance="localhost:9090"} が Key で、50 が Value になるデータで表せる。 このとき、cpu_usage は メトリクス名、instance="localhost:9090" はラベルと呼ばれる。

メトリクスを取得するには、PromQL を使ってクエリを実行する。http_requests_total{instance="prometheus"} というクエリを実行すると、http_requests_total というメトリクスのうち、サーバー名に "prometheus" という文字列が含まれる情報を取得できる。

k8s 上のセットアップ

Prometheus の管理の効率化と自動化が可能になる、Prometheus Operator を使うのが良い。 カスタムリソースによって監視ターゲット設定を簡単に定義できる。

監視ターゲットを追加するには ServiceMonitor や PodMonitor を、ルールを追加するには PrometheusRule といった対応する CRD を定義する。

冗長構成

Prometheus の可用性を高めるためには、Prometheus 本体の冗長化と、AlertManager の冗長化をそれぞれ行う必要がある。

Prometheus 本体には冗長化の仕組みは備わっていないので、実現するには同一の設定を持つ Prometheus を二つ構築する方法が取られる。 こうすることで Prometheus はそれぞれ独立してメトリクスを収集する。

AlertManager では単純に複数起動するとそれぞれの AlertManager からアラートが発報されてしまうので、クラスタリングの仕組みが用意されている。 AlertManager を起動する際に別の AlertManager のアドレスをフラグで渡すことで、クラスターが構成される。 Prometheus からはクラスタ化された二つの AlertManager の両方にアラートを通知すればよく、あとは重複排除されて一つだけアラートが発報されるようになる。

ストレージ

Prometheus が利用するストレージには、ローカルストレージリモートストレージの二種類がある。

  • ローカルストレージは、Prometheus を起動しているサーバー上に作成されるストレージである。デフォルトでは、Prometheus のバイナリと同じディレクトリに data ディレクトリを作成し、そこに TSDB(Time Series Database)を構築する。
  • リモートストレージは、外部のストレージソフトウェアへメトリクスを転送する機能のことを指す。リモートストレージを利用するには、Prometheus のリモートストレージ機能に対応したソフトウェアを用意する必要がある。たとえば InfluxDB は標準機能として Prometheus のリモートストレージ API をサポートしており、比較的安定している OSS の時系列データベースである。

短期間かつ小規模であればローカルストレージでも十分実用に耐える。逆に、長期間データを運用する場合、PromQL のパフォーマンス低下などが問題になるのでそのような現象に陥った場合はリモートストレージの利用を検討すべき。

読書メモ: 大規模言語モデル入門

どんな本?

大規模言語モデルの理論と実践についての入門書。 英語だと、大規模言語モデルは Large Language Mode / LLM になる。

前半は大規模言語モデルの理論的な解説。 後半は Hugging Face が開発する Python ライブラリの transformers などを使って、実用的に使える日本語自然言語処理のモデルを作成する。

大規模言語モデルって何?何が新しいの?

大規模言語モデルは要するにニューラルネットワークのこと。 大規模なテキストデータを使って訓練された大規模なパラメーターで構成されたことで、飛躍的に性能が向上した点が新しい。

これを使うと自然言語処理(Natural Language Processing)のさまざまなタスクを解くことができる。 例えば、

  • 文書分類(document classification): ニュース記事などをジャンルに分類する
  • 自然言語推論(natural language inference; NLI): 二つのテキストの論理関係(矛盾してるとか)を予測する
  • 意味的類似度計算(semantic textural similarity; STS): 二つのテキストの意味が似ているかを予測する
  • 固有表現認識(named entity recognition; NER): テキストに含まれる固有表現を抽出する
  • 要約(summarization generation): 比較的長い文章から短い要約を生成する
  • 質問応答(question answering): 質問にコンピューターが回答する
  • 機械翻訳(machine translation)
  • 対話システム(dialogue system)

Transformer とは

Transformer を理解しないとこの本の前半を読むのが厳しかったので、まずはそこだけ他の資料も見つつまとめておく。

Transformer とは 2017 年に Google が提案したニューラルネットワーク。GPT などの今世の中で使われているモデルはこれをベースにしてるので大事。

GPT (Generative Pre-trained Transformer) は Transformer を採用した最初の大規模言語モデルで、2018 年に OpenAI が提案した。Transformer が設計で、GPT がその実装という関係。BERT とか T5 も実装。

ニューラルネットワークを歴史的にみると、Convolutional Neural Network (CNN) は画像などを扱う Vision タスクを解くには優秀で、2012 年以降写真の中の物を特定するとかは進展していた。一方で、言語の解析は遅れていて Recurrent Neural Network (RNN) が使われていた。RNN の問題は、長いテキストを扱えないこと、トレーニングを十分に並列して行えないので遅く結果として大量のデータを扱えないことの二つがあった。Transformer は両方の問題を解消したことで、GPU を同時に動かして、例えば GPT-3 なら45TBのトレーニングデータを使った大規模言語モデルを実現している。

www.youtube.com

どうやって大規模言語モデルを使うの?

本書の後半ではいくつかの自然言語処理を解くために、公開されているモデルとデータセットを transformers から利用する方法が紹介されている。 基本的には、大規模言語モデルがすでにあって、そこにファインチューニングを施して個別タスクに適応させて解く。

transformers を使えばかなりシンプルだが、それでも評価・エラー分析は地道に作業が必要。

本書の最後には ChatGPT API を使った検索システムの実装例も載っている。外部の情報を検索させるために、プロンプトにクエリの関連情報を載せる手法が紹介されている。

2023 年振り返り

あけましておめでとうございます。2023年もお世話になりました。

2024 年もよろしくお願いします。

私生活

  • 体調管理
    • 健康です。熱が突然出るとかはあった気がしつつ、仕事を休むほどにはならなかったです
    • 家族の体調に左右されてリモートワークを選択することが何度かありました。アフターコロナ時代で気を遣った面もあります
  • 旅行
    • 5月に軽井沢に二泊三日しました。東京からのアクセスは抜群で、北陸新幹線なら70分です。ゴールデンウィークの翌週に行ったので混雑なく過ごせました。山を散策したり、いちご狩りしたり、自然を満喫できました。
    • お気に入りの白馬キャンプは、当日に子どもが高熱でキャンセルしました 😿

野鳥の森でバードウォッチング

  • 子育て
    • 四歳になって色々できるようになってきたので、サッカー教室に入れました。気に入ったようで、公園遊びのレパートリーに「サッカーボールで遊ぶ」が増えました
    • 本がそんなに好きそうでなかったので、毎日朝晩絵本の読み聞かせ + 隔週で図書館通いを一年間徹底したら、本好きになりました。表紙が飾れるラックに借りてきた絵本を並べておくと興味が向きやすくなるのでオススメです。
  • 読書
    • 英語の勉強も兼ねて、2023年は小説を多めに読みました。「Secret Garden」「Matilda」「To Kill a Mockingbird」など。
    • 中でも 「Anne of Green Gables(赤毛のアン)」は実は続編があって、シリーズ2と3まで読むほどには面白かったです。シリーズ3「Anne of the Island」はアンの大学時代(18~22)の話です。1915年出版にも関わらず、大学生活は今とそんなに変わらないのが読んでいて新鮮な驚きでした。
  • 雑感
    • 夏の間、貸菜園でトマト・きゅうり・なす・ピーマンなどの夏野菜を育てました。1~2週間ごとに通うのは大変でしたが、収穫は週末の楽しみになってました。10月になると寒いので一夏でやめましたが、また機会があればやりたいです。
    • インフレと円安もあって、2023年はお金のことを考える機会が多かったです。

炎天下の中、野菜のお世話

仕事

  • 入社から三年半経ちました
  • 開発チーム全体を振り返ると、施策の開発スピードは相変わらず早く、一方で安定性と開発生産性をバランス良く維持できたと思います 👍
  • 個人的に大きな issue として、前半に Vitess の本番導入が、後半に Flink のステージング導入ができました。Vitess は当初から期待していた MySQL 接続集約による接続上限の回避に加えて、Thread_running が下がったことで slow query を大きく減らす効果もありました。一方で、proxy の割にリソース消費量が大きいのは意外でした。Flink は今後のシャーディングを見据えて、複雑な JOIN クエリのマテリアライズテーブルをリアルタイムに作成するために使う予定です。
  • 2024 年のあけおめピークは無事障害もなく乗り越えることができて感無量です 😂
  • 2023/12/21 4:00 に障害がありました。GKE クラスタの Pod が問題なく起動するにも関わらず LB(NEG)に入るタイミングで Error syncing to GCP: error running backend syncing routine: googleapi: Error 400: Invalid value for field 'zone': ''. Must be a match of regex '(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?)', invalidという身に覚えのないエラーが発生しはじめて、リクエストが全く届かなくなりました。結局、その場で GKE クラスタをコピーして Ingress 含めた主要リソースを全部移行するという大工事で乗り切りました。GCP サポートとともに原因特定に取り組んでますが、GKE 自体に根本原因がある可能性も残ってます。

ソフトウェアエンジニアリング

  • 2023 年も Vitess に bug fix や feature request の PR を送り続けました。中でもパラレルのユースケースで特に対応が必要だった issue は次の三つです。
    • github.com
      • v15, v16 には、MySQL サーバーが高負荷で一時的に応答不能になるとその前段にある vttablet component の再起動が走るバグがありました。v17 では修正されてます。
    • github.com
      • v15, v16 には、vttablet の rolling update 時に最大1分間リクエストが MySQL まで届かなくなるバグがありました。こちらも v17 で修正されてます。
    • github.com
      • vitess-operator で unmanaged MySQL 構成を実現する場合、MySQL Replica ごとに vtgate/vttablet のセットが必要です。次にリリース予定の v2.12 ではこの冗長な設定は不要になります。
  • 自分のプロジェクトだと protolint 関連のメンテナンスが中心でした。
    • protolint のインストール方法がさらに増えました。Maven Central 経由npm パッケージ経由 でも利用できるようになりました。
    • もともと実装されていた、キャメルケースをスネークケースに変換する処理(例: accountStatus -> account_Status)でエッジケースの要望対応に少し苦労しました。小文字と大文字の間に _ を入れるというシンプルな実装になっていたので、ITDepartmentRegion は ITD_epartment_Region に変換されてました。それを IT_Department_Region に変換したいという話です。最初、大文字二つと小文字があったら大文字間に _ を入れるルールを追加したら、この例は解決しました。一方、今度は ListAccountOAuthsEnabledFilter が List_Account_O_Auths_Enabled_Filter になるデグレが報告されました。結局、大文字が三つ以上並んで最後が小文字のときに限定して、末尾の大文字二つの間に _ を入れるとちょうど良くまとまりました。こういう地味な作業は ChatGPT ならすぐに書いてくれると思ってましたが、全く要件を満たしていないコードを生成し続けるので、愚直にやりました。

2022 年振り返り

あけましておめでとうございます。

2022 年は、生活全般と専門的なソフトウェアエンジニアリングを分けて、振り返りをまとめました。 この記事は生活全般についてです。

私生活

  • 体調管理
    • 風邪症状は何度かありましたが、仕事に支障が出るほどではありませんでした。
    • どちらかというと、予防接種や自宅待機、保育園休園によってリズムが崩れることが多い一年でしたね。
    • 子どもの予防接種全三回が 2023 年の 4 月までに終わるので、それで多少は落ち着いてくれるのを祈る日々です。
  • コミケ
    • 2022 年は夏コミ(C100, 8/14)と冬コミ(C101, 12/31)の二回手伝いに行きました。人も徐々に戻ってきているような気がして、C99 ほど寂しい感じではなかったです。
    • 冬コミは二スペだったのでスペースが広く使えて、いつもより余裕を持って望めました。毎回こうだと長時間緊張を強いられなくて済みます。
      設営をはじめて少し経った 7:27 AM の東ホール
  • キャンプ
    • 白馬駅から歩いて 10 分ほどのところにある、「スノーピークランドステーション白馬」でキャンプしました。最高だったので、2023 年も行きます。緑溢れる自然の中にスタバがあって便利なので、小さいお子さんがいる家族でのキャンプデビューにぴったりですよ。
    • 行きは新宿駅から白馬駅まで「特急あずさ」、帰りは長野駅から東京駅まで「新幹線かがやき」に乗ったので、鉄ちゃんの息子も 😁
      キャンプ場の区画。真ん中のテントを建ててから BBQ して寝袋に入ります
      その裏側にある、スタバやトイレを含む現代的な建物
  • 子育て
    • 三歳になると、言語能力がメキメキ上がってきます。一年前は聞いて理解はできていそうだけど〜、というくらいでしたが、今やしっかり自分の考えを伝えられるし、理解できなければ質問してきます。それに伴いある程度落ち着けるようにもなったので、一安心です。
    • ピアノ教室はリトミッククラスから鍵盤クラスに上がりました。毎日、練習してます。白馬に行ったときもおもちゃのピアノを持っていき、保育園を休んだ日もピアノだけは休まずやっている気がします。自分も一緒にピアノを最初から練習できて楽しいですね。小学生に上がるくらいまでは自分の方が常に先を行けるだろうと思ってましたが、暗譜能力・絶対音感・小指と薬指の稼働は早々に抜かされてしまいました。特にピアノの音当てゲームを一緒にやったときは全くわからないので、ふざけてると思われてしまい、終始怒られました。
    • 足が早いです。外に行ったときの彼の第一声はいつも「よし、走ろう!」です。長所を伸ばしてあげるために、最近は週末遠くの公園や庭まで足を運んで、自然の荒れた地面で走り込みさせてます。「教育の森公園」にある「占春園」という庭園がお気に入りです。
      これは文京区にある「関口台公園」です。死んで浮き上がった魚を枝でつついているところ

ベスト of 2022

  • 買ったもの:
    ホワイトボード
    • ピアノ練習の、「長期課題」とそこからブレークダウンした「やること」が多すぎる上に更新も早くて漏れやすいので、導入しました。
    • 両面があって大きいホワイトボードを選びました。一面は親が使う用で、もう一面は子どもが自由に書く用です。両面必要かとか大きい必要があるか迷いましたが、子どもからひらがなや数字を聞かれたときにすぐに書いてみせることができるし、大きい字で書いてあげられるのでこれで正解でした。
  • ブック(フィクション)
    • www.goodreads.com
    • 19 世紀のイギリス(主にロンドン・オックスフォード)と中国(広東)が舞台。アヘン戦争がはじまるまでの歴史を忠実になぞるように物語が進むので一見フィクションではないようですが、描かれる世界には一つだけフィクションが入ってます。この一つのフィクションを背景に、幼少期を中国で過ごした少年がイギリスに単身連れて行かれて青春期を過ごすという話です。アヘン戦争を前にして、どちらの母国のために戦うのかというのが一つの盛り上がりです。「現実に一つだけ映像映えするフィクションがある」、「アウトサイダーがインサイダーになろうと葛藤する」、「主人公が特別なスキルを持っている」、という特長に興味がそそられるようであればオススメの一冊です。
  • ブック(専門書)
    • www.goodreads.com
    • books.rakuten.co.jp
    • パラレルで業務委託を長く続けてくださってる、インフラスペシャリストの大山さんに二〜三年前におすすめされた本。こちらは和訳もありますが、原著でも平易な英語で書かれていて読みやすかったです。
    • 3 年前からベビーシッターさんをお願いするときには、英語の勉強も兼ねて日本人ではない人にもお願いしてます。2022 年だけでも、フィリピン人・インド人・中国人・アメリカ人・イギリス人・カナダ人・フランス人・ロシア人・スペイン人が家に来ました。Agency を挟まないで直接やり取りすることが多いので、しっかりコミュニケーションを取らないといけません。ただ、中々思い通りに動いてもらえないことも多く、何かしらコミュニケーションのヒントを得るために手に取りました。
    • 本を読むだけで解決とはいかないですが、「国(異文化)による常識には大きく違いがあること」、「そういう常識が大きく違う人同士でのコミュニケーションに最適化されたプロトコルがあること」、を豊富な具体例で説得力を持って知れたのは良かったです。異文化チームを率いるときのプロトコルは例えば、リーダーが自分の常識を事前にチームに伝えておくことでチーム共通のルール・基準を最初に設けること、などがありました。常識の違いについては、日本人が相当変わっているというのに気付かざるを得ないので読んでいてクスっとできます。下のマッピングは同じ著者の別記事からの引用ですが、リーダーシップと意思決定に関わる日本人の特徴が際立ってます。日本は Hierarchical x Consensual の象限の端で孤立してますよね。ボスの指示を待って部下は意見は控えることが社会的に期待される一方、いざやるとなったら部下全員の合意を得ないと反発が起きるという意味です。その象徴が稟議(Ringi)です。真反対に位置する Egalitarian x Top-down のアメリカは、コミュニケーションは対等にやるのが好まれる一方で、決めるのはボス一人に一任されているので部下の合意は必要ないという常識です。そういう視点でアメリカのドラマを見ると確かにと思うことがありますよ。また、日本のような文化でアイデアを出してもらうなら、あえて上司抜きでミーティングする機会を作るのが有効と書いてありました。

リーダーシップ文化のマッピング

仕事

  • 会社
    • 入社から二年半経ちました。
    • Tech Group のオーナーを担当してました。この Group の中に、Native Unit / Unity Unit / Infra Unit があって、全エンジニア職がどこかに所属してます。Infra Unit のオーナーも兼務していて、この中にさらに Database Team / Android Infra Team / Voice Chat Team がありました。
    • Database Team は MySQL の負荷対策を中心に行っていて、スケールアウトを見据えた Vitess 導入はここで行ってます。Vitess はシャーディング用途だけが注目されますが、シャーディング前であっても ProxySQL のように間に挟めばコネクション集約によるパフォーマンスメリットが期待できます。これを Unsharded Vitess と呼びます。MySQL スケールアウトのソリューションは色々ありますが、アプリケーションコードの変更が最小限であること、段階的に導入できること、費用が抑えられること、InnoDB の過去の知見が活かせること、類似ユースケースにおける導入実績、を踏まえて Vitess 採用を決めました。
    • Android Infra Team は Android アプリのパフォーマンスと開発生産性を改善するために立ち上げました。前者は例えば起動時間(Cold Start)が 50 % 削減できましたね 🎉 後者は開発生産性がどういう開発のときに落ちるのかを整理した上で人のアサインや体制変更で手当したのが即効性があって有効だったと思います。道半ばではありますが、たくさんのアイデア出しと分析・検証作業を通して、どれくらい理想と今が乖離していて、そのために何をどれくらいのリソースでやる必要があるのか、即答できるくらいに自分たちのシステム理解が深まったのも一つの成果だと思います。
    • Voice Chat Team は通話品質を安定的に提供するための改善や不具合修正を行いました。このチームが立ち上がった一年半前は社内外から通話トラブルの報告を常に受けていた状況でしたが、2022 年後半にもなるとすっかり落ち着きました。通話はネットワーク回線、デバイス、周辺機器(イヤホン・マイク)からも影響を受けるので完全に問い合わせがなくなるということはないですが、そこから共通する要素をまとめて issue 化する段階になると数はぐっと少なくなりました。また、ノイズキャンセル機能の導入(Android)・安定化(iOS)もできましたね。
  • 実装
    • Amazon Prime 同時視聴機能の POC を iOSAndroid で作って、これは最終的にパラレルに組み込まれました。デジタルコンテンツの著作権を管理するための技術である DRM(FairPlay/Widevine)にはだいぶ詳しくなりましたよ 😎
    • MySQL のシャーディングソリューションとして Vitess の導入作業中です。まだシャーディングはしないので、Proxy として間に挟むだけです。
  • 障害
    • 2022 年は障害の規模も小さく、頻度も少なかったです。障害の都度、再発防止策を入れてもらったおかげでどんどん安定化してきたと思います。
    • と思っていたら年明け早々の 2023/1/1 0 時にサービスダウンしました。その間終始パソコンの前に張り付いて対応しました。あけおめ時のスパイクがすごかったですが、もっと精進が必要だと思った 2023 年幕開けでした。Twitter のトレンドに「パラレル」が載っていたようで規模感を知る貴重な機会にはなりましたね。
  • 第一回 Tech Group 忘年ホームパーティ
    • 同僚のお家でお昼から忘年会させてもらいました 🏠 素敵な新居でクリスマスツリーをバックに記念写真まで撮ってしまいました。幹事をしてくれた別の同僚含め、ありがとうございました。
    • 東京近辺も広いので、こういう機会を利用して馴染みのない土地に足を運べるのはそれだけで学びがあります。
    • 生産性の秘訣を探るためにリモートワーク環境を拝見させてもらうという裏目的をすっかり忘れていたので、次回はそこの見学ツアーも打診しなくては(その前にまずは自分が招待する側にいってからだろうというツッコミも)。

2022 年振り返り(ソフトウェアエンジニアリング)

あけましておめでとうございます。

オープンソース活動は変わらず元気に続けてます。 子育ての中で時間を見つけるのは大変なので、子どもが生まれた直後からベビーシッターを積極的に利用してます。

2022 年は、専門的なソフトウェアエンジニアリングと生活全般を分けて、振り返りをまとめました。 この記事はソフトウェアエンジニアリングについてです。

登壇

4/23(土)に開催された Go Conference 2022 Spring に登壇しました。

Protocol Buffer 言語をパースする go-protoparser と Protocol Buffer スキーマファイルをチェックする protolint を自作したときの経験はどこかで整理しておきたいと思っていたので良い機会になりました。

Go Conference 2022 Spring で採択されたプロポーザル(CfP) - yoheimuta’s blog にそのときの Cfp(プロポーザル)を公開しているので、発表背景など詳しくはそちらを参照してください。

発表後、下のように書いてくれた方(自分のすぐ後のセッションで GC について発表されていた)がいました。 レキサーとパーサーを分けて実装するという一番伝えたかったポイントに言及されていて良かったです。 自分にとっては当たり前になってましたが、実際に書くときにはその発想にならなかったり、どう書くかわからないものだと思います。

他人がオーナーのプロジェクトへの参加

所属企業で MySQL のシャーディングソリューションに Vitess を採用することにしたので、今年の後半は合間の業務時間も使って Vitess にいくつかコミットしました。 Vitess の CDC キャプチャには Debezium を使うのが一般的なので、そちらにもコミットしました。

debezium/debezium-connector-vitess

Debezium は変更データキャプチャ (Change Data Capture; CDC) のための低遅延データストリーミングプラットフォームを提供するオープンソースプロジェクトです。Debezium は MySQL のシャーディング用プロキシである Vitess にも対応していて、debezium-connector-vitess がその実装にあたります。

github.com

  • debezium-connector-vitess には他のすべての connector(例えば debezium-connector-mysql)でサポートされている、起動時のスナップショット取得機能がサポートされていませんでした。Vitess のバックエンドである MySQL のバイナリログは通常一定期間で削除されるので、バイナリログのストリーミングだけだと完全なデータを再現することができません。例えば、シャーディング後の MySQL クラスタで以前のような複雑な JOIN クエリをサポートするために、Flink などでリアルタイムに Materialized テーブルを作ろうと思ったとき、これだと困ります。
  • この PR がマージされた v2.1 からは、Debezium を起動するとまず MySQL クラスタ(Vitess)の現在のスナップショットを一貫性を保ったまま取得して、それが完了してから更新履歴のストリーミングがはじまるようになります。
  • Slack/Salesforce 社もこの機能を要望していて、設計やレビューを手伝ってくれました。最初に大規模に使いそうなので、問題なく動いて欲しいです 🙏
  • v2.1 は 2022.12.22 にリリースされてました

vitessio/vitess

Vitess は MySQL の水平スケーリングを行うためのデータベースクラスタリングを提供するミドルウェアです。 YouTube が開発して後に OSS 化されました。データベース分野で初の CNCF の Graduated project です。

Slack, Square, GitHub, Shopify, HEY (DHH の) など導入事例は豊富で、これらの会社が積極的にコントリビュートしてます。

github.com

  • Vitess は多くのコンポーネントが協調動作して動くので、開発環境を作ったりそれぞれの挙動を理解するのが最初の関門です。幸い examples ディレクトリがあるので、そこで docker-compose up -d したらまさかの失敗、というわけで修正しました。レビューの過程で開発者から細かいところを指摘してもらえたので、システム理解を深める良い機会になりました。Vitess v15 リリースのコントリビューターにちゃっかり入ってたのは 🉐。

github.com github.com

  • 前述の debezium-connector-vitess にスナップショット機能を追加する上で見つけた課題に対応した PR たちです。ほかにも一つがレビュー中、もう一つが実装待ち(自分の)です。Vitess v15 以下には connector のスナップショット機能を fault tolerant するために必要な機能が欠けてます。fault tolerant とは例えば、失敗したらリトライするなどです。Vitess 公式 slack のスレッドで相談しつつ、issue 化しては PR を投げてます。これ専用のスレッドは、2022.08.22 からはじまり、こんなに長いスレッドはお目にかかれないというほど長大になってます。振り返ると、メンテナーに意図が伝わらなかったり、レスポンスが長い時間返ってこなかったときには焦った記憶があります。
  • Vitess v16 に入る予定なので、そちらがリリースされたら debezium-connector-vitess にリトライ機能を入れたいです。

自分がオーナーのプロジェクトへの参加

細かいのを挙げるときりがないので、印象深いリリースや出来事を五つ挙げます。

yoheimuta/protolint

protolint は Protocol Buffer のスキーマファイルが、Google公式スタイルガイドに関するルールや独自ルールに違反していないかチェックする linter/fixer です。 plugin も書けるので社内独自ルールの違反も見つけることができます。

今年は、長年の悲願だった「必須ルールすべてに fixer を実装する」がようやくできました。 fixer が揃ってからスター数の増加傾向も少し高くなっている気がします。353 ⭐

github.com

  • linter で見つかったルール違反を自動で直してくれます。自分の中では fixer と formatter は別物という認識で、formatter はあるスタイルに沿って全部書き換える一方、fixer は一つ一つの細かいルールごとに修正してくれるものです。その意味で、protobuf の fixer でこれくらい細かいルールに対応しているソフトウェアは OSS にはないと思います。
  • 需要があるのはわかりつつ、linter の中で lexer を再度動かすという設計(これが一番書く量が少なく実装できる)に二の足を踏んでいたのですが、下のようなツイートを燃料に書き上げました。おっしゃるとおり。今はそんなことないので、使ってみてください 🤝

github.com

  • fixer の拡充が終わって数カ月後に受け取った機能リクエスト。fixer で直されると後方互換性が壊れるから、違反した行だけ 「rule 適用を無効にするコメントを挿入する」オプションを用意して欲しいという内容。
  • これは感銘を受けたリクエストで、API 用途で利用されるスキーマファイルの linter/fixer ならではの要望です。ソースコードの変数名のケースを直して互換性が壊れることはあまりないので、普通の fixer はそこまで考慮を求められないと思います。この人たち(当時は Secureworks という会社のアカウントっぽかった)のように多くのリポジトリを抱えているところには大事そうなので対応しました。
  • この対応で、protolint -fix -auto_disable=next . で互換性を保ったままスタイル違反を修正できるようになりました。すでにコメントを使って rule 適用を無効にする機能はあったので、この PR ではそのコメントをルール違反があった行の直前に挿入するだけで楽勝だと思っていたら全然そんなことはなくて、だいぶうんうん唸りながら書くことになりました。まず、すべてのルール修正が後方互換性を壊すわけではなくて、例えば import 文のソートは API 処理時には関係ないのでこれをどう扱うかを決める必要があります。後方互換性を壊すルールだけコメントを挿入してそれ以外は今まで通り直してしまうことにしました。次に、コメントをどこに挿入するかで、シンプルなのは違反がある行の上になるのですが、リクエストをくれた人は inline コメントがお望みでした。inline だとすでにコメントが書いてあったときどうするのかとか、1 行 80 文字を超えたときに別の lint エラーが発生するのをどうするか、複数の rule 違反が同じ行で重なったときにはどうするかなど色々なケースを考える必要が出てきます。あまりオプションを増やしたくもないので、良いバランスを探るのとそれを実装するのが大変でした。

blog.cybozu.io

  • cybozu.com のバックエンドでスキーマファーストな開発手法を導入するにあたって、社内独自ルールを強制するために protolint の plugin 機構を活用している、というアドベントカレンダー記事です。
  • 記事中、感謝されたり褒められていて読んでて気持ちよかったです(耳が痛いところは素通りしているとも言えるかも)。plugin の作り方がすごい丁寧に書いてあるしサンプルリポジトリもあるので、README からリンクを貼らせてもらったほうがいいかも。
  • plugin 機構はどれくらい使われているかわからなかったので、あまり目が行き届いていないかもしれません。ビルトインのルールと同じことができないとかあったら feature request か PR ください 😸

github.com

  • 自分が知らないところで、Homebrew 本体に protolint が年末組み込まれていました。5,463 個(現時点での homebrew-core にある Formula 数)あるソフトウェアの仲間入りです。これで brew install protolint だけでダウンロードできるようになりました 🎉

yoheimuta/action-protolint

reviewdog で protolint を動かす GitHub Action を公開しました。

github.com

reviewdog を使ってみたい、GitHub Action を公開してみたい、というだけをモチベーションに着手しました。 reviewdog は PR review 時に見やすくて便利、GitHub Action は Dockerfile と action.yaml の二つを用意すれば簡単に作れる、という触れ込み通りだけど、その実感を得られたのが学びですね。

投稿した記事

itnext.io

  • MySQL をシャーディングしたときに isolation level がどう変わるのか整理した記事を書きました。MySQL のデフォルトの isolation level は REPETABLE READ ですが、一般にシャーディングするとより isolation が緩い READ COMMITTED になると言われてます。そのような挙動の変化がどうして発生するのか、また具体的なアプリケーションでそれぞれがどう動くのか、自分が Vitess 導入過程で気になった疑問に答えました。整理しながら、分散 MySQL で REPETABLE READ を実現している TiDB はすごいと思う一方、それがすべてのユースケースで求められる要件でもないと再確認できました。

読んで良かった本

www.oreilly.com

  • Apache Flink 入門書です。少し情報は古いですが、公式ドキュメントよりも体系的にまとまっているので、まず全体像を掴むのにとても役に立ちました。子どもとコロナ隔離中に読んでいたので、子どもには「今からリスの図鑑読むね」と言い聞かせてました。
  • この本を読みながら、Flink SQLflink-cdc-connectors を使った MySQL の Materialized Table リアルタイム作成の POC を作りました。Flink SQL は強力で、いつも通りの JOIN 文を書くだけで、裏側で負荷を考慮したストリーミング JOIN を行う JobGraph (Dataflow) が出来てしまいます。flink-cdc-connectors は Debezium を内包しているので、インフラ構築は Flink 導入だけで済みます。処理を再開するときには、MySQL のバイナリログを途中から読むか、スナップショットを取って最初から読み込めばいいので、わざわざ Kafka を立てる必要もありません。

Go Conference 2022 Spring で採択されたプロポーザル(CfP)

4/23(土)の Go Conference 2022 Spring に登壇しました。
アーカイブTrack B sponsored by 株式会社ビットキー - Go Conference 2022 Spring Online - YouTube から視聴できます。

プロポーザル(CfP)を PaperCall に提出するのは今回がはじめての経験でした。
採択された CfP [1] や書き方のガイドライン [2] を公開している方がいて、それらは CfP のドラフトを書く際に参考にさせてもらいました。

そういうわけで、自分の CfP も将来の応募者の参考のため、公開します。

CfP の中でも、セッション詳細(Description)はまとめるのに時間と考慮が必要ですね。
今回は、背景(Background)、動機(Motivation)、課題(Problem)、提案(Proposal)の順に整理しました。
それらの内容が自分の経験に裏打ちされているものであると伝えるのが大事です。

プロポーザル(CfP)

Title

ゼロから作る Protocol Buffer のパーサーとレキサー

Elevator Pitch

インタプリタコンパイラの基礎になる字句解析器(lexer)と構文解析器(parser)の実装はgoyaccなどのジェネレーターを使うか手書きするかの基本二択になります。goyaccに関する実践的な情報は多いですが学習カーブが伴います。私はProtocolBufferスキーマ定義ファイルのパーサーとレキサーをGoの標準パッケージだけで実装しました。ASTを標準出力するだけでなくVisitorパターンを導入すると使い勝手が増します。これらの知識は普段使う静的解析ツールのカスタマイズにも役立ちます。本セッションでは実際に動く最小構成の実装からはじめてGoでのプログラミング手法をご紹介します。

Talk Format

Long session (40 mins)

Description

Go 言語がチームでの開発に採用される理由の一つに型があることによる可読性や開発効率の向上があります。 一方で、型の表現力を補うために、Go 言語のエコシステムには、バイナリディストリビューションに含まれる vet コマンドや静的解析の自作を容易にする準標準ライブラリ golang.org/x/tools/go/analysis [1] があります。 また、大規模システムのトレンドに目を移すと、安定化や生産性を向上させるために、静的解析ツールを駆使した事例はあとを絶ちません。 GitHub では MySQLパーティション対応をするために SQL linter を開発して Rails システムの安全な移行を実現しました[2]。国内では GREE が既存システムの AST からコードを自動生成する Rector というツールを使うことで、機能開発を止めない PHP フレームワーク移行を実現しました[3]。また DeNA の SWET チームはアーキテクチャを段階的に変更する場合に自作した Android Lint を使って、新規に追加されるコードに修正対象となる実装が含まれない仕組みを自動化しているという知見を共有しました[4]。

このように静的解析は一部のエンジニアだけが使うものではなくなっています。 しかしながら、静的解析を自分のプロダクト固有の問題に役立てるためには、その土台となる AST(抽象構文木)を理解して活用するスキルが求められます。 私は Go 言語を使って Protocol Buffer の文法規則[5]から AST を構築する作業を通じて、scanner/lexer/parser の役割分担とインタラクション、lexeme/token/ast などの関係性、print/walk/visitor というインターフェースで期待される動作を体系的・実践的に整理できました。この中には静的解析一般に通じる汎用的な知見が含まれるため、パーサー・レキサーを自作したい人だけでなく、静的解析の可能性に関心がある人や既存ツールを有効に活用したい人にも役立つはずです。goyacc などのパーサージェネレーターを使っていないので理解に必要な学習カーブは Go 言語が大半を負うため、この発表だけで収まります。

パーサーとレキサーの実装は goyacc などのジェネレーターを使う[6]か手書き[7]するかの基本二択になります。 手書きでの実装を決めたとしても正規表現を中心に据える[6][8]のか、Go 標準パッケージの text/scanner を lexer として使う[7]のか、lexer も自作する[8]のか選択肢は多く、私自身書いてある最中に何度も方針を転換して試行錯誤しました。結果的に、これに関しては正規表現、text/scanner の順に途中まで試行して最終的に自作しました。 また、これら Go言語で実装する既存の例はいずれもシンプルなものが多く、trial-and-error を伴うパースに不可欠なバックトラッキング処理[9]が欠けていたり、AST 利用者側の視点に立った Visitor パターン[10]の考察や参考実装がありません。

そこで本セッションでは、Protocol Buffer の Extended Backus-Naur Form (EBNF) で記述された文法規則のパーサーとレキサーの Go 言語実装を解説します。 その過程で、言語処理系のバックグラウンドがない私自身整理が必要だった用語や概念を合わせて紹介します。 コードを書いていてとても楽しかったのでそういう気持ちも伝えたいと思います。

Notes

対象ユーザーは Go を読めるソフトウェアエンジニアです。理解するために、それ以外の技術要件は要求しません。 発表内容に関しては go-protoparser という OSS の形で公開しています。この OSShttps://github.com/apigee/registry など一定の利用があって継続的に運用してます。また、自分でもこれを利用した protolint という Protocol Buffer の linter を実装しているので実用に耐えるクオリティだと考えてます。

Bio

Goは protolint などの自作ツールを通じて日常的に書いてます。Contribution 待ってます 😁 業務では Go 製の OSS に PR を送ったり、以前は自社製プロダクトにも使ってました。主にバックエンドを中心に、システムを自分たちの制御下に置くために必要な仕事を担当してます。

当日の発表について

登壇後、Twitter でいくつかフィードバックを見つけました。
CfP で提案した内容がいくらか伝えられましたかね 😺
このトークのゴールは、文法規則を持ったテキストを処理する際のエンジニアの道具箱に正規表現とともに、自作レキサー・パーサーも入れてもらうことでした。

皆さん、聴いてもらってありがとうございました。

最後に、カンファレンスの準備や登壇者のサポートをしてくださっていたスタッフとスポンサー様、ありがとうございました。