ほげほげ見聞録

技術メモ、備忘録、使い方はそのうち覚える

MySQLのautocommitが原因でセーブポイントのエラーが出た時のメモ

事の始まり

PHPUnitでtestsuiteを実行したら以下エラーが発生。

PDOException: SQLSTATE[42000]: Syntax error or access violation: 1305 SAVEPOINT LEVEL2 does not exist


エラー自体は公式のマニュアルにある。
セーブポイントが削除されているのに操作すると発生…という事は分かった。
MySQL :: MySQL 5.6 リファレンスマニュアル :: 13.3.4 SAVEPOINT、ROLLBACK TO SAVEPOINT、および RELEASE SAVEPOINT 構文

原因調査

エラーが起きるのは以下のテスト。

DBにテストデータを作る
→DB操作のテスト
→テストデータ消去と「ALTER TABLE t1 AUTO_INCREMENT=1」実行

この流れの「DB操作のテスト」内に実行するコミットで前述のエラーが発生している。

発生条件は以下。

  • testsuiteで複数テストを実行
  • 最後の「ALTER TABLE」を実行(代わりに「UPDATE」など実行してもエラーは出ない)

条件を絞る

テスト範囲を絞ってみると、エラーが起きる流れが分かった。テスト2でトランザクションの開始が増えるとセーブポイントのレベルの数が変わる。

  1. テスト1でトランザクションの開始とセーブポイントの作成をして、コミットしないままALTER TABLEを実行
  2. テスト2でトランザクションの開始とセーブポイントの作成をして、コミット(ここでエラー)
    解決方法はテスト1でコミットを実行する事だと分かった。
    しかし、原因が分からない。

チョットデキル人に聞いてみる

Google先生に張り付いて検索頑張るのも良いけど、詳しい人に教えを乞うのもたまには良いんじゃないかなと。
TwitterMySQL Casual(http://mysql-casual.org/https://mysql-casual.slack.com/)というSlackが存在する事を知っていたので、参加して聞いてみることにした。

世界のyokuさんからの回答抜粋

ALTER TABLEは暗黙のコミットを引き起こすのでそれが原因かもしれません
MySQL :: MySQL 5.6 リファレンスマニュアル :: 13.3.3 暗黙的なコミットを発生させるステートメント


「暗黙のコミット」という機能があるらしい。
というわけで、フレームワーク内で起こっているのは以下の挙動のようだ。

テスト1でコミット時にセーブポイント1作成
→ALTER TABLEでMySQLの方でコミットされてセーブポイント1が消える(セーブポイントのレベルはフレームワーク内に残る)
→テスト2でセーブポイント2作成
→ネストしているトランザクションを順次コミットする際に、消えたセーブポイント2をRELEASEしようとしてエラー


また、ERROR 1305が発生する以下の一連のクエリも教えていただいた。
確かにALTER TABLE実行後にコミット(RELEASE)しようとしてERROR 1305が発生している。

mysql57 5> BEGIN;
Query OK, 0 rows affected (0.00 sec)

mysql57 5> SAVEPOINT LEVEL1;
Query OK, 0 rows affected (0.00 sec)

mysql57 5> ALTER TABLE t1 Engine = InnoDB;
Query OK, 0 rows affected (0.32 sec)

Records: 0  Duplicates: 0  Warnings: 0


mysql57 5> RELEASE SAVEPOINT LEVEL1;
ERROR 1305 (42000): SAVEPOINT LEVEL1 does not exist

mysql57 5> SAVEPOINT LEVEL2;
Query OK, 0 rows affected (0.00 sec)

mysql57 5> RELEASE SAVEPOINT LEVEL2;
ERROR 1305 (42000): SAVEPOINT LEVEL2 does not exist


んでもって、以下autocommitをオフにした場合のクエリを教えていただいた。

mysql57 5> SET autocommit= 0;
Query OK, 0 rows affected (0.07 sec)

mysql57 5> BEGIN;
Query OK, 0 rows affected (0.00 sec)

mysql57 5> SAVEPOINT LEVEL1;
Query OK, 0 rows affected (0.00 sec)

mysql57 5> ALTER TABLE t1 Engine = InnoDB;
Query OK, 0 rows affected (0.19 sec)

Records: 0  Duplicates: 0  Warnings: 0


mysql57 5> RELEASE SAVEPOINT LEVEL1;
ERROR 1305 (42000): SAVEPOINT LEVEL1 does not exist

mysql57 5> SAVEPOINT LEVEL2;
Query OK, 0 rows affected (0.00 sec)

mysql57 5> RELEASE SAVEPOINT LEVEL2;

Query OK, 0 rows affected (0.00 sec)


autocommitをオフにしてテストを実行してみる

テスト1のALTER TABLE前に`SET autocommit= 0`を実行したところ、テスト2のコミット時にセーブポイントのエラーが出なくなった。

参考

なにはともあれマニュアルを読む。
MySQL :: MySQL 5.6 リファレンスマニュアル :: 13.3.1 START TRANSACTION、COMMIT、および ROLLBACK 構文

autocommitの挙動の話。
MySQLのトランザクション制御がキモい話 - なからなLife
MySQLのAUTOCOMMIT(オートコミット)覚え書き | OpenGroove

autocommitはセッション内でしか有効にならないが、my.cnfでも設定できるらしい。
MySQLのautocommitとトランザクション分離レベルのメモ - Qiita

まとめ

何もない時は一般的なDB知識でクエリを作って実行させるだけだが、謎のエラーに出くわした時に困るので使用DBMSのマニュアルはこまめに確認しておこう。
関連コミュニティなどで詳しい人に助けを求めるととても勉強になる。

回答の詳細はMySQL Casualに参加すると見られるので、気になる人は登録するといいんじゃなかろうか。
自分の質問文は分かりにくいので、もっとうまく質問できるようになりたいなぁ。

PHPUnitをJenkinsから実行するメモ


Jenkinsのジョブを実行してPHPUnitを実行する設定のメモ。
更に、テスト実行のジョブを下流ジョブに設定してメールの送信も試す。

例によってフレームワークFuelPHP
デプロイ前にテストを実行出来たら良いのだろうけど、Jenkinsのあるサーバの環境をいじれなかったので流れは以下。

  • developの内容をテスト環境にデプロイ
  • テスト環境上でPHPUnitを実行

そもそもテスト環境でPHPUnitを実行できるか

SSH使ってテストサイトで「php oil test --testsuite=app」を実行。
エラー発生。
「fuel/vendor/bin/phpunit: 許可がありません」

以下のファイルに権限の設定をする必要がある。
chmod 755なら問題ないだろう。


再度実行すると、oilパッケージ内で以下エラー。
「Tests Running...This may take a few moments.: そのようなファイルやディレクトリはありません」

エラーが出ているのはoilパッケージ内のpassthru()で実行しているコマンド。
「fuel/vendor/bin/phpunit -c "fuel/app/phpunit.xml"」

statで確認したが、二つとも存在。どちらもアクセス権は問題ないようだ。
実際に問題だったのは以下のファイル。

これらを色々いじっていたら、上書きした時に「php oil test」できることが判明する。
はて面妖なと考えていると、再び「そのようなファイルやディレクトリはありません」エラーが発生。
どうやらJenkinsが動いたので、ファイルを上書きされたらしい。

アップロードする方法によってテスト実行できるか試す。

  • FTPでアップロード:テスト実行可能
  • Jenkinsでアップロード:エラー発生

FTPでアップロードとJenkinsでアップロードしたファイルの違い

FTPでアップロード

$ stat fuel/vendor/bin/phpunit
  Size: 603             Blocks: 8          IO Block: 4096   通常ファイル


Jenkinsでアップロード

$ stat fuel/vendor/bin/phpunit
  Size: 620             Blocks: 8          IO Block: 4096   通常ファイル

なんかファイルサイズ増えてるんですけど……。
この現象はもう一つのファイル「fuel/vendor/phpunit/phpunit/phpunit」でも発生していた。

アップロード方法によらず、ファイルの文字列は同じ。
ということは、改行コードの問題だろうと分かる。

ちなみに、Backlogのリポジトリから取得したファイルはFTPでアップロードしたものと同サイズ。
つまり、Jenkinsでデプロイする際に一度ワークスペースにファイルを落としてきているのが原因ではないか。
実際に確認すると、ワークスペースのファイルの改行コードはCR+LF、リポジトリはLF。CR+LF→LFに変換するとリポジトリと同サイズになる。

どうやら、Gitの改行コード変換の問題らしい。
Git - Git の設定

Jenkinsのサーバで以下のコマンドを実行。

git config --global core.autoCRLF false


再度デプロイした後にPHPUnitを実行すると、無事に実行できた。

JenkinsジョブからPHPUnitを実行

ファイルのアップロードは済んでいる状態。
別ホストのテスト環境にログインするには「SSH Plugin」使うと楽。

認証情報の追加は、システムの設定 > SSHリモートホスト > 追加。
テスト接続で「Successfull connection」が出れば良し。

ジョブの設定で、ビルド > リモートホストでシェルを実行を追加。
SHリモートホストに追加したサイトを選択。

cd 【oilパス】
php oil test --testsuite=app

ビルド実行してログに「[SSH] exit-status: 0」出力されていれば成功。

パイプラインから下流プロジェクト実行

外部から呼び出すのはパイプラインのジョブなので、これにPHPUnitを実行するジョブを組み込む。
デプロイが成功していればテストを実行するという流れ。

ビルドの成功チェックは以下で可能。

def jobBuild = build job
def result = jobBuild.getResult()

if (result == "SUCCESS") {
}

Google グループ

パイプラインだとエラー時の動作設定がないのでscript中に追記。
Cleaning up and notifications

Scripted Pipeline(nodeで囲む)でなくDeclarative Pipeline(pipelineで囲む)使うと、エラー時の動作などかけるので便利。
Pipeline Syntax
Pipeline Syntax

Declarative PipelineでJenkinsfileを書いてみた(Checkstyle,Findbugs,PMD,CPDとか) - Qiita

というわけでスクリプトの流れ。

def jobResult

pipeline {
    agent any

    stages {
        stage('deproy') {
            steps {
                script {
                    // デプロイ
                    build Deploy
                }
            }
        }
        stage('PHPUnit') {
            steps {
                script {
                    // テスト実行
                    build Test
                    // テスト結果をメール
                    def consoleURL = "${env.JENKINS_URL}/job/【ジョブ名】/${env.BUILD_NUMBER}/consoleText"
                    def data = new URL(consoleURL).getText()
                    mail to: '【アドレス】',
                         subject: 'test',
                         body: data
                }
            }
        }
    }

    post {
        failure {
            script {
                //エラーメール送信
            }
        }
    }
}



変数

pipelineを使うDeclarative Pipelineを調べていると、変数をenvironmentで宣言するなどという情報が出てくる。
試してみたが、environmentで宣言したものは定数で後から変更できなかった。
変数宣言はpipelineの外で行わないとエラーが出る。
ぶっちゃけdefなしだとglobalな変数になるようなので、宣言しない手もある。

The Apache Groovy programming language - Documentation

テスト結果のメール

PHPUnitを実行すると、以下のようなメールが飛ぶ。
プラグイン導入してカバレッジ出す方が良さそうな気もするが、とりあえずどの程度テストが実行されているかのチェックはしやすい。

[SSH] script:

php oil test --testsuite=app

[SSH] executing...
[0;32mTests Running...This may take a few moments.[0m
PHPUnit 6.0.0 by Sebastian Bergmann and contributors.

.......................S...SSSSSS  33 / 156 ( 21%)
...RR..RRR
[SSH] completed
[SSH] exit-status: 0

Finished: SUCCESS
-------------------------

テスト結果が途中で途切れるのは、リダイレクトが実行された場合など。
最後まで到達してテストが失敗になっていないと、失敗として認識されないらしい。

余談:Jenkinsからメールを飛ばす設定

ビルドの成功失敗やテストの結果など、毎回Jenkinsを開いて確認するのは面倒。
そこで、Jenkinsの管理でメール送信の設定をする。

envelope-from(return-path)は、Jenkinsの位置 > システム管理者のメールアドレスに設定。
紛らわしいが、E-mail 通知 > 返信先アドレスではないので注意。

E-mail 通知の項目で入力値を使ったテスト送信ができる。
ただしテスト送信の結果が正常でも、envelope-from(return-path)が存在しないアドレスだとエラーになってメールが届かない場合がある。
インフラの人曰く、ダブルバウンスが発生するのだそうだ。
Bounce address - Wikipedia
Double bounce

この設定をすると、各ジョブでビルド失敗時などにメールを飛ばすことができる。

Jenkinsに何かとPOSTする簡易サーバを作るメモ


前に書いたBacklogからJenkinsを実行する記事の補足。
Backlogの情報をJenkinsに投げる部分についてメモ。
dwmemo.hatenablog.com

前回までのあらすじ

JenkinsのCSRFチェックを外さずにジョブを走らせたい、しかしBacklogからのPOST値取得にはcrumbも一緒にPOSTしなければならなかった…。
そこで、簡易サーバを噛ませてJenkinsを走らせるという遠回りな方法を選んだのであった。
別に簡易サーバ作ってみたかったとかそういうわけではない。

流れ

  • Backlogでリポジトリへのプッシュを検知、簡易サーバに情報をPOST
  • 簡易サーバでJenkinsのcrumbを取得、Backlogの情報と併せてJenkinsにPOST
  • Jenkinsで簡易サーバの投げたデータを取得、解析して各種ジョブを実行

Node.js簡易サーバを作る

Node.jsで作ると簡単ダヨーと聞いたので、Node.jsを使う。
ドキュメント読みつつ各種サイト見つつ見よう見まねなのでエラー処理とか真面目にやってない。

とりあえず簡易サーバ作る

画面に「Hello Server!」を表示するだけのものを作成。

var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello Server!\n');
}).listen(【サーバのポート】, '0.0.0.0'); // localアクセスだけなら127.0.0.1でも可

まずはサーバのローカルアクセスできるか確認。
http://127.0.0.1:【サーバのポート】

次に、外部から該当ポートを使用できるようFireWallに設定追加。
外部からアクセスできるか確認。
http://【ホスト】:【サーバのポート】

これを元に、POSTデータの取得、crumbの取得、取得データをまとめてJenkinsに投げる…を行う。

POSTデータの取得

var http = require('http');
var querystring = require('querystring');

http.createServer(function (req, res) {
    var body = '';
    var jsonString = '';

    if (req.method != 'POST') {
        res.write('Fail!\n');
        res.end();
        req.connection.destroy();
        return;
    }

    req.on('data', function (data) {
        body += data;
    });

    req.on('end', function () {
        var post = querystring.parse(body);
        jsonString = post.payload;    // BacklogのJSONはpayloadに入っている

        try {
           jsonString = JSON.parse(post.payload);
           res.writeHead(200, {'Content-Type': 'text/plain'});
           res.write('Hello Server!\n');
        } catch (err) {
           res.writeHead(500, {'Content-Type': 'text/plain'});
           res.write('Error.\n');
           res.end();
           req.connection.destroy();
           return;
        }

        res.end();
    });

}).listen(【サーバのポート】, '0.0.0.0');

先程試した「http://【ホスト】:【サーバのポート】」に「payload["data":"{}"]」をPOSTして、200が帰ってくるか確認。

crumbの取得

crumbは以下のURLから取得する必要がある。

【JenkinsURL】/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,%22:%22,//crumb)

安直にhttp.getで取得してみると、この処理を待たずに先の処理を実行する事態が発生。

javascript - NodeJS wait for HTTP request - Stack Overflow

Promiseを使うと非同期処理できるらしい。
「module request-promise」はnpmでインストール。

npm install --save request
npm install --save request-promise

crumb取得URLを叩いて、取得出来たらthen句を実行するという流れ。 promiseの外に書くと、上と同じで処理を待たずに実行してしまう。

        promise('【JenkinsURL】/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,%22:%22,//crumb)')
            .then(token => {
                var crumb = parse_crumb(token);    // crumb部分を取得する適当な関数

                const options = {
                    method: 'POST',
                    hostname: 【Jenkinsホスト】,
                    port: 【Jenkinsポート】,
                    path: '/job/【ジョブ名】/buildWithParameters?payload=' + encodeURIComponent(post.payload),
                    headers: {
                        'Content-Type': 'multipart/form-data',
                        'Jenkins-Crumb': crumb    // k-vでなくtokenそのまま書けば行けるかもしれない
                    }
                };

「http://【ホスト】:【サーバのポート】」に「payload["data":"{}"]」をPOSTして、Jenkinsが実行されpayloadに値が入っているか確認。
プッシュしたブランチの情報などはここで整形して投げるのもありだが、簡易サーバが止まったりする場合を考えてPOST受け取り・投げる以上の事はしない方が良さそう。

余談:簡易サーバを起動させ続ける

Node.jsのサーバを起動させ続けるにはどうするか、forever使うとできるらしい。
GitHub - foreverjs/forever: A simple CLI tool for ensuring that a given script runs continuously (i.e. forever)
公式の通り導入したら起動させたいファイルを指定すれば終わり。

forever server.js


わざわざ調べたが、リモートアクセス使うので普通にコマンドから起動して放置で良いのではと考えて使用は止めた。

Promise参考

作業中に見たり、後から調べたりして参考になったURL。

ASP.Netのメモ

ASP.Netを使うことがあったので、その時に詰まった部分のメモ。ほんと只のメモ。

.aspx側で対応する.csのプロパティ表示

.csのクラスのプロパティをpublicで宣言(privateだとダメ)。

  • エスケープする<%: Id %>
  • エスケープしない:<%= Id %>

URLパラメータ取得

Request.Querystring["param"]を使う。
「?param=」がないとnullになる。
How to get parameter in url ( by C# for .net) | The ASP.NET Forums

URLに危険な文字列を入れた場合

例えば「http://【パス】?【クエリ】=<script>alert()<」を入力してアクセスすると、以下のエラー発生。
HttpRequestValidationException (0x80004005): 危険な可能性のある Request.QueryString 値がクライアント (ak="<script>alert()<") から検出されました。

このエラーはRequest.Querystringを取得する部分でtry-catchしても引っかからない。
Global.aspxの方でcatchする必要があるらしい。
c# - How to catch HttpRequestValidationException in production - Stack Overflow

Console.Write()が出力ウィンドウに出ない

今回はWebアプリだったので、出力できないらしい。
c# - Console.WriteLine does not show up in Output window - Stack Overflow
Possible to output to console from within a class library C#? - Stack Overflow

VS起動時エラー

Visual Studio は例外を検出しました。拡張機能が原因である可能性があります。」

C:\Users\【ユーザ】\AppData\Roaming\Microsoft\VisualStudio\14.0\ActivityLog.xml
    <type>Error</type>
    <source>Editor or Editor Extension</source>
    <description>System.IO.FileNotFoundException: &#x30D5;&#x30A1;&#x30A4;&#x30EB;&#x307E;&#x305F;&#x306F;&#x30A2;&#x30BB;&#x30F3;&#x30D6;&#x30EA; &apos;Microsoft.VisualStu→dio.Data.Tools.Delta.UI,


Tmp削除すると直るという情報があったが、Tmp削除後もエラー出る。
Visual Studio が起動時にエラーがでるようになった&直した - Qiita

結局、インストーラで修復実行したらエラー消えた。
c# - Could not load file or assembly 'Microsoft.VisualStudio.Data.Tools.SqlEditor.dll' - Stack Overflow

GridViewのページングリンク

自動でページングリンクを作成するが、リンク押下時のイベントを設定しておかないとエラーが出る。
勝手に作ってくれないの…。

「GridView 'GridView1' はハンドルされていないイベント PageIndexChanging を送出しました。」
OnPageIndexChangingの設定を.aspxと.csに追加。

PHPStanを試してみた

PHPerKaigiで紹介されていたPHPStanが面白そうだったので試してみた。
昨日今日でざっくりお試しなのでまだ設定できる部分とかありそう。

PHPDocが書いてあるプロジェクトにcomposer requireしてみて、躓いた点等ざっくりメモ。

必要なもの

PHP7.1。環境パスは通した。
あとは実行したいプロジェクトで「composer require --dev phpstan/phpstan」するだけ。
PHPStanのREADME:phpstan/README.md at master · phpstan/phpstan · GitHub
※追記
試しで使う場合は、COMPOSER_HOMEにグローバルインストール方がやりやすそうだ。
実行コマンドにbootstrapファイルを指定する必要はあるが、複数のプロジェクトに使ってみたいときは既にあるファイルをいじらなくて良い。

バージョン確認。

vendor/bin/phpstan -v

phpstan.neon

設定ファイルはvendor/phpstan/phpstan/confフォルダにある。
phpstan/README.md at master · phpstan/phpstan · GitHub

この中で間違いがあると何のメッセージもなく終了するので少しやりづらい。
autoload_directoriesとautoload_filesで使う%rootDir%はvendor/phpstan/phpstanにある。vendor/binだと思ってたら違った…。

とりあえず解析実行

vendor/bin/phpstan test.php

test.phpが以下のようなクラスのない場合はグリーンバー。

<?php
echo 'hello world!';


test.phpがクラスのある場合は「Class not found autoloadがどうのこうの」エラーが出た。

<?php
class test {
    function hoge() {
    }
}


autoloadで読み込まないといけないらしい。
Class not found (in same class file) · Issue #661 · phpstan/phpstan · GitHub

それぞれコードが長い8ファイルを対象にしたら、途中で以下エラー発生。

 ! [NOTE] PHPStan crashed in the previous run probably because of excessive memory consumption.
It consumed around 20 MB
 !        of memory.

To avoid this issue, allow to use more memory with the --memory-limit option.

エラーが多すぎてダメだったのかもしれない(GitHubのissueでとりあえずメモリエラーを吐くとか書いてあった気がする)。
一ファイルだけ対象にすると最後まで解析実行され、エラーも出た。

とりあえずbootstrapを用意

設定ファイルにbootstrapを設定できるので、PHPStan用に作成。autoload_filesに指定でも問題なかった。

bootstrap: %rootDir%/../../../phpstan-bootstrap.php


ちゃんとパス設定しておかないと以下のようなエラーが出る。
「Error - Package 'parser' could not be found at '/parser/' in core/classes/package.php on line 89」

プロジェクト全体の解析実行

coreとかその他を省いた部分に関して解析してみた。
行数の多いファイルが300個くらいだと、途中まででも結構時間が掛かった。

途中でFatal Error発生

当然ながら止まる。

時間かかりすぎる

止まる。
「Fatal error: Maximum execution time of 300 seconds exceeded in vendor\phpstan\phpstan\src\Analyser\Scope.php on line 1654」

bootstrapに「ini_set('MAX_EXECUTION_TIME', -1);」を設定してみた。
やっぱり止まる。
「Fatal Error - Maximum execution time of 300 seconds exceeded in vendor/nikic/php-parser/lib/PhpParser/PrettyPrinter/Standard.php on line 940」
出ている場所が違うが、エラーの出力が多いと面倒なので対象フォルダを分けた方が良さそうだ。

50ファイルを対象にしたら全部解析できた。

関数名のチェックで怒られる

「Call to method Imagick::readimage() with incorrect case: readImage」
マニュアル読む限りreadImage()が正しそうだが、readimage()にしろというエラーが出た。
PHP: Imagick::readImage - Manual
不明点。

※追記
コメントいただいて判明。
なんと、pecl-imagickのメソッド名はすべて小文字で定義されているそうだ。
imagick/imagick_class.c at 3acc635fe321bde7e2154f0cd01e6aa7d970bc3f · mkoppanen/imagick · GitHub
何のためのマニュアルだよぅ!
そういえばクラス名をImagickではなくimagickにしてるとエラーが出ていた。こういう、PHPだと実行・IDEでスルーされるエラーも見てくれるのは良いなぁ。

使い心地

bootstrapはPHPUnitのを使いまわしですぐに作れるし、導入は比較的容易、コマンド一つで解析できるのはとても良いと思った。

また、パッケージ導入忘れのエラーに気が付く事ができた。
例えばパッケージAはパッケージBをextendしているが、必要なのはAだけでBを導入しなくても何故かエラー出なかった場合など。
※追記
どうやらclass_exists()でどちらかのパッケージがあるか判断している部分で、AとB両方チェックしてしまっているらしい。
これに関してはエラーを除外するとか、そもそもパッケージのファイルまで解析しにいかないとか対応が必要そうだ。

困った事としては、stringのパラメータの関数でNULLチェックをする場合に「Call to function is_null() will always evaluate to false.」が出るという点。
@param string $valの場合、stringだからNULLはあり得ないという事だと思われる…。
明示的or予期せずNULLを入れる場合がありえるのでNULLチェックは入れておきたい。
ignoreErrorsとかで弾いたり、エラーレベルを下げたりするのが良いのかもしれない。

composerのパッケージが増えるのでプロジェクトに組み込んでもらえるかは怪しいが、様子を見て提案してみようかなと思った。既にあるプロジェクトでCIに組み込むのはハードルが高いかも。
あとはレベル5くらいで結構な数のエラーが出たので、どこのタイミングで直せるかなという問題。

PHPerKaigi 2018本編に行ってきた

行ってきた

dwmemo.hatenablog.com というわけで続き。


内容

本編の発表は六つ(とランチセッション)とLT。
前夜祭が行われたホールでは発表が行われるTrack A、隣ではコミュニケーション用のTrack Bの二面構成になっていた。
聞けた発表メインにメモを書いていく。

今からでも出来る!Webサービスモニタリング!!

紹介文

スピーカー:曽根 壮大(@soudai1025)さん

サービスを作ることの大切さは皆さんよく知っているとおもいます。 しかし如何に育てるか、如何に運用していくかはあまり語られていないのではないでしょうか? そこで今回は - サービスの監視とはなにか? - 運用とはなにか? - どのようにして未来を予測していくか に注目してサービスのモニタリングについてお話します。 この話を聞いた皆さんは明日からサービスの見え方がかわるはず!?

speakerdeck.com soudai.hatenablog.com

モニタリングは大事だよという話。
モニタリングが必要な理由は、素早く障害に気付く為、原因を究明する為、未然に防ぐ為…なのだそう。
自分はインフラ担当でないので、動き始めたサービスがどんな状態かを知らされない。この発表で、サービスを大きな「生き物」として見る必要があると分かった。
モニタリングするにも、サービスを構成する層は複数あるので各所やりやすさが異なる。クライアント部分での不具合は環境が再現できない場合が多いので自分もよく悩まされる。
「リソースを正しく使えているか(不足しても余らせてもダメ)」という話は、PG目線だと気にしない点だった。
また、誰も見ないグラフは意味がないから消す(データだけは取る)というのも、大事だなぁと思った。思い切りが必要だと思うけど。
まとめ「テストコードはプログラムの品質の可視化、モニタリングはサービス品質の可視化」!

スライド中参考ブログ:2015年Webサーバアーキテクチャ序論 - ゆううきブログ

SOLIDの原則って、どんなふうに使うの?

紹介文

スピーカー:後藤秀宣(@hidenorigoto)さん

オブジェクト指向プログラミングを勉強したことのある人なら、一度は聞いたことのあるSOLIDの原則。 特にオープン・クローズドの原則って、意味が分からない! という感想を持った方は少なからずいらっしゃるかと思います。 この講演では、PHPのコード例を示しながら、どのような状況でSOLIDの原則を持ち出すのか、および、原則を適用するとコードがどのように変化するのかを、分かりやすくお話します。

speakerdeck.com blog.hidenorigoto.com

SOLID原則が「良い設計」をよく反映しているという前提で、新人PHPerのコードをリファクタリングしていきながらSOLID原則の有用性を見ていくストーリー仕立ての発表。
どうしてコードが読みにくくなっているのか、どのポイントに注意するとSOLID原則に準拠したコードが書けるのかが分かりやすかった。
時間にせかされてしまうとついついSOLID原則を考えるのが疎かになるし、思ってもみなかったところがバリエーションになるプロジェクトにいるので、今一度勉強しておきたいなぁ。
自分はFactoryパターン使う場合が殆どなので、対応の判定を各クラスに持たせるのが良いかもなぁと思った。

ランチセッション 株式会社ORATTA

スピーカー:渡辺さん
speakerdeck.com

海鮮丼美味しかったです!! ごちそうさまでした!!!
ランチ食べながらの、宣伝とのろけ話が挟まる発表。
バイナリセーフかどうかは罠が多いから気を付けないとなぁ。
いつか自分にもWBMPを使う機会が来るんだろうか…。

サーバーが完膚なきまでに死んでもMySQLのデータを失わないための表技

紹介文

スピーカー:yoku0825(@yoku0825)さん

MySQLのバックアップの話をします! - バックアップ手法のおさらい - mysqldump, 物理コピー, .. - 複数のバックアップ手法を組み合わせて運用する - 日々のバックアップにまつわるタスク - どこがどう壊れた時にどうリストアするか - ぼくがかんがえたさいきょうのサーバー1台で自作サービスを作ってる時のちょっとのコストで実現するバックアップのしくみ - ※個人の感じ方によります

www.slideshare.net

スライドの背景の可愛い子が気になって読むのに時間がかかってしまう ><
※追記
コメントいただいて判明。
日本MySQLユーザ会の「非公式な」キャラクター、舞奈たん!
https://github.com/yoku0825/MyNA/tree/master/unofficial_myna_girl

バックアップにも種類があるんだな、沢山あってそれぞれ一長一短みたいだ。
リプレイとはなんぞや、専門用語ではなくログのクエリを再実行する…という使われ方をしている?
グローバルトランザクション識別子 (GTID)とは、各トランザクションに関連付けられる一意識別子だそうで。なるほど分からん。
DB周りはクエリを走らせるといった基本的な部分でしか使っていないので、バックアップという運用面での話は新鮮だった。

参考URL

発表内容の発端の記事:ログの復旧を実施しました – お題箱ブログ
関数を使う場合のリプレイ参考:日々の覚書: MySQLのNOW関数はどのようにして安全にスレーブでリプレイされるのか
GTID :MySQL :: MySQL 5.6 リファレンスマニュアル :: 17.1.3.1 GTID の概念

Hackで作るマイクロフレームワーク

紹介文

スピーカー:竹澤有貴(@ex_takezawa)さん

PSRを活用して、phpのライブラリとの互換性をもたせながら、 自由に入れ替えが可能なライブラリ、コンポーネント作りの作法、 Hackのタイプチェッカーと厳格モードとの付き合い方、 async awaitのハマりどころなどを共有し、 Hackにチャレンジしたくなる内容をお届けします

speakerdeck.com ytake.hateblo.jp

Hackは型チェックがちゃんとしているのがやはり売りなんだなぁ。
HHVM(HipHop Virtual Machine)なんでHipHopなんだろう?
Hackの為のPSRがあるというのは驚きだった。これからどんな独自進化を遂げていくんだろうか…。
自分は型が厳格なのが良ければC#とか他の言語使っちゃえばいいんじゃね…と思ってしまうが、PHPの延長線でできるというのは良いことかもしれないなと思った。特に初学者とか、PHPに慣れ過ぎて他の言語でギャップに苦しむ事がなさそう。

参考URL

Hack:HHVM/Hackはじめの一歩 - ytake Hatena
HHVM:HipHop Virtual Machine - Wikipedia
HHVM:Hack: a new programming language for HHVM | Engineering Blog | Facebook Code | Facebook

BEAR.Sunday

紹介文

スピーカー:郡山昭仁(@koriym)

DI、AOP、およびRESTは開発に役立つ強力なパターンです。BEAR.Sundayフレームワークは大規模PHPアプリケーションにも適用できる強力なアーキテクチャの基礎として採用し、アプリケーションにシンプルな美しさとパワーを提供します。このセッションでは3つのコア技術を説明しながら、フレームワークでどのように適用しているかを紹介します。

speakerdeck.com

この発表はTrack Bに行っていたので聞けなかった、残念…。以下スライドを読んでのメモ。
BEAR.Sundayって何ぞや、PHPのWebアプリケーションフレームワークらしい。
API駆動開発というものあるのか初めて知った。
発表の録画とかしてあったら聞いてみたいなぁ。

参考URL

BEAR.Sunday:BEAR.Sunday


Interactive Round Table テーマ2:DevOps/テスト/CI

司会者:渡辺一宏(@kaz_29)さん
(自動)テストの事が気になったので、ディスカッションメインのTrack Bに行ってみた。
1セッション20分で自分は30分程参加したが、あっという間に時間が過ぎていて驚いた。
テストを上手く回している人、回せていない人それぞれの話が聞けて良かった。
以下は出てきた話題のメモ。

  • テストを書く習慣がなく理解もないチームでテストを理解してもらうには?(書いたテストが間違っていたら意味がないと思われている)
    • あくまでツールとして使う事を分かってもらう
    • コードレビュー時にテストがないコミットをチェック、テストが正しいかチェック

テスト書くのが当たり前でない会社にいるので、以前行ったTDD読書会と同様に「この世界にはテスト書いている人類がいるんだ…良かった」みたいな気持ちになれた。
工数の事とか言われると下っ端は何も言えないので、どうにかこうにか働きかけるしかないのが悲しいところ。

  • メンバーが少ない場合にコードレビューをどうするか、皆が同じレベルでレビューが表層だけになってしまう場合

テックリード、聞いたことはあるけど自分の所にはないので調べてみた。
CTOの進化前みたいな感じだろうか。
テックリードという役割 – Shimpei Takamatsu – Medium

  • レビューのやり取りがそっけないと他の部署から仲が悪いのかと言われる
    • 絵文字やLGTMを使う
    • 修正してもらったらちゃんと返信する

仕事で絵文字はちょっと…と普段から思っていたが、文字だけだと情報量足りなくてきつめに見えてしまうのはあるかもなと。
こちらの修正、修正依頼に対する返答にお礼言うのはレビュー続きだと億劫になるけど、今後も続けた方が良さそうだ。

  • PHPUnitだとstaticメソッドのモックは作れないが、何か方法は?
    • Mockery使うとか
    • staticメソッド作らないのが手っ取り早い
    • 結局はテストしやすい設計にしていくしかない(ルンバが掃除しやすいように家具を配置するのと同じ)

テストを書き始める前から続いている案件をいじっていると、テストし難い事し難い事…。
DB周りとかFWの関数とかはstaticが殆どでどうしようかと思っていたけど、Mockery使いやすそうだし調査してみよう。
他にもrunkit等、真面目に探すとモック系は意外とあるんだなぁ。

  • Seleniumなどで画面の自動テストをした方が良いのか
    • 維持が大変、文言変わっただけでもテストを修正する必要あり
    • 最近は細かい動きにも対応してきたが、人力で確認した方が良い場合もある

確かに参加中の案件でも最初はSeleniumのテスト作っていたけど、FireFox未対応になったり開発が落ち着いたりして忘れ去られている。
FireFoxSeleniumは開発止まったらしいけど、Chromeは未だ息しているらしい。

  • CIやったらテストも走らせるようにした方が良い

この間Jenkinsおじさんをプロジェクトに組み込んだので、次はテストかな…。

PHPStanで始める継続的型検査

紹介文

スピーカー:hiraku(@Hiraku)さん

PHP7から戻り値の型も記載できるようになり、より静的解析をしやすくなったPHPですが、IDEやエディタの中だけでなく、CIのフローの中でも型検査を行いたいものです。 このトークでは、メルカリ社でも活用しているPHPStanというツールを取り上げ、型検査をCIの中で実行する方法や、今のPHPにおける型システムの解説を行います。

speakerdeck.com tech.mercari.com

コード解析はプログラムを動かして読む(dynamic)か動かさずに読む(static)かという分類。
同じ静的解析のSonarQubeは導入検討していたが、PHPStanは名前を初めて聞いた。
stableとdevでオプションに違いあり、設定も違うのでdev版の方がいいんだとか。名前からしてstableの方が良いのかなとか思ってしまうので、こういう話を聞けるのは助かる。
PHPDocを使うので書いてさえいれば楽に導入できそうかも。型固定配列(\DateTime[])が使えるのは初めて知った。
結局大事なのは、開発の負担を減らす・品質確保する事。それができるなら、dynamicでもstaticでも得意な方で柔軟に対応すれば良いという事が良く分かった。

というわけで早速、某案件のローカルにPHPStanを導入してみた。
結果、クラスのないファイルはエラーなし、クラスのあるファイルは「Class not found autoloadがどうのこうの」エラーが出た。
PHPerぢからが足りなかった…。ドキュメント読み直して動かせるようにしたいなぁ。
※追記 dwmemo.hatenablog.com

参考URL

PHPStan本家:GitHub - phpstan/phpstan: PHP Static Analysis Tool - discover bugs in your code without running it!
PHPStanの中の一部:GitHub - nikic/PHP-Parser: A PHP parser written in PHP
PHPStanの対抗馬:GitHub - phan/phan: Phan is a static analyzer for PHP. Phan prefers to avoid false-positives and attempts to prove incorrectness rather than correctness.
ReviewDog:reviewdog を飼ってコードレビューや開発を改善しませんか - haya14busa
PHPDoc:fig-standards/phpdoc.md at master · php-fig/fig-standards · GitHub



PHPerKaigi 2018セッションの動画

www.youtube.com
いつの間にか上がっていた…!
PCのオーディオが息してないのでまだ見れていないが、振り返り等に使えそうだ。

まとめ

自分はこの業界に入ってから外の勉強会に行った事が殆どなく、PHPerkaigiのような規模が大きめの会は初めてだった。
前夜祭の記事でも書いたけど、運営の方々が親切に対応してくださったので安心して会場に向かえたのは本当に感謝感謝です(なお初日は勢い余って練馬文化センターに突撃した模様)。
PHPのコアな面、サービスの一つとしての面等、普段の仕事では考えもしなかった話がたくさん聞けたのは得難い体験だったなぁと思う(呟きすぎてTwitterがロックかかったりしたけどそれはそれ)。
あとは、前夜祭の方が会場がノリノリだったので、本編もBeerを早めに解禁しても良かったのでは…!(自分は呑めないけど周りがほろ酔いでフワフワしている雰囲気は良いなと思ってる)
何はともあれ、次回も参加したいと思える楽しいカンファレンスでした!!



PHPerKaigi 2018前夜祭に行ってきた

行ってきた

phperkaigi.jp 行こうと思った動機は忘れたけど、面白い話が聞けたので行って正解だった!
運営の方々にはチケット買う前、会場到着前、開場後も困ってた系のTwitterの呟きをサッと拾ってもらい、本当にありがとうございました!!!


以下は発表を聞いてのメモ。
いつも通りの雑文体でざっくり書いていく。

内容

前夜祭の発表は三つ(シークレットライブ合わせると四つ)。
PHPerチョットデキルすごい人の発表ばかりで、凡才は話を追うので精いっぱいなところもあった。
といっても各所フックがあるので、どの発表も時間をかけて理解してみようと思えた。



PHPでテスティングフレームワークを実装する前に知っておきたい勘所

紹介文

スピーカー:うさみけんた(@tadsan)さん

PHPを書くと一度はフレームワークを作ってみたくなるものです。ここではテスティングフレームワークに一般的に求められる機能の概要や、プログラミング言語としてのPHP固有の事情、既存プロダクトでの実装例などを紹介します。 TDD/BDDなどの手法そのものの紹介は今回の発表での主な関心ではありませんが、PHPUnitなどの構造を知ることは実業務に活きることがあるかもしれません。(ないかもしれません)

niconare.nicovideo.jp

テスティングフレームワークを実装する話。
自分もPHPUnitを導入する際に依存パッケージが多くて面倒だったなぁと思い出した。
自前でフレームワーク作る余裕と許容はプロジェクトにないので、こういうことできる会社さんはすごいなぁ。
以前の記事でも書いたようにボチボチ自動テストは書いているが、メンテするシステムが増えたという実感がある(あまり真面目にやってないからとかはさておき)。
自前のフレームワークも合わせると、メンテは大変じゃないのかなと思ったりもした。
自動テストを書き始める前のコードのテストで悩むことが多いので、(今回発表から削られてしまった)テストしにくい状況についても聞きたかったなぁ。

サンプルコード:GitHub - zonuexe/phperkaigi-test: PHPerKaigi 2018で話したアレです



php と SAPI と ZendEngine3 と

スピーカー:do_aki(@do_aki)さん

紹介文

php のコアであるZendEngine(ZE) 。 php5 から php7 になり、ZEのバージョンも 2 から 3 にあがりました。 この発表では、ZE と php 、そしてこれらのブートストラップである SAPI の関係を整理し、 ZE2 と ZE3 とでなにが変わったのか(あるいはなにが変わらないのか)を解説したいと思います。

www.slideshare.net d.hatena.ne.jp

PHPスクリプトではなく、PHP実行環境についての話。
Server APIphpの設置するときなんかに見かけるくらいで馴染みがなかった。読みはサピだと思っていた。
普段のコーディングでは意識に上がらない、基本の部分の話で面白かった(ちゃんと知ってないとまずいだろうけど!)。
PHPのlife cycleは図になると分かりやすい。プログラミングする上でこういう部分を気にしないといけないシーンってどんなのだろうか。速度とか?
PHP7系ではコアの部分も変わったようなので、そこも含めて勉強しないとなぁと思った。

大統一PHP

スピーカー:uzulla(@uzulla)さん

紹介文

PHPは分断されていると言われる(要出典)ので、私はここらで大統一して行くしかないと思っています。今回、私の研究を発表したいと思います。 「大統一の意味がわからない」「異端」「それ意味あるの?」等の感想をいただくかもしれません。しかし、PHPerは多様性ある人たちだと思いますので勇気をだして発表します。(ここで紙面がつきた模様

speakerdeck.com

httpdになる!」「エラーは画面に出るすごい便利という世界観」
などのパワーワードが溢れてすごかった。
なんかすごかった事しか記憶にないので、資料上がったら読み直す…。
※追記
他の言語において「普通」は実行環境を用意するのが単純、バイナリも一つの場合が殆ど。
PHP使わない人にはPHPのバリエーションが多くて混乱する、そこで「普通」に合わせていく(統一)という話。
そこで、とりあえずhttpdになる(?)という話になった。

僕(httpdってApacheのアレやろアレhttpd.confとか設定面倒な、…実際何なのか考えたことないな?)
というわけで、そこから調べてみた。
httpd(HyperText Transfer Protocol Daemon)つまり HTTPデーモン、サーバへの要求の受け答え等やっている。

PHPApacheがやっているけどGoとかRubyとかetc.では「自分がhttpdになる」のだそうで(PHPのビルトインサーバーは開発用なので…httpdになれない)。

デモのコード:GitHub - uzulla/Tinitter at reactize
実行コードが失敗する、exit() or die()するとサーバも落ちる等の問題があるそうなので、PHPは「普通」に合わせずそのままでいいんじゃないかと思った。

今まではPHPApacheとセットで…という固定観念があったが、「httpdになることができる!」という事は驚きだった。
PHPは単なる仕事道具だと見ていたが、ワンバイナリや1ファイルにするという「遊び」もできると分かって良かった。



PHP7.1 is fast(?)

speakerdeck.com

uzulla(@uzulla)さんによる突然のシークレットライブ。スライドが全部英語だった!
abとかwrkとか、各種ベンチマークのツールを使ってPHP7.1のベンチを計ってみたという話。
ツールによってベンチマークがバラバラになるのはkeep-alive等の扱いがツールによって違うからかも、だそうで。
測定するツールがどのような振る舞いをしているのか把握しておかないと、測定対象の性能について断言できないんだな。
ベンチを計る機会は殆どないが、ツール毎の違いを調べてみるのも面白いと思った。

出てきた測定ツール。