稼動中の ttserver をデュアルマスタ化した。

現状

192.168.1.1:1978 で ttserver が稼動中。
バックアップも何もないので、二重化したい。

目標

192.168.1.1:1978 (Server.A) と 192.168.1.2:1978 (Server.B) でデュアルマスタ化して、バックアップついでに可用性も確保する。

方法

Server.A の起動コマンドを確認する

まず、 Server.A は -sid -ulog オプションがついている必要がある。
ついていなければ、再起動する。
再起動は十分早いので、稼動中でも別に良い……んじゃ……ないかな。

# Server.A の起動コマンド
/usr/local/bin/ttserver \
-host 192.168.1.1 \
-port 1978 \
-sid 1 \
-ulog /var/ttserver/ulog-1 \
/var/ttserver/casket.tch
Server.A のバックアップを取る

まずバックアップスクリプトを作成する。
スクリプトの場所はどこでもいい。
ここでは /path/to/script.sh とする。
また、バックアップファイルが作成されるディレクトリを、 /path/to/backup とする。

#!/bin/sh
BACKUP=/path/to/backup # バックアップディレクトリ
SRCPATH="$1"
BASENAME=`basename "$SRCPATH"`
DESTPATH="$BACKUP/$BASENAME.$2"
rm -f "$DESTPATH"
cp -f "$SRCPATH" "$DESTPATH"

バックアップする。
バックアップ作成中は、データベースがロックされるので注意。

/usr/local/bin/tcrmgr copy -port 1978 192.168.1.1 '@/path/to/script.sh'

これで、 /path/to/backup/casket.tch.XXX が作成される。
なお、 XXX は 16 桁のタイムスタンプ。
これをそのまま Server.B に送る。

Server.B を設定する

Server.A から送られてきたバックアップファイルを、 /path/to/backup/casket.tch.XXX とする。

BACKUP=/path/to/backup/casket.tch.XXX

# データベースファイルと更新ログを置くディレクトリを作成。
mkdir -p /var/ttserver/ulog-2

# ファイル名が cas.ket.tch.XXX である等、 . が 2 つ以上含まれている場合は失敗するので注意。
BASENAME=`basename "$BACKUP" | cut -d. -f1,2`
RTS=`basename "$BACKUP" | cut -d. -f3`

mv $BACKUP /var/ttserver/$BASENAME
echo "$RTS" > /var/ttserver/2.rts

# 起動。
/usr/local/bin/ttserver \
-host 192.168.1.2 \
-port 1978 \
-sid 1 \
-ulog /var/ttserver/ulog-2 \
-mhost 192.168.1.1 \
-mport 1978 \
-rts /var/ttserver/2.rts \
/var/ttserver/casket.tch

これで Server.A => Server.B のレプリケーションが出来る。

Server.A を設定する

Server.B => Server.A のレプリケーションを設定する。

# これだけ。
/usr/local/bin/tcrmgr setmst -port 1978 -mport 1978 192.168.1.1 192.168.1.2
終了

バックアップ作成さえ何とかなれば、無停止でデュアルマスタ構成に出来るよ!
やったね!

今後の課題

バックアップの作成がネックだけど、 cp じゃなくて LVM のスナップショットにすれば、何とか出来るかも。

運用について

運用時は、常に Server.A に対して更新するようにする。
Server.A に対する更新が失敗した場合のみ、 Server.B に対して更新する。

<?php
function get_tt()
{
    try {
        return new TokyoTyrant('192.168.1.1', 1978);
    } catch (Exception $e) {
        try {
            return new TokyoTyrant('192.168.1.2', 1978);
        } catch (Exception $e) {
            return null;
        }
    }
}

この程度でいいんじゃないかしら。

VMware Player をサービス化する。

  1. sexe をインストール。
  2. sexe 起動
    1. ファイルに vmplayer.exe を指定。
    2. 起動時オプションに vmx ファイルのフルパスを指定。
    3. サービスの名前は適当に。
    4. OK 。サービスがインストールされる。
  3. 管理ツール→サービスを開き、インストールしたサービスのプロパティを開く。
    1. ログオン
      1. アカウントを選択し、自分のアカウントでも入れておく。
      2. OK 。
  4. サービスを再起動。

多分これで大丈夫。

何ていえばいいのこのパターン

<?php

abstract class CtorProxy
{
    protected function __construct()
    {
        $args = func_get_args();
        call_user_func_array(array($this, '_ctor'), $args);
    }

    protected function _ctor()
    {
        // should be overridden.
    }
}

class PublicClass extends CtorProxy
{
    public function __construct() { echo __METHOD__; }
    public function getProtectedClass() { return new ProtectedClass(); }
}

class ProtectedClass extends CtorProxy
{
    protected function _ctor() { echo __METHOD__; }
}
<?php

$publicClass = new PublicClass(); // PublicClass::__construct
$protectedClass = $publicClass->getProtectedClass(); // ProtectedClass::_ctor

$protectedClass = new ProtectedClass(); // fatal error

オブジェクトの生成を制限しまくりたい私にとって、結構有難い。
どっかで既にありそうなパターンなんだけど、何ていう名前なんだろう。

マルチバイト対応 preg_match_all

他に書くこと考えなきゃなあ。


preg_match_all() のマルチバイト文字列版、 mb_ereg_all() 。

  • 第 3 引数が省略できない。 (PHP 4 対応の為)
  • PREG_OFFSET_CAPTURE 未対応。
    • 確か、やたら面倒になりそうだったから。覚えてない。
<?php
// coding: UTF-8

require 'mbstring.php';

mb_internal_encoding('UTF-8');
mb_regex_encoding('UTF-8');

$string = <<<EOS
色は匂へど 散りぬるを
我が世誰ぞ 常ならん
有為の奥山 今日越えて
浅き夢見じ 酔ひもせず
EOS;

$pattern = '([あ-ん])[あ-ん]*';

$count = mb_ereg_all($pattern, $string, $matches, PREG_PATTERN_ORDER, 0);
var_dump($count, $matches);
続きを読む

PHP の型検査を模倣する。

今日も常用している関数を晒してみる。

bool cast_arg(mixed &$given, int $expect[, int $offset]) 値の型を検査する。
http://xif.jp/php/cast_arg.phps

利用方法。

<?php

require 'cast_arg.php';

/**
 * 模倣するのは
 *
 * function() expects parameter 1 to be long, array given
 * Object of class ClassName could not be converted to string
 * function() supplied resource is not a valid stream resource
 * function() parameter 1 must be a valid callback
 * +
 *  in <b>/path/to/callee.php</b> on line <b>#</b><br />\n
 *
 * Array to string conversion は出ない。
 * (function() expects ... になる)
 */

function foo($arg)
{
    echo 'gettype(): ', gettype($arg), "\n";
    if (!cast_arg($arg, TYPE_LONG, 1)) {
        echo '$arg is not int', "\n";
        echo 'var_dump(): ';
        var_dump($arg);
        return false;
    }
    echo '$arg is int', "\n";
    echo 'var_dump(): ';
    var_dump($arg);
    return true;
}

foo(1); // returns true
// gettype(): integer
// $arg is int
// var_dump(): int(1)

foo('1'); // returns true
// gettype(): string
// $arg is int
// var_dump(): int(1)

foo('bar'); // returns false
// gettype(): string
// Warning: foo() expects parameter 1 to be long, string given in ...
// $arg is not int
// var_dump(): int(0)
続きを読む

IP アドレスから携帯キャリアを判別する。

127.0.0.1 のようなドット表記の IP アドレスから、携帯キャリアを判別する関数を生成します。


検索部分は http://d.hatena.ne.jp/tasukuchan/20071231/1199105717 を参考にしました。


異なる部分として

  • 全部 PHP
  • IP アドレスは手動設定。
    • フルブラウザの IP アドレスなどは除外したかった。
    • クローラ作るのも面倒だし、いっそ手動ということで。
  • コードサイズの圧縮。相当小さくなります。
    • else がありません。 if と elseif だけ。
  • 32bit と 64bit を分けた。
    • 必ず int で比較するようにする為。
    • 当然、32/64bit 間で互換性はなし。それぞれ別個に生成すること。
    • →やっぱり止めた。
続きを読む