プロジェクト

全般

プロフィール

高頻度のオープン・クローズ処理中、存在するリストにメールが配送されない問題対策

※Sympaに限った話ではないが……

Sympaはリストの作成・閉鎖時に「sympa_aliases(sympa_transport)」を編集し、berkleydb
の配送テーブルを生成する。

Postfixがそのテーブルを参照してエイリアス配送を行うが、あまりに書き換えが高頻度で
あったり、かつテーブル参照が頻繁である環境の場合、Postfixは書き換え途中のテーブル
内容を参照してしまい、配送テーブルに存在しない(User Unknown)としてエラーを返してしまう。

これはhash形式のテーブルファイルに対するPostfixの仕様の問題で、DBファイルへの
書き込みが終わるまでアクセスを待つ仕様となっていたいため。

ローカルディスク上では余程のことがない限りは大量アクセスでもこの問題は起きないと
思われるが、特にディスクの書き込みが遅いNFSなどではこの現象が発生しやすい。

対策

http://postfix-jp.info/trans-2.3/jhtml/DATABASE_README.html#safe_db
上記によると、hash形式のpostmapコマンドは書き換え中の状態にアクセスされる可能性があるため、次の対策を
行うようにとのこと。

  • 対策案
    • テーブルファイルにhash形式ではなくcdb形式を利用する
      hash形式と違い、DBの書き込み完了を待って読み込み処理を行ってくれる
    • 別名で作成したdbファイルを、参照用のdbファイルにmv(置き換え)する
      mvすることにより、完成済みのファイルに即座に参照先が変わり、書き込み中のアクセスが発生しない

cdb形式はRHEL/CentOSでは標準対応していない為、後者の手法を行う。
またNFS上のmvはキャッシュを参照されてしまうため、dbファイルはローカル上に配置する

  • 構成変更前
    • Sympaは共有ディスク上のsympa_aliasesファイルを読み書きする
    • sympa_aliasesに変更があった場合、postmapし共有ディスク上にdbを作成する
    • postfixはこの共有ディスク上のsympa_aliases.dbを参照する
  • 構成変更後
    • Sympaは共有ディスク上のsympa_aliasesファイルを読み書きする
    • sympa_aliasesに変更があった場合、一時ファイル名で各ローカルにコピーする
    • 一時ファイル名のファイルをpostmapし、ローカルにdbを作成する
    • 一時ファイル名のdbをsympa_aliases.dbにmvする
    • postfixはこのローカル上のsympa_aliases.dbを参照する
  • postmapしていた処理にローカルコピー等の割り込みを行うようSympaの設定を変更する
    vi /etc/sympa/sympa.conf
    --------------------
    #aliases_program postmap
    aliases_program /etc/postfix/robust_postmap
    
  • ローカルコピーやmv操作を行うrobust_postmapプログラムを作成する
    vi /etc/postfix/robust_postmap
    --------------------
    #! /bin/bash
    set -eu
    
    ALIASES_TYPE=hash
    CP=/bin/cp
    MV=/bin/mv
    SSH=/bin/ssh
    POSTMAP=/usr/sbin/postmap
    RM=/bin/rm
    CONFDIR=/etc/postfix
    CHOWN=/usr/bin/chown
    CHMOD=/usr/bin/chmod
    
    ALIASES_FILE="$1" 
    if [ -z "$ALIASES_FILE" ]; then
        exit 1
    fi
    if [ -e "$ALIASES_FILE" ]; then
        :
    else
        exit 1
    fi
    
    TEMPFILE=$(basename ${ALIASES_FILE}).$$
    ALIASFILE=$(basename ${ALIASES_FILE})
    
    $CP -f ${ALIASES_FILE} ${CONFDIR}/${TEMPFILE} || exit $?
    
    $POSTMAP "${ALIASES_TYPE}:${CONFDIR}/${TEMPFILE}" || exit $?
    $CHOWN root.root ${CONFDIR}/${TEMPFILE} || exit $?
    $CHOWN root.root ${CONFDIR}/${TEMPFILE}.db || exit $?
    
    $CHMOD 644 ${CONFDIR}/${TEMPFILE} || exit $?
    $CHMOD 644 ${CONFDIR}/${TEMPFILE}.db || exit $?
    
    $MV -f ${CONFDIR}/${TEMPFILE} ${CONFDIR}/${ALIASFILE} || exit $?
    $MV -f ${CONFDIR}/${TEMPFILE}.db ${CONFDIR}/${ALIASFILE}.db || exit $?
    
    exit 0
    
  • postfixの設定変更を行う
    vi /etc/postfix/main.cf
    --------------------
    transport_maps = hash:/etc/sympa/transport.sympa,
      hash:/etc/postfix/sympa_aliases ←/var/lib/sympa/sympa_aliases(または/etc/sympa/sympa_transport)からスクリプトが作るファイルへ変更
    
  • 各サービスの再起動を行う
    systemctl restart postfix
    systemctl restart sympa
    
  • 上記でpostmapを伴う「リスト変更・削除」時に、scpやmvなどのローカルファイル更新処理が両ノードで動作する。