【メモ】【読書】Clean Architecture 達人に学ぶソフトウェアの構造と設計 第Ⅰ部 第2章

はじめに

前回の続き huji-mori.hatenablog.com

第Ⅰ部

第2章 設計とアーキテクチャ

「振る舞い」と「構造」

まずは用語の確認。 振る舞い→システムの動作、機能を指す。フリーマーケットのサービスを例にあげると、「出品する」、「入金する」、「商品を表示する」などが「振る舞い」に相当する。 構造→アーキテクチャの「形状」を指す(構造の説明がされないままアーキテクチャの話が始まってしまったので正確な意味は分からない)。

ビジネスマネージャーのような非エンジニア層からは「振る舞い」に比べて「アーキテクチャ」の価値が軽視されていると問題提起している。

アイゼンハワーマトリックス

アイゼンハワーマトリックスに優先順位を付けると以下のようになると本書で述べている。

1.緊急かつ重要

2.緊急ではないが重要

3.緊急だが重要ではない

4.緊急でも重要でもない

コードのアーキテクチャは 1 と 2 (重要)、コードの振る舞い(緊急)は 1 と 3に位置するらしい。 ソフトウェアの開発者アーキテクチャの重要性のために戦わねばならないと述べられている(大変だ)。

以上。

【メモ】【読書】Clean Architecture 達人に学ぶソフトウェアの構造と設計 第Ⅰ部 第1章

はじめに

「Clean Architecture 達人に学ぶソフトウェアの構造と設計」は少し前にエンジニアたちの中で話題になった書籍です。 そろそろクリーンアーキテクチャの勉強を始めようと思い、その足掛かりとして本書を読み始めました

以下は読書メモになります。

第Ⅰ部

第1章 設計とアーキテクチャ

アーキテクチャ」と「設計」という言葉の定義について

第1章の冒頭では「アーキテクチャ」と「設計」という言葉について以下のように述べられていました。 - 一般的に「アーキテクチャ」とは下位レベルの詳細とは切り離された文脈で使用される傾向にある。

  • 一方で「設計」は下位レベルの構造や意思決定を表す文脈で使用される傾向にある。

  • これらの言葉に違いはなく、下位レベルの詳細と上位レベルの構造は全体の設計の一部となる。

↑について、自分の中でも「アーキテクチャ」と「設計」は繋がりのない言葉として認識していましたが、この章を読んで「確かに」となりました。

崩壊したコード vs クリーンなコード

崩壊したコードとは簡単に言えば開発スピード優先で機能を次から次へと追加した末のコードを意味する。 一方で、クリーンなコードとはテスト駆動開発(TDD)などのコードをクリーンにする規律を適用した作られたコードを意味する(クリーンなコード=TDDを適用したコードとは言ってないので注意)。 Jason GormanはTDDを取り入れた開発とそうでない開発の作業時間を計測して、TDDを取り入れた開発の方が作業時間が短かったという結果を本書で述べている。 ↑について、そういえばテストをしっかり書いた時の方が作業の出戻りは少なかったなぁとか思い出しました。

以上。

【メモ】他人が書いたソースコードを読むときのアレコレ

はじめに

業務中にソースコードを読んでいると、以下の問題が発生する。

  • 一つの機能を追いすぎる
  • そもそも自分が何を目的に読んでいたのか途中で分からなくなる/忘れる
  • オブジェクト間のデータの受け渡しの流れが分からなくなる

これらの問題をどうにか解消したいと思い、参考になりそう記事を探した。

後で自分が読み返すようにメモとして残しておく。

 

リンク集

qiita.com

www.itmedia.co.jp

medium.com

 

せっかくなので、自分がプライベートで使用頻度が高いGoのソースコードの読み方も調べた。

devblog.thebase.in

 

まとめ

とりあえずメモを取るところから始めようかな。。。

 

おまけ

実際に自分が業務でソースコードを読むにあたって、苦労したこともメモとして残しておく。

 

ドキュメントが無い or メンテナンスが止まっている

あるあるだと思います。

使用しているフレームワーク(自家製)、ライブラリ(自家製)のドキュメントが無かったりメンテナンスが止まっている。

原因不明のバグを踏んだ時は、ひたすらデバッグモードで動作を追いかけるしかなくなる。

 

(社内ツールとかの)ソースコードをプロジェクト・パッケージ単位で残しておいてくれない

複数のフォルダに分けて1ファイルずつネットワークドライブに保管していたりする。なんで?

 

ソースコードのコピーが大量に作られている

↓みたいなのも見かける。ちなみに最新の日付が最新とは限らない。

tool_A.sh

tool_A_20220819.sh

tool_A_20220820.sh

 

【メモ】asdf 経由でインストールした Go のパスを Visual Studio Code が読み込んでくれない現象

原因を調べていくうちに、以下の記事が見つかった。

blog.framinal.life

 

www.web-dev-qa-db-ja.com

 

bash 関連の諸々の設定をVisual Studio Code が読み込んでいないのが原因らしい。

自分の場合、Visual Studio Code に GOROOT の設定について怒られたていた。この問題を settings.json に GOROOT の設定を書くことで解決した。

>|json|

{
  ...
  "go.goroot": "/home/hujimori/.asdf/installs/golang/1.17.6/go",
  "go.gopath": "/home/hujimori/go"
}

||<

 

本当だったら、リンク先のように設定する方がいいのだろうか。とりあえず、問題は解決したので良しとする。

 

チーム「DREAM TEAM TRIANGLE」で ISUCON12 予選に参加しました

はじめに

チーム「DREAM TEAM TRIANGLE」で ISUCON 12 予選に参加しました(通算3回)。

まずは、このような素敵な大会を開催していただいた運営様に心より感謝申し上げます。

 

今年は例年より個人的な勉強に加えて一か月半ほど前からチーム内勉強会(週末に6~8時間/回  程度)を開いて準備を行ってきました。

気合を入れて予選に挑みましたが、本番ではうまくいかないことも多く、予選敗退と辛酸をなめる結果に終わってしまいました。

ですが、得られた学びも多く、それらを次に生かすためにも今回の参加記録を残したいと思います。

チームメンバー・役割

基本的は以下の役割で作業を進めました。

<hujimori(自分)>

・マニュアル理解

・アプリケーション改修

<4423>

・チーム創設以来、じっくりコトコト煮込み続けた秘伝のタレの作成者

・deploy 環境構築

<kakuden>

・DB周りのお世話

使用したツール・言語

・Slack → スロークエリ の解析結果、top の実行結果などを通知する先 

・New Relic  → ログ解析

・Pyroscope → アプリケーションのプロファイリング

・Discord → 連絡手段

・Go → 選択した言語

Visual Studio Code → コーディング、confファイルを弄る用

今回、私たちが使用したツールの中で注目すべきは Pyroscope です。

pprof と同じようにプロファイリングができます。 そして、UI がイケています。

Pyroscope 

詳細は下記を見てください。

pyroscope.io

 

最終スコア

最終スコアは 9417 でした。

順位は上から数えて 103 位でした。予選敗退とは言え、参加チーム数 600 超えの大会で 103 位という順位はまぁまぁ良いのでないかと思っています。

スコアの推移(17時点)

やったこと

10:00~11:00 準備・deploy 環境構築

<hujimori(自分)>

 問題の理解を進める。

→ 事前の勉強会で全く触らなかった SQLite に登場にビビる。

きっと、一護だって驚く

 

<4423>

deploy 環境の構築を進める。

 

<kakuden>

MySQL のバージョン、テーブルの構成を確認する。

 

11:00~12:00 準備最終段階

<hujimori(自分)>

自PCにインストールしていた Go のバージョンが古いせいでビルドエラーになることに気づく。

→ 大慌てで Go のバージョンを上げる(心臓が止まりかけた)。

 

<4423>

New Relic と Pyroscope を設定する。

→ NewRelic でログが出力されない問題が発生。

 

<kakuden>

スロウクエリにインデックスを張る。

 

12:00~15:00 本格的にチューニングを始める

<hujimori(自分)>

昼食をとる。

→ メンバー間で作業状況の確認をおこなう。

GET /api/player/competition/:competition_id/ranking が重かったので、API 内で大会のデータを毎回取得しているを部分をキャッシュしてみる。

→ キャッシュした結果、ベンチマークの整合性チェックが失敗する。

→ 終了した大会のデータもレスポンスしてしまったことが原因と分かる。

→ 解決方法が思いつかなかったので改善は断念。

 

<4423>

NewRelic でログの出力は断念。

→ tenantDB の connection pool を実装する。

public.pem を起動時に読み込むように修正する。

→ スコアが上がりだす。

 

<kakuden>

visit_history に index を張る作業を進める。

→ スコアには影響は出ず。

 

15:00~17;30

<hujimori(自分)>

POST /api/organizer/competition/:competition_id/score の for 文の中で INSERT クエリを呼び出している部分を見つける。

ソースコードを読むと、for 文の外にINSERTを切り出すだけで直ぐにBULK INSERT にできそうだと分かる。

→ BULK INSERT にする。

→ スコアが少し上がる。

 

<4423>

GET /api/player/player/:player_idをキャッシュを試みるが断念。

GET /api/player/competition/:competition_id/ranking をキャッシュする。その他いろいろな部分でキャッシュを試す。

→ 放置していた isu3 を使えるようにする。

→ 秘伝のタレ(設定ファイル)を反映する。

 

<kakuden>

isu3 を DB サーバー専用にする。

 

17;30~18;00

<hujimori(自分)>

悪あがきでマックスコネクションを弄ってみる。

→ スコア上がらず。むしろ下がる。

→ 燃え尽きる。

 

<4423>

モニタリング/デバッグ用設定を削除する。

→ スコアが上がる。

 

<kakuden>

燃え尽きる。

個人的に良かった点・反省点

良かった点

・INSERT クエリを BULK INSERT にできたこと(去年はそれすらもできなかった)。

・勉強会で事前に練習していたこと(ロードバランシング、アプリケーションサーバーとDB サーバーの分離)が実施できたこと。

・Pyroscope が見やすかった。来年も使う。

反省点

・Go のバージョンを事前に上げ忘れていたこと。

→ 準備不足。プログラミング言語に限らず競技で使うミドルウエアもバージョンは事前に確認しておくこと。

 

・マニュアルの理解が足りていない状態で、闇雲に作業をやりはじめたこと。

→ ISCUON 中にやるのはコスパが悪いのかもしれないが、アプリケーションの構成図やER図を書いてチーム内で共有しても良かったかもしれない。

 

・New Relic の操作に迷うことが多々あった。

→ 習熟度が足りない。

 

・競技時間が経過するにつれて、パフォーマンスの確認を怠りつつあった。

→ 作業に集中すると、気に掛けるなくなってしまう。。。

まとめ

私個人としてはアプリケーションの仕様を理解する能力が足りていないなと感じました。

事前学習では、N+1 の解消やキャッシュのコーディングなど、主にプログラミング面の勉強を重点的に行いました。そのせいか、予選前日までは「今回は結構やれちゃうんじゃない?」と内心調子に乗っていました。

しかし、いざ本番で問題を目の前にすると、どこから手を付けて良いか分からず途方に暮れていました。その結果、「良く分からんけど、なんとなく遅そうだからチューニングしてみる」と手当たり次第に作業を進めてしまい、スコアも大して上げることができませんでした。

ISUCON を戦う上でコーディングはもちろん、各種ミドルウェアの使い方、ログを分析するスキルは重要だと思っています。それらのスキルを活かすにはアプリケーションの仕様を理解してチューニングの方針を決めていくことが大切であると痛感しました。

 

今回の経験を糧にして、来年も頑張りたいと思います。

 

 

 

【メモ】【勉強】nginxでロードバランス先を出力する

nginxでロードバランスを行った際、
デフォルトの設定ではアクセスログにロードバランス先が出力されない。

ロードバランス先の出力方法を調べてみると、以下の記事が参考になったのでメモとして残しておく。
yapud.hatenablog.com

修正内容は以下の通りになります。
ポイントは、nginx.confでは複数のログフォーマットをそれぞれ別名で定義できること。

下記の場合だと、nginx宛のアクセスログのフォーマットを「main 」、
ロードバランス先のアクセスログのフォーマットを「upstreamlog」という名称で定義しています。

※まだ検証途中なので、nginxとロードバランス先のサーバーは同じインスタンスで動かしています。

worker_processes 1;

events {
        worker_connections 1024;
}

http {
        include mime.types;
        default_type application/octet-stream;

        log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" '
        access_log /var/log/nginx/access.log main;
        
        # 追記したログフォーマット
        log_format upstreamlog '[$time_local] $remote_addr $host $upstream_addr $request';
        access_log /var/log/nginx/upstream.log upstreamlog;

        upstream backends {
                keepalive 32;
                server 127.0.0.1:8080;

        }

        server {
                listen 80;
                server_name www.example.com;

                location / {
                        proxy_http_version 1.1;
                        proxy_set_header Connection "";
                        proxy_set_header Host $http_host;

                        proxy_pass http://backends;
                }
        }
}

以上。

【メモ】【勉強】EC2にnginxをインストール

下記コマンドでnginxをインストールする。

$ amazon-linux-extras | grep "nginx" 
38 nginx1 available [ =stable ] 

$ sudo amazon-linux-extras install nginx1
Installing nginx Loaded plugins: extras_suggestions, langpacks, priorities, update-motd Cleaning repos: amzn2-core amzn2extra-docker amzn2extra-kernel-5.10 amzn2extra-nginx1 0 metadata files removed
略


インストール後、下記コマンドでnginxを起動。

$ sudo systemctl start nginx

下記コマンドでEC2起動時に自動的にnginxを起動するように設定。

$ sudo systemctl start nginx

下記コマンドでStatusを確認。

$ systemctl status nginx
 ● nginx.service - The nginx HTTP and reverse proxy server Loaded
略

EC2のパブリックIPにブラウザからアクセスして確認。

f:id:huji_mori:20211228225027p:plain
ブラウザでnginxの起動を確認