noob

底辺オペレーターのメモ

Zabbix 2.2 イベントの詳細にトリガの説明(comments)を表示させる

Zabbixでログ監視を設定したのは良いものの,上がってきたイベントにはログが表示されてなくて,わざわざヒストリを見にいくのは不便だったので無理やり改造してみた.

改造の流れ

  • 「トリガの設定」で「名前」にマクロ{ITEM.VALUE}を入れるだけで,「イベント詳細」画面左カラムにある「イベントソースの詳細」テーブルの「トリガー」行にログが表示される.
    • 悪くないけど「トリガ画面」の「名前」列の表示もログになってしまって微妙だったのでもう少し頑張ってみる.
  • 「イベント詳細」画面左カラム「イベント詳細」テーブルに「イベント」行がある.
    • デフォルトだとトリガの名前が入ってるけど,ここにログが表示されれば入れられれば収まりがよさそう.
      • 「トリガの設定」に「説明」(comments)欄があるのでここにマクロ{ITEM.VALUE}を書いて,それが上の「イベント」行に表示されるようにしよう.

改造内容

表示するデータを作っているinclude/events.inc.phpmake_event_details()内にある
CMacrosResolverHelper::resolveEventDescription
CMacrosResolverHelper::resolveTriggerDescriptions
あたりに変更したら行けそうにみえたけど,マクロ{ITEM.VALUE}の変換結果が常に最新のログになってしまったので少し強引に改造.

events.php

--- events.php.ORG  2013-12-11 02:45:09.000000000 +0900
+++ events.php      2014-02-07 14:11:58.165892875 +0900
@@ -652,7 +652,7 @@
                                'triggerids' => zbx_objectValues($events, 'objectid'),
                                'selectHosts' => array('hostid'),
                                'selectItems' => array('name', 'value_type', 'key_'),
-                               'output' => array('description', 'expression', 'priority', 'flags', 'url')
+                               'output' => array('description', 'expression', 'priority', 'flags', 'url', 'comments')
                        ));
                        $triggers = zbx_toHash($triggers, 'triggerid');
  • トリガのデータを取得しているところで「説明」欄の内容も取得するように変更.

include/events.inc.php

--- include/events.inc.php.ORG  2013-12-11 02:45:05.000000000 +0900
+++ include/events.inc.php      2014-02-07 11:01:27.587964245 +0900
@@ -179,7 +179,13 @@
        $config = select_config();
        $table = new CTableInfo();
 
-       $table->addRow(array(_('Event'), CMacrosResolverHelper::resolveEventDescription(array_merge($trigger, $event))));
+       $trigger_for_comments = $trigger;
+       $trigger_for_comments['description'] = $trigger_for_comments['comments'];
+
+       $table->addRow(array(_('Event'), CMacrosResolverHelper::resolveEventDescription(zbx_array_merge($trigger_for_comments, array(
+               'clock' => $event['clock'],
+               'ns' => $event['ns']
+       )))));
        $table->addRow(array(_('Time'), zbx_date2str(_('d M Y H:i:s'), $event['clock'])));
 
        if ($config['event_ack_enable']) {
  • 「イベント詳細」画面左カラム「イベント詳細」テーブル「イベント」行を作成するところで内容を差し替える.
    • CMacrosResolverHelper::resolveEventDescriptionが引数にとる配列のキーdescriptionを変換していたので,これを変更する.
      • 他に影響が出ないように引数用の配列$triggerを別配列$trigger_for_commentsにコピー.
      • 配列$trigger_for_commentsのキーdescriptionをキーcommentsで上書き.
        • キーcommentsは上記events.phpの改造で使えるようになっている.
    • CMacrosResolverHelper::resolveEventDescriptionの引数を配列$trigger_for_commentsに変更.

おまけ

「最新のイベント」画面の「説明」カラムにトリガの「説明」を表示させる

--- events.php.ORG  2013-12-11 02:45:09.000000000 +0900
+++ events.php      2014-02-07 14:11:58.165892875 +0900
@@ -702,6 +702,16 @@
                                        'ns' => $event['ns']
                                )));
 
+                               $trigger_for_comments = $trigger;
+                               $trigger_for_comments['description'] = $trigger_for_comments['comments'];
+
+                               $comments = CMacrosResolverHelper::resolveEventDescription(zbx_array_merge($trigger_for_comments, array(
+                                       'clock' => $event['clock'],
+                                       'ns' => $event['ns']
+                               )));
+
+                               $description = $comments;
+
                                $triggerDescription = new CSpan($description, 'pointer link_menu');
                                $triggerDescription->setMenuPopup(getMenuPopupTrigger($trigger, $triggerItems, null, $event['clock']));

表示できるようにしたログが20バイトで切られてしまっていたので1024バイトまで表示されるようにする

--- include/items.inc.php.ORG   2013-12-11 02:45:05.000000000 +0900
+++ include/items.inc.php       2014-02-07 11:43:03.020497499 +0900
@@ -989,8 +989,8 @@
                // break; is not missing here
                case ITEM_VALUE_TYPE_TEXT:
                case ITEM_VALUE_TYPE_LOG:
-                       if ($trim && zbx_strlen($value) > 20) {
-                               $value = zbx_substr($value, 0, 20).'...';
+                       if ($trim && zbx_strlen($value) > 1024) {
+                               $value = zbx_substr($value, 0, 1024).'...';
                        }
 
                        if ($mapping !== false) {

ZWSでCGI

http://support.zeus.com/ZWSUserGuide.pdf を自分用にまとめ

ZWSも基本はCGIスクリプトの場所指定

  • CGIスクリプト用の特別なディレクトリをConfiguring Aliases for CGI Script DirectoriesからZWSに設定する.
    • Configuring Aliases for CGI Script Directories≒ApacheのScriptAlias

任意のディレクトリでCGIスクリプトを実行

You can choose whether to enable CGI scripts to run in any directory, or whether to confine them to specified directories. 
If they can be run from any directory then CGI scripts are identified using the application/x-httpd-cgi MIME type.
  • 任意のディレクトリでCGIスクリプトを実行するためには,どのファイルがCGIスクリプトであるかをサーバに伝える必要がある.
  • 上記の通りZWSではMIMEタイプを使って判別している.
    • application/x-httpd-cgi に設定されている拡張子を持つファイルをCGIスクリプトとして実行する.

ApacheでCGI

http://httpd.apache.org/docs/2.2/howto/cgi.html を自分用にまとめ

基本はScriptAlias

  • CGIプログラム用の特別な「別」ディレクトリをApacheに設定する.
  • Apacheはこのディレクトリ中の全てのファイルをCGIプログラムであると仮定して,実行を試みる.

ScriptAlias

ScriptAlias /cgi-bin/ /usr/local/apache2/cgi-bin/
  • /cgi-bin/で始まるリソースへのリクエストは,ディレクトリ/usr/local/apache2/cgi-bin/にマップしてCGIプログラムとして扱うようApacheに示している.
  • Aliasディレクティブとかなり似ている.
    • AliasとScriptAliasは通常,DocumentRootディレクトリ外のディレクトリのために使用される.
    • AliasとScriptAliasの違いは,ScriptAliasが接頭辞で始まるすべてのURLをCGIプログラムとみなすという追加の意味を含んでいること.

ScriptAliasディレクトリ外のCGI

  • 任意のディレクトリでCGIの実行を許可するには二段階の設定が必要になる.
    1. AddHandlerやSetHandlerディレクティブによってcgi-scriptハンドラを使用可能にする.
      • どのファイルがCGIファイルかをApacheに伝えている.
    2. OptionsディレクティブでExecCGIを指定する.
      • 特定のディレクトリ配下でCGIの実行を許可している.

AddHandler

AddHandler cgi-script .cgi 
  • 拡張子extensionが名前にあるファイルを指定されたhandler-nameで扱うようApacheに設定する. マッピングは既にある他のマッピングに追加され,同じ拡張子extensionのマッピングを上書きする.
  • 上記の例は拡張子".cgi"で終わるファイルをCGIスクリプトとして扱う設定.

SetHandler

SetHandler cgi-script
  • .htaccessやDirectoryセクション,Locationセクションに書かれた場合,このディレクティブはそこにあるすべてのファイルがhandler-nameで指定されたハンドラで扱われることを強制する.
  • SetHandlerはデフォルトのハンドラをオーバーライドするので,通常の挙動,たとえば,スラッシュで終わるURLがリクエストされたときにディレクトリやインデックスファイルを返すような挙動が行われなくなる.
  • 上記の例はすべてCGIスクリプトとして扱う設定

ExecCGI

<Directory /usr/local/apache2/htdocs/somedir>
    Options +ExecCGI
</Directory>
  • mod_cgiによるCGIスクリプトの実行を許可する.
  • 上記の例では/usr/local/apache2/htdocs/somedir配下でCGIの実行を許可している.

その他

  • CGI
    • Common Gateway Interface. ウェブサーバが外部プログラムと協調して動作するための方法を定義している.
  • ハンドラ
    • ファイルが呼ばれたときに実行される動作のApacheにおける内部表現.
    • 通常,ファイルにはファイルタイプに基づいた暗黙のハンドラがある.
    • すべてのファイルは単にサーバに扱われるが,ファイルタイプの中には別に「ハンドル(扱う)」されるものもある.
    • ファイルタイプに関係なくファイルの拡張子や置いている場所に基づいて明示的にハンドラを指定することもできる.
      • ファイルにタイプとハンドラの両方を関連付けることができるという点で優れている.
    • ハンドラはサーバに組み込んだり,モジュールとして含めたりして追加することができる.
  • mod_cgi/cgi-scriptハンドラ
    • cgi-scriptハンドラが指定されているファイルはCGIスクリプトとして扱われ実行される.
    • ファイルはAddHandlerディレクティブに指定された拡張子を名前に含むか, ScriptAliasディレクトリに存在することによりこのハンドラになる.
    • 後方互換性のため,MIMEタイプがapplication/x-httpd-cgiのファイルにも CGIスクリプトのハンドラが有効になる.
      • この独自MIMEタイプの利用は非推奨.

トランザクション分離レベル

じぶんなりにまとめてみた

トランザクション分離レベル

  • 処理が「複数同時に並行して」実行されようとした場合,「待ち時間を減らすためどれだけデータの一貫性を犠牲にして良いか」を定めたもの
  • ANSI/ISO SQL標準で定められている

SERIALIZABLE

  • 最も強い分離レベル
  • トランザクションを時間的重なりなく逐次実行した場合と同じ結果となる
  • ただし同じ結果とされる逐次実行の順はトランザクション処理のレベルでは保証されない

REPEATABLE READ

  • 同じトランザクション中では同じデータは何度読み取りしても毎回同じ値を読むことができる
  • ただしファントム・リードが発生する可能性がある

READ COMMITTED

  • 他のトランザクションによる更新については,常にコミット済みのデータのみを読み取る
  • MVCCはREAD COMMITTEDを実現する実装の一つ
  • ファントム・リード に加え,非再現リードが発生する可能性がある

READ UNCOMMITTED

  • 他の処理によって行われている、書きかけのデータまで読み取る
  • ファントム・リード,非再現リードさらにダーティ・リードが発生する可能性がある

ファントム・リード

  • 並行して動作する他のトランザクションが追加したり削除したデータが途中で見えてしまう現象

非再現リード

ダーティ・リード

  • 確定前の不完全なデータや,計算途中のデータを読み取ってしまう現象

トランザクション分離レベルと発生する現象の表

分離レベル ファントム・リード 非再現リード ダーティ・リード
SERIALIZABLE 発生しない 発生しない 発生しない
REPEATABLE READ 発生する 発生しない 発生しない
READ COMMITTED 発生する 発生する 発生しない
READ UNCOMMITTED 発生する 発生する 発生する

これらの分離レベルを実現するため暗黙的にロックが行われているはずだけど,ステートメントに応じてどのような暗黙的ロックが行われるのかはデータベースマネジメントシステムの実装による(と思う)

用語

MVCC

  • MultiVersion Concurrency Control(多版型同時実行制御)
  • データベース管理システムの可用性を向上させる制御技術のひとつ
  • 複数同時に処理要求が行われた場合でも同時並行性を失わずに処理し,かつ情報の一貫性を保証する仕組み
  • READ COMMITTEDを実現する実装の一つ

疑問とか

SLELECT, UPDATE, DELETEはレコードが存在しているので挙動を想像しやすいが,トランザクション開始時点でレコードの存在しないINSERTは挙動が想像しにくい たとえば,分離レベルがSERIALIZABLEである複数トランザクションから同一キーをを持つレコードをINSERTしようとした時はどのような挙動になるだろう?

推測

以下の観点から考えてみた

こたえあわせ

mysql> use isolation_test
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> set session transaction isolation level serializable;
Query OK, 0 rows affected (0.00 sec)

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| SERIALIZABLE   |
+----------------+
1 row in set (0.00 sec)
mysql> use isolation_test
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> set session transaction isolation level serializable;
Query OK, 0 rows affected (0.00 sec)

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| SERIALIZABLE   |
+----------------+
1 row in set (0.00 sec)

mysql> 
mysql> insert into id_tbl values (1, 'taro');
Query OK, 1 row affected (0.00 sec)

mysql> select * from id_tbl;
+----+------+
| id | name |
+----+------+
|  1 | taro |
+----+------+
1 row in set (0.00 sec)
mysql> select * from id_tbl;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

ファントム・リードが起きていないことの確認のつもりだったが,ブロックされてしまった

mysql> insert into id_tbl values (1, 'taro');

予想通り待たされる

ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'

これも予想通り制約違反となった

ちょっと深追い

概ね予想通りだったが,後続トランザクションでSELECTがブロックされたのが気になったので調べてみた

mysql> select * from information_schema.innodb_trx order by trx_id\G
*************************** 1. row ***************************
                    trx_id: 1817
                 trx_state: RUNNING
               trx_started: 2013-06-25 20:06:39
     trx_requested_lock_id: NULL
          trx_wait_started: NULL
                trx_weight: 3
       trx_mysql_thread_id: 24
                 trx_query: select * from information_schema.innodb_trx order by trx_id
       trx_operation_state: NULL
         trx_tables_in_use: 0
         trx_tables_locked: 0
          trx_lock_structs: 2
     trx_lock_memory_bytes: 376
           trx_rows_locked: 1
         trx_rows_modified: 1
   trx_concurrency_tickets: 0
       trx_isolation_level: SERIALIZABLE
         trx_unique_checks: 1
    trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
 trx_adaptive_hash_latched: 0
 trx_adaptive_hash_timeout: 10000
          trx_is_read_only: 0
trx_autocommit_non_locking: 0
*************************** 2. row ***************************
                    trx_id: 1818
                 trx_state: LOCK WAIT
               trx_started: 2013-06-25 20:07:11
     trx_requested_lock_id: 1818:6:3:4
          trx_wait_started: 2013-06-25 20:07:11
                trx_weight: 3
       trx_mysql_thread_id: 25
                 trx_query: select * from id_tbl
       trx_operation_state: fetching rows
         trx_tables_in_use: 1
         trx_tables_locked: 1
          trx_lock_structs: 3
     trx_lock_memory_bytes: 376
           trx_rows_locked: 3
         trx_rows_modified: 0
   trx_concurrency_tickets: 0
       trx_isolation_level: SERIALIZABLE
         trx_unique_checks: 1
    trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
 trx_adaptive_hash_latched: 0
 trx_adaptive_hash_timeout: 10000
          trx_is_read_only: 0
trx_autocommit_non_locking: 0
2 rows in set (0.00 sec)
mysql> select * from information_schema.innodb_locks order by lock_id\G
*************************** 1. row ***************************
    lock_id: 1817:6:3:4
lock_trx_id: 1817
  lock_mode: X
  lock_type: RECORD
 lock_table: `isolation_test`.`id_tbl`
 lock_index: PRIMARY
 lock_space: 6
  lock_page: 3
   lock_rec: 4
  lock_data: 3
*************************** 2. row ***************************
    lock_id: 1818:6:3:4
lock_trx_id: 1818
  lock_mode: S
  lock_type: RECORD
 lock_table: `isolation_test`.`id_tbl`
 lock_index: PRIMARY
 lock_space: 6
  lock_page: 3
   lock_rec: 4
  lock_data: 3
2 rows in set (0.00 sec)

先行トランザクションにより対象テーブルがロックされていてSELECTがブロックされているのかと思ったが,実際は先行トランザクションが排他ロックしたレコードを後続トランザクションが共有ロックしようとして競合しロック獲得待ちになっていた

CentOS 6.3にMySQL 5.6インストールでmysql-libs-5.1.61-4.el6.x86_64と競合する問題

CentOS 6.3にMySQL 5.6のLinux GenericなRPMをインストールしようとすると,

# rpm -ivh MySQL-server-5.6.12-1.linux_glibc2.5.x86_64.rpm
準備中...                ########################################### [100%]
        ファイル /usr/share/mysql/charsets/README (パッケージ MySQL-server-5.6.12-1.linux_glibc2.5.x86_64 から) は、パッケージ mysql-libs-5.1.61-4.el6.x86_64 からのファイルと競合しています。
        ファイル /usr/share/mysql/czech/errmsg.sys (パッケージ MySQL-server-5.6.12-1.linux_glibc2.5.x86_64 から) は、パッケージ mysql-libs-5.1.61-4.el6.x86_64 からのファイルと競合しています。
        ※※※ 中略 ※※※
        ファイル /usr/share/mysql/charsets/macroman.xml (パッケージ MySQL-server-5.6.12-1.linux_glibc2.5.x86_64 から) は、パッケージ mysql-libs-5.1.61-4.el6.x86_64 からのファイルと競合しています。
        ファイル /usr/share/mysql/charsets/swe7.xml (パッケージ MySQL-server-5.6.12-1.linux_glibc2.5.x86_64 から) は、パッケージ mysql-libs-5.1.61-4.el6.x86_64 からのファイルと競合しています。

のような感じで元から入っているMySQL 5.1の共有ライブラリと競合してしまう.

この共有ライブラリを一旦消して入れなおそうにも

# rpm -e mysql-libs-5.1.61-4.el6.x86_64                                     
エラー: 依存性の欠如:
        libmysqlclient.so.16()(64bit) は (インストール済み)postfix-2:2.6.6-2.2.el6_1.x86_64 に必要とされています
        libmysqlclient.so.16(libmysqlclient_16)(64bit) は (インストール済み)postfix-2:2.6.6-2.2.el6_1.x86_64 に必要とされています
        mysql-libs は (インストール済み)postfix-2:2.6.6-2.2.el6_1.x86_64 に必要とされています
# rpm -e postfix-2.6.6-2.2.el6_1.x86_64
エラー: 依存性の欠如:
        /usr/sbin/sendmail は (インストール済み)cronie-1.4.4-7.el6.x86_64 に必要とされています
        /usr/sbin/sendmail は (インストール済み)redhat-lsb-4.0-3.el6.centos.x86_64 に必要とされています

依存性にはばまれて中々めんどくさい.

この問題を以下のようにして解決できたのでメモ.

MySQL 5.6の互換共有ライブラリをインストール

# rpm -ivh MySQL-shared-compat-5.6.12-1.linux_glibc2.5.x86_64.rpm 
準備中...                ########################################### [100%]
   1:MySQL-shared-compat    ########################################### [100%]

「互換」がポイント. rpm -qiしてみると

This package includes the shared client libraries for previous MySQL versions.
Install this package, if you have applications that are dynamically linked
against older versions of the MySQL client library and you want to upgrade
to MySQL 5.6.12 without breaking the library dependencies.

と書かれていて,まさにって感じ.

MySQL 5.1の共有ライブラリを削除

# rpm -e mysql-libs-5.1.61-4.el6.x86_64
#

MySQLクライアント向けの共有ライブラリをMySQL 5.6の互換共有ライブラリに置き換えたことで,元から入っていたMySQL 5.1の共有ライブラリに対する依存性が解消されている.

MySQL 5.6 Serverのインストール

競合していたパッケージを削除できたので,あとはインストールするだけ

# rpm -ivh MySQL-server-5.6.12-1.linux_glibc2.5.x86_64.rpm 
準備中...                ########################################### [100%]
   1:MySQL-server           ########################################### [100%]
※※※ 後略 ※※※

カーディナリティが多対多の概念データモデルがなぜ関係モデルとして論理データモデルに変換できないか

今読んでいる本に
「多対多のリレーションシップが存在すると、そのままでは関係モデルとして取り扱うことができません。」
とだけ書かれていて,よくわからなかったので自分なりに考えてみた.

前提

  • まずいろいろ考えてて以下のことに気づいた

    関係モデルとして概念データモデルを論理データモデルへの変換する時,エンティティ間のリレーションシップは共通の属性を持つことで表現する

  • 次にリレーションシップのカーディナリティが多対多となる概念データモデルを考えてみる
    • エンティティ「売上」
      • 属性「売上番号」
      • 属性「売上日」
    • エンティティ「商品」
      • 属性「商品コード」
      • 属性「商品名」
    • 売上に複数の商品が含まれることも,商品が複数の売上に含まれることもあるので
      エンティティ「売上」とエンティティ「商品」間のリレーションシップのカーディナリティは多対多になる.

エンティティ「売上」に属性「商品コード」を持つことでリレーションシップを表現する場合の関係モデル

売上番号売上日商品コード
0012013-06-05Item017
Item044
Item057
  • 一つの売上に複数の商品が含まれる場合に商品コードのところがおかしなこと(非単純定義域)になってしまう.

エンティティ「商品」に属性「売上番号」を持つことでリレーションシップを表現する場合の関係モデル

商品コード商品名売上番号
Item017おもち001
007
059
  • 一つの商品が複数の売上に含まれる場合に売上番号のところがおかしなこと(非単純定義域)になってしまう.

というわけで,たしかにそのままだと関係モデルとして扱えないことがわかった.

おまけ

  • 関係モデルとして概念データモデルを論理データモデルへ変換する際,リレーションシップを表現するために共通の属性を持つときは
    「多」側のエンティティに「1」側の属性を持つ
    ようにする. (非単純定義域のことを考えると当たり前なんだけど,この当たり前がイメージできなかった…)

nginxのエラーログ

Note that error_log off does not disable logging - the log will be written to a file named "off". To disable logging, you may use:

error_log /dev/null crit;

オフにできないからレベルを上げたうえで/dev/nullに捨てるのかぁ.