がるの健忘録 このページをアンテナに追加 RSSフィード

2017-06-18

[]簡単な「メッセージキュー」の仕組みを作ろうかなぁ……

んと…いわゆる

バラバラタスクを不定期に詰め込んで

・定期的なバッチで「積みあがった」タスクをまりもりとこなす

ってのは、例えばAWSなんかだとSQSとかで提供されている機能なので、まぁある程度のニーズはあろうかと思われるのです。


ただ一方で「AWS使ってないときは?」とかってなったりいろいろとあるので。

微妙に習作的なニュアンスも込みで、作ってみようかなぁ、と。


目標としては、以下の通り。

・「複数の処理バッチ」が動いても安全:障害耐性がある程度ある

 →「複数台のマシン」で動かしても問題ないようにする

・可能な限り「どんな処理でも乗る」ように、マージンは広めにとっておく

・シンプルにする

・常駐は、してもいいけど基本は「常駐させずに定期的にcronとかで呼ばれる」


こんな感じかなぁ、と。

突っ込みなどありましたらお待ちしております。


SQL部分

色々と考えていたのですが。

「バッチに渡すべきデータ」は、内部的な処理のみなので「serialize()で作った文字列」でよかんべではないかなぁ、と。

個人的には「配列を格納して配列を受け取る」事を推奨したいところなんだけど、まぁ「インスタンス突っ込むんならそれはそれで自己責任のもとによい」のではないかなぁ、と。


その辺を前提にすると。

最低限のDDLとしてはおそらく

CREATE TABEL message_queue (
    message_queue_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '一意のID:ソート条件にも使う',
    data blob NOT NULL COMMENT '引き渡すデータ:中はなんでもよいような感じで(サイズが足りないことが懸念されるならでっかく)',
    status TINYINT UNSIGNED DEFAULT 0 COMMENT '処理ステータス。 0:未処理, 1:処理中',
    processing_at DATETIME COMMENT '処理開始時刻。statusが0の時はNULLが想定されている',
    error_count TINYINT UNSIGNED DEFAULT 0 COMMENT '処理時のエラー回数(10回くらいを最大に考えてるのでTINYINT)',
    create_at DATETIME NOT NULL COMMENT 'このキューの作成時刻',
    PRIMARY KEY (`message_queue_id`)
)CHARACTER SET 'utf8mb4', ENGINE=InnoDB, COMMENT='1レコードが「1つのキュー」を意味するテーブル';

こんな感じ。


INSERTはたぶん、何も考えずに「INSERT文一発」で終了。

まぁエラー補足はしたほうがよいと思うので「うまくINSERTできなかった」らエラー処理、くらいかなぁ。


で、バッチ処理用に「キューを取得する」方法。

現状は、以下を想定している感じ。

BEGIN;
SELECT * FROM message_queue WHERE status=0 AND error_count < 10 ORDER BY message_queue_id LIMIT 0,1 FOR UPDATE;
if (SELECTがempty) {
    COMMIT; // ROLLBACKとどっちがよろしかんべか?
    プログラム終了;
}
UPDATE message_queue SET status=1, processing_at=now() WHERE message_queue_id=[上で取得したID];
COMMIT;


処理本体


if (処理でエラー発生) {
    UPDATE message_queue SET error_count=error_count+1, status=0 WHERE message_queue_id=[上で取得したID];
} else {
    DELETE message_queue WHERE message_queue_id=[上で取得したID];
    (DELETEできなかったらテキストログにでも書き出しておく)
}

これが基本。

トランザクションの中でがっつりロックが発生するんだけど「UPDATEしたらすぐに手放す」から、多少の競合が発生しても、重たいロックにはならんのではなかろうかなぁ、と。多分。


レコードは物理削除。

論理削除はお好まない感じなので。


あとは、定期的にガベコレ的な処理として

SELECT * FROM message_queue WHERE status=1 AND processing_at < 現在時刻から1時間前

とかって感じで捕捉しておくと。

ざっくりした予想だけど「上述のSQLに当てはまるような処理はたぶん"バッチが処理中に落ちてる"と思われる」ので。

「改めて別プロセスで処理(ざっくり系)」でもいいし「管理画面等にアラートを上げる(丁寧系)」でもいいし、お好みで。


同じくガベコレ的処理として

SELECT * FROM message_queue WHERE error_count >= 10;

があって、これは「何度も(今回のケースだと10回)投げたけど毎度毎度、処理がエラーになるよ?」なので、こいつは管理画面にアラート、じゃないかなぁ。


とまぁ、こんな風に作ったらあらかたいけるような気がしてる。

なんとなく。


プログラム部分

こちらで組む側はクラス作って。

仮に、クラス名を「mw_message_queue」と仮定して。


タスクを突っ込む時は

mw_message_queue::enqueue(mixed データ, PDO $dbh=NULL);

でよいかなぁ、と。

テーブル名とかその他諸々の可変になりうる変数は「メソッドで切り出し」しておいて「変えたかったら継承して子クラス作ってそっち呼んで」くらいの感じで。

第二引数についても「基本はいる」んだけど「継承した子クラスでget_dbhを実装していれば省略できる」風に作ろうかなぁ、と。

処理的には

static pub func enqueue(mixed $data, PDO $dbh=NULL) {
    //
    if (NULL === $dbh) {
        $dbh = static::get_dbh();
    }
    // チェック
    if (NULL === $dbh) {
        // 適当にエラー吐いて終了
        exit;
    }
    // 続く

}
// 親で実装してる空っぽなメソッド
static protected func get_dbh() {
    return NULL;
}

的なのをイメージ。


タスクの処理のほうは、考えているのが

mw_message_queue::dequeue(callable $callback, PDO $dbh=NULL);

これくらい。

細かい「1バッチで処理する回数の上限」とか「(1マシン内での)多重起動の制限個数」とかはメソッドで切り出しておいて以下略。


callableが意外と丁寧な処理が必要そうなので、ここはいろいろ想定。

関数引数戻り値については「bool 関数(unserialize()されたデータ);」ってフォーマットを想定……DBの項目でほかに欲しい情報とかあるかしらん???


処理的には

文字列なら、可変関数としてcall

・Closureクラスインスタンス(無名関数)なら、そのままcall

配列の場合

 →[0]がインスタンスなら、->でcall

 →[0]が文字の場合

  →[0]のクラスの[1]のメソッドがstaticなら、::でcall

  →[0]のクラスの[1]のメソッドがstaticでないなら、->でcall

で、おかしな値は一通り「エラーをゲロって終了」と。

この辺でなんとかなるはずなんだけどなぁ……たぶんこの実装は、MagicWeaponに吸収するwww


で、ガベコレ系についてもおそらくは

// 「仕掛中のまま止まってる」子の処理
mw_message_queue::gc_processing(callable $callback, PDO $dbh=NULL);
// 「エラーで止まってる」子の処理
mw_message_queue::gc_error(callable $callback, PDO $dbh=NULL);

でよいかなぁ、と。


ざっくりした設計諸々。

突っ込みがあったらよろです。

なかったら、たぶんそのうち、実装しますw

2017-05-21

[]教わる側の意見

元ネタ

エンジニアを指導する立場の人こそ読んでほしい、新卒エンジニアが1年間で上司に感じた5つのこと

http://qiita.com/H_Crane/items/22ea96300dda82ec5b02


どちらかというと「教える」事が多くなった立場のおいちゃん的に「下はどう感じてるのか」ってのは、色々と興味深いのだよねぇ。


1、「本当にそれググった?」って聞き返さないで欲しい

まぁ本文に書いてあるんだけど、おいちゃんが聞くとしたら「どんな風にググった?」かなぁ。

「本当にググった?」って聞かれても、聞かれた相手「ググったにきまってるやん」くらいにしか思わないだろうし。

ただ「ググリ方」があるから、その辺をレクチャーしていく感じかなぁ。


2、やたら「同じミスはするな」って言わないで欲しい

言わない。言ったらブーメランになるからwww

大体「癖(くせ)的なミス」はしょっちゅうやるもんなので、そこが見えた時点で「こーゆー癖があるよねぇ」から始まって「こーゆーやり方をすると防止しやすいよ」と、あとは「自分には"こーゆミスをしやすい癖がある"って覚えておくと、チェックしやすいよ」って教えるくらいかなぁ。


3、意見やツールの押し付けをしないで欲しい

基本はしない。

聞かれたらおいちゃんの好みは伝えるけど……基本「補助輪を外しまくった」ツールを使う事が多いので、止めはしないが勧めもしないw

ただまぁ現場によっては「ツールはこれ」って決まっちゃってるのもあるので、その場合は「あきらめろ」とは言うかなぁw


"上司の考える効率"というものが、「俺の方が経験あるし正しいに決まってる」という個人の行き過ぎた考え方なのか、「客観的見ても」そうであるかどうかが重要ではないかと思います。

この辺については、正直「そのツールに対する慣れとか好みの方向とか」色々あるしなぁ。

とりあえず「お勧め」はするけど、それ以上のものではないかなぁ。

そのツールを使って「明らかにはまりそうな沼」があるなら、それは伝えるが「それでも使う」ってんなら、止めるほどのもんでもないしなぁ。


4、もっと褒めて!

割と大事ではあるのよねぇマジなところ。


5、「わからないことはすぐ聞いて」という人ほど聞くと怒る

別所でも(定期的に)書いているような気がするんだけど。

おいちゃんは「一定時間(1時間とかが多い)調べて、1時間したら、解決してようがしてなかろうが、報告して」って話をするかな。

んで、聞かれて怒ることはないから、「すぐ怒る」のあたりは、ぶっちゃけ理解不能かなぁ(苦笑

「質問したら怒る人」は、多分「そんな事聞かれても自分のスキル知識が低いからわからないんだけどわからないっていうにはプライドが邪魔をするから怒る」んじゃないかしらん? くらいにしか見ていないしw


……ふむ。

特段に違和感もなにもない感じかなぁ。普段やっている事についても、違和感がある感じではないし。


僕自身ここまでの反響が(炎上が?笑)起こったことに驚いております。

ってあるから、もうちょっと色々とアレがナニな感じなのか、と思ったんだけど、さほど違和感なく読めました。まる。

2017-05-18

[][]軽めネタ2種

ちょいと前に、twitterで軽くアンケートをさせていただいていたのをすっかりとまとめ損ねていたので。

ふとした疑問。

ECサイト(色々あると思うのでお好みのECサイト)にて。「買い物カゴに商品を入れる」アクションでCSRF対策は必要でしょうか? 主観バリバリでよいので、アンケートさせてください。

https://twitter.com/gallu/status/717927849726861312


不要のほうが多いのが、興味深いような納得感があるような。

おいちゃん的には「CRUDのうち、CUDにまつわるものは一通りCSRF対応の対象」って考えているのですが。

この辺にもまぁ、色々な色があって面白いなぁ、と。

ECサイトでいうと「勝手に商品をカゴにぶち込まれる可能性」があって。いやまぁ大体、支払いのタイミングで気づきそうな気もするのですが、「うっかり気づかない」のほかに「ECサイトの性質的に(大量に買う前提なので)気づきにくい」とかあったり、なんてのを考えると、色々。


もう一つ。

そういえば、な疑問。

「社内(あえて規模は書かない)で使うシステム」のセキュリティって………

https://twitter.com/gallu/status/793076216450338816


これは割れたw

19% 通常のopenなWebと同じ程度にがっつり

32% IPAの推奨に準拠する程度にはしっかり

31% 多少穴があっても気にならない

18% XSSやらSQL-Injectionやらも無問題

興味深いのが、2割くらい「穴がっぽがっぽでも無問題」ってあたり。「多少の穴があっても」を含めると5割。

多分「社内の管理ツールだから」なんだろうなぁ、と。

おいちゃん的には「内部犯行とかもあるしねぇ」って考えちゃうので、その辺はまぁ、人それぞれ。


おいちゃんのスタンスとしては割とはっきりしていて。

上のECのにしてもなんにしてもそうなんだけど「IPA推奨レベル、程度まではしっかりとセキュアに」が基本。

なんでかってぇと、それくらいなら「(事前にちゃんと学習コストが支払われている前提で)さほどセキュリティかけるのにコストがかからない」ので、天秤的に、やらないのほうに傾きようがないから。

で、それ以上の「特別にコストかけてでもがっちょりとセキュリティをはるか?」については、ケースバイケース。


うんまぁ前提が「IPAの内容とか、徳丸本とか、それくらいのレベルの学習はきちんとなされていること」なんだけど。

学習コスト自体は……エンジニア特性その他にもよるにしても基本「支払ってるでしょ? 支払うでしょ? そのルート作るでしょ?」って発想が強いしなぁ正直。

最近だと法的リスクの問題もあるし(苦笑


どれが正解、ってもんでもないのですが。

まぁ「こんな話もあったよ〜」的なネタとして。

2017-05-03

[]どうすっかねぇ(ユニットテスト変)

いくつも起因するお話はあるのですが、例えば最近おきたおもころいあたりだと、この辺(から、少し発展させた流れ)。


https://corp.gmo-pg.com/newsroom/pdf/170501_gmo_pg_ir-kaiji-02.pdf

P16

通常、コードレビューは、

コーディングを担当していないメンバーで

実施

されるところ、

保険特約料支払

サイト

の開発では、オンライン部分の構築

を担当していた

メンバー

コーディングをしつつ、自らコードレビューも行

っていた。また、

保険特約料支払

サイト

においては、

コードレビュー後

に、検証が十分に行えていなかったこともあり、結果的にセキュリティコー

ドが出力されることになった

*1


「自分でコーディングして自分でコードレビュー」ってあたりで色々と駄目感が漂う感じではあるのですが、じゃぁ「ほかの人がチェックしてたら気付けたんだろうか?」ってあたりで、一つ二つ。

で…まぁ今回は(できてなかったとはいえ)「コードレビュー」なので、「ほかの人がちゃんと読んでたらきっと気づいたんじゃないかなぁ」と好意的に考えたいところではあるのですが。


これが「ユニットテストによるチェック(と、ユニットテストコードのコードレビューっつかチェック)」だとすると。

ちょいと憂慮している可能性があって、そこについて、どうやってヘッジしていきましょうかねぇ? というあたりで、割と長々と、考えていたりしたので、思考整理かねてBlogってみようか、と。


例えば。それはもぉドドド簡単な関数ですが、以下の関数があって、それをテストしたいと考えます。


関数仕様

関数名は f_add

引数は2つ。intを想定。処理は「二つの引数を算術加算した結果をreturn」

引数の「int以外のケースのエラー処理」は未想定。stringなら「PHPデフォルトの変換に従う」とかしておく


例えばPHPUnitだと、こんなテストコードになると思われるのですざっくりですが。

public function testHoge() {
    $this->assertSame(f_add(1, 0), 1);
    $this->assertSame(f_add(1, 1), 2);
    $this->assertSame(f_add('a', 'b'), 0);
}

これでテストコードが通れば、まぁとりあえず「実装はよいかなぁ」になるケースが多いのかなぁ、と思うのですが。

が。

が。


function f_add($i, $j) {
    if ( $i === 1 and $j === 0) {
        return 1;
    }
    if ( $i === 1 and $j === 1) {
        return 2;
    }
    if ( $i === 'a' and $j === 'b') {
        return 0;
    }
}

って実装になってたら、どうすんだべさなぁ??? っと。


上述は些か「わかりやすく駄目すぎるコード」ではあるのですが。

実際に、これに近いような「駄目」コードを拝見した記憶が全くないわけではないような記憶が闇と深淵の彼方にわずかばかりに確認されている事が否定しきれない事実でありまして*2

持ちろん「コードレビューしろ」って話にはなるかと思うんですが、「コードレビューしっかりしているうちはやらかさないんだけどだんだんコードレビューを薄くしていくと途端にやらかし始めるケース」なんてのも見受けられるわけでして……いやまぁぶっちゃけ「どうしたもんかなぁ?」と。


多分問題の根っことしては「"テストが通る事"は二次目標のはずなんだが一次目標としてとらえられて苦労するケースをどうするか(一次目標等は http://d.hatena.ne.jp/gallu/20101101/p1 参照)」ってあたりなんだろうなぁ、と。

で、これが「エンジニア間」であればまだ色々となんとかなりそうなのですが、発注側が「受け入れの検収タイミング」でこれに近い事をやらかされると「研修OK、いさ実際に使いだすと問題がじゃかすか」って話になって。

ここに「契約でがっちんがっちんに"テストが100%通る事"」になってると、発注側的に困るシーンもあるんじゃないかなぁ、などというあたりで、冒頭のPDF関連のお話にも少し絡んできそうな気がするわけですよ。


最終的にはまぁもちろん「受発注ともに、お互いとお互いのビジネスに敬意を払いましょう」ってお話にはなると思うのですが「払えない輩がいるからこその法的束縛」が必要なのですが……この辺って、どんなもんなんですかねぇ色々と。


なんてな事を、定期的に考えたり思案したりしています。

*1:しかし、よぉわからん改行が大量に入ってるなぁ……なんだべさ??

*2:要約:あった

2017-04-03

[]さじ加減のむずかしさ

まぁ毎度同じみ的な内容なのですが、色々な角度から。


考察の元ネタは、こちら。

https://twitter.com/nfujita55a/status/847131544166719488

コードが面白いので、頑張って書いてみました。

Runtime r = Runtime.getRuntime();

if ( hasA == true && hasB == true && hasC == true ) {
    r.exec( "foo -a AAA -b BBB -c CCC" );
} else if ( hasA == true && hasB == true && hasC != true ) {
    r.exec( "foo -a AAA -b BBB" );
} else if ( hasA == true && hasB != true && hasC == true ) {
    r.exec( "foo -a AAA -c CCC" );
} else if ( hasA != true && hasB == true && hasC == true ) {
    r.exec( "foo -b BBB -c CCC" );
} else if ( hasA == true && hasB != true && hasC != true ) {
    r.exec( "foo -a AAA" );
} else if ( hasA != true && hasB == true && hasC != true ) {
    r.exec( "foo -b BBB" );
} else if ( hasA != true && hasB != true && hasC == true ) {
    r.exec( "foo -c CCC" );
} else if ( hasA != true && hasB != true && hasC != true ) {
    r.exec( "foo" );
} else {
    System,out.println("あり得ない");
}

付帯情報として

この方、10年選手で、余裕のある状況で書かれたものです

との、事。


さて……まぁ本音ぶっちゃけますと「現場で見たらこぶしで殴る」ような感覚をたっぷりと感じるところではあるのですが。

実際に色々と考えてみると、なかなかに「根の深い」お話、になります。


ツイートにも書いてあったのですが

「これ直してよ」と私がいったら、このソースの開発者曰く「8ケースとも単体テスト通ってOKだったから品質は問題ありません」と言われ、気が遠くなった。

このあたり。

「ちゃんと動くから問題ない」というのは、一つの観点として、ないわけではないです。

もちろん、コードとしては「循環的複雑度の観点」とか色々あるのですが。

「馬力で常に全部テストしているから何が問題なのか?」と問われた時に、どの辺で突っ込むか僅かに悩むわけですまぁ「無駄な時間つかってムダ金使うな」で一撃ではあるのですが。


この辺はおそらく、ごくわずかに思考していれば、もうちょっと簡単に書けると考えられます。

PHPで書いてしましますが、基本的には

$command = 'foo';
if (true === $hasA) {
    $command .= '  -a AAA';
}
if (true === $hasB) {
    $command .= '  -b BBB';
}
if (true === $hasC) {
    $command .= '  -c CCC';
}

ってな記法でいけるはずですし。これだと「パラメタがさらに1つ2つ追加」されても、ケース数は爆発しませんし。


技術的知見の多く*1は、こんな風に「知らないと非常に無駄であったり非効率的であったりするものを、効率的にする」ものでございます。

アルゴリズム各種とか、その最たるものですね。

で、その手の知見のもう一つ大きな特徴が「知らないと、そもそも"困っている事が理解できない"から、"必要になったら学ぶ"の、まず"必要である"が認識できない」ところ、でございます。


ヒソカ…だと思ったんですが、確認したらウイングさんの台詞ですね。

極寒の地で

全裸で凍えながら

なぜ つらいのか

わかっていない

ようなもの

「知る」ってのは、とても大事な事でございます。


ただ……じゃぁ「どこまで知ってればいいのさ?」ってのが、当然、出てきます。

その辺の加減を間違えますと、ガッツが言うところの

それともお前

何十年も修行して、達人にでもなるのを待ってから戦場に出るつもりか?

気の長げェ話だな

ってな事になってしまいます。


この辺のバランスって、本当に難しいなぁ、と、多分これは、生涯悩み続けるんだろうなぁ、とか思ってたりします。

おいちゃんのスタンスとしては「百尺竿頭進一歩にもあるように生涯日々精進しつつも、"精進中である"事を言い訳にせずに行い、新しい教えがあったら躊躇なく受け入れる」ってなあたりかなぁ、と。

薄氷を踏むような、危ういバランスですが(笑


この辺のむずかしさとか大切さを、なんとか、色々と伝えられるとよいなぁ、とか思ったりはするのですが。

伝える事もまた、難しいなぁ、とは、常々感じているものではございます(苦笑

*1:……だと思いたい