読者です 読者をやめる 読者になる 読者になる

恥知らずのウェブエンジニア

これは一歩を踏み出すことができない者たちのブログ

phpmdでコード解析+チェックルールのカスタマイズしてみる

php phpmd

youngforever.hatenablog.com

前回のphpcsに続きコーディングの品質向上のためphpmdを使うようにしてみました。

PHPMD - PHP Mess Detector

phpmdは潜在的にバグになりそうなコードや改善の余地があるコードなどを検出してくれるツールです

phpmdインストール

公式通り、composer.jsonに下記を追記してcomposer installします

{
    "require-dev": {
        "phpmd/phpmd" : "*"
    }
}

使い方

コマンドラインで実行する際は下記のように実行できます。

phpmd /path/to/file xml codesize,naming,cleancode

xml以後のオプションで検出ルールを指定します。 ルールが設定してあるxmlvendor/phpmd/phpmd/src/main/resources/rulesets/以下にあります。

デフォルトでは下記があります。

またそれぞれのルールの実装はvendor/phpmd/phpmd/src/main/php/PHPMD/Rule/にあります。

なので、ルールのカスタマイズするには、新規にruleを定義したxmlを作成して、それに対するチェックの実装を行っていく感じになります。

カスタマイズルールを作ってみる

今回はtypoチェックを実装してみました。

まずカスタマイズルール用にxmlを作ります。

vi vendor/phpmd/phpmd/src/main/resources/rulesets/myStandard.xml


<?xml version="1.0"?>

<ruleset name="My Standard Rules">

    <description>
This ruleset contains a my standard rules.
    </description>

    <rule name="CheckTypo"
          since="0.2"
          message = "Maybe typo. {0} : {1} -> {2}"
          class="PHPMD\Rule\MyStandard\CheckTypo"
          externalInfoUrl="#">
        <description>
            <![CDATA[
Maybe typo.
            ]]>
        </description>
        <priority>1</priority>
        <properties />
        <example>
            <![CDATA[
protocal->protocol
recompence->recompense
            ]]>
        </example>
    </rule>
</ruleset>

上記のように定義して、PHPMD\Rule\MyStandard\CheckTypoにチェックルールの実装を行っていきます。

typo例はこちらのものを参考としました。

Wikipedia:Lists of common misspellings/For machines - Wikipedia, the free encyclopedia

既存の実装を元に下記のように実装してみました。 ./resources/typoList.csvtypo単語リストを置いています

vendor/phpmd/phpmd/src/main/php/PHPMD/Rule/MyStandard/CheckTypo.php


namespace PHPMD\Rule\MyStandard;

use PHPMD\AbstractNode;
use PHPMD\AbstractRule;
use PHPMD\Rule\FunctionAware;
use PHPMD\Rule\MethodAware;

/**
 * This rule CheckTypo.
 * typo check
 *
 */
class CheckTypo extends AbstractRule implements MethodAware, FunctionAware
{

    private static $typoList = [];

    /**
     * This method checks typo
     *
     * @param \PHPMD\AbstractNode $node
     * @return void
     */
    public function apply(AbstractNode $node)
    {
        $typoList = $this->getTypoList();

        foreach ($node->findChildrenOfType('Variable') as $variable) {
            $image = $variable->getImage();
            foreach ($typoList as $key => $value) {
                if (preg_match("/$key/", $image)) {
                    $this->addViolation($variable, array($image, $key, $value));
                    break;
                }
            }
        }
    }

    /**
     * get typoList
     * @return void
     */
    public function getTypoList()
    {
        if (self::$typoList) {
            return self::$typoList;
        }
        $typoList = [];
        $fp = fopen(dirname(__FILE__) . "/resources/typoList.csv", "r");
        while (!feof($fp)) {
                $tmpArr = fgetcsv($fp);
                $typoList[$tmpArr[0]] = $tmpArr[1];
        }
        self::$typoList = $typoList;
        fclose($fp);

        return self::$typoList;
    }
}

実行はこんな感じで、 phpmd /path/to/file xml myStandard

既存の実装を元になんとなくやってみましたが、変数名のtypo検出ができました!

こんな感じでカスタマイズルールを実装することでプロジェクト共通で品質向上やレビューの負荷軽減等していけたらと思いますん。

phpcsでカスタマイズしたコーディング規約をチェックする

php phpcs

正直なところ、今まであまり厳格に規約に沿ってコーディングしてきませんでした。

  • ある程度守っていればいいだろ
  • 統一させたいなら整形ツール的なやつで自動化すればいいじゃん

など思っていたんですが、OSSなど作っていきたいと考えた時、ちゃんと標準的なコードを書きたいと思い、コーディング規約をしっかり守りたいと考えるようになりました。

また何が標準かというのを理解するために整形ツールなどではなく、phpcsを使ってコーディング規約をチェックするようにしました。

phpcsインストール

github.com

PHP_CodeSnifferをインストールすることでphpcs,phpcbfなどがまるっと使えるようになります。

composer global require "squizlabs/php_codesniffer=*"

ドキュメント通り、composerでさくっとinstall

使い方、規約のルール指定

Usage · squizlabs/PHP_CodeSniffer Wiki · GitHub

phpcs --standard=PSR2 /path/to/file

などで指定したコーディング規約でチェックをすることができます。

規約ルールのカスタマイズ

現在のプロジェクトのコーディング規約はフレームワーク側の兼ね合いもありPSR2をベースにして、一部フレームワーク側の合わせたものになっています。

なので、そのままPSR2を適用すると意図しない規約のチェックが行われるので、PSR2をベースに一部をカスタマイズしたものを作成しました。

vendor/squizlabs/php_codesniffer/CodeSniffer/Standards以下に各種規約ルールがあります。

ここにカスタマイズした規約ルールを作成し、その規約を指定してphpcsを実行するイメージです。

//MyPSR2という規約ルールを作成する
mkdir vendor/squizlabs/php_codesniffer/CodeSniffer/Standards/MyPSR2 
vi vendor/squizlabs/php_codesniffer/CodeSniffer/Standards/MyPSR2/ruleset.xml

今回はPSR2をベースにインデントを4タブ、いくつかのルールを除外したものを作成しました。

exclude nameはphpcs -sなどで実行して除外したいルールを追加していく感じです

ruleset.xml


<?xml version="1.0"?>
<ruleset name="MyPSR2">
 <description>The MyPSR2 coding standard builds on the PSR2 coding standard.</description>
 <arg name="tab-width" value="4"/> <!-- タブ -->

 <exclude-pattern>*/Tests/*</exclude-pattern>

 <!-- Include the whole PSR2 standard except FunctionComment, which we override -->
 <rule ref="PSR2">
  <rule ref="Generic.WhiteSpace.DisallowSpaceIndent"/>
   <rule ref="Generic.WhiteSpace.ScopeIndent">
    <properties>
     <property name="indent" value="4"/>
      <property name="tabIndent" value="true"/>
     </properties>
   </rule>

  <exclude name="PSR1.Files.SideEffects.FoundWithSymbols"/> <!--  -->
  <exclude name="PSR2.Methods.FunctionCallSignature.Indent"/> <!--  -->
  <exclude name="PSR1.Methods.CamelCapsMethodName.NotCamelCaps"/> <!--  -->
  <exclude name="PSR1.Classes.ClassDeclaration.MissingNamespace"/> <!--  -->
  <exclude name="Generic.WhiteSpace.DisallowTabIndent.TabsUsed"/> <!--  -->
  <exclude name="Generic.WhiteSpace.DisallowTabIndent.NonIndentTabsUsed"/> <!--  -->
  <exclude name="Generic.WhiteSpace.ScopeIndent.Incorrect"/> <!--  -->
  <exclude name="Squiz.Classes.ValidClassName.NotCamelCaps"/> <!--  -->
 </rule>

</ruleset>

ルール作成後に、

phpcs --standard=MyPSR2 /path/to/file のように実行すればカスタマイズされたルールで規約のチェックをすることができます。

f:id:ogataka50:20160705182312p:plain

これでしっかりコーディング規約にそったコードを書いていけますね! 次はいちいちコマンドラインから実行するのもアレなので、エディタからコーディングチェックできるようにしていこうと思います。

New Relic ハンズオン・ワークショップ in Tokyoに行ってきた

newrelic

newrelic.com

行ってきてみました。

new relic使っているものの、transactionからどの処理が遅いか的な使い方しかしてなかったので。

f:id:ogataka50:20160531125042j:plain

スピーカーはNew Relic の Joe LoCascio さん。前編英語かと思いきやしっかり通訳の方がいらっしゃって一安心。

アジェンダ

f:id:ogataka50:20160531131604j:plain

ざっくりメモ

new relicの大枠の機能

  • APM

    • サーバー側のプロファイリング
  • BROWSER

    • フロントエンド側のプロファイリング
  • SYNTHETICS

    • サーバーの死活監視。ブラウザテスト的なこともできるらしい
  • MOBILE

    • ネイティブアプリのプロファイリング
  • SERVERS

    • サーバーのリソース監視
  • PLUGIN

  • INSIGHTS

    • new relicで収集したデータをSQL的な感じ抽出してデータ分析できる

apdex

  • レスポンスタイムからユーザ満足度を図った数値

  • 基準レスポンスタイムを設定し、そこから割合判定

  • apdex計算式

    • apdex = (satisfied + (tolerating / 2)) / total requests

    • satisfied -> 満足。レスポンスタイムが基準値以下

    • tolerating -> まあまあ。基準値の4倍以下

    • flustrated -> 不満足。基準値の4倍以上

key transaction

  • 特定のtransactionをkey transactionに設定することでより詳細な情報を取得できるようになる

  • x-ray sessionで関数レベルで分析できる

    • あとから調べたら現状Java, Python, Rubyのみ対応の模様・・・

error analytics

  • クラスごと、トランザクションごとグルーピングできる
  • error traceもできる
  • newrelicのモジュールを追加すれば、newrelic専用エラー出力もできる

newrelicのAPIでデプロイメントマーカーを設定できる。

  • リリース前後での変化を可視化

custom dashboard

  • 監視したい情報を自分用にピックアップできる

using alerts

  • アラートポリシー設定可能

  • アラート条件、通知チャンネル柔軟に設定可能

所感

  • やろうと思えばもろもろ監視系すべてイケるっぽい

    • でもすでにmuninとかもあったり、他の監視ツールとの住み分けなり統合なり考えないといけなそう
  • 色々できそうだけどnewrelicのオーバーヘッドはないのだろうか・・・

    • 過去にnew relicからマスターDBへ定期的にデータ取得してたりとかあった

    • 野良のプラグインとか要調査が必要そう

  • どうやらもろもろphp対応は後回しの模様・・・

    • 最近風当たりの強さを感じる
  • 頂いたnew relic Tシャツが意外とアグレッシブ

f:id:ogataka50:20160531215105j:plain

でした!

負荷かけず、ゆるやかにdrop tableを実行するスクリプトをつくってみた

php mysql

稼働中のサービスで安全にdrop tableする方法

こちらで記載した内容のことを実行するスクリプトをつくってみました

drop_table_gently

READMEに記載してある通りですが、

DBの設定情報を記載したini,とdropしたいテーブルを羅列したcsvを用意すれば、それにそってゆるやかにdrop tableする感じです。

ゆるやかにdropするおおまかな処理の流れ

  1. 実行前チェックとして、DBサーバーがリモート場合はsshできるか、data_fileがあるか確認を行う
  2. drop対象のテーブルのdumpファイルを保存
  3. dropするテーブルの関連データファイルに対して、ハードリンクを貼ります ハードリンクを貼ることでdrop時にデータファイルの同期的な削除を防ぎます
  4. drop tableを実行
  5. ハードリンクを貼ったファイルをtruncateコマンドで徐々にサイズを切り詰めた後に、rmを行います

中々強引にphpから外部コマンドを実行して,mydqldump,truncate,rm等々行っているので、事前にdry_runして実行されるコマンドを確認してから、実行してくださいませ・・・

crontabのエラー、(CRON) bad minuteはただの改行の場合でも出る

cron crontab

タイトルでもうほぼ終了なのですが、 crontabを更新した後に、cronのエラーログ/var/log/cronを確認すると下記のエラー

Feb 23 13:29:01 localhost crond[4436]: (xxxxxx) RELOAD (/var/spool/cron/xxxxxx)
Feb 23 13:29:01 localhost crond[4436]: (CRON) bad minute (/var/spool/cron/xxxxxx)
Feb 23 13:29:01 localhost crond[4436]: (CRON) bad minute (/var/spool/cron/xxxxxx)

書式間違っちゃったかなと思い、確認するも明らかに正しいはず・・・ 試しに全てコメントアウトしても上記のエラーが出る始末

よくよく見るとエラーの数と改行の数が一緒 crontabを改行なしにしてみると、エラーは出なくなりました

もうちょっとそれっぽいエラーにして欲しかった感。。。

golangでcrontabからドキュメントを作成するコマンドラインツール crondocを作った

golang cron go言語

javadoc,phpdoc的にcrontabからドキュメントを生成するツール
crondocをgolangでつくってみました

きっかけ

いくつかのサーバーの設定、ミドルウェア変更することになり、
影響範囲を確認していたのですが、サーバーごとにcronの運用が統一されていなかったのでcronまわり影響範囲を確認するのに時間がかかってしまいました

そこでcronの運用フローを統一する過程でcronの簡単なドキュメントを作成したくなりました

その際、javadoc,phpdocのcron版みたいのがないか探してみたのですが、ないようなのでせっかくなので自作してみました

crondocの機能

ざっくり下記のような機能で作ってみました

  • 標準入力、ファイル指定でcrontabからドキュメントを出力
  • 改行までをひとグループとしてドキュメント出力
  • ドキュメントを出力はmarkdowm形式
  • @authorなどのタグ付ができる

出力例

現状下記のようなcrontabの場合はこんな感じです

  • crontab
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/

# @title monday.sh
# @author hoge
# @param env[dev|stg|prod]
# start every monday
0 10 * * 1 sh /home/hoge/happy_monday.sh prod

↓↓↓

Environment variable

- SHELL=/bin/bash
- PATH=/sbin:/bin:/usr/sbin:/usr/bin
- MAILTO=root,hoge@mail.co.jp
- HOME=/

monday.sh

  • Author : hoge
  • param : env[dev|stg|prod]
  • start every monday
min hour day month day week command
0 10 * * Mon `sh /home/hoge/happy_monday.sh prod`

使い方

ソースはこちら
github.com


バイナリを落としてきて、下記のようにすればドキュメントが出力されるはずです

$ crodoc /etc/crontab
$ crontab -l | crodoc -s

まとめ

cron周りはあまり触る回数、人も多くなく属人化しやすい箇所なのでフロー、ドキュメント共有して見える化していければ


せっかく作るならコマンドっぽくしたいってだけでgolangで作ってみたのですが、
わからないこと多く時間かかったし、全然golang的な書き方できてないがいい経験になった。今後も継続!



感謝致します


f:id:ogataka50:20160219114823j:plain

続 サービス稼働中に不要なDBのテーブルを安全に削除する方法

mysql

前回の続編です。

youngforever.hatenablog.com

前回まで

前回はdropするテーブルのデータファイルにハードリンクを貼ることでdrop時にファイル削除されることを回避することができました。

しかしioniceで優先度を下げても、巨大なテーブルのデータファイルなどを削除する際はCPU使用率など見る限り負荷がかかっていました。 ioniceは緩やかに削除というより実行の順番の優先度を下げるという挙動のようです。

なので、次に負荷をかけずに巨大なファイルを削除するということで下記を参考にさせてもらいました。 www.nari64.com

truncateコマンドで徐々にファイルサイズを削り、小さくした後に削除するというやり方です。

安全にテーブルdropする手順

まとめて下記のような流れで処理しました

  • 削除前に念のため、dump取る
mysqldump DB_NAME hoge_table > dump_hoge_table.sql
  • 対象テーブルのデータファイルにハードリンクを貼る
ln /usr/local/mysql/data/DB_NAME/hoge_table.ibd /root/hoge_table.ibd
ln /usr/local/mysql/data/DB_NAME/hoge_table.frm /root/hoge_table.frm
  • drop tableを実行
mysql DB_NAME -e "DROP TABLE hoge_table"
  • ハードリンクを貼ったファイルをtruncateコマンドで徐々にサイズを削った後に、rmを実行
//ファイルサイズ取得
$file_size = 0;
$cmd    = "du -m {$file_path} | awk '{print $1}'";
exec($cmd, $output, $ret);
if($ret == 0) $file_size = $output[0];

//truncateで徐々に切り詰める
if($file_size){
  $i = 1;
  while ($i <= $file_size) {
    $tmp_size = $file_size - $i;
    $cmd    = "truncate -s {$tmp_size}M {$file_path}";
    exec($cmd, $output, $ret);
    //0.1sec sleep
    usleep(100000);
    $i++;
  }
//ファイル削除
$cmd = "rm {$file_path}";
$res = exec($cmd, $output, $ret);

的な感じで、稼働中の本番DBで百数テーブル、260GB程度削除しましたが、負荷なく実行できました。 そこそこ時間はかかりますが、、、truncateの部分はもっと攻めて良さそうですがそれはまた次の機会に・・・

これでどんどん不要なテーブル消せますね!

感謝致します。

f:id:ogataka50:20151020111355j:plain