叫ぶうさぎの悪ふざけ

うさぎが目印のWebエンジニアが、得たことや思ったことを言の葉に乗せて叫ぶ場所です。

学習記録:12月20日(木):はじめよう!要件定義 Chapter-07 [準備編]全体像を描こう(つづき)

これは 俺のインプットアウトプット記録 Advent Calendar 201820日目のエントリーです。

昨日は全体像を描く手順として、アプリケーションを真ん中に置いて、登場人物とやることを書き込んでいく、というところまで進みました。 結果、ソフトウェアが利用者に提供するものが見えることで、迷走しにくくなる、取りこぼしが減る、という感じだったかと思います。

今日もみっちり要件定義の研修を受けてきて、一昨日以上に濃密なインプットを得てきたところです。毎回発見があるし、毎回腑に落ちるし、確実に自分が前に進んでいるな、って実感できるのは幸せなものです。

この研修、有料であるにも関わらず受けさせてくれる代表には本当に感謝です。

閑話休題

というわけで、前回の 学習記録:12月19日(水):はじめよう!要件定義 Chapter-07 [準備編]全体像を描こう(前編) に引き続き要件定義の学習を進めます。今週はずっとこれです。

Chapter-07 [準備編]全体像を描こう(つづき)

昨日に引き続き読み進めます。

全体像を描くときの注意点

2つあるとのこと。

システム管理者を忘れずに

あ、これうちの代表が言ってたやつだ。以前のエントリーに一言だけ追記した気がする。

というわけで 利用者としてのシステム管理者を忘れずに含める とあります。これ以上の詳細は書かれていませんが、書かなければ登場人物としての考慮が漏れることになります。

システム管理者は最高権限を持つユーザー。しかも使い回しではいけない。例えば複数いるシステム管理者のうち誰かが退職したとしたら、同じように最高権限を持つユーザーによって適切に削除されなければなりませんし、それは下位の権限のユーザーには実行できない。

そのほかにも書いてある通りの重要な機能があり、それらをうっかり定義し忘れてしまうと「あれ、ログ確認できない(残してない)ので監視できてなかった」などのトラブルを招きそうです。

image.png (152.3 kB)

連携するシステムを書く

外部、内部問わず、連携するシステムやソフトウェアがあればそれも書くとあります。他システムからCSV取り込んだり、逆に他システム用にCSV出力したり、API叩いたり、色々あると思います。

また、特に業務システムの場合は 旧システム(移行元、現行)から新システム の流れとしても、連携するシステムを書くのを絶対に忘れないとあります。

書籍の図にちょっと書き足すとこんな感じかな。

image.png (169.5 kB)

解説

システム管理者が「不正アクセスを監視する」のなら、それは逆に「システム管理者に対し、不正アクセスを監視できる仕組みを提供する」という要件を 定義しないと機能が実現できず漏れてしまう 、とあります。

なので、「この人が必要なものって他にあるかな?」と指差し確認できる必要があるよ、と。よかったさっきの認識間違ってなかった。

また、連携しているシステムやソフトウェアがあるなら、連携先の要求に応える仕組みを要件に追加しなくちゃいけない。 場合によっては連携先に追加や変更を依頼する必要も出てくる 。それをこの時点で洗い出すということと理解しました。

特に移行元のシステムがある場合、移行に必要な要件(データや一時的な機能などかな)、並行稼働のための要件が想像以上に出てくるよ、と。それらを常に確認できるよう、利害関係者(ステークホルダー)としての、連携するソフトウェア、システムを必ず記載すること、とあります。

感想

新しいシステムですから、なにがしかのデータ設計変更、もしくは作り直しが発生し、データベースが違ってくることの方が多いでしょう。

普通ならデータベースも刷新だと思いますし、その間の並行稼働やデータ移行は各所で苦労しているのを見かけますし、話題が尽きることもない気がします。

関係するものは最初から全部洗い出すのは、言われてみれば当たり前ですけど。普段からずっと使っていて当たり前になっていたり、滅多に連携しないけど重要なシステムがあったり、意識から外れてしまう、言語化されない、ということがあるよな、と思いながら読んでました。

連携する内容も忘れずに

連携先を書いたら、何を連携するのか描きましょうねと。全部書き切る必要はないよ、というのは登場人物(利用者)のところと同じです。極端な話として、移行先との連携は「移行対象データ」とだけでも構わないとあります。

そこに意識することができるのがまずは重要なんだなと理解しましたし、何も書かないとミスの遠因になるということなので、やはりそういうことだなと思います。

というわけで自分なりに書籍から内容を書き足して図にしてみます。

image.png (405.5 kB)

利用者不在のものも連携対象

IoTなど、利用者不在のセンサーコンピューティングでも、そのセンター端末がステークホルダーでもあるし、連携先でもある、というようなケースもあるよ、とあります。

その場合もきちんと明記しましょうね、と。

複雑なIoTの環境を構築しているのなら、書いておかないと絶対に漏れるなってのは容易に想像できます。

余談ですけど、農業でもIoTが利用されていて、ビニールハウスの温度湿度管理や、農作物の様子など、いろんな使われ方をしてる場合もあるなって思うし、これがクルマなら、これまた様々なセンサー類があるよなって思いながら読んでました。

全体像を魅力的にお化粧する

全体像を作ってきたわけですが、全体像はパンフレットのようなものでもあり、誤解を招かない程度に装飾するとあります。

社内業務システムならば、利用者つまりスタッフへプレゼンテーションもするでしょうと。お化粧にはUIイメージなども加え、より魅力やメリットを感じてもらわないと、先行き心もとないよね、ともあります。

使ってもらう人に魅力的だと期待してもらえるほうが良いですよね。(過度な期待を持たせない程度に)

というわけで、この全体像の枠内が今回の要件定義の対象範囲、つまりスコープであり、引き続きこの枠内を掘り下げていきます、ということで Chapter-08 に続きます。

読んでみて

登場人物から、連携するソフトウェアやシステムを書いて、全体像を把握する。それをスコープとする。今までなんとなく感覚で書いてたものが 明確な理由と根拠を示されながら解説された ことで、わかってるけど説明できなかったことが、なんとか説明できるレベルにはなったと思います。

で、研修でも出てきたんですけど、この説明ができないとプロとは言えないよね、ということがよくわかります。

当たり前のことなんですけど、その当たり前に敏感であれ、という話も聞けて、それは要件定義だけじゃなく、コードを書く場合にも言えることだなって思いながら研修を受けてました。

今日の研修では、要件定義するには層がいくつかあるという話もありました。

  • ゼロ層
    • カスタマージャーニ、カスタマーエクスペリエンス
    • このソフトウェア、システムでBeforeがどうAfterで幸せになるのか
  • 第一層
    • ユーザーシナリオ、ビジネスシナリオ
    • とにかくユーザーが何をやるのか書き出していく
  • 第二層
    • サービスシナリオ
    • 業務側が何をするのかを、ユーザーシナリオと1対になりながら書き出していく
  • 第三層
    • 操作シナリオ、UI
    • 操作の流れ、ユーザーが触れる部分
  • その先
    • ソフトウェア要件

で、この層を意識しつつ、それぞれの層で抜け漏れなく、かつシンプルに分割していく。これって僕らが普段やっている単一責任の原則にも繋がるよなって思いました。

ここら辺の、いわゆるプロセス設計の話は別の書籍「はじめよう!プロセス設計」に詳しいので、次の課題図書だったりもします。

引き続き学習しながら、プロセス、要件定義からコード、運用まで、単一責任の原則(もちろんそれ以外もですが)を意識しながら今後の仕事を続けていこうと改めて思ったりした次第です。

追加変更しやすく。それは実装以前に始まってるんだなって思ったりした今日でした。

学習記録:12月19日(水):はじめよう!要件定義 Chapter-07 [準備編]全体像を描こう(前編)

これは 俺のインプットアウトプット記録 Advent Calendar 2018 の19日目のエントリーです。

昨日は要件定義からかなり脱線して、家事育児をどうするかを考えてたりしました。結論は 家事育児は生き物、人間のコピーを作れないのと同じ ってことがわかりました。個別に切り出してタスク化することはできても、それは家事育児を生き物として扱ってる人からみたら本質の解決にはならんよ、ということを学びました。

閑話休題

というわけで、前回の 学習記録:12月18日(火):はじめよう!要件定義 Chapter-06 [準備編]企画を確認する(つづき) に引き続き要件定義の学習を進めます。今週はずっとこれです。

Chapter-07 [準備編]全体像を描こう

どんなものができたら嬉しいのか、を描いていくんだろうな、という予想をしながら読み進めます。

全体像を描くプロセス

企画を確認(プロジェクト仕様)したら、これから作るソフトウェアの全体像(オーバービュー)を描こうとあります。

仕事とは行動と成果がセットなので、という意図で以下の図が書かれているんだと思います。

  • 材料:企画
     ↓
  • 仕事:全体像を描く
     ↓
  • 成果:全体像

全体像とは

確認した企画の中の、 「何を」の部分をビジュアルに表現したもの 、コンテキスト図と呼ばれる場合もある。

利用者が何を使ってどうなるのか、を大枠で書いていくのかな、と想像します。

描き方のポイント

緻密さよりも大枠で、とあります。よかったあってたっぽい。

大枠の理解を共有することが主眼であり、この枠を超える要件はスコープ範囲外だということが関係者に伝わるものを描く、とあります。

なるほど、今まで僕はちょっと誤解してたな。全体像を描く=必要と言われてること全部を「納まるように描く」ことあったなって思いました。

んで、今から作ろうとするソフトウェアのパンフレットを作る、企画から確認したプロジェクトの紹介から、全体像として プロジェクトの成果としてのソフトウェアの紹介 を作っていくことだとあります。

  • 材料:企画
     ↓
  • 仕事:ソフトウェア開発プロジェクト
     ↓
  • 成果:ソフトウェア

全体像を描く理由

これは企画を確認する理由とほぼ同じで、 何が出来上がったら完成なのか という問いに こういうものを実現しようとしているんだよ と提示できるようにする、ということとあります。

なので、要件定義そのものじゃなく、その前段階で描かれていて然るべきものである、と。

よくある話

企画書の一部に「実現する機能の例」としてスクリーンショットが貼られていることがあるが、これは断片的であり、全体像ではないし、全体像ではないのに全体像っぽい資料に描くことで、逆に 目に見えない重要な機能が漏れることがある とあります。

で、その重要な機能とは 実現できて当たり前 と企画立案者が考えているためであり、当たり前のことに対しては あーそりゃそうよね って感じでぞんざいに扱うので、重要なのに忘れられがちだよ、と。

結果として、次の要件定義の段階でも大雑把に検討されたり、そもそも忘れたれたりするぞ、と。

要件定義って 企画者と、ソフトウェアを実現する側の当たり前を漏れなく定義するもの だと僕は認識しているので、あーなるほどな、そうだよな、って思いながら読んでました。

なので、指差ししながら「こういうことを実現するよ」と言える状態にする、上空からの俯瞰図を用意する、とあります。

全体像を描く手順

把握できればどんな形でもいいけど、最低限これだけは欲しいよね、ということで解説が始まります。

ソフトウェアを真ん中に置く

  • 真ん中に四角を描く
  • これが今回作るソフトウェア

というわけで広いキャンパスの真ん中に、ぽつねんとソフトウェアを描きます。

image.png (31.7 kB)

利用者を配置する

ソフトウェアを利用するあらゆる人を描く感じですかね。

image.png (63.4 kB)

利用者が行うことを書く

それぞれの利用者に、このソフトウェアを使って行うことを書き込んでいきます。おお、全体像っぽい。

image.png (182.8 kB)

解説

全体像を逆からいうと このソフトウェアは利用者に対してこういうことを行えるようになることを提供する ことを示すとあります。

細かい機能は書ききれないし、要件定義を進めていくと修正なども出てくるのは大前提、とはいえ 全体像が杜撰だと合意形成が迷走しやすくなります よ、と。

んで、全体像を描いていくうちに、意外といろんな疑問が生じるので、要件定義の前にたくさんのことが確認できる。ゆえに この時点での疑問はしっかり確認を取り、その確認結果を、前行程の企画書、本行程の全体像に反映 させましょう、という内容で、全体像を描く手順が締められています。

この時点ですでに、前工程との回転が始まり、ブラッシュアップがされていくのだな、と認識しました。

読んでみて

なるほどこれをやれば、当たり前を見える化できるな、と思いましたし、発注側の業務の当たり前は、おそらく発注段階で必ず漏れるんだろうなって思いました。

実際に漏れてる要求の方が圧倒的に多いし、以前RFPの仕事をした時も、ソフトウェアを開発する際に必要なもの、という観点でお客さんに接した記憶が蘇ります。

要件定義って 企画者と、ソフトウェアを実現する側の当たり前を漏れなく定義するもの だと僕は認識している、と書きましたけど、まさにこれを漏れなく見えるように落とし込んでいく工程なのだなと思っています。

余談

僕がこの業界に入ったのは1992年、バブルが崩壊したと同時でした。今から見ると、まだまだ旧態依然とした体制だったし、古い言葉の定義から、新しい世界へと一生懸命変わろうとしすぎて、言葉ばかりが一人歩きしていた時期でもあったなあと思います。

が、古いと言っても悪いわけじゃなくて、枯れた言葉や技術って、普通に有用だったりするし、むしろないがしろにしてはいけない筆頭になることの方が多いと僕は思っています。

何が言いたいかというと。

上流工程、下流工程っていう表現はなんだかアレなので別の表現を使った結果、かえってわかりにくい場面が往往にしてあるな、ってことです。

いいんですよね、上流下流で。別にどっちが偉いわけでもないし、使役されるわけでもない。

どっちもがないと何も生まれない。

けど、これも日本的文化なのかもしれませんが、職人気質をずっと引きずっているがために、つまんないところで民族的なプライドが顔を出すのかなあ、などと妄想したりもしました。

余談2

先日の研修でも面白い話が聞けて、英語圏と日本語圏ではそもそもいろんなことが違うよ、ってことがありました。

英語圏の彼ら、特にアメリカは、ジーザスに誓いを立て、自分の言葉と行動と結果に嘘偽りがないことを無意識に証明しながら動いている、というような意味のことでした。それをやることで、死後の世界が約束される、ってのが染みてるんだよと。

だからこそひとつひとつの意味を明確にしていくし、そこに自分は何ができて何ができないのかはっきりさせるし、呼ばれたMTGでの役割やMTGのBefore-Afterの定義が曖昧な場合は「なんやねん」っていうし、などなど、興味深い話でした。

ここはもっと思考を深くして、色々検証したいなと思ってはいますが、主題と大きくそれるのでこの辺にしときます。

別に日本の文化を否定してるわけじゃないんですけど、そう考えた方が自然に仕事ができるよな、という話のうちの1つとして書いておきたかったのでした。

というわけで

小出し小出しにいきますが、研修が終わってその結果をインプットし、アウトプットし、回転させるまでは、しっかりとやっていくぞ。

学習記録:12月18日(火):はじめよう!要件定義 Chapter-06 [準備編]企画を確認する(つづき)

これは 俺のインプットアウトプット記録 Advent Calendar 2018 の18日目のエントリーです。

ちなみにこのエントリー、最後に盛大なポエムというか、なんというかな話が入ります。そんな話はいらんわ、って場合は飛ばしてもらえればなあと思うんですが、本題よりそっちの方をエントリーにした方がよかったんじゃないかな…と思わなくもないので、もし何か思うところがありましたらば、忌憚のないご意見をいただけたらいいなあと思います。

閑話休題

本日は約4時間の研修を受けてきて、情報が溢れ気味でございます。が、間違いなく今後の仕事の仕方を変える内容でしたし、いかに僕らが 当たり前を具現化する仕事をしている のか、そしてそれを しょっちゅう忘れて頻繁に事故を起こす のかを思い知らされました。

研修内容をそのまま公開するわけにはいかないので研修レポートは書きません(手元にはしっかり残す)が、はじめよう!要件定義、を読み進めるにあたり、研修で学んだことなどは取り入れつつアウトプットにしたいと思います。

というわけで、前回の 学習記録:12月17日(月):はじめよう!要件定義 Chapter-05 要件定義、その前に & Chapter-06 [準備編]企画を確認する に引き続き要件定義の学習を進めます。

Chapter-06 [準備編]企画を確認する

昨日はこの章の途中までで終わっていました。企画書でゴール、つまり実現したいものを確認し、実現したいものからの逆算で「UI、機能、データ」を合意して進めていくわけですが、そのゴールが曖昧だとダメで、じゃあまとめるためにどんな項目があるのか、ということで以下の項目があればいいよ、まで進みました。

  1. プロジェクトの名称
  2. なぜこのプロジェクトをやるのか
    • 目的
  3. 何を
    • 目的達成のために作るもの
    • 作るものの説明
    • 作るものを利用する人
    • 利用する人が得られる便益
  4. どのように
    • 作るための体制
    • 期限

企画の内容を関係者が理解するための企画書

重要なのは企画を通すための企画書ではなく

  • 通った企画がいったいどのようなものなのか、を
  • 企画を実現するために作業する関係者、が
  • 理解するために必要な企画書

である、とあります。当たり前すぎてぐうの音も出ないんですけど、今まで多数の 企画を通すための企画書 を見てきましたし、そういう企画書ほど、 企画をするための企画書 だったりしたなあと思い返したりしました。

そもそも出発点は何かしらの情熱であり、それを実現するために前に進むための企画であり、その内容を共有するための企画書である。

だから、意思決定に対する「企画提案書」ではなく、実施が決まった企画の「決定報告書=プロジェクトそのものの仕様」が必要とあります。

つまり企画書を通す前の お願いします!の企画書 と 企画書を通した後にやる決定報告としての これをやります!の企画書=プロジェクト仕様書 が必要と理解しました。

RFPやプロジェクト計画書との違い

RFP支援の仕事もしたことがあるので、大変興味深い章です。要望を形にして、実現可能性や技術検討ができるようにするもの、くらいのふわっとした認識でした。

企画書が、RFP、いわゆる提案依頼書や、プロジェクト計画書と何が違うのか。

プロジェクト計画書から、マネジメント向けの情報を除外したサマリー

それが欲しいとあります。

  • 除外したいもの
    • 予算、リスクなど実務スタッフの担当領域外の情報
  • 不安や反発をなくす
    • プロジェクト仕様を知らないまま指示通りに作業しろ、と言われても…
    • 知らないことは先手を打つこともできない。プロなのに。
    • 何より人間は腑に落ちないことに不安や反発を覚え、手が止まりがち
    • 作業そのものが停滞しちゃうよね、と

企画書とは何か

プロジェクトについての紹介を、5分で手際よく事前知識のない人にできますか という問いかけが出てきます。

実務担当に不要な情報や、不安や反発を招きそうな内容だと、これから目指すゴールを紹介するのは難しいよな、と思います。

今から自分たちはこういうゴールを目指していくのだ と大雑把に理解できるために、 紹介するための企画書を作成 する。

そしてそれを共有する。 これが最初の材料 となる。

サンプル①:施設予約システム

サンプルとして、以下のプロジェクト仕様が掲載されています。僕なりに少し情報を足してみます。

サンプルには別紙参照の別紙がありませんが(そりゃそうだ)、それも含めてプロジェクト仕様書だと認識しました。で、そのプロジェクト仕様書を関係者で共有することで、自分たちが向かうゴールとその理由がわかる、ということかなと理解しました。

  • プロジェクトの名称
    • 施設予約システム
  • なぜ
    • 目的
      • インターネットを通じ、利用者が手軽に施設予約できるようにすることで、施設の稼働を活性化する
  • 何を
    • 目的達成のために作るもの
      • インターネットからの施設予約
      • 予約状況の確認
      • 実績データの他のシステムとの連携
    • 作るものの説明
      • 別紙参照(各機能の必要性とかUIとかかな)
    • 作るものを利用する人
      • 一般利用者
      • 窓口担当者
      • マネージャ
      • 経費部門
      • システム管理者
        • アカウント管理にシステム管理者を常に含めるのは重要
    • 利用する人が得られる便益
      • 別紙参照(フローや図があったりして別資料なのかな)
  • どのように
    • 作るための体制
      • 別紙参照(体制図が決まってないからかな)
    • 期限
      • 今年度中に完成
      • 新年度から運用開始

書籍にはもう1つサンプルがあったのですが、そちらは割愛します。ゲームを作るとなった場合にどうなるか、が書かれていました。

コラム:企画の良し悪し

企画もなしにいきなりソフトウェアは作れないよね、要件定義できないよね、狙いや企てのない取り組みはあり得ないよね、とあります。

まさにその通りで、企画の原点には情熱や思いから発する狙いや企てがあると僕も思います。

それは、趣味のプログラミング(他の趣味にも当てはまりそう)ですら、狙いや企てはある、と。そして、企画には良し悪しがある。

企画の良し悪しはどこに起因する?

利用者便益(ベネフィット)の観点から精査されているかどうか。

と述べられています。

成果と評価は違うとあります。

  • 成果はアウトプットであり、作り手が生み出す
    • 目玉焼きそのものは成果、しかし評価じゃない
  • 評価は受け入れる側が生み出す
    • 成果を受け入れ側がインプットし、そこから生まれる
    • 美味しい、いまいち、など
  • 評価の後は効果
    • 美味しければ嬉しくなったり幸せな気持ちになる
    • いまいちなら残念な気持ちになったり怒ったりする

この違いを認識せず、成果ばかりに目がいってしまい、評価や効果に対する狙いがあやふやなのが、筋の悪い企画であると述べられています。

効果の想定が曖昧

使い勝手のいいアプリを作る、という例で進んでいきます。

  • 使い勝手がいい、と評価するのはユーザーであり作り手ではない
  • ユーザーが評価した結果、どんな効果を狙うのか、が意外と曖昧
    • たぶん、職人文化を色濃くもつ日本人にすごくありがち
    • So What?(で?だからなに?) と問われたら回答できない

あるよなあそういうこと、って思いながら読んでました。

ストーリーを描けることが重要

  • 成果=例えば完成した「在庫管理システム」
  • 利用者が「仕事がしやすくなった!」という評価の先に…
  • 在庫回転率が向上するという効果が生じる
  • というストーリーが描けるものが良い企画

しかし実際にはストーリーに飛躍や断絶があって、実際の要件定義をする段になると、プロジェクトの目的に対して辻褄が合わない、矛盾した要件になってしまうよね、とあります。

重要なのは個別の正確性より全体の一貫性

個々に優れていても、例えば洗濯物を片付けたい、と言われ、いかに高性能かつ最先端の洗濯機を激推ししたとしても、おそらく以下の「洗濯」というプロセスに置いて、洗濯機は一部の、つまり個別の正確性の1つでしかないんじゃないかな、って考えました。

  • 洗濯物が発生するタイミング
    • 毎日、毎時、人、など多数かつランダム
  • 洗うと判断するタイミング
    • 定量に達したら?
    • 旅行などのイベントに関連する?
  • 洗うという行為
    • 洗濯物別に区分け
    • ネットに入れる
    • 順番を考えて実行する
  • 洗濯開始までの導線
    • カゴに放り込まれている
    • 仕分けする
    • ネットで包む
    • 洗濯機に入れる
  • 洗濯機を操作する
    • 洗剤を入れる
    • 柔軟剤をポケットに入れる
    • 水の量を設定する
    • 時間を設定する
    • 乾燥が必要か判断する
    • 乾燥のON・OFF
    • 洗濯実行する
  • 洗濯機が回る
  • 洗濯が完了する
  • 洗濯機から取り出す
  • 干す
    • ハンガーに通す
    • 洗濯バサミで止める
    • 洗濯棒にかける
    • 乾燥にかける
    • 陰干しにする
  • 乾いたか確認する
  • 乾いていれば取り込む
    • 定められた位置へ積む
    • 分ける必要があれば分ける
  • たたむ
    • しまう場所別にたたむ
    • 利用する人別にたたむ
    • 簡単なものからたたむ
  • しまう
    • タンスにしまう
    • クローゼットにしまう
    • 脱衣所にしまう
    • あるべき利用位置に設置する

思うこと

まさにその通りだなってのは前回にも思いを綴った記憶があるんですけど、仕事のための仕事を作る人っているよな、とも思いました。誰かのために、つまり利用者に便益をもたらすことから発進しているはずなのに、そこがすっぽり抜け落ちている。

利用者に便益をもたらす観点から考えずに、 企画を立ち上げなくてはならないからターゲットと市場を考えて、無理に企画を考案する なんてことあるよな、と。

得てしてそういう企画は利益にも結びつかず、便益をもたらす利用者の数も非常に限定的かもしれないなって思いました。

とはいえ、世の中の不便なことをちゃんと調べないと、企画も出てこない、ということも言えると思います。ゆえに、思いつきを徹底的に精査する、矛盾や無理のない内容であるかを突き詰める。

そうして初めて、企画としての実現可能性が見えてくるんだろうなって思いました。

やってみたいこと&やったこと

洗濯する、をちょっと考えても、これだけのプロセスとタスクと手順があり、しかもそれが天気や家族の健康状態、学校、イベント、ともすると気分にも左右され、かつ大物(布団カバーとか)があるか否かで分岐も変化するよなってことに気づきました。

また、今までずっと考えていて行動に移せなかった おとーさんとして1日を回すために経験すべきこと をどう認識するか。それって要件定義と同じ考えで形にできないかな、と考えたりしました。

で、24時から1時間ほど奥さんとずっと話してたんですけど、

  • 「家事育児、俺ができるようになるために経験したい、見える化したい」
  • (この間、色々やりとりあり、奥さんは辛抱強く僕の主張を聞いてくれている。眠いのに)
  • 「で、何がしたいねん。母親の代わりをしたいのか、手伝いたいのかわからないよ」
  • ( ゚д゚)ハッ!
  • (自分に問いかけたのち)「俺にできる1日の回し方を体験したい」
  • 「つまり、おとーさんとして1日を回したいってことね」

ってことを話してました。かなり要約すると。このあいだの1時間、奥さんはすごい忍耐力と言語力で僕に接してくれたと思います。

我が家は僕が外貨を稼ぎ、奥さんが専業主婦です。当然役割も意識も求められるものも違う。

僕は外貨を稼ぐためにタスクを見える化し、確実にこなして成果に繋げ、評価してもらう。その結果として対価をいただく。

では奥さんは?

常に変動する要素に対し、分単位、ときに秒単位で判断し、行動している。毎日命がけで。その行動の元となる情報は天気だったり子供の健康状態だったり控えている学校行事だったりするし、ご飯をどのくらい食べたかで次のやることにも影響してくる。

これらを、家の中にあるあらゆる要素、あらゆる外的要因、あらゆる子供の状態をインプットとして無限の分岐から判断を下して行動する。

僕とは根本的にやってることが違うわけです。

で、その認識が僕にはない。なぜならそこまで全部判断するような一気通貫の1日を回しているわけではなかったから。

でも、知りたい。なぜか。できるようになりたい。なぜか。奥さんが1日家を空けても回るようにしたい。なぜか。奥さんが入院したら回らないから?それだけじゃない。俺にできることを増やしたい。なぜか。奥さんが大変そうな、辛そうなことを減らしたい。減るのか。減らない。それに意味はあるのか。意味はあると思う。経験を積むために協力してもらうことはできないか。それは可能といえば可能。

こんな問答に付き合ってくれて、以下のことがわかりました。

  • 1日のゴールは娘が時間内にご飯を食べ、明日のために19時台に寝ること
  • ゴールに到達するためには、日によって選択肢が全く違うこと
  • 正解は何もないこと
  • おかーさんの代わりは絶対にできないこと
  • 部分的な家事育児の積み重ねの経験は必要なこと
  • 経験から、部分の前後関係に気づけるようになること
  • 連鎖していくことで、イレギュラーな分岐に対応する能力が上がること(おかーさんほどではない)

他にもたくさん話したし、おかーさんの家事育児は本当に命がけで休みがないです。だからこそ話の途中では非常に厳しく固い雰囲気だなって僕が感じる場面もありました。

命がけだから当たり前の話し方をしてるんですよね。それは奥さんにとっては当たり前。

でも僕はその当たり前を本質的に知るまでに至っていなかった。部分的にやっていただけだった。

それがわかり、厳しいわけでも固いわけでもなく、淡々と解説して、先を読み、僕の話を聞いてくれ、それに答えてくれている。

で、上記のことがわかった、というか初めて共有できたんですね。

そして、なんでそんなこと急に、しかもこの寝る直前に言ったのか。という話になって。

そういうことが考えられるくらい、気持ちに余裕ができてきたのかもね

って言ってくれたのがすごく嬉しかったし、今の僕の状態を奥さんも喜んでくれている、ということもわかりました。

実際、11月に入ってから、僕はお腹を壊していません。それまでは何かある都度、ひどい腹痛に10年くらい悩まされてきましたが、それが初めてピタリと止んでいるんですよね。(なので、根っこの体調そのものは非常に良好です)

そんな話から、生活や環境を今よりもっとよくしていくにはどうしようか?来年度からPTA役員だから家にいてほしいスケジュールはなるべく早く共有するからサポートよろしくね。そういえば洗濯機そろそろやばいよね。2月になったら最終決断しようか。そういえば食洗機ってどう?便利は便利、でもたまに傷がつくのよね。えーまじで。ほらほらこんな感じ、だから使うときは洗えてもお気に入りの食器はやめようね。そうだね、大事な子たちだもんね。そういえばソファを占領している大きなぬいぐるみ(すみっこぐらしのプライズ、つまりゲーセンから連れて帰ってきた子たち)のおうちどおしようかしらね?壁に固定する家具を選ばなくてわ!そういえばクリスマスはお揃いのちっこいバッグでいいかな?娘も欲しいって言ってたから共同で使うかな。それはいいわね。

などなど話は飛躍したし、要件定義について学習した結果の何気ない(と思っていた)問いかけが、奥さんに負担を強いてしまったんですけど、少なくとも僕は 家事育児が全然わからない。俺は雰囲気で部分的にやっている 状態から 命がけでやってるし代わりはできないし、おとーさんとして考える ってことはわかったし、それによって 24時間臨戦態勢のおかーさん をたまの1日でも休んでもらえるために僕にできること、をより具体的に考えることができるようになったし、あとは経験を積もう、って意識になれました。

要件定義から大きく話は逸れましたし、家事育児はそもそも要件とかそういう次元ではないので、安易に仕事っぽく話した結果の責任は僕には負えませんが、僕としては言葉にできないくらいの出来事に繋がったりしました。

仕事の思考で家事育児をやろうとすると破綻するのはわかったし、根本的に違うのもいったんはわかったし、最終的に言えることは、僕の奥さんはやっぱり最高だな。

この人に命を預けるって決めてからもう10年以上経っていますが、様々な環境の変化についていけていなかった自分がいたんですよね。それはきっと自分にとっては負い目に近かったんじゃないかなって今では思います。

今年は色々あったし、 おとーさんとしての1日を回したい なんて話、奥さんにとっては「めんどくさい話だな!」って一言で片付けられてしまわれかねない。手伝いって意識じゃ全然ダメだし、やりたいって言った以上はアテにするわけだから、依頼したら100%やれるもんじゃないと依頼できないし、依頼するんじゃなく行動をみて自分で判断できないと本当はだめ、ってこともあります。

けど、僕の気持ちは汲んでくれて、僕自身は前に進むことができた。あとは行動で示すしかないなって気持ちを新たにして、今日は寝ようと思います。

( ˘ω˘ )スヤァ

学習記録:12月17日(月):はじめよう!要件定義 Chapter-05 要件定義、その前に & Chapter-06 [準備編]企画を確認する

これは 俺のインプットアウトプット記録 Advent Calendar 2018 の17日目のエントリーです。

さて、明日は 徹底的に要件定義について学ぶ研修 があり、その教材の元になっているのが、この はじめよう!要件定義 という書籍だったりします。

ですので今日はMySQLはいったんおやすみし、前回の 学習記録:12月10日(月):はじめよう!要件定義 Chapter-03&Chapter-04 に引き続き要件定義の学習を進めます。

Chapter-05 要件定義、その前に

というわけで前章までは

  1. UI
  2. 機能
  3. データ

を決める、という流れで、実際に詳細を見ていきますというところで終わっていました。

UI定義という工程

UIが決まった、というゴールに到達するには、何をどうすればいいかと問われます。

どんな材料を使って成果物(ゴール)に向かうのか。それをまずUI定義から始めるということかな、と理解してます。

要件定義の旅への出発準備

目玉焼きという成果を出すには卵という材料が必要なのと同じく、定義されたUIという成果を出すにも材料が必要と書かれています。

そこで材料を揃えるための要件定義の旅、その出発の準備をするとあり、要件定義そのものではないとあります。

要件定義を行うために必要な材料を揃えるための作業がしばらく続くとあり、僕自身、要件定義を「何を持って完了とするか」を曖昧なまま今まで来てしまったので、非常に楽しみな内容です。

現実のプロジェクトあるある

要件定義と称して、要件定義の準備を行なっているケースが非常に多い、とあります。

よって、これから出てくる準備段階を要件定義としてやってしまっているってことは、要件定義の前工程である準備を先送りした事実の現れである、という理解を僕はしました。

そんなプロジェクトは好ましい状態じゃないよね、ということで次に続きます。

Chapter-06 [準備編]企画を確認する

僕は企画の部署に所属していたことがあるのでなんとなくわかるのですが、要件の前に企画が必ずあるんですよね。なのでそのことかなと思いつつ読み進めてみます。

ゴールを確認する

ソフトウェアを作る、というゴールを確認すると、依頼する・作るからには、思いつきだけじゃない、 企画意図が明確に定義されている はずとあります。

本当にその通りだと思いますし、思いつきだけで作ったプロダクトで成功した物を僕はみたことがありません。

以前、事業コンテストをきっちり行なっていた会社でも、思いつきだけではなく、役員全員に対しプレゼンをし、利益の可能性、市場の状態、人員確保、スケジュール、損益分界点など、裏付けの情報を含めてしっかりやっていました。

また、そうじゃないと予算など確保できないよね、とあり、その通りだよなと思います。

で、ソフトウェアはその企画意図を達成できるものじゃなきゃいけないし、企画意図から外れた要件を定義してしまったら、いくら要件定義通りに作っても納得が得られない、ということになるとあります。

当たり前の話ですけど、新規事業でも、受託の要件定義でも、できてない場面たくさんあるし、たくさん見てきたなー、と思い出します。

企画書を確認できない=ゴールの定義が不明瞭

企画確認は企画書を見るのが手っ取り早い。しかし現実では見ることができない場面がある、とあります。

  • 社内秘なので企画書を外部に見せられない
  • そもそも企画書がない

大雑把にいうとこの2つですとあり、そうだよなって思います。

重要なこと

企画書が見られない場合、それに代わるものが存在しない、つまりゴールが不明瞭である状態が問題であるとあります。

不明瞭とは、これを見ればわかるよ、という状態が得られないこと。それにより

  • 毎回口頭で解説を受けることになる
  • 記録に残らない
  • 時間の経過で簡単に変わってしまうことになる
    • 口頭の内容がブレる
  • 説明が面倒で端折る

ことで、結果として内容理解が関係者でバラバラになる。それぞれの「今回のゴール」がバラバラになり、理解の個人差が混乱を招くとあります。

全くその通りだし、ゴールが曖昧なままなのは、社内の他のチームから見ても不安になりますし、「何を目的にして、何を利益に還元するのだろう」と疑問にも思います。

次は「企画書を作成するためにまとめる項目」が具体的に出てきます。

企画書を作成するためにまとめる項目

バラバラになる問題は、プロジェクトマネジメントの問題で、要件定義の作業の問題ではないとあります。そりゃそうですよね。仕組みや組織の問題ってことでいいのかな。

しかし材料を揃えないといけない。揃わないと不明瞭な要件定義のまま進むことになる。

ゆえに、関係者全員がひと目で「理解を共有できる」企画書を作る必要があり、その最低限の項目が列挙されています。

  1. プロジェクトの名称
  2. なぜこのプロジェクトをやるのか
    • 目的
  3. 何を
    • 目的達成のために作るもの
    • 作るものの説明
    • 作るものを利用する人
    • 利用する人が得られる便益
  4. どのように
    • 作るための体制
    • 期限

この程度で構わない、と文中にありますが、この程度が全然できてないの多いよなーと思ってたら、次の文に このようなサマリーが存在しないプロジェクトが実に多数見受けられます とあってわかりみが深かったです。

なので、企画会議に出た人以外は、企画意図を理解する手がかりがないまま進んでしまう、つまり 企画者が後工程に対して理解不足である とあります。

これ、エンジニアでもマーケターでもディレクターでもなんでも、職種に関係なく、理解不足な場面あるよなって思いましたし、企画意図って上述の項目全部に理由があるはずで、それが 解説できなかったらその時点でその企画はダメなんだろうなあ と思いました。

僕が思うこと

名前って大事だよなって思います。業務システムで曖昧なまま進むのをみたことありますが、呼び方ひとつ取っても曖昧で、なんというか情熱というか、気持ちが入りにくいんじゃないかなって思ったりします。

目的はもっと大事というか、これ企画する一番最初にあるよねって思いますし、 目的ないのはただの趣味 だよなって思ったりもします。

また、目的達成のために作るものは何で、その内容はどんなもので、利用する人、つまりターゲットだと思いますが、そのターゲットはどんな人たちなのか。

で、その ターゲット=利用する人が何を得られるのか。押し付けじゃないよね、使って幸せになれるよね、って話だよな って思いながら読みました。

この辺に 無理なこじつけがある場合は、企画そのものに無理がある んだろうなあ、と思ったりもします。

また、企画といってもゼロスタートのサービスじゃなく、 既存サービスの新機能にも同じことが言える と思います。

以前、人材系のサービスに関わっていたときのプロジェクトマネージャは非常に優秀な人で、 各部署で企画が立ち上がったら片っ端からエンジニアを派遣し、スピーディーに実現可能性について即答できる状況を作った りしてました。

エンジニアだからといって企画、要件定義に関わらなくていいてことは全くなくて、むしろそこから関わることで、熱量や背景、歴史、目的、つまり 企画意図が正しく伝わり、同じ方向のゴールをみてプロジェクトが進められる んだろうなって思います。

読んでみて

ずっと自分の中にあった 思いつきだけじゃない、様々な根拠に裏付けられた企画意図 があるのが大前提だよな、という思いが、ここで見事に解説されていて、頷きしかないなって思いました。

そして、世の中にはそういった、思いつきだけで突っ走っては消えていくサービスが多いよな、ということも思いました。

それが良い悪いはさておき、思いつきだけで走るのは博打以外の何者でもなく、それが道楽ならいいですけど、利益をあげる必要がある場所で開発に関わるなら、 勝算のある博打を打つ 必要があるよなって強く思いました。

また、自分の今までの事業や企画に対する思いが間違えていなかった。それを確認できただけでも、読んでみて得られることは大きかったです。

次以降、読み進めるのが非常に楽しみですし、明日から始まる研修が楽しみで仕方ありません。

というわけで今週は要件定義週間になりそうです!

学習記録:12月16日(日):【MySQL】frmファイル欠損、ibdファイル存在、テーブル構成を推測したい【リストア不能】

これは 俺のインプットアウトプット記録 Advent Calendar 2018 の16日目のエントリーです。

結論から言ってしまうと、テーブル修復でエラーになっていたのは .ibdファイル がそもそもなかったからでした。おそらく、MacBookのストレージ容量不足でファイルを退避させた際に、なぜか .frmファイル を一緒に退避させなかったみたいですね(遠い目)

というわけで、振り返りつつ、どうにか .ibdファイル を直接読むことはできないものか、ともがいてみます。

昨日まで

昨日は MySQL 5.7.19 の環境をマイナーバージョン指定で作ろう、と思い立ったものの、おそらくソースからの make install じゃないと無理では…ということで、もともと使っていた環境に、エラーのあったテーブルをリストアし、mysql_upgradeを再実行すればいいかな、と思っていました。

なんと .frmファイル欠損

が、いざリストアしてみると肝心のテーブルがない…。あ、テーブル個別でバックアップ取っていたな、と思ってzipファイルを開くと…なんと .ibdファイルそのもの

.frmファイル がテーブル構造を表すファイルで、 .ibdファイル が実データを表すファイル。これではテーブル構造がわからぬ…しかも前回、公式にしたがって DROP TABLE しているので構造がわからない。

うーむ困った…。

`.ibdファイルを直接読んで、レコードの状態をみることはできないものか。

と探していると、 innodb_ruby というツールを提供しているかたを発見。

wiki に書かれている通りにインストールし、使ってみました。

innodb_ruby

以下の手順でインストールしました。

インストール

# バックアップを取っていたと思ったらibdファイルだけだったディレクトリで作業
$ pwd
/Users/mamy1326/dev/vagrant/mysql57/var/lib/mysql/mamy1326/innodb_ruby
$ sudo gem install innodb_ruby
Password:
Fetching: bindata-2.4.4.gem (100%)
Successfully installed bindata-2.4.4
Fetching: digest-crc-0.4.1.gem (100%)
Successfully installed digest-crc-0.4.1
Fetching: innodb_ruby-0.9.15.gem (100%)
Successfully installed innodb_ruby-0.9.15
Parsing documentation for bindata-2.4.4
Installing ri documentation for bindata-2.4.4
Parsing documentation for digest-crc-0.4.1
Installing ri documentation for digest-crc-0.4.1
Parsing documentation for innodb_ruby-0.9.15
Installing ri documentation for innodb_ruby-0.9.15
Done installing documentation for bindata, digest-crc, innodb_ruby after 3 seconds
3 gems installed

GitHubから諸々取得

$ git clone https://github.com/jeremycole/innodb_ruby.git
Cloning into 'innodb_ruby'...
remote: Enumerating objects: 2663, done.
remote: Total 2663 (delta 0), reused 0 (delta 0), pack-reused 2663
Receiving objects: 100% (2663/2663), 16.79 MiB | 483.00 KiB/s, done.
Resolving deltas: 100% (1451/1451), done.
naosama-mac:mamy1326 mamy1326$ cd innodb_ruby
naosama-mac:innodb_ruby mamy1326$ innodb_space -s ../
c_log.ibd    innodb_ruby/ 
naosama-mac:innodb_ruby mamy1326$ innodb_space -s ../c_log.ibd system-spaces
name                            pages       indexes     
/Library/Ruby/Gems/2.3.0/gems/innodb_ruby-0.9.15/bin/innodb_space:206:in `block in system_spaces': undefined method `pages' for nil:NilClass (NoMethodError)
    from /Library/Ruby/Gems/2.3.0/gems/innodb_ruby-0.9.15/bin/innodb_space:211:in `system_spaces'
    from /Library/Ruby/Gems/2.3.0/gems/innodb_ruby-0.9.15/bin/innodb_space:1976:in `<top (required)>'
    from /usr/local/bin/innodb_space:22:in `load'
    from /usr/local/bin/innodb_space:22:in `<main>'
$ cd innodb_ruby

innodb_space 実行

藁をもすがる気持ちで実行してみます。 なお、実行コマンドは wiki と、 日々の覚書 mysqlディレクトリーに知らない.ibdファイルがある in MySQL 8.0.0 を参考にさせていただきました。

  • -f オプション
    • 対象はibdata1ではなく、テーブル個別のibd
  • 対象ファイル
    • c_log.ibd ファイルに対し読み込みを実行する
  • page-dump
    • innodb_rubyが理解できるほとんどの構造体の表現を含むページの内容をインテリジェントにダンプします
    • 直訳ですけど、page単位でdumpしてくれる…のか?
    • -p 5 が5ページ分のdumpってことかな?
  • -T オプション
    • 指定されたテーブル名を使用します、とのこと。データベース名とテーブル名を指定します

で、dumpした結果。正直よくわかりませんな(遠い目

$ innodb_space -f ../c_log.ibd -T mamy1326/c_log -p 5 page-dump | less
#<Innodb::Page::Index:0x00007fe6b10dcb78>:

fil header:
{:checksum=>816151722,
 :offset=>5,
 :prev=>nil,
 :next=>nil,
 :lsn=>42947405534,
 :type=>:INDEX,
 :flush_lsn=>0,
 :space_id=>139}

fil trailer:
{:checksum=>816151722, :lsn_low32=>4292699870}

page header:
{:n_dir_slots=>3,
 :heap_top=>274,
 :garbage_offset=>0,
 :garbage_size=>0,
 :last_insert_offset=>265,
 :direction=>:right,
 :n_direction=>4,
 :n_recs=>11,
 :max_trx_id=>0,
 :level=>2,
 :index_id=>106,
 :n_heap=>13,
 :format=>:compact}

fseg header:
{:leaf=>
  <Innodb::Inode space=<Innodb::Space file="../c_log.ibd", page_size=16384, pages=559616>, fseg=6>,
 :internal=>
  <Innodb::Inode space=<Innodb::Space file="../c_log.ibd", page_size=16384, pages=559616>, fseg=5>}

sizes:
  header           120
  trailer            8
  directory          6
  free           16096
  used             288
  record           154
  per record     14.00

page directory:
[99, 181, 112]

system records:
{:offset=>99,
 :header=>
  {:next=>125,
   :type=>:infimum,
   :heap_number=>0,
   :n_owned=>1,
   :min_rec=>false,
   :deleted=>false,
   :length=>5},
 :next=>125,
 :data=>"infimum\x00",
 :length=>8}
{:offset=>112,
 :header=>
  {:next=>112,
   :type=>:supremum,
   :heap_number=>1,
   :n_owned=>8,
   :min_rec=>false,
   :deleted=>false,
   :length=>5},
 :next=>112,
 :data=>"supremum",
 :length=>8}

garbage records:

records:
{:format=>:compact,
 :offset=>125,
 :header=>
  {:next=>139,
   :type=>:node_pointer,
   :heap_number=>2,
   :n_owned=>0,
   :min_rec=>true,
   :deleted=>false,
   :length=>5},
 :next=>139}

{:format=>:compact,
 :offset=>139,
 :header=>
  {:next=>167,
   :type=>:node_pointer,
   :heap_number=>3,
   :n_owned=>0,
   :min_rec=>false,
   :deleted=>false,
   :length=>5},
 :next=>167}

{:format=>:compact,
 :offset=>167,
 :header=>
  {:next=>181,
   :type=>:node_pointer,
   :heap_number=>5,
   :n_owned=>0,
   :min_rec=>false,
   :deleted=>false,
   :length=>5},
 :next=>181}

{:format=>:compact,
 :offset=>181,
 :header=>
  {:next=>195,
   :type=>:node_pointer,
   :heap_number=>6,
   :n_owned=>4,
   :min_rec=>false,
   :deleted=>false,
   :length=>5},
 :next=>195}

{:format=>:compact,
 :offset=>195,
 :header=>
  {:next=>209,
   :type=>:node_pointer,
   :heap_number=>7,
   :n_owned=>0,
   :min_rec=>false,
   :deleted=>false,
   :length=>5},
 :next=>209}

{:format=>:compact,
 :offset=>209,
 :header=>
  {:next=>223,
   :type=>:node_pointer,
   :heap_number=>8,
   :n_owned=>0,
   :min_rec=>false,
   :deleted=>false,
   :length=>5},
 :next=>223}

{:format=>:compact,
 :offset=>223,
 :header=>
  {:next=>237,
   :type=>:node_pointer,
   :heap_number=>9,
   :n_owned=>0,
   :min_rec=>false,
   :deleted=>false,
   :length=>5},
 :next=>237}

{:format=>:compact,
 :offset=>237,
 :header=>
  {:next=>251,
   :type=>:node_pointer,
   :heap_number=>10,
   :n_owned=>0,
   :min_rec=>false,
   :deleted=>false,
   :length=>5},
 :next=>251}

{:format=>:compact,
 :offset=>251,
 :header=>
  {:next=>265,
   :type=>:node_pointer,
   :heap_number=>11,
   :n_owned=>0,
   :min_rec=>false,
   :deleted=>false,
   :length=>5},
 :next=>265}

{:format=>:compact,
 :offset=>265,
 :header=>
  {:next=>153,
   :type=>:node_pointer,
   :heap_number=>12,
   :n_owned=>0,
   :min_rec=>false,
   :deleted=>false,
   :length=>5},
 :next=>153}

{:format=>:compact,
 :offset=>153,
 :header=>
  {:next=>112,
   :type=>:node_pointer,
   :heap_number=>4,
   :n_owned=>0,
   :min_rec=>false,
   :deleted=>false,
   :length=>5},
 :next=>112}

次の課題

まずもってテーブル構造を、レコードから推測できないことにはリストアは不可能そうです。

しかしせっかく .ibdファイル を読み込むところまできたので、できる限りのことをしてみようと思います。

これで自由にレコード読めるようになったら面白いじゃん?

せっかく便利なツールを作ってくれている人がいるし、個人的に中身を直接読んでみたいと常々思っていたので、趣味的に願ったり叶ったり。

アップグレードはしばし棚上げして、 .ibdファイル の読み方を学習してみたいと思います。

もしこのエントリを読んでくださっているかたがいらしたならば、おそらく面白くないんじゃないかなあ…と思いながらも、自分自身はやっていて楽しいのでこうしてエントリにしているわけでございます。

1歩ずつ地道に進めてみて、結果までたどり着き、誰かしらの有益な情報になればいいなあ、と思いながら、やれるところまでやってみようと思います。

で、もしダメなら諦めて、くだんのテーブルは捨ててアップグレードを進めたいと思います。

アップグレード時にテーブル修復が失敗していたのも、そもそも .ibdファイル がなかったからで、ないならないで、消して進めれば本題はクリアできると思いますので。

というわけで、明日も引き続き .ibdファイル と戯れたいと思います!

学習記録:12月15日(土):【MySQL8.0アップグレード】5.7.19→5.7.24→8.0.11 アップグレード手順【5.7.24 アップグレード後編-MySQL 5.7.19の環境を新規に作ってみる】

これは 俺のインプットアウトプット記録 Advent Calendar 2018 の15日目のエントリーです。

昨日の振り返り

昨日のエントリーを出して間も無く、yoku0825さんに考察していただき、ログ( /var/log/mysqld.log )を遡った結果、エラーを起こしていたテーブルの .ibd ファイルが最初からなかったことが判明したので、ちょっと遠回りだけど以下の方法で再実行してみようと思います。

今後の方針

  • 5.7.19 の環境を作る
  • リストアする
  • .ibdファイルが揃っていることを確認する
    • 今回エラーのあったテーブルにSELECTできることも確認する
  • 5.7.24 へアップグレードする
  • 結果を確認する

んで、無事に 5.7.24 になったら、8.0.11 へさらにアップグレードしてみる計画です。

MySQL 5.7.19の環境を構築する

手順は以下を想定しています。

  • vagrantでubuntu18.04の環境を作る
  • MySQL 5.7.19 をインストール
  • データをリストア
  • MySQL 5.7.24 にアップグレード
  • エラーが出ないことを確認

昨年7月に vagrant で作った際は、リストアするデータが大きすぎて vagrant のデフォルト容量である 4GB をオーバーし、リストアできませんでした。

そこで、容量を 40GB まで増やして boxを作成し、リストアしました。が、昨年やったときはすごくめんどくさかった(イメージファイルを変換したり色々やった。正直覚えておりません…)です。しかし今はデフォルトで100Gあるのでそのままやります。

新しい vagrant box を作成

まっさらから作っていきます。Dockerでやればいいと思うかたもいらっしゃるとは思うんですが、今回の主題は

MySQL 5.7.19 から 5.7.24 にアップグレード したのち MySQL8.0.11(またはもっと最新)にアップグレード

することです。Dockerでそれをやる方法を少なくとも僕は知らないし、手軽に色々いじり倒したいので、vagrantを採用しています。

ここら辺はちゃんと理解してないんですが、主題とは逸れるので掘り下げずに進めます。

バックアップ

忘れずに以下をバックアップします。

  • Vagrantfile
  • my.cnf
  • データベース

box作成

ubuntuで作っていきます。

$ cd ~/dev/vagrant
$ mkdir mysql8.0
$ cd mysql8.0
$ vagrant init bento/ubuntu-18.04
A `Vagrantfile` has been placed in this directory. You are now
ready to `vagrant up` your first virtual environment! Please read
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.
$ vim Vagrantfile
$ cat Vagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
  config.vm.box = "bento/ubuntu-18.04"

  # ポートとIPアドレスの設定
  config.vm.network "forwarded_port", guest: 80, host: 8080
  config.vm.network "private_network", ip: "192.168.33.10"

  # マウントするディレクトリ
  config.vm.synced_folder "./vagrant", "/vagrant"

  # メモリ容量(6G)
  config.vm.provider "virtualbox" do |vb|
    vb.memory = "6144"
  end
end
# マウントするディレクトリ忘れずに
$ mkdir vagrant

vagrant up でbox作成

ポートとか他のboxとぶつかってないか確認忘れずに。ぶつかると以下のエラー出ます。

ポートフォワードのエラー

ついうっかり他からコピーしてきた時のあるあるですかね。俺たちは雰囲気でvagrantを利用している。

==> default: Setting the name of the VM: mysql80_default_1544881511628_84089
Vagrant cannot forward the specified ports on this VM, since they
would collide with some other application that is already listening
on these ports. The forwarded port to 8080 is already in use
on the host machine.

To fix this, modify your current project's Vagrantfile to use another
port. Example, where '1234' would be replaced by a unique host port:

  config.vm.network :forwarded_port, guest: 80, host: 1234

Sometimes, Vagrant will attempt to auto-correct this for you. In this
case, Vagrant was unable to. This is usually because the guest machine
is in a state which doesn't allow modifying port forwarding. You could
try 'vagrant reload' (equivalent of running a halt followed by an up)
so vagrant can attempt to auto-correct this upon booting. Be warned
that any unsaved work might be lost.

vagrant up でbox作成

$ vagrant up
$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Box 'bento/ubuntu-18.04' could not be found. Attempting to find and install...
(中略)
==> default: Mounting shared folders...
    default: /vagrant => /Users/mamy1326/dev/vagrant/mysql8.0/vagrant
$ vagrant status
Current machine states:

default                   running (virtualbox)

The VM is running. To stop this VM, you can run `vagrant halt` to
shut it down forcefully, or you can run `vagrant suspend` to simply
suspend the virtual machine. In either case, to restart it again,
simply run `vagrant up`.

vagrant box の中で諸々更新

ubuntu18.04.1 のboxができました。次は諸々更新。

$ vagrant ssh
Welcome to Ubuntu 18.04.1 LTS (GNU/Linux 4.15.0-29-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Sat Dec 15 13:56:05 UTC 2018

  System load:  0.0               Processes:           90
  Usage of /:   2.8% of 61.80GB   Users logged in:     0
  Memory usage: 3%                IP address for eth0: 10.0.2.15
  Swap usage:   0%                IP address for eth1: 192.168.33.10


0 packages can be updated.
0 updates are security updates.


$ sudo apt update -y
(中略)
$ sudo apt upgrade -y
(中略)
# 途中GUIが立ち上がり、キーボードについて聞かれるので適切に選択する、主に言語配列かな
(中略)
update-initramfs: Generating /boot/initrd.img-4.15.0-29-generic
vagrant@vagrant:~$ 

aptitude インストール

ubuntuのパッケージ管理ソフトウェアのaptitudeをインストールしておく。どうやら16.04あたりからデフォルトでインストールされないようになってるらしい。

$ sudo apt-get install aptitude
Reading package lists... Done
(中略)
update-alternatives: using /usr/bin/aptitude-curses to provide /usr/bin/aptitude (aptitude) in auto mode
Processing triggers for libc-bin (2.27-3ubuntu1) ...
vagrant@vagrant:~$ 

MySQL 5.7.24 のインストール

マイナーバージョンの指定の仕方がわからず。5.7系で最新の 5.7.24 をインストール。make installしかないのかな?

$ mysql --version
-bash: mysql: command not found

# パッケージを確認する
$ sudo aptitude search ^mysql-.*5.7
p   mysql-client-5.7                                                               - MySQL database client binaries                                                          
p   mysql-client-5.7:i386                                                          - MySQL database client binaries                                                          
p   mysql-client-core-5.7                                                          - MySQL database core client binaries                                                     
p   mysql-client-core-5.7:i386                                                     - MySQL database core client binaries                                                     
p   mysql-server-5.7                                                               - MySQL database server binaries and system database setup                                
p   mysql-server-5.7:i386                                                          - MySQL database server binaries and system database setup                                
p   mysql-server-core-5.7                                                          - MySQL database server binaries                                                          
p   mysql-server-core-5.7:i386                                                     - MySQL database server binaries                                                          
p   mysql-source-5.7                                                               - MySQL source                                                                            
p   mysql-source-5.7:i386                                                          - MySQL source                                                                            
p   mysql-testsuite-5.7                                                            - MySQL 5.7 testsuite                                                                     
p   mysql-testsuite-5.7:i386                                                       - MySQL 5.7 testsuite

# 必要な物を選んでインストール
$ sudo aptitude install -y mysql-client-5.7 mysql-client-core-5.7 mysql-server-5.7 mysql-server-core-5.7
The following NEW packages will be installed:
(中略)
Processing triggers for ureadahead (0.100.0-20) ...
                                         
vagrant@vagrant:~$ mysql --version
mysql  Ver 14.14 Distrib 5.7.24, for Linux (x86_64) using  EditLine wrapper

マイナーバージョンアップの再現どうするよ

現時点でマイナーバージョン指定のインストール方法がわからず(あんまり指定してインストールすることないもんなあ)

マイナーバージョンアップを実体験して成功させる、のも主目的の1つなので、昨日使っていた環境に対し、以下を実行してみます。

  • データベース削除
  • リストア
  • mysql_upgrade 実行

前の環境に戻って作業

先ほどバックアップはとりましたので、順番に実行していきます。

vagrant に対する操作

一応快適に操作したいので、今作った環境は止めて、前の環境で作業します。

$ vagrant halt
(中略)
$ cd ~/dev/vagrant/mysql57
$ vagrant up
$ vagrant ssh

データベース削除&作成

リストアの前に削除&空のデータベース作成します。

$ mysql -uroot -p mysql
Enter password: 
mysql> drop database mamy1326;
Query OK, 40 rows affected (0.58 sec)
mysql> create database mamy1326;
Query OK, 1 row affected (0.00 sec)

リストア

$ mysql -uroot -p mamy1326 < mamy1326.sql
Enter password: 

時間切れ

ここで今日の学習時間オーバー。15GBくらいのリストアなので、そこそこ時間かかります。

一応問題なくテーブルはリストアされていて、くだんの大きなテーブルも順調にサイズが増えているので、待てばリストアされるでしょう。

ここはリストアを待って、引き続き日が変わってから(になると思う)作業を続けたいと思います。

感想

久しぶりにリストアとかやるんですけど、去年散々やったのでこの辺は覚えてるもんですね。

あと、今までマイナーバージョンまで指定(というか意識)してインストールしたことなかったので、やり方がわかりませんでした。

マイナーバージョンが最新のならサクッとインストールできるんですけどね(当たり前)

ここら辺、検証したいわー環境個別に作りたいわー、っていうニーズは確実にあるはずなので、方法を探したいところです。

幸い前の環境は削除してないので、そちらで作業を続け、バージョンアップについての知見を得られ、アウトプットできたら、素直にMySQL 8.0の環境を普通に作って今後の検証作業とスライド作成など進めようかな、って思っています。

ただ、yoku0825さんにいただいたご恩を返すには、バージョンアップを成功させることが必要。これだけはしっかりやっておきたいと思います。

よっし引き続きやるぞー。

学習記録:12月14日(金):【MySQL8.0アップグレード】5.7.19->5.7.24->8.0.11 アップグレード手順【5.7.24 アップグレード後編-エラーを調べる】

これは 俺のインプットアウトプット記録 Advent Calendar 2018 の14日目のエントリーです。

結論からいうと、どうも サイズの大きなibdファイルが失われている ようです。結果、データファイルが読み取れず、アップグレードのチェック時にエラーとなっていた模様。

今回はそのエラーを /var/log/mysqld.log から、なるべく1つずつ調べていきます。

昨日の振り返り

趣味なので振り返りからやっていきます。

/var/lib/mysql/mysql_upgrade_info ファイルが書き込めなかった

昨日はパーミッションの関係で、以下のファイルが書き込めなかったのを解決し、そのさきへ…というところで止まっていました。

Could not create the upgrade info file '/var/lib/mysql/mysql_upgrade_info' in the MySQL Servers datadir, errno: 13

12月15日(土) 1:36 追記

最強の外部API神であるお豆腐さん が今日と昨日のエントリーをみてくれて、なんとまとめてくださった…あなたが神か(神です)。本当にありがとうございます!

とある豆腐のエラー考察(未完)

で、 /var/lib/mysql/mysql_upgrade_info ファイルが書き込めなかった の正解を引用します。

さすがやで…根拠をインクルードファイルで提示までしてくださる…。というわけで、 mysql_upgrade 実行ユーザーが vagrantユーザーで、/var/lib/mysql への書き込み権限を持たなかったため でした。(あってるかな?)

これもっかい最初からやり直そうかなって思ってます。

/var/lib/mysql/ パーミッション変更し、mysql_upgradeを再実行

で、パーミッションを変更してmysql_upgradeを実行し、ファイルが出力されるところまで実行しました。

$ sudo chmod 775 /var/lib/mysql
$ ls -lda /var/lib/mysql
drwxrwxr-x 7 mysql mysql 4096 1213 22:53 /var/lib/mysql
$ mysql_upgrade -uroot -p
Enter password: 
(中略)
Upgrade process completed successfully.
Checking if update is needed.

バージョン情報のテキストファイルだった

で、結果としては 5.7.24 のアップグレードのバージョンが出力されるファイルだったようです。

$ cat /var/lib/mysql/mysql_upgrade_info
5.7.24

それから

とはいえ本題のエラーはまだこれからでした。

Tablespace is missing for table

で、続いて昨日は以下のエラーが出て、特定のテーブルが5.7.19から5.7.24へのアップグレードされる段階でエラーを出力していました。ちなみにMySQL自体はアップグレードができています。

$ mysql_upgrade -uroot -p
(中略)
mamy1326.c_log
Warning  : InnoDB: Tablespace is missing for table mamy1326/c_log.
Error    : Tablespace is missing for table `mamy1326`.`c_log`.
error    : Corrupt

そこで今日は、このエラーの原因を究明、解決し、テーブルを復旧するところまで進めたいです。

試しにSELECT

MySQLの起動はできたので試しにSELECTしてみましたが、同じエラーになります。 というわけでこれは趣味。丁寧に調べていきます。

mysql> select * from c_log limit 1;
ERROR 1812 (HY000): Tablespace is missing for table `mamy1326`.`c_log`.

/var/log/mysqld.log エラーログを見る

困った時のエラーメッセージ。素直にエラーログをみます。

該当部分を抜き出し

今回のテーブルの部分のみ抜き出してみました。 色々出てますので、1つずつ調べてみます。趣味ですので。

2018-12-14T13:21:28.160781Z 2 [ERROR] InnoDB: Failed to find tablespace for table `mamy1326`.`c_log` in the cache. Attempting to load the tablespace with space id 139
2018-12-14T13:21:28.160847Z 2 [ERROR] InnoDB: Operating system error number 2 in a file operation.
2018-12-14T13:21:28.160856Z 2 [ERROR] InnoDB: The error means the system cannot find the path specified.
2018-12-14T13:21:28.160862Z 2 [ERROR] InnoDB: Cannot open datafile for read-only: './mamy1326/c_log.ibd' OS error: 71
2018-12-14T13:21:28.160868Z 2 [ERROR] InnoDB: Operating system error number 2 in a file operation.
2018-12-14T13:21:28.160873Z 2 [ERROR] InnoDB: The error means the system cannot find the path specified.
2018-12-14T13:21:28.160880Z 2 [ERROR] InnoDB: Could not find a valid tablespace file for `mamy1326/c_log`. Please refer to http://dev.mysql.com/doc/refman/5.7/en/innodb-troubleshooting-datadict.html for how to resolve the issue.
2018-12-14T13:21:28.160996Z 2 [Warning] InnoDB: Cannot calculate statistics for table `mamy1326`.`c_log` because the .ibd file is missing. Please refer to http://dev.mysql.com/doc/refman/5.7/en/innodb-troubleshooting.html for how to resolve the issue.

Failed to find tablespace for table mamy1326.c_log in the cache. Attempting to load the tablespace with space id 139

直訳すると以下になります。

キャッシュ内のテーブル mamy1326.c_logのテーブルスペースを見つけることができませんでした。スペースID 139で表スペースをロードしようとしています

何のことやらさっぱりです。 グーグル先生に頼っても「クラッシュしたんで復旧してや」って記事ばかり。俺はこのエラーメッセージの意味と原因を知りたいんじゃああああああ!

と思うけど他にもエラーメッセージがたくさん出てるので次々調べてみます。

Operating system error number 2 in a file operation.

これも直訳します。

ファイル操作中のオペレーティング・システム・エラー番号2。

ざっくりとしかわからへんがな(´・_・`)

次調べましょう。

The error means the system cannot find the path specified.

これも直(ry

エラーは、システムが指定されたパスを見つけることができないことを意味します。

お、これは前のエラーと繋がってることを意味しそうです。何かパス間違ってるんかな。ibdファイルかな。うーん。

しかし my.cnf へのパスの情報はこれくらいしか設定してない。

[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
symbolic-links=0
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid

とりあえず次のエラーを調べます。

Cannot open datafile for read-only: './mamy1326/c_log.ibd' OS error: 71

おおおおっ。これは…ッ!ibdファイルが開けない…ッ!パーミッションか…ッ!

突然のJOJO風、失礼しました。取り乱しました。

やっとエラーメッセージみただけでわかりそうな感じになってきたので、ファイル確認します。

$ sudo ls -la /var/lib/mysql/mamy1326/c_log.ibd
ls: /var/lib/mysql/mamy1326/c_log.ibd にアクセスできません: そのようなファイルやディレクトリはありません

テーブルの実体ファイルがないじゃない。ちなみに定義ファイルはありました。

Could not find a valid tablespace file for mamy1326/c_log. Please refer to http://dev.mysql.com/doc/refman/5.7/en/innodb-troubleshooting-datadict.html for how to resolve the issue.

結局のところ、最後のエラーメッセージで、公式をみてトラブルシューティングをするべし、とありがたくも指摘までいただけているのでみていくことにします。

というかこれ、アップグレードしようとして、ファイルでかすぎて変換できなかったんじゃないかな、と今の時点で予測を立ててます。

公式を参照する

公式の 14.19.3 InnoDB データディクショナリの操作のトラブルシューティング を参照します。(内容は全て翻訳機にかけた内容です)

序文

テーブル定義に関する情報は、.frm ファイルと InnoDB データディクショナリの両方に格納されます。.frm ファイルをあちこちに移動したり、データディクショナリの操作の最中にサーバーがクラッシュしたりすると、これらの情報のソースに整合性がなくなることがあります。

frmファイルにテーブル定義、ibdファイルがデータディクショナリ、という認識です。これがクラッシュしたら整合性取れなくなるよと最初に言われています。

データファイルを開くことができません。

公式の前半部分に、今回のエラーメッセージズバリの内容が書かれていました。innodb_file_per_tableパラメータはデフォルトでONになっているので、表領域は共有ではなく個別領域です。その表領域である .ibd ファイルが欠落しているよと言われます。

innodb_file_per_table (デフォルト)有効にした場合、次のメッセージが起動時に表示されることがあります ファイルあたりのテーブル 表領域のファイル(.ibdファイル)が欠落しています。

はい、その通りでした。サンプルのエラーメッセージも今回のものとほぼ同じです。

[ERROR] InnoDB: Operating system error number 2 in a file operation.
[ERROR] InnoDB: The error means the system cannot find the path specified.
[ERROR] InnoDB: Cannot open datafile for read-only: './test/t1.ibd' OS error: 71
[Warning] InnoDB: Ignoring tablespace `test/t1` because it could not be opened.

で、解決方法としては、DROP TABLEしなさいと書かれています。

これらのメッセージに対処するには、DROP TABLEステートメントを発行して欠落しているテーブルに関するデータをデータ辞書から削除します。

DROP TABLEしてみる

.ibd ファイルが欠落した理由は今のところわかっていませんが、幸いこのテーブルはバックアップがあります。そこで素直に公式に従い、DROP TABLEしてみます。

ちなみにログインした結果から、 Server version: 5.7.24-log MySQL Community Server (GPL) が確認できます。

$ mysql -uroot -p mamy1326
Enter password: 
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 3
Server version: 5.7.24-log MySQL Community Server (GPL)

Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> DROP TABLE c_log;
Query OK, 0 rows affected (0.02 sec)

無事DROP TABLEすることができました。

テーブル定義も消えてますね。

$ sudo ls -la /var/lib/mamy1326/c_log.*
ls: /var/lib/mamy1326/c_log.* にアクセスできません: そのようなファイルやディレクトリはありません

次の課題

DROP TABLEしたものの、こいつを復元するのはどうやって?がまだわかっていません。

バックアップからリストアするんだろうな。ということでリストアするとは思います。

今回はここまで

さて、いったん学習時間としては時間切れ。引き続き明日調べていきます。

それにしても5.7.19で構築した環境にテスト的に大きめなデータを入れた状態で、マイナーバージョンアップしただけでもこういうことが起きる。いい経験をしているなーと思います。

現段階での予測では、以下の状態だったのかもしれないなあと思ったりしてます。

  • vagrantは40GB
  • テーブルスペースの総容量は20GBちょっと
  • ストレージの空き容量は4GB程度
  • なんらかの理由でデータファイルを消した
    • 手で消してた?
  • またはmysql_upgrade実行時に復元できなかった
    • 容量不足?

12月15日(土) 2:10 追記

/var/log/mysqld.log を遡ってみると前から起きているエラーがあった。

  • そもそもibdファイルを作成できていなかった?
    • 過去のmysqld.logを遡ったら Cannot open datafile for read-only: './mamy1326/c_log.ibd' OS error: 71
    • そもそも去年の7月に環境作ったとき、ディスクフルで書き込めてなかった
      • 2017-07-07T22:01:02.800765Z 3 [ERROR] /usr/sbin/mysqld: The table 'c_log' is full
    • よってvagrantのbox容量を40Gに拡張したのち、リストアしていた
      • しかしそれでもibdファイルは正しく作られていなかったか、どこかで消えていた
      • 2017-09-21の時点でも同じように、ibdファイル読めないよ(ないよ)と言われていた
      • 2017-09-21T13:53:39.939744Z 0 [ERROR] InnoDB: Cannot open datafile for read-only: './mamy1326/c_log.ibd' OS error: 71
  • ログには別のエラーも出ていたが、MySQLの起動には成功していた
    • 例えばこれ
    • 2018-04-29T13:07:39.271664Z 3 [ERROR] InnoDB: Failed to find tablespace for tablemamy1326.c_login the cache. Attempting to load the tablespace with space id 139
    • このエラー出てる場合、MySQLの起動に失敗する事例しか検索できなかったけど、なんでだろう…
  • ibdファイルがないため、統計情報の計算ができません、と言われているので、この時点で欠落していた
    • 2018-04-29T13:08:18.405618Z 3 [Warning] InnoDB: Cannot calculate statistics for tablemamy1326.c_logbecause the .ibd file is missing. Please refer to http://dev.m ysql.com/doc/refman/5.7/en/innodb-troubleshooting.html for how to resolve the issue.

つまり、 アップグレード実行時に .ibdファイルが欠落していた ことがわかりました。

今後の進め方

幸いバックアップデータがあるので、リストアしてもう一度実行してみようと思います。

今度は、全てのibdファイルが揃っていることを確認した上で。

で、ストレージ容量不足なのか、データが悪いのか、順番に切り分けしてみようと思います。

いやーでも手動でibdファイルは消さないと思うんだよなあ…。

12月15日(土) 2:15 追記

最初から .ibd ファイルがなかったことが判明したので、ちょっと遠回りだけど以下の方法で再実行してみようと思います。

  • 5.7.19 の環境を作る
  • リストアする
  • .ibdファイルが揃っていることを確認する
    • 今回エラーのあったテーブルにSELECTできることも確認する
  • 5.7.24 へアップグレードする
  • 結果を確認する

んで、無事に 5.7.24 になったら、8.0.11 へさらにアップグレードしてみる計画です。

さて、明日もやるぞ!