銀行員からのRailsエンジニア

銀行員からのRailsエンジニア

銀行員から転身したサービス作りが大好きなRailsエンジニアのブログです。個人で開発したサービスをいくつか運営しており、今も新しいサービスを開発しています。転職して日々感じていること、個人開発サービス運営のことなどを等身大で書いていきます。

当ブログではアフィリエイト広告を利用しています

【読書まとめ29】学びを結果に変えるアウトプット大全

久々に技術書以外も読みたいな、と思い「学びを結果に変えるアウトプット大全」を読みました。
 
この本に書かれていた次のことがいいなと思いました。
勉強/読書前に何を学びたいかを自分に問いかけると良い。その内容に注意が向くようになり、関連する情報が出た時に集中力が高まる。

(読む前)この本で学びたいこと

① アウトプットの効果
② 今日から始められるアウトプットの仕方
 
アウトプットするのが良いというのはなんとなく体感としてありましたが、具体的にどんな良いことがあるのか知りたかったです。
また、息をするようにアウトプットができるタイプではないので、気軽に今日から始めれられるアウトプットの方法が知りたいと思いました。 
 

(読んだ後)この本で学べたこと

① アウトプットの効果

学んだことが定着しやすくなる
脳はインプットしてもその情報を何度も使わないとすぐ忘れてしまう。脳に入力された情報は海馬に2~4週間の間、仮保存される。海馬への仮保存中にその情報が何度も使われると、脳はその情報を重要と判断して側頭葉の長期記憶に移動する。目安として2週間で3回以上アウトプットすると、長期記憶として残りやすくなる
参考書を読むだけで問題集を解かなかったり、技術書を読むだけで実際に手を動かさないとすぐ忘れてしまうという体感と一致し、納得感がありました。
ただ、1度アウトプットすれば良いものでもなく、2週間に3回以上必要というのは思っていたよりも多く、意識してアウトプットしなければ到達できなさそうだな、と思いました。
 

② 今日から始められるアウトプットの仕方

日記を書く

日記は手軽なアウトプットトレーニング。ポジティブ、楽しい出来事を中心に書くことで、日常から楽しいことを発見する能力が高まる。1日10分間日記を書くと幸せになるという研究結果もある

過去日記を書いていた時期はあるのですがやめてしまっていたので、この本を読んで再開してみました。
特に何もないような日でも日記を書くと毎日楽しいことがあると実感でき、この本の通りポジティブになれている気がします。
 
会社で続けている技術書の輪読会を継続する
人に教えることを前提に勉強するだけで記憶力がアップする。教えることで、自分の理解度や不十分な点が明確に見えてくる
 
説明することによって記憶に残りやすくなる。なぜなら、説明することによって意味記憶からエピソード記憶に変換されるため
意味記憶apple=りんごのように関連性の薄い組み合わせ。記憶に残りにくい
エピソード記憶:過去にあった出来事や体験、ストーリーとしての記憶。記憶に残りやすい
会社のメンバー数人で、技術書を1章ずつ順番に説明する輪読会を1年くらい続けています。説明したり議論したりすることで理解が深まったり、記憶に残りやすくなる実感があったので納得感がありました。
 
本を読んだらブログに残す
本を読んでも気づきを書き留めないと自己成長につながらない。現実を変えるためには行動を変える必要があるため、TODOリストを作成すると良い
これまでは特にいいなと思った本・技術書のみをブログに残していましたが、基本ブログを書く前提で読もうと思います。
この本を読んだ上での僕のTODOは、「② 今日から始められるアウトプットの仕方」の3つと「読書前に学びたいことを決めるこの読書法」の計4つを継続し、習慣にすることです。
 

その他覚えておきたいことのメモ

  • 理想的な費やす時間の比は インプット:アウトプット = 3:7
  • 文章を速く書く方法
    • 時間を決めて書く:締め切りを決めることで集中して一気に書くことができる
    • 先に構成を決める
  • ぼーっとしてる状態がひらめきやすい。ぼーっとしている時、脳内では通常の活動時の15倍ものエネルギーが消費されている。空いた時間はスマホで埋めるのではなく、意識的に何も考えない時間を取ると良い

 

おわりに

ここまでお読みいただきありがとうございます。

これまで何となく本を読んで何となくメモを残したりすることが多かったですが、この本を読んで「明確な目的を持って本を読み、行動を変えるための読書法」に切り替えるきっかけが得られたと思います。

ここに書いたこと以外にも多くの内容が詰まっており、刺さる部分は人によって違うと思うので気になった方は是非読んでみてください。

学びを結果に変えるアウトプット大全

学びを結果に変えるアウトプット大全

 

 

【読書まとめ28】アジャイルサムライ 達人開発者への道

勤務している会社のPJでアジャイル開発を取り入れはじめました。

ただ雰囲気でやっている部分も多く、より効果的にアジャイルを活用するため、まずは体系的に学びたいと思いこの本を読みました。

これから実践していく際にこの本に立ち返りやすいよう、ポイントをまとめました。

f:id:ysk_pro:20210228104556p:plain

 

プロジェクト全般

  • ソフトウェア開発の3つの真実
    • プロジェクトの開始時点に全ての要求を集めることはできない
    • 集めたところで、要求はどれも必ずといっていいほど変わる
    • やるべきことはいつでも、与えられた時間と資金よりも多い
  • ソフトウェアの64%の機能は、ほとんどあるいは全く使われていない。このことからも、本当に重要なことだけに集中すべき
  • アジャイル開発の原則として、ビジネス側の人と開発者は、プロジェクトを通じて日々一緒に働かなければいけない

プロジェクト開始時

  • チームメンバーが誰もいないところで合意したことを前提にすると、プロジェクトはダメになる。プロジェクトの開始時点で関係者の認識が揃っていないのは当然なので、プロジェクト開始前に関係者全員でプロジェクトについて話し合うべき。そんな時にインセプションデッキが有効
  • インセプションデッキについて
    • プロジェクトを開始する前に聞いておかなければならない10個の質問で構成されている
    • プロジェクトを核心まで煮詰めて抽出した共通理解を、開発チームだけでなくプロジェクト関係者全員へ手軽に伝えるためのツール
    • 仕事場の壁に貼り出しておき、何を作ろうとしているのか、そしてそれはなぜなのかを時々眺めて思い出すとよい
    • プロジェクトの真のゴールは、よくよく話し合い、議論を重ねて理解を深めることでしか浮かび上がってこない
    • プロジェクトの基本的な考え方やスコープ、存在意義が変化してしまったら、全員でもう一度インセプションデッキを見直して、チームの向いている先と、プロジェクトへの理解が揃っていることを確認すべき

プロジェクト運営

  • 良いユーザーストーリーの6つの要素(英語の頭文字をとって INVEST と呼ばれる)
    • 独立している(Independent):ストーリーが互いに独立していると、必要に応じてスコープを柔軟に調整しやすい
    • 交渉の余地がある(Negotiable):予算などに応じて実現方法を選択することができる
    • 価値がある(Valuable):顧客が対価を払っていいと思えるもの
    • 見積もれる(Estimatable)
    • テストできる(Testable):完了の基準が明確になる
  • ユーザーストーリーのテンプレート
    • 〈ユーザーの種類〉として、〈達成したいゴール〉をしたい。なぜなら〈理由〉だからだ
    • テンプレートを使うことで、誰が、何を、なぜ、という重要な疑問を明確にできる
  • ベロシティはチームとしての生産性を計測する。個人の生産性を測るのは良くない。個人の生産性を測ると、協調しようとか知見を共有しようという気持ちがなくなり、各人が何としても自分自身の生産性を確保しようと躍起になってしまう
  • プロジェクトの完了時期の見通しを立てるにはバーンダウンチャートが便利。ただ、バーンダウンチャートだと途中でのストーリー追加がわかりづらいので、途中でのストーリー追加をわかりやすくしたければバーンアップチャートが便利

開発関連

  • アジャイルプログラマが当然のように実践していること
    • テストをたくさん書く。設計のためにテストを活用することも多い
    • プロジェクトの進行中にも、アーキテクチャの設計と改善に継続的にり組む
    • コードベースをいつでもリリースできる状態にしておく
  • バグを見つけたら、修正する前にバグが原因で失敗するユニットテストを書く。こうすることで、同じバグが2度と現れないことを保証できる
  • TDD(テスト駆動開発):以下の短いサイクルを繰り返しながら少しずつソフトウェアを設計していく手法
    • レッド:必要なコードが既にあると考えて、失敗するユニットテストを書く。設計についてしっかり考え、コードの意図をテストで示す
    • グリーン:とにかくテストに成功するコードを書く

おわりに

ここまでお読みいただきありがとうございます。

本書は翻訳本に関わらず読みやすく、アジャイル開発の考え方を理解するのに最適だと思ったので、ご興味ある方は是非読んでみてください。

 本書で学んだことを、明日から1つずつ業務で実践していこうと思います。

以下の記事を合わせて読むことでアジャイル開発、アジャイル開発の1つであるスクラムについての理解がさらに深まると思うので、ご興味ある方は是非ご覧ください。

ysk-pro.hatenablog.com

ysk-pro.hatenablog.com

ysk-pro.hatenablog.com

 

エンジニア3年目の2020年振り返り

こんにちは。2020年ももう終わってしまいますね。
気づけば、エンジニアになって2年半が経っていました。

去年と一昨年も年末に振り返り記事を書いているので、今年もやってきたことをまとめておこうと思います。

去年の記事:エンジニア2年目の2019年振り返り - 銀行員からのRailsエンジニア
一昨年の記事:エンジニアになって2018年にやってきたことを一覧にしてみた + KPT振り返り - 銀行員からのRailsエンジニア

仕事のことはあまり詳細に書けないので、主に仕事以外でやってきたことを書いていきます。

f:id:ysk_pro:20201231081124p:plain

振り返り記事なので、アイキャッチは振り返ってるうちの猫です。かわいい 😻

個人開発

2つサービスをリリースできました 🎉

楽々ゴルフ

移動時間で絞り込めるゴルフ場検索サービスです。

今年のお正月(約1年前)に、妻と2人で作りました。

「既存のゴルフ場検索サービスでは、自宅からの車での移動時間で絞り込めない」という僕の思っていた課題を解決できて嬉しかったです。

フロントエンドにはReactを初めて使いましたが、妻に手取り足取り教えてもらったのでだいぶ理解できるようになりました。

バックエンドはAWS Lambda(言語はRuby)、API Gateway、DynamoDBでサーバレスの構成にしてみました。

ysk-pro.hatenablog.com

Golf Medley

ゴルフ練習場の口コミサイトです 🏌️‍♂️

「楽々ゴルフを見たのですがこんなもの一緒に作らないですか」というTwitterDMをいただいたのがきっかけで生まれたサービスです。楽々ゴルフを作って公開したことで、このサービスに繋がりました。

現在はゴルフ練習場の口コミ機能のみですが、ゆくゆくはゴルフにまつわる総合サイトにするため、様々な機能を検討・実装中です。これまで僕はサービスを作って終わりというのが多く、継続的に開発している個人開発のサービスは初めてなので、少しずつ良いものにしていきたいです。

開発は妻と僕、機能の検討やデータ入力等は声をかけていただいた方が行う、3人チームで進めています。

技術的にはフロントエンドにNext.js、Typescript、認証にAuth0(JWTトークン認証)、バックエンドにRailsという構成で、Rails以外ほとんど触ったことのない個人的に攻めた構成にしました。

元々フロントエンドはReact SPAで作り始めたのですが、SNSシェアされた時に展開される情報を動的に変えられないことが辛く、SSRができるNext.jsに切り替えました。ページごとにSSR, SSGを切り替えられたり、SSGでも(ほぼ)動的なサイトを実現できたりとNext.jsが便利すぎて驚きました。

golf-medley.com

AtCoder

2019年にハマった競技プログラミングを継続していました。
今年の前半は、ほぼ毎週オンラインでのコンテストに出場していました。

基礎的なアルゴリズムは習得できたと思います。

念願だった水色になってからレートが停滞して、徐々にやらなくなってしまいました。

ysk-pro.hatenablog.com

ysk-pro.hatenablog.com

CTF

CTFとはセキュリティ系のコンテストで、Web、暗号化、ネットワーク幅広い分野が対象です。詳細を知りたい方はこちらがとてもわかりやすいです。
CTF(Capture The Flag)とは?概要から基本ルール、メリットデメリットまで徹底解説

AtCoderと入れ替わりで始めてみました。

主にWeb分野の過去問に取り組んでおり、SQLインジェクションなどで攻撃するのが楽しいですw(そういう問題なので攻撃OKです)

デザインパターンの勉強

2019年から始めたデザインパターンの勉強を継続していました

Rubyによるデザインパターン を読んで、紹介されている16のパターンを1パターンずつオリジナルのサンプルコードと共にブログにまとめました

かなり時間がかかりましたが、デザインパターンをざっくり理解することができ、コードの良し悪しが少しずつ分かるようになった気がします。
まとめた自分のブログを見ながらコードを書くことが多くなりました。

ysk-pro.hatenablog.com

仕事

APIを一から作ってパフォーマンスの問題に苦しんだり、スクラム難しいなーてなったり、Kubernetesを触ったり、大きめの障害を起こしてヘコんだり、新しいサービスの技術選定・設計をし始めたりしてました。

現職で2年半以上経ちましたが、メンバーに恵まれているのと、次々に初めて経験する難しい課題が現れるので全く飽きずに楽しめています。

おまけ(プライベート編)

3月からフルリモートになり家の環境整えました

よくあるデスク環境紹介ブログを書いてみました。

ysk-pro.hatenablog.com

広い家に引越して、猫を飼い始めました

リモートワークになり、会社の近くに住む意味がなくなったので、広くて猫飼育可の物件に引っ越して猫を飼い始めました。
めっちゃかわいいです...!

ジムは解約し、家トレを毎日継続してます

コロナの影響でジムに行きづらくなったので、家でトレーニングできる環境を整えました。

ジムは2日に1回行く程度でしたが、家だとすぐに取りかかれるのが楽で毎日筋トレをする習慣がつきました。

マンガをたくさん読みました

リモートワークになって自由な時間が増えたのもあり、マンガをたくさん読みました。

技術書もそうですが、妻と2人で読めば実質半額でお得です。

僕が今年読み始めたマンガでの個人的なTOP3は、1位:ワールドトリガー、2位:七つの大罪、3位:呪術廻戦 です。

(今Amazonで調べてたらワールドトリガーが2021/1/11まで1巻〜9巻 Kindle版無料で読めるらしいすごい)

妻とゴルフレッスン通い始め、夫婦でゴルフ旅行に何度か行きました

夫婦でゴルフ旅行という、ゴルフを始めた頃の夢が叶いました。最高です。

おわりに

ここまで読んでいただきありがとうございます。

今年も色々取り組めていい年でした。

来年は Golf Medley を成長させつつ、興味の赴くままに新しいことに色々チャレンジする1年にしたいです。

ご興味あれば、去年・一昨年のブログも合わせてご覧ください。

ysk-pro.hatenablog.com 

ysk-pro.hatenablog.com

良いお年を!

【読書まとめ27】Atomic Design 〜 堅牢で使いやすいUIを効率よく設計する

個人で開発しているサービスに Atomic Design を採用しているのですが、雰囲気で使っており一度体系的に学びたいと思いこの本を読みました。

Atomic Design やその周辺知識をまとめて得ることができたので読んで良かったです。

後から見返せるように、ポイントを簡潔にまとめました。 

f:id:ysk_pro:20200825083937p:plain

Atomic Designとは

 

コンポーネントベースでのUI開発のメリット

  • 再利用性が高い
  • 再利用されることで、統一された使い勝手をユーザーに提供できる

 

各階層について

小さい方から1つずつ説明します。

Atoms

  • これ以上UIとしての機能性を破壊しない最小単位。使い回しやすいように、抽象的な機能だけを持つように作られる
  • (例)ボタン、テキストフォームなど
  • (関心事)デザインの統一性

Molecules

  • 機能を組み合わせてユーザーの具体的な行動に応える機能の単位
  • (例)Atomsのボタン、テキストフォームなどを組み合わせた検索フォームなど
  • (関心事)行動を阻害しない操作性

Organisms

  • 独立して成立するコンテンツを提供する
  • (関心事)ユーザーの行動を促すコンテンツ

Templates

  • 具体的なコンテンツを持たないページの雛形。OrganismsやMolecules、Atomsのコンポーネントを実際のページの通りに配置する
  • (関心事)画面全体のレイアウト

Pages

  • Template層のコンポーネントに実際のコンテンツを流し込んで、ユーザーが実際に触れるもの
  • 各ページに1つ作られ、この層は再利用されない

 

コンポーネントを階層化するメリット

  • 全体を考慮する必要がなく、1つの層の関心事に集中できる
  • 同一層に属するコンポーネントであれば差し替えやすく、A/Bテストなどもスムーズに行うことができる
  • 更新頻度の高い上位層(Pages、Template)のUIコンポーネントの変更が、下位層に影響しない

 

知っておくと役に立ちそうなこと

  • MoleculesとOrganismsの分け方
    • どちらに含めるか悩むケースは多いので、次のような判断基準で分けるのがよい
    • Organisms:独立して存在できるコンポーネント単体で見たときに意味が分かる
  • 命名について
    • 命名の基本は「コンポーネントが持っている機能に対して、過不足ない名前をつける」ことであるがとても難しい。以下の命名のサンプルが参考になる
    • Atoms:Button, Image, Text
    • Molecules:SearchForm, Pagenation
    • Organisms:Header, NotificationList
    • Templates:MyPageTemplate, ContactTemplate(Orgainsms層との名前の重複を避けるため、共通してTemplateを後ろにつける方法がよく使われる)
    • Pages:MyPagePage, ContactPage(Template層のコンポーネントに実際のデータを適用したものがPages層なので、Template層のTemplateをPageに変えると分かりやすい)
  • ReactはコンポーネントベースでUI開発することに特化したJavaScriptライブラリで、Atomic Designと相性がよいため一緒に使われることが多い

 

おわりに

ここまでお読みいただきありがとうございます。

本書ではReactを使った豊富なサンプルがあり、さらにテストにも言及しており学ぶことがとても多かったので、ご興味ある方はぜひご覧ください。

この本を読んで、個人開発しているサービスのコンポーネントの分け方で改善したいところが色々と出てきたので、どんどん実践していこうと思います。

自己yieldについて【Ruby】

こんにちは。

最近 メタプログラミングRuby を読み印象に残るところが多かったので、1つずつブログにまとめています。

今回は第2弾で、自己yield についてまとめました。

f:id:ysk_pro:20200711095214p:plain

自己yield とは

Rubyはメソッドにブロックを渡すことができます。
メソッドに渡されたブロックは、メソッド内で yield とすることで実行できます。

自己yield は、メソッド内で yield を使ってブロックを呼び出すときに、yield self としてオブジェクト自身(self)を引数としてブロックに渡すことを言います。

これがどのように使えるのか、サンプルコードを見ていきましょう。

サンプルコード

自己yield を使っていないコードを自己yield を使って書き換えていきます。

まずは、自己yield を使っていないコードです。

class PC
  attr_writer :name, :os, :price

  def initialize(name, os, price)
    self.name = name
    self.os = os
    self.price = price
  end
end

mac_book_air = PC.new('mac book air', 'mac', 150_000)
p mac_book_air #-> #<PC:0x00007fcf05863158 @name="mac book air", @os="mac", @price=150000>

インスタンス作成時にインスタンス変数をいくつかセットするだけのシンプルなコードです。
インスタンス変数が3つだと問題はなさそうですが、数が増えると引数の順番に気をつかう必要が出て分かりづらくなりそうですね。

このコードを自己yield を使って書き換えると次のようになります。

class PC
  attr_writer :name, :os, :price

  def initialize
    yield self
  end
end

mac_book_pro = PC.new do |pc|
  pc.name = 'mac book pro'
  pc.os = 'mac'
  pc.price = 200_000
end
p mac_book_pro #-> #<PC:0x00007f8015017128 @name="mac book pro", @os="mac", @price=200000>

PCクラスのインスタンスを作る際にブロックを渡しており、PCクラスの initializeメソッドでそのブロックを呼び出しています。

ブロックでインスタンス変数をセットしていることで、どのインスタンス変数にどの値を入れているか分かりやすくなりましたね。

さらに、メソッドにブロックが渡されたか判定できる block_given? メソッド を使えば、どちらのインスタンス作成の方法にも対応することができます。

class PC
  attr_writer :name, :os, :price

  def initialize(name = nil, os = nil, price = nil)
    if block_given?
      yield self
    else
      self.name = name
      self.os = os
      self.price = price
    end
  end
end

実際に使われているところ

自己yield が実際に使われている例を見てみましょう。

Faraday

よく使われている HTTPクライアントライブラリ Faraday で自己yield が使われています。

Faraday は次のように、分かりやすく HTTPリクエストを行うことができます。

resp = Faraday.get('http://sushi.com/search') do |req|
  req.params['limit'] = 100
  req.headers['Content-Type'] = 'application/json'
  req.body = {query: 'salmon'}.to_json
end
# => GET http://sushi.com/search?limit=100

このコードは 公式ドキュメント からの引用です。

ブロックを使うことで、クエリパラメータやリクエストヘッダ、ボディに直感的に値をセットすることができます。

Faraday のコード内で yield self が実行されています。

tap

tap はメソッドチェーンの途中で値をチェックしたい場合などに使われるメソッドです。

次のようによく使われます。

a = ['r', 'u', 'b', 'y'].map(&:next).pop
puts a #-> z

# メソッドチェーンの途中で値を確認する
a = ['r', 'u', 'b', 'y'].map(&:next).tap{|x| p x}.pop #-> ["s", "v", "c", "z"]
puts a #-> z

実際の tap はRubyで書かれていませんが、Rubyで実装すると自己yield を使った次のようなシンプルなコードになります。

class Object
  def tap
    yield self
    self
  end
end

おわりに

ここまでお読みいただきありがとうございます。

自己yield は色々なところで使われており、ソースコードを読む際にも必要になるので、知っておいて損はないと思います。

今回紹介した自己yield はメタプログラミングRuby の「付録A よくあるイディオム」に記載されていました。

メタプログラミングRuby 第2版

メタプログラミングRuby 第2版

次回もまた、メタプログラミングRubyを読んで印象に残ったところをまとめていこうと思います。

アクセスメソッドの注意点について書いた前回の記事も是非合わせてご覧ください。
ysk-pro.hatenablog.com

Waiting for table metadata lockとオンラインDDLについて【MySQL5.6】

MySQLで「Waiting for table metadata lock」のエラーが発生して、詳しく知りたくなったので、関連しているオンラインDDLとともに調べ、手元で試してみたことをまとめました。

MySQLのバージョンは 5.6、ストレージエンジンは InnoDB です。

この記事の多くは、MySQL 公式ドキュメントの以下のページを参考に書いています。
公式ドキュメント①:14.11.1 オンライン DDL の概要
公式ドキュメント②:14.11.2 オンライン DDL でのパフォーマンスと並列性に関する考慮事項

f:id:ysk_pro:20200708084439p:plain

DDLとは

DDL は Data Definition Language の略で、データ定義言語です。
SELECT などのテーブルのレコードを操作するものではなく、データベース自体を操作するものを指します。
CREATE, ALTER, DROP などが DDL です。

対して、SELECT, UPDATE, DELETE などの、テーブルのレコードを操作するものは DML(データ操作言語、Data Manipulation Language)と呼ばれています。

オンラインDDLとは

MySQL5.6より前のバージョンでは、一部を除いたDDLは、テーブルの全ての行のコピーやDDL実行中のDMLのブロックを必要とするコストの高い操作でした。
そのため、DDLを行うためにはサービスをメンテモードにするなどの対応が必要でした。

これに対し、MySQL5.6で拡張されたオンラインDDLは、テーブル全体のコピーを行わず、実行中のDMLのブロックも必要とせずDDLを行えるようになりました。
テーブル全体のコピーとDMLブロックが不要なDDLをオンラインDDLと呼んでいますが、「テーブルコピーは不要だがDMLブロックは必要」などといったDDLも一部存在します。

公式ドキュメント① にオンラインDDLについての詳しい説明があります。

オンラインDDLを有効にするために特別なことをする必要はなく、オンラインDDLが可能な場合はオンラインDDLが適用されます。
このことは、MySQL公式ドキュメント 14.11.3 オンライン DDL の SQL 構文 に記載があります。

ただ、全てのDDLがオンラインDDLに対応している訳ではありません

オンラインDDLに対応しているDDL

公式ドキュメント① に、それぞれのDDLでオンラインDDLのどの操作が可能かが一覧表でまとまっています。

一覧表で「インプレース?」が「はい」になっているものがテーブル全体のコピーが不要なDDL、「並列DMLを許可?」が「はい」になっているものがDDL実行中にDMLの実行が可能なものです。

ざっくりですが、よく行う操作だと カラムのデータ型変更以外はほぼオンラインDDLに対応していると思って良いと思います。

便利な機能として、DDLに「ALGORITHM=INPLACE, LOCK=NONE」というオプションをつけて実行すると、オンラインDDLができない場合に実行せずにエラーを返してくれます
「ALGORITHM=INPLACE」は、テーブル全体のコピーが必要ない INPLACE 方式でDDLを実行することを指定し、「LOCK=NONE」はDDL中にロックせずに並列DMLを可能にすることを指定します。これらのオプションは、指定した方法でDDLが実行できない場合にエラーにします。
確実にオンラインDDLで実行したい場合は、このオプションをつけてDDLを実行すると良さそうです。
ALGORITHM=INPLACE は公式ドキュメント①、LOCK=NONE は公式ドキュメント② の中で説明されています。

また、DDLを実行した際にテーブル全体のコピーを行ったかどうかを確認できます。
DDL実行のレスポンスが「0 row affected」の場合、テーブル全体のコピーが行われずにDDLが実行されています。
こちらは、公式ドキュメント② で説明されています。

オンラインDDLができないカラムの型変換で、実際にオプションを試してみました。

まずオプションをつけずに実行すると問題なく成功しました。usersテーブルの age カラムを varchar(255) 型に変更しています。

mysql> ALTER TABLE `users` CHANGE `age` `age` varchar(255) DEFAULT NULL;
Query OK, 4800 rows affected, 2 warnings (0.19 sec)
Records: 4800  Duplicates: 0  Warnings: 2

レスポンスが「4800 rows affected」となっているので、テーブル全体のコピーが行われたと分かります。

オプションをつけて実行すると想定通りエラーとなりました。

mysql> ALTER TABLE `users` CHANGE `age` `age` varchar(255) DEFAULT NULL, ALGORITHM=INPLACE, LOCK=NONE;
ERROR 1846 (0A000): ALGORITHM=INPLACE is not supported. Reason: Cannot change column type INPLACE. Try ALGORITHM=COPY.

INPLACE 方式だとできないので、テーブル全体のコピーを伴う COPY 方式で実行してね、というエラーが発生しています。

Ruby on RailsのMigrationでオプションをつける方法

私は普段 Ruby on Rails を書いており、Rails の Migration で前述のオプションをつける方法を調べました。

私が調べた限り、execute で任意のDDLを実行する以下の方法で行うしかなさそうでした。この例は、usersテーブルに test というコメントをつけるDDLです。

class Test < ActiveRecord::Migration
  def up
    execute "ALTER TABLE `users` COMMENT 'test', ALGORITHM=INPLACE, LOCK=NONE"
  end
  def down
    execute "ALTER TABLE `users` COMMENT '', ALGORITHM=INPLACE, LOCK=NONE"
  end
end
== 20200706064613 Test: migrating =============================================
-- execute("ALTER TABLE `users` COMMENT 'test', ALGORITHM=INPLACE, LOCK=NONE")
D, [2020-07-06T16:15:50.402667 #2523] DEBUG -- :    (15.1ms)  ALTER TABLE `users` COMMENT 'test', ALGORITHM=INPLACE, LOCK=NONE
   -> 0.0155s
== 20200706064613 Test: migrated (0.0156s) ====================================

execute を使った任意のDDL実行については、こちらの記事を参考にしました。
Execute SQL in Rails migrations. You may come across the following… | by Josua Schmid | Medium

migration時に実行されるSQLを確認する方法は、こちらの記事を参考にしました。(デフォルトでは migration時に実行される SQL は出力されません。)
Rails で migrate 時に実行される SQL を確認する - volpe’s diary

オンラインDDLの注意点とWaiting for table metadata lock が発生するケース

オンラインDDLには注意点があり、オンラインDDLの開始前、完了前にそれぞれ短時間ではあるものの排他的アクセスが必要となります。
つまり、オンラインDDLの開始前と完了前にDDLの対象テーブルに実行中のトランザクションがあった場合、そのトランザクションがコミットまたはロールバックするまで待機する必要があります。

さらに、オンラインDDLトランザクションの完了を待機している状態で同じテーブルへDMLを実行すると、DMLの対象レコードがトランザクションの対象レコードと別であってもオンラインDDLと同様に待機状態になってしまいます。

このようなケースで、待機状態になっているDDLDML が Waiting for table metadata lock の状態になっています。
メタデータ(meta data)とは、カラム名、データベース名などのデータベースについての情報のことです。
トランザクション中のメタデータの変更を防ぐため、トランザクション中はメタデータのロックがかかるようになっており、DDLを実行すると Waiting for table metadata lock となります。

トランザクションが長引いたりすると、Waiting for table metadata lock がたまり続けて障害の引き金になる可能性もあります。

図にすると次のようになります。左の列がトランザクション、中央の列が DML、右の列がオンラインDDL を表しています。

オンラインDDLとmetadata lockの関係図

詳細は 公式ドキュメント② と、公式ドキュメント 8.10.4 メタデータのロック に記載されています。

Waiting for table metadata lock が発生するケースを実際に試してみる

トランザクション中にDMLのみを行うケース

まずはオンラインDDLを実行しないケースを試してみます。

トランザクションを実行します。

mysql> start transaction;
Query OK, 0 rows affected (0.01 sec)
mysql> update users set name='test2' where id=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

別セッションで、トランザクションを実行しているテーブルに DML を行います。

mysql> select name from users where id=2;
+-----------+
| name      |
+-----------+
| taro      |
+-----------+
1 row in set (0.00 sec)

無事結果が返ってきました。

オンラインDDL を実行していない通常のケースでは、同じテーブルでトランザクションが実行中であっても DML は正常に完了しています。

トランザクション中にオンラインDDLDMLを行うケース

トランザクションを実行します。

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> update users set name='test' where id=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

別セッションで DDL を行います。(実行しているカラムへのコメント追加はオンラインDDLが可能なものです。)

mysql> ALTER TABLE `users` CHANGE `gender` `gender` int(11) DEFAULT 2 NOT NULL COMMENT 'test comment';

(応答なし)

結果は返らず、トランザクションの完了待ちになっています。

さらに別セッションで、トランザクションを実行しているテーブルに DML を行います。

mysql> select name from users where id=2;

(応答なし)

こちらもトランザクションの完了待ちになりました。

この時に、show full processlist を実行すると、応答なしとなっている2つのセッションが「Waiting for table metadata lock」となっています。

mysql> show full processlist;
+----+------+-----------------+------+---------+------+---------------------------------+------------------------------------------------------------------------------------------------+
| Id | User | Host            | db   | Command | Time | State                           | Info                                                                                           |
+----+------+-----------------+------+---------+------+---------------------------------+------------------------------------------------------------------------------------------------+
|  1 | root | localhost       | fril | Sleep   |   47 |                                 | NULL                                                                                           |
|  2 | root | localhost       | fril | Query   |   36 | Waiting for table metadata lock | ALTER TABLE `users` CHANGE `gender` `gender` int(11) DEFAULT 2 NOT NULL COMMENT 'test comment' |
|  3 | root | localhost       | fril | Query   |   24 | Waiting for table metadata lock | select name from users where id=2                                                              |
|  4 | root | localhost       | fril | Query   |    0 | init                            | show full processlist                                                                          |
+----+------+-----------------+------+---------+------+---------------------------------+------------------------------------------------------------------------------------------------+

そしてトランザクションを commit すると、応答がなかった2つのセッションで応答が返ってきました。

おわりに

オンラインDDLが可能なDDLだからといって、何も考えずにDDLを行うのは危険ですね。
ただ、長時間のトランザクションが発生しないテーブルであれば、オンラインDDLを実行するオプションをつけて実行することでリスクはかなり小さく抑えられそうです。

オンラインDDL実行前には、show full processlist でオンラインDDL をしようとしているテーブルに滞留しているトランザクションがないことを確認した方が安全ですね。
トランザクションの実行時間を調べる際にはこちらの記事が参考になりそうです。
【MySQL】トランザクションの実行時間を調査する - stmn tech blog

ただし、オンラインDDL 終了時にも短時間の排他的ロックが必要なので、オンラインDDL 開始時にトランザクションがないからといってノーリスクになる訳ではないそうです。

公式ドキュメントを読み込み、手元で実際に試してみると理解がかなり深まりますね。

MySQLの理解がまだまだ浅いので、バージョンが古いですが、過去読んだこの2冊を読み直していこうかな、と思っています。

現場で使える MySQL (DB Magazine SELECTION)

現場で使える MySQL (DB Magazine SELECTION)

間違えている箇所や説明が分かりにくい箇所等あれば、コメントなどでご教示いただけると大変ありがたいです。

attr_accessor(attr_writer)でインスタンス変数をセットするときはselfを省略できない【Ruby】

こんにちは。

最近 メタプログラミングRuby を読んだので、印象に残ったところをブログにまとめています。

今回は、Rubyのアクセスメソッド attr_writer, attr_accessor でインスタンス変数をセットするときにレシーバ(self.)を省略できないことについてまとめました。

サンプルコード

かなりシンプルなこちらのコードを実行すると何が出力されるでしょうか?

class Sample
  attr_accessor :name

  def set_name
    name = 'デフォルト太郎'
  end
end

sample = Sample.new
sample.set_name
puts sample.name #=> ?

ほぼ同じ内容をTwitterのアンケート機能で聞いてみた結果がこちらです。

attr_accessor は Sample クラス内に以下のコードを自動で定義してくれるので、name= メソッドによってインスタンス変数がセットされて「デフォルト太郎」と出力されそうな気がしますが、実際は何も出力されません

def name
  @name
end

def name=
  @name = name
end

この予想に反した結果となっている原因は、set_name メソッドにあります。

「name = 'デフォルト太郎'」が、ローカル変数の name に値を代入しているのか、attr_accessor で生成された「name =」メソッドをレシーバを省略して呼び出しているかが Ruby に判断できずRuby では前者(ローカル変数への代入)を採用することになっています。

よって、set_name メソッドはローカル変数の name に値を代入しているだけとなり、sample.name は 定義されていない インスタンス変数 @name を参照しているため、何も出力されないという挙動になっていました。

set_name メソッドを以下のようにすれば「デフォルト太郎」と出力されるようになります。

def set_name
  self.name = 'デフォルト太郎'
end

先ほどは省略していたレシーバ(self.)を明記することで、「name =」メソッドを呼び出していることが明確になったためです。

また、アクセスメソッドを使用せず、インスタンス変数に直接値を入れる以下の形でも「デフォルト太郎」が出力される挙動にすることができます。

def set_name
  @name = 'デフォルト太郎'
end

おわりに

ここまでお読みいただきありがとうございます。

意図しない挙動を避けるためのコードを2種類紹介しましたが、実務のコードでは self. をつける形もインスタンス変数に直接値を入れる形も見かけるので、どちらを選ぶかは好みな気がします。

メタプログラミングRuby では、今回取り上げたような Ruby の小ネタ的な話も色々と紹介されており面白いのでおすすめです。

メタプログラミングRuby 第2版

メタプログラミングRuby 第2版

(追記)
メタプログラミングRubyを読んで印象に残ったところまとめの第2弾として「自己yield」についてまとめました。
是非合わせてご覧ください。
ysk-pro.hatenablog.com