[groonga-dev,01797] Re: FW: mroonga適用テーブルへの「WHERE IN 多数」クエリでエラー

Back to archive index

Kimura A a.kim****@live*****
2013年 9月 20日 (金) 19:53:58 JST


木村です。
先ほど、手違いで少し古いファイルをペーストして送ってしまいました。すみません。
Insertシェルについては以下の内容でテストよろしくお願いしますo(>_<)o


<?php
App::uses('AppShell', 'Console/Command');

/**
 * mroonga「WHERE IN 多数」クエリのテスト用データ生成シェル
 *
 * @property Model $Model
 */
class InsertShell extends AppShell {

/**
 * メイン処理
 *
 * @param Model $Model
 * @return void
 */
    public function main() {
        $limit = 100;        // 1度にいくつのレコードをINSERTするか
        $minutes = 5;        // 実行を許可する最大分数。cronによる動作時の重複回避用
        $headRoomSec = 10;    // 実行許可分間のうち処理の重複回避のために何秒の余裕を残すか

        // モデルを準備して接続先をtestデータベースに切り替え
        $this->Model = ClassRegistry::init('Product');
        $this->Model->setDataSource('webTest');

        // ロックファイルがある場合
        if (file_exists(TMP . 'mroonga_errored')) {
            if ($this->Model->find('first', array('fields' => array('id')))) {
                // productsテーブルが空でない場合:エラー再現条件をすでに満たしているとみなして終了
                CakeLog::debug("mroonga_erroredファイルが存在するためINSERT処理を回避します");
                return;
            }
            @unlink(TMP . 'mroonga_errored');
        }

        // 実処理
        CakeLog::debug("{$limit}件単位の一括INSERT処理({$minutes}分間)を開始します");
        $startTS = time();                                            // 処理開始TS
        $timeLimitTS = $startTS + (60 * $minutes) - $headRoomSec;    // 想定上のタイムリミットTS:$headRoomSec秒の余裕を確保
        $loopSecMax = 0;                                            // 1回のループに要した最大秒数を保持
        for ($i = 0; $i < 10000; $i++) {
            if ($i) {
                // 初回ループ以外は1秒のインターバルを挟む
                sleep(1);
            }

            // ループ処理開始時刻を保持
            $loopStart = time();
            // 一括INSERT実行
            if (!$this->insert($limit)) {
                // エラー時停止
                return;
            }
            // 一括INSERTの所要秒数を算出
            $loopSec = time() - $loopStart;
            // 一括INSERTの最大所要秒数を特定
            $loopSecMax = max(array($loopSecMax, $loopSec));

            // 一括INSERTの最大所要秒数を基準に、トータル処理時間が次のループで所定の秒数を超過しないかチェック:後続のSelectシェル処理との重複回避
            $loopEndTS = time();
            if ($loopSecMax > ($timeLimitTS - $loopEndTS)) {
                // 超過の可能性がある場合はログを出力して処理を停止
                $loopCount = $i + 1;
                $totalSec = $loopEndTS - $startTS;
                $maxId = $this->getMaxId();
                $dbNameData = $this->Model->query('SELECT database() as `db`;', false);
                $dbName = $dbNameData[0][0]['db'];

                CakeLog::debug("{$minutes}分間のタイムリミットが近いため処理を終了します。DB名:{$dbName}/最大products.id:{$maxId}/トータル動作秒数:{$totalSec}/ループ回数:{$loopCount}/ループ最大所要秒数:{$loopSecMax}/ヘッドルーム秒数:{$headRoomSec}");
                break;
            }
        }
    }

/*
 * 現時点での最大products.id値を取得
 */
    public function getMaxId() {
        $record = $this->Model->find('first', array(
            'fields' => array('id'),
            'order' => 'Product.id DESC'
        ));
        return empty($record['Product']['id']) ? 0 : $record['Product']['id'];
    }

/*
 * 一括INSERT実行
 */
    public function insert($limit) {
        // クエリの構成
        $sql = "INSERT INTO test.products (id, code, description, vendor_id, tag_pool) VALUES ";
        $id = $this->getMaxId();
        $vendorId = 1;
        for ($i = 0; $i < $limit; $i++) {
            // 個別レコードを準備
            $id++;
            $sqlChild = "(null";
            foreach (array('code', 'description', 'vendor_id', 'tag_pool') as $field) {
                switch ($field) {
                    case 'code':
                        $sqlChild .= ", 'code{$id}'";
                        break;
                    case 'description':
                    case 'tag_pool':
                        $sqlChild .= ", '" . $this->getText(1000) . "'";
                        break;
                    case 'vendor_id':
                        $sqlChild .= ", 1";
                        break;
                    case 'title':
                        $sqlChild .= ", 'タイトル{$id}'";
                }
            }
            $sqlChild .= ")";

            if ($i) {
                $sql .= ', ';
            }
            $sql .= $sqlChild;
        }
        $sql .= ";";

        // 一括INSERT実行
        $this->Model->query($sql);
        return true;
    }

/*
 * TEXTカラム用のランダム文字列を構成
 */
    public  function getText($len = 100) {
        // 5文字程度で半角スペース区切りになった文字列データを生成するためスペースを混入させておく
        $sCharList = "abcde fghij klmno pqrst uvwxy zABCD EFGHI JKLMN OPQRS TUVWX YZ012 34567 89_-, .";
        mt_srand();
        $sRes = '';
        for($i = 0; $i < $len; $i++) {
            $sRes .= $sCharList{mt_rand(0, strlen($sCharList) - 1)};
        }

        return $sRes;
    }
}
?>
 		 	   		  



groonga-dev メーリングリストの案内
Back to archive index