あっさりと

ソフトウェアや技術話など諸々

ファイル削除してもサーバー容量が増えない場合の対処例

現象

Linuxサーバーの容量が著しく減少しているので調査すると某検索システムプロセスのログファイルが大きいのが原因だった。
しかし、単純に当該ファイルを削除してもサーバー容量は増加しなかった。

問題点

ログファイルに対してプロセスからの処理(書き込み等)が続行されている場合、単に削除しても容量が開放されない。
プロセスの停止 or 再起動が必要となる。

状況確認方法

lsofコマンドでプロセスが制御しているログファイルの状態を確認する。

$ lsof 〈対象ファイル〉


コマンド名、PID、ファイルサイズ等の情報が確認出来る。
何も出力されなければ対象ファイルをオープンしているプロセスは存在しないという事になる。

対処手順

最初からrmでファイル削除すると一見消えたように見え判別し辛くなるので以下手順を推奨。

1) リネームする
*この時点ではlogfile_nextへのプロセスからの書き込みは止まらない

$ mv logfile logfile_next

2) 当該プロセスを停止 or 再起動する
*この時点でlogfile_nextはプロセスから解放されファイル操作可能な状態となる

$ kill 〈対象プロセスID〉
or 
対象プロセスの再起動手順


Tips) 低負荷なファイルコピー

例えば本番環境下で別ドライブ(や別ホスト)へ巨大なログファイルをコピーするような場合、高すぎる I/O 負荷が発生するとサービス障害を引き起こす可能性が有る。

以下コマンドで I/O 処理速度を制限しつつ低負荷なコピーが可能。
*bwlimitオプション 帯域制限を(kilobytes per second)で指定できる。

$ rsync -av --bwlimit=10000 /hd1/logfile /hd2/logfile


MySQL デッドロック回避パターン

目的

SELECT実行
   ↓
(結果行が無ければ)INSERT

(結果行が有れば)UPDATE

上記の様なパターンでクエリを発行しているシステムでDBのエラーが稀に出ていた。
解決過程の備忘録。

前提

MySQL 5.6 INNODB (REPEATABLE READ)
対象処理回数は約5〜10万回/日(秒間レベルで複数アクセスが発生している)
ユニークキーが存在する(=SELECTのWHERE条件)

最初の状態

コード(雰囲気)は以下のような感じ。

START TRANSACTION;

SELECT 〜  (ユニークキーで検索)

if(結果有る){
    UPDATE 〜 
}
else {
    INSERT 〜 (SELECTで利用したユニークキーを挿入)
}
COMMIT;

1日に1,2回Duplicateエラーが発生していた。

対処1

エラーの原因として最初のSELECTが同時に走って後続の処理がユニーク制約に引っかかっている事が予想できた。
以下のように排他ロックを利用し対処した。

START TRANSACTION;

SELECTFOR UPDATE (ユニークキーで検索)

if(結果有る){
    UPDATE 〜 
}
else {
    INSERT 〜 (SELECTで利用したユニークキーを挿入)
}
COMMIT;

ところが、今度は1日に数回デッドロックエラーが発生する様になった!

対処1の問題点

更に調査を進めると原因が以下である可能性が高い事がわかった。

  • SELECTが空振りするとギャップロックになる

  • ギャップロック同士は排他ロックされない

  • ギャップロックは別のトランザクションのINSERTをブロックする

  • 同時に複数のギャップロックが発生するとお互いをブロックしデッドロック状態となる

対処2

以下の様に再対処を行った。

INSERT INTO 〜(ユニークキーをINSERT)
  ON DUPLICATE KEY UPDATE 〜(目的のUPDATE処理)

 *トランザクションは未使用に変更

問題のデッドロックエラーも出力されなくなり解決!

雑感

  • 標準SQLの教科書通りの知識だけでは通用しない事が有る。(=経験大事!)

  • 素直にDBMS依存の機能に頼った方が得策?

  • DUPLICATE KEY UPDATE動作時にプライマリキーがauto incrementされるので注意が必要。*AUTO_INCREMENT指定有り

  • SHOW ENGINE INNODB STATUSとか自由に使えない現場はツライ

参考

なかったらINSERTしたいし、あるならロック取りたいやん?

Gitでの一括切り戻し方法

目的

プロダクション環境への適用後に障害が発生した場合に備えて速やかに切り戻しを行える方法を確保したい。
Gitでデプロイ直前の状態に戻す方法の備忘録。

前提

・commit, pushした後に本番デプロイする手順を採用している。
・commit量が多い。(数百コミットとか)
・master が本番環境のブランチ、 featureが追加機能ブランチ
・featureにmasterはマージ済み

今回使用出来なかった解決方法

以下の様にsquashでまとめる方法を採用しようとしたが上手くいかなかった。

$ git checkout feature

$ git rebase master

$ git checkout master

$ git merge --squash feature

数十commitくらいの規模で行ったときは問題無かったが今回は変更量が多すぎるのかrebaseの段階で大量のconflictが発生。。
詳細にcommitログ残したかったらそもそもsquashしない方が良いかも^^;

解決方法

単純に--no-ffで解決可能。

$ git checkout master

$ git merge --no-ff feature

--no-ffオプションでマージしておけば変更内容の一括打ち消しが出来る。
問題が発生して切り戻す時はmasterブランチで以下を実行。

$ git revert -m 1 マージした時のコミットハッシュ

mオプションはmainlineの意味で
1:マージする側(master)に戻る 2:マージされる側(feature)に戻る。

切り戻し内容を再マージしたい時

問題特定出来たら以下コマンドで切り戻し分を再マージしてから修正する。

$ git revert リバートした時のコミットハッシュ

業務システムのための上流工程入門 を読んで

目的

良いアウトプットをする為にコーディング能力は必要だがチームで動く場合は要件、仕様を固めて関係各所に共有するという能力が更に重要となってくる。
そのあたりの内容についてヒントとなる事が書かれていた。

全体の感想

入門というだけあり、前提知識無しで読むことが可能。

基本となる概念の説明
 ↓
具体的な上流工程の資料作成のテクニック
 ↓
説明したテクニックを使った仮想案件でのセッション例

上記の流れで進むので入門から活用まで一通り学べる。

誰向けか

・上流工程の進め方を1から学びたいSE
・コーディング先行で激しく手戻りしてしまうプログラマ^^;
・プロジェクトメンバーに仕様をスムーズに共有するヒントを知りたい方

役立ちポイント

受託案件以外の自社案件プロジェクトでも非エンジニアを含むプロジェクトメンバーと仕様を調整したり共有したりする事が多々あり、伝わりづらくて悩ましい時が有る。
そのような場合でも書籍内の業務フローやモデリング、画面例のフレームワークが大いに参考になるのでそのままパクれば 採用すると伝わりやすい資料作成が出来ると思う。

業務システムのための上流工程入門

業務システムのための上流工程入門

PHPのtraitを使ってラク出来た件

はじめに

traitを使って既存の動作にほぼ影響を与えず機能追加出来たパターンが有った。
使い方例をメモ。

元々有ったコード

ClassBase.php

class ClassBase
{
    private $value;
    
    static function getBody(){
        return "";
    }
    
    static function getObjcet()
    {
        $className = __CLASS__;
        return new $className(static::makeArg());
    }
    
    static function makeArg()
    {
        // バイト長を返却
        return strlen(static::getBody());
    }
    
    function __construct($value)
    {
        $this->value = $value;
    }
    
    function echoValue(){
        echo $this->value, PHP_EOL;
    }
}

ClassA.php

class ClassA extends ClassBase 
{
    static function getBody()
    {
        return "body";
    }

 〜
 色々なメソッド
}
$obj_classA = ClassA::getObjcet();
$obj_classA->echoValue(); // 4



要件(前提条件)

・バイト長では無く特定要素でjsonエンコードした情報を出力する処理を追加する。
・既存のバイト長を出力する処理も残す
・既存の動作にほぼ影響を与えないようにする(既存変更影響のテスト工数はあまり取れない)
・メソッド名(echoValue)は同じで動作させる。


今回は使えなかった解決方法

・ClassBaseを変更
 → 多くのクラスで継承されているので影響範囲が大きく手を入れにくい状態
・ClassBaseを継承する新たなクラスを作成
 → ClassA内のメソッドも使用する場合があるので同じコードをコピペで入れたくない


traitを使って解決

TraitCommon.php

trait TraitCommon
{
    static function makeArg()
    {
        $json = [];
        $json["hoge"] = static::getBody();
        // jsonに変更
        return json_encode($json);
    }
}

ClassTraitA.php

class ClassTraitA extends ClassA 
{
    use TraitCommon;
}

既存処理に影響を与えず追加処理を用意出来た。

$obj_classA = ClassA::getObjcet();
$obj_classA->echoValue(); // 4

$obj_traitA = ClassTraitA::getObjcet();
$obj_traitA->echoValue(); // {"hoge":"body"}


ClassBaseを継承したClassBやClassCが有っても同じように共通処理として簡単に展開していける。

グラス片手にデータベース設計~販売管理システム編を読んで

全体の感想

販売管理システムを構築する時に基本的に網羅すべき事がわかる。
要求される事が多いパターンに応じた仕様構築や設計方針が詳しく記載してある。
要望 → 仕様 → データベース構成 と前後関係から理解しやすい内容となっている。

読者層

・販売管理の基本設計工程にアサインされたもののどこから開始して良いかイメージが湧かないSE
・局所的に販売管理システムに関わっているが全体を俯瞰したい担当者
・ユーザーとの打ち合わせ前に少しでものたたき台をイメージしておきたい方

その他

ユーザー企業の業界により慣習が違うのでそれに沿ったシステム設計が必要となってくる。
そのような視点のヒントを与えてくれる。(*例えば百貨店業界の委託販売形態の特殊さなど。)

クラッチ開発等では本当に0から打ち合わせしていくとかなり辛いので本書籍を参考にたたき台 イメージを作って話を進めるだけでもかなり効率的に開始できると思われる。

グラス片手にデータベース設計~販売管理システム編 (DBMagazine SELECTION)

グラス片手にデータベース設計~販売管理システム編 (DBMagazine SELECTION)