モーグルとカバとパウダーの日記

モーグルやカバ(EXカービング)山スキー(BC)などがメインの日記でした。今は仕事のコンピュータ系のネタが主になっています。以前はスパム対策関連が多かったのですが最近はディープラーニング関連が多めです。

EC2でのMeltdown/Spectreパッチでの性能低下

AWS EC2で動いるシステムが、1月4日のMeltdown/Spectreパッチ適用以降すごくパフォーマンスが落ち、その影響でシステム障害が発生しました。
そのとき、twitterFaceBookで情報いただいた件を簡単にまとめます。


EC2(m1.medium)上で動いている非常にDBヘビーな商用CMSがあり、年明け後そのCMSMySQLへのセッション数が溢れてしまうことが原因でシステム障害が発生していました。
調査すると1月4日以降、MySQLで域値を0.1sに設定してあるslow-queryの発生数が何倍にも増えていることがわかり、少なくともMySQLのパフォーマンス低下があることがわかりました。
1月4日にはEC2の計画再起動が行われており、これはMeltdown/Spectreパッチ適用によるものでした。


そのため、パッチ適用前と後のDB処理速度を数値的に比較できるものがないか探してみました。
MySQLのslow-queryのログに、毎日定期で重いバッチクエリのものが出ていたため、この処理時間の変動である程度比較できるのではと考えました。
そこで以下のように、4つの重いバッチクエリに対して、パッチ適用前後5日のクエリ処理時間平均を算出しました。

   適用前   適用後   倍率
A  226.26s  375.06s  1.66倍
B  110.09s  210.94s  1.92倍
C   30.68s   56.83s  1.85倍
D   23.18s   74.56s  3.21倍

1.6から3.2倍となっており、少なくともこれらのクエリについては2倍弱は遅くなっている感じがわかりました。


この結果から、twitterFaceBookで同様の状況になっている方がいないか聞いたところ、何人もの方から同じような状況だったり、そのためにインスタンスタイプを変更されているとのお話をうかがいました。

どうもEC2の旧世代PV方式インスタンスのものだと、特に今回のMeltdownパッチの影響を大きく受けるようで、少なくとも20%程度、RDBだとそれ以上の速度低下があるようです。
Auroraでは6割から7割程度までにパフォーマンスが下がっているというレポートもあり、それはこの障害で出ている状況とも感覚的に一致していました。
ただt2インスタンスなど、新しいHVM方式のものだと性能も上がっているうえ、パッチの影響によるパフォーマンス低下も少ないため、問題が起きないようです。


(参考)

MeltdownとかSpectreとか騒ぎがあったので、Amazon Aurora(MySQL互換)R4インスタンス再テスト(mysqlslap) - Qiita
Auroraでのパフォーマンスが63%〜73%程度に落ちていることがレポートされています。

投機実行の脆弱性修正、Haswell世代以前では性能への影響大 〜I/O集中型アプリケーションを利用するサーバーは慎重な選択を。AMDはほぼ影響受けず - PC Watch
Haswell世代以前の古いCPUのほうが影響を受けることが指摘されています。


ただし、AWSの公式見解としてはMeltdown/Spectreでの性能低下はほとんどなかった、という立場のようです。

AWSもSpectreとMeltdownの対策完了を報告。対策後、Amazon EC2で性能の低下は見られないと − Publickey


そのため、インスタンスタイプを

m1.medium → t2.medium

に載せなおすのが最善策ではないかという結論になりました。


PV方式のインスタンスからHVM方式のインスタンスへの載せ替えについて、下記エントリ

AWS EC2のインスタンスが新年早々各地で起動しなくなる可能性がある件 - 株式会社アクシア

に、EBSをほぼそのまま付けなおして移行する手法が、ある程度詳しく書かれていました。
HVM方式が始まったあたりにPV→HVMへの移行について書いてあるものは、変換が必要だったりともっと手数が掛かって大変なものが多いので、この手法が良さそうに思います。
ただ、bootのコピーなどの詳細までは書かれていないので、多少実験して経験を積んでから行ったほうが安全そうです。


先日twitterFaceBookで情報やアドバイスいただいた皆様、ありがとうございました。

MySQL-5.1からMariaDB-5.5へのアカウント情報の移行

CentOS6系からCentOS7系への移行案件があり、そこでMySQL-5.1からMariaDB-5.5への移行を行う必要がありました。
他にも色々と変わる部分があるため、一旦動作テスト用のサーバを作って確認することになり、データの移行を行いました。
そのとき、アカウント情報の移行ではまったのでメモしときます。


まずアカウント情報は普通にフルダンプしても取れないため、下記のようにmysqlテーブルを指定して取ります
正式移行の際には -x オプション付けてロックしたほうがよいでしょう。

# mysqldump -u root -p --allow-keywords mysql > user_dump.sql


その後、テスト用サーバにコピーしてリストアします。

# mysql -u root -p mysql < user_dump.sql


自分はもうこれだけでいいのかな、と思っていたのですが、これだけだと認証が通りませんでした。

まずバージョンが上がっているため、mysql_upgradeを掛ける必要があるそうです。

# mysql_upgrade -u root -p


また、移行元の環境はフロントのwwwサーバとバックエンドのdbサーバが分かれているのですが、テストサーバでは同一上で動かしているため、アカウントの接続元サーバ名の指定がそのままだと通らないので、クエリでlocalhostに書き換えました。

が、それだけでは動かないで悩んでいたところ、flush privileges掛ける必要があると教えてもらいました。

update mysql.user set Host="localhost" where Host="www";
update mysql.db set Host="localhost" where Host="www";
flush privileges;


これで無事に動くようになりました。

とみたさんありがとうございます!!

pearでXML_RPCのインストールに失敗する

とあるWebサーバのリプレース案件でPHP環境の移行準備をしていたのですが、pearXML_RPCを入れようとするとこんな感じにエラーが出てインストールできない問題が起きました。

# pear install XML_RPC
…略…
Could not download from "https://de.pear.php.net/get/XML_RPC-1.5.5.tgz", cannot download "pear/XML_RPC" (Connection to `de.pear.php.net:443' failed: )
Error: cannot download "pear/XML_RPC"
Download failed


手で試してみるとこの「https://de.pear.php.net/get/XML_RPC-1.5.5.tgz」をダウンロードすることは可能なのですが、SSL証明書が2013/8/21で切れており、セキュリティ例外に追加しないと取得できませんでした。
たぶんそのせいでダウンロードに失敗するのだと思います。


そこで下記のように、curlで一度tgzファイルをダウンロードし、ダウンロードしたファイルを使ってpearでインストールすることで、無事インストールすることができました。

curl -k -O https://de.pear.php.net/get/XML_RPC-1.5.5.tgz
pear install XML_RPC-1.5.5.tgz


まあ今となってはpearXML_RPCを入れたいという需要は少ないとは思いますが。

nseg94で「障害対応の失敗事例から学ぶこと」というLTをしました

久しぶりにnseg(長野のIT勉強会)に参加させていただき、LT大会だったので、LTもさせていただきました。


NSEG94
https://nseg.connpass.com/event/69877/


ETロボコンGoogle Homeプログラミングなど、自分はまだ触ったことないような新しいことの発表が多く、刺激を受けました。
やっぱり技術者は常に新しいものを触っておかないとダメですね…


あと、以前やったディープラーニングハンズオンに参加していただいた方と久しぶりに会って、ディープラーニング系の話が出来たりして楽しかったです。
最近は記事や本を読むくらいで、実際に動かして試すとかしてなかったので、また再開せねばと思いました。
もくもく会みたいなのやろうかな。


自分のLTは「障害対応の失敗事例から学ぶこと」という題で、障害対応で起きた問題のその本当の原因は何か、そして自戒も含めて撤退戦にはどう対処するべきか、というような話をしました。
…というと、なんかちょっと生産的な話っぽく聞こえますが、すごく後ろ向きで救いのない話でした。
なので、資料も配信もありません。


ちなみに質問で@kotyさんから、そのJKは真の問題点について上長に指摘したのか?という話がでましたが、ちょっと僕の解答外してたなと思うところがありました。ので蛇足ながら追記します。

JKは手順書ではなくスクリプトで自動化すべき、という提案をして、その際に今の技術者がいない状況では自動化しないと障害時に回らないから、という説明をしたらしいです。
(その時「この件は手順書の更新がされていなかったのが原因だ」という認識だったのでやる気が失せたそうです)
「対応できる技術者がいない」のが大元の問題である、というのは当然向こうもわかっているがそれはどうにもできない、ということで自動化の提案をしたそうです。
なので技術者を増やすべきだ、という提案はしていないそうです。


(追記)

ちなみに一つ前のエントリー

findで特定ディレクトリ以下のみを検索 - モーグルとカバとパウダーの日記
http://d.hatena.ne.jp/stealthinu/20171020/p1

がこの話関連だったりします。


@matchy3 さんが動画上げてくれてました。

長野のテック勉強会 #NSEG 94 フリーテーマプレゼン大会 - YouTube

findで特定ディレクトリ以下のみを検索

なにかで障害が起きて、メールスプールから障害時点以降のファイルだけを抽出したい、みたいなことって時々あると思うんです。てかよくありますよね!


そういう時は普通、findを使って抽出すると思いますが、Maildir形式で保存している場合、例えば

/var/mail/exampleuser/Maildir/dovecot.index
/var/mail/exampleuser/Maildir/maildirsize
/var/mail/exampleuser/Maildir/new/...
/var/mail/exampleuser/Maildir/cur/...
/var/mail/exampleuser/Maildir/tmp/...

のようなファイルとディレクトリ構成になっているとき、これを単純に

find /var/mail -mtime -5 -type f

とすると、検索したい対象は「Maildir/new/」と「Maildir/cur/」以下だけなのに、dovecot.indexとか、それ以外な不要なファイルが引っかかってくる可能性があります。

なので、検索するディレクトリを「Maildir/new/」と「Maildir/cur/」に絞りたいなと思いましたが、うまいことやる方法が思いつきませんでした。

というようなことをtweetすると、ふみやすさん @satoh_fumiyasuから -path オプションを使う方法を教えてもらいました。


find /var/mail \( -path '*/Maildir/new/*' -o -path '*/Maildir/cur/*' \) -mtime -5 -type f


findの使い方が解説してあるページでも「-path」オプションについて記述してるところ少なくて、とりあえずgnu findtuilsのmanページ

https://linuxjm.osdn.jp/html/GNU_findutils/man1/find.1.html


自分はこのtweet間に合わなくて、結局一旦textに吐いてからgrepで除去してやりました。
メールスプールに対してfind掛けるような行為はとにかくファイル数が多くて、すげえ時間がかかって泣けますね…

httpsのサイトがChromeのみ「このサイトへの接続は完全には保護されていません」と言われる

httpsのページだとURLの横のところに、保護されたページであることを示す「緑の鍵」マークが表示されるようになります。


とあるページをChromeで見ると、httpsのページなのですが「緑の鍵マーク」ではなく、「灰色の(i)マーク」となっており、ここをクリックすると

このサイトへの接続は完全には保護されていません

と表示されるものがありました。


ただ、FirefoxIEで見ると問題がなく保護されたページとして表示されていました。


ChromeのDeveloper ToolsでSecurityタグを確認したところ

Non-secure form
The page includes a form with a non-secure "action" attribute.

と書かれていました。


そこでHTMLソースを確認すると

<form name="tmp_gsearch" action="http://example.jp/search/result.html">

となっているところがありました。


Chromeの場合、ここで「action」に指定されている先が「https」ではなく「http」のため、この表示が出てしまうようです。
つまり、現在危ないのではないけど、ここからなにか操作したら漏れるから危険、ということですね。

MRTGなどでSNMPからCPU使用率を得る

MRTGでCPUのロードアベレージではなくCPU使用率を表示したい場合、ネットを検索するとSNMP

  • ssCpuRawUser.0(.1.3.6.1.4.1.2021.11.50.0) → ユーザCPU使用率
  • ssCpuRawSystem.0(.1.3.6.1.4.1.2021.11.52.0) → システムCPU使用率

で取れるという話が出てきます。


これをsnmpwalkで確認すると

$ snmpwalk -c example -v 2c localhost .1.3.6.1.4.1.2021.11.50.0
UCD-SNMP-MIB::ssCpuRawUser.0 = Counter32: 1043492

となにやらよくわからん数字が出てきてほんとにいいのか…?という気になります。


で、この話を調べていくと最初に詳細書かれた方のエントリがあるのですが、すでにサイトがなくなっていて参照できなくなっていました。

なのでWaybackMachineで探して読んでみました。


Net-SNMP & MRTGでCPU使用率 - 空振り日誌
https://web.archive.org/web/20070312134025/http://www.strikeout.jp:80/karaburi/2004/08/netsnmp_mrtgcpu_1.html

Net-SNMPのUCD-SNMP-MIB::ssCpuUserなどでCPU使用率[%]を取得したいけどDeprecatedになってるし、そもそも正しい値じゃなさそうだ。じゃあどうしたらいいのだろう。

CPU使用率[%]を単位時間あたりに消費されたCPU時間の割合と考える。その上でNet-SNMPMRTGでCPU使用率を取得するには、

  • UCD-SNMP-MIBのssCpuRaw〜がCPU時間の累積を表しているので、これを利用する
  • ssCpuRaw〜のカウンタ値の単位はtickらしい。Linuxでは1tick=10msらしい†1
  • MRTGのサンプリング時間は300秒(5分)†2で 300 * 1000[ms]だ
  • なので300,000ms間でCPU時間が何ms消費されたか分かればCPU使用率が出せる

以上を踏まえて、MRTGのデフォルト計算式は以下になるので

(今回の値 - 前回の値) / サンプリング時間

これをssCpuRawUserを例にして当てはめると

(今回のssCpuRawUserの値 - 前回のssCpuRawUserの値)[tick] / サンプリング時間[s]
= CPUタイム[tick] / サンプリング時間[s]

となる。これを以下のように計算する。

= CPUタイムT[tick] / サンプリング時間S[s] … (1)
= T*10[ms] / S*1000[ms] … 単位をmsにあわせる
= T / 100S … 約分する。ここまでCPU消費時間の割合が出る
= T / 100S * 100 … 百分率(%)で表したいので100をかける
= T / S … (2)
= CPU使用率[%]

結果的に(1)と(2)は同じ式になり、MRTGのデフォルト計算でCPU使用率が求められる。MRTGの設定としてはOptionsにguageも absoluteも指定しなければよい。ただ、これは1tick=10msだというのが前提なので、Kernelの設定なんかでこれが変更できたりする場合は適用できない。

前提条件がちょっと怪しいけど、vmstatを5分間隔で実行しときの結果とCPU使用率がほぼ同じになるので、これで良しとしている。


ということで、そのままMRTGに渡してやれば使えるようになってるとのことでした。
なので下記のように設定すれば良いようです。


mrtg.cfg 一部抜粋

Target[cpu_use]: .1.3.6.1.4.1.2021.11.50.0&.1.3.6.1.4.1.2021.11.52.0:example@localhost
MaxBytes[cpu_use]: 100
Options[cpu_use]: growright, noinfo, nopercent


(参考)

MRTGによるサーバ監視(SuSE編)
http://www.aconus.com/~oyaji/suse/mrtg_suse.htm

vagrantのVMファイルサイズを縮小する

今どきHDDの容量不足に苦しむことはあまりないと思いますが、SSDだけのマシンで多数のVMを動かしている場合なんかだと、容量不足であたらしいVMが立てられない… という悲しい状況に陥ることもあると思います。


開発用VM群の中で一台のVMが突出して容量を食っており、調べてみたところそのVMで使ってるMySQL上にアクセスログのような消しても良い、大量のデータが載っており、それを消せば何10Gか減らせることがわかりました。


しかし、実際にVMのファイルサイズが縮小されるまでには結構な手数と時間がかかったのでその手順をまとめました。


この例ではVMの名前やUUID(仮想サーバや仮想ディスクを指定するためのユニークID)が下記のようになっていたとします。

  • vagrantの仮想サーバ名「example」
  • 「example」のVirtualBoxディレクトリ名「example_default_12345」
  • 「example」のサーバUUID名「5612d588-c7f6-48c0-8ac1-0c20a541d030」
  • 「example」の元の仮想ディスクUUID名「1759a61c-7445-4c58-af32-540c348c4ff6」
  • 変換後の仮想ディスクUUID名「fcccb41b-2ed1-46c1-bb37-308610cc1229」

MySQLのデータベースファイルサイズを小さくする

VM内の不要なファイルや、データベース内の不要なデータを削除します。


truncateでテーブルを消してしまえばそれだけでファイルサイズが小さくなるそうです。

しかし、deleteでテーブル自体を残してデータだけを消した場合、データベースファイルのサイズはそのままで、小さくなりません

その場合、MySQLOPTIMIZEコマンドで削除されたデータの分を詰めて小さくします


VirtualBoxクライアント上の、MySQLのrootで

mysql> OPTIMIZE TABLE example_table;

この処理は結構な時間がかかります。


(参考)
第35回 OPTIMIZE TABLEでテーブルを最適化する:MySQL道普請便り|gihyo.jp … 技術評論社
http://gihyo.jp/dev/serial/01/mysql-road-construction-news/0035

使われなくなった領域を0で埋める

仮想ディスクを縮小する際に、使われていない部分の値が「0」である必要があります

単にファイルを削除しただけではこの状況にならないため、ディスク空き容量全体を「0」で埋めて、そのファイルを消すことで、空き容量全体の中身を空にします


VirtualBoxクライアント上のrootで

# dd if=/dev/zero of=zero bs=4k; \rm zero

この処理は結構な時間がかかります。


(参考)
仮想ディスクの圧縮 | VirtualBox Mania
http://vboxmania.net/content/%E4%BB%AE%E6%83%B3%E3%83%87%E3%82%A3%E3%82%B9%E3%82%AF%E3%81%AE%E5%9C%A7%E7%B8%AE

縮小可能な仮想ディスク形式に変換する

vagrantでマシンを生成するとVMDK形式の仮想ディスクになりますが、このVMDK形式だとディスクサイズを縮小することが出来ません

そこで縮小が可能なVDI形式の仮想ディスクへ変換してやる必要があります


VirtualBoxホスト上で、該当のディスクの情報を取得し、VDI形式に変換した仮想ディスクを新たに作成

$ vboxmanage list hdds
..略
UUID:           1759a61c-7445-4c58-af32-540c348c4ff6
Location:       /home/example/VirtualBox VMs/example_default_12345/_centos-6.6-x86_64-disk1.vmdk
Storage format: VMDK
..略

$ vboxmanage clonehd 1759a61c-7445-4c58-af32-540c348c4ff6 \
  "/home/example/VirtualBox VMs/example_default_12345/example-2.vdi" --format vdi

この処理は結構な時間がかかります。


また、一時的に同サイズの仮想ディスクが作られるため、そのファイルを作れるだけの空き容量が必要です。
一時的に他のVMイメージを他のストレージに移すなどして容量を空けます。


(参考)
VirtualBox の仮想ディスクのサイズを変更する - Qiita
http://qiita.com/niwashun/items/f71b0b805a6f97b514ec

VMの仮想ディスクを縮小する

VDI形式の仮想ディスクだとmodifyhdで「compact」という使われていない部分の仮想ディスクサイズを縮小することが出来ます。


VirtualBoxホスト上で、新たに作られたVDI形式の仮想ディスクの情報を取得し、やっと本来の目的である仮想ディスクの縮小をする

$ vboxmanage list hdds
..略
UUID:           fcccb41b-2ed1-46c1-bb37-308610cc1229
Location:       /home/example/VirtualBox VMs/example_default_12345/example-2.vdi
Storage format: vdi
..略

$ vboxmanage modifyhd fcccb41b-2ed1-46c1-bb37-308610cc1229 --compact

この処理は結構な時間がかかります。


(参考)
仮想ディスクの圧縮 | VirtualBox Mania
http://vboxmania.net/content/%E4%BB%AE%E6%83%B3%E3%83%87%E3%82%A3%E3%82%B9%E3%82%AF%E3%81%AE%E5%9C%A7%E7%B8%AE

小さくなった仮想ディスクに付けなおす

新しく作られた仮想ディスクはまだVMと紐付いていないため、元々のディスクイメージとの付け替えを行います


VirtualBoxホスト上でVMに紐付いているディスクを付け替えます。
このとき「storageattach」で「storagectl」「port」「device」で指定する値は「showvminfo」で仮想ディスクが結び付けられている行の値になります。
「storageattach」の説明されているエントリではこの値がよく違っているため、たぶんバージョンにより変わることが多いようなので、実際に「showvminfo」で確認してその値を使ったほうが良いです。

$ vboxmanage list vms
..略
"example_default_12345" {5612d588-c7f6-48c0-8ac1-0c20a541d030}
..略

$ vboxmanage showvminfo 5612d588-c7f6-48c0-8ac1-0c20a541d030
..略
IDE Controller (0, 0): /home/example/VirtualBox VMs/example_default_12345/_centos-6.6-x86_64-disk1.vmdk
  (UUID: 1759a61c-7445-4c58-af32-540c348c4ff6)
..略

$ vboxmanage storageattach 5612d588-c7f6-48c0-8ac1-0c20a541d030 --storagectl "IDE Controller" \
  --port 0 --device 0 --type hdd --medium fcccb41b-2ed1-46c1-bb37-308610cc1229

$ vboxmanage showvminfo 5612d588-c7f6-48c0-8ac1-0c20a541d030
..略
IDE Controller (0, 0): /home/example/VirtualBox VMs/example_default_12345/example-2.vdi
  (UUID: fcccb41b-2ed1-46c1-bb37-308610cc1229)
..略


(参考)
VirtualBoxの仮想ディスクをコピーしてみた。
http://takaq1.plala.jp/freebsd/9_0r/kasou_copy.html

動作を確認し不要なディスクイメージを削除する

vagrant upして問題なく起動するか確認し、問題なければ不要となった大きなディスクイメージを削除します。


VirtualBoxホスト上で

$ vboxmanage closemedium disk 1759a61c-7445-4c58-af32-540c348c4ff6 --delete


これだけやってやっとこ場所が空きますが、各処理がだいぶ時間がかかると思ってやってみてください。
自分でその辺の店に行って外付けディスク買ってきて繋いでやる!という気分になります…

URL中の文字をmod_rewriteのRewriteMapで全置換する

とある問題でURL中のクエリ文字列に含まれる「&amp;」を全て「&」に戻したいことがありました。
mod_rewriteを使ってなんとか実現できたんですが、結局もっと正しい修正箇所がわかったので使われませんでした。
が、せっかく新たに得た黒魔術だったので、エントリ化しときます。


URLが例えば本来は「/hoge?foo=1&bar=2&baz=3」となるものの予定が、この他に「/hoge?foo=1&amp;bar=2&amp;baz=3」のようにして渡ってきてしまうものもあるとします。

これを本来想定している「/hoge?foo=1&bar=2&baz=3」にmod_rewriteを使って戻したいわけですが、mod_rewriteでの置き換えでは「s/&amp;/&/g」みたいな全部置換するルールは簡単に書けません。


でこれをやるために、RewriteMapという機能から外部プログラムを呼び出すことで行うことが出来ます。
パラメータはSTDINから渡されるので、そこからプログラムで適当に修正してやれば全置換等もできるわけです。

http://httpd.apache.org/docs/2.4/mod/mod_rewrite.html#RewriteMap


mod_rewriteのための設定追加と、perlなどで書かれたフィルタが必要になります。


/etc/httpd/conf/http.conf

RewriteMap amp_replace prg:/etc/httpd/conf.d/query_amp_replace.pl
RewriteCond %{REQUEST_FILENAME} ^/hoge$
RewriteCond %{QUERY_STRING} ^(.*&amp;.*)$
RewriteRule .* /hoge?${amp_replace:%1} [R,L,NE]
  • フィルタを宣言するRewriteMapで、スクリプトのパスの前に「prg:」というMapTypeを付ける
  • RewriteRuleでフィルタを呼び出す時は「${フィルタ名:パラメータ}」という書式
  • QUERY_STRINGに当てたい場合、事前にRewriteCondで%1等にパラメータを取得しておく必要がある
  • RewriteCondで「&amp;」がある場合だけフィルタするよう制限しないとループしてしまう
  • RewriteRuleで「%」とかをエンコードされてしまわないよう「NE」を付ける

といったところを注意します。


/etc/httpd/conf.d/query_amp_replace.pl

#!/usr/bin/perl
$| = 1;
while(<STDIN>) {
  chomp;
  s/&amp;/&/g;
  print $_."\n";
}
  • バッファリングをさせないようにする(Perlなら「$| = 1」)
  • 出力の最後は改行が必要(この例だとchompしなければ"\n"は不要のはず)

という点を注意します。下記ページが参考になります。

Using RewriteMap - Apache HTTP Server Version 2.4
http://httpd.apache.org/docs/2.4/rewrite/rewritemap.html


これでどんな複雑な変換も可能になるわけですが、当然毎回perlなりスクリプトが起動するためパフォーマンスは悪いはずです。
ここでは対象となるプログラムの呼び出しで、かつパラメータに「&amp;」が含まれているときのみ起動するようルールが書いてあるため、そこまでではないと思いますが、注意が必要でしょう。

文法エラーのあるHTMLで古いIEのみjQueryがエラーを起こす

CMSで作られたページが、とある環境でのみjQueryの特定行でエラーがポップアップしてきてしまう、という相談を受けました。
ポップアップされる内容はjQuery内で「Invalid argument」でエラーが起きているというものでした。
エラーが起きているjQueryの箇所は「this.parentNode.insertBefore(a,this.nextSibling);」という部分でした。
何かシステム的な変更があったわけではなく、通常のHTML編集しかやっていなかったのに、いつの間にかこれが出るようになったとのことでした。


調査していくと、エラーが出るのがCMSの編集(プレビュー)画面のみで、他の環境で試すと問題が出なかったため、なんらかの環境依存であることがわかりました。
IE7/8/9 の(既にサポートが切れてる)古いIEでは同じエラーが出ており、開発者モードではないのでエラーがポップアップしないだけ(下部のステータス行には黄色「!」でエラーが表示されている)であり、CMS依存ではなくjQueryIE7/8/9依存であることがわかりました。


そこでエラーメッセージやエラーの出た部分の内容から「jquery Invalid argument nextSibling」あたりで探していくと、やっとこ該当しそうな件がありました。


[SOLVED] JavaScript Error in IE8 - Webpage error details "Message: Invalid argument." | Zyxware Technologies
http://www.zyxware.com/articles/2724/solved-javascript-error-in-ie8-webpage-error-details-message-invalid-argument

just check for HTML errors and fix them one by one.


HTMLに腐ってるところがあると、jQueryがパースする時にIE7/8/9のときだけエラーで止まってしまうようです。


今回の例だと、下記HTMLのように

<li><a href="…"><b>hoge<a href=…">fuga</a></b></a></li>
  • aタグが入れ子になっていること
  • aタグとbタグが「<a><b></a></b>」のように互い違いになっていること


というようなおかしなHTMLがありました。ユーザが修正時に間違って入れ込んでしまったようです。
そこでこのHTMLを修正することで、エラーが出なくなることが確認できました。


jQuery内のInvalid argumentなので、なにかのJSから呼ばれている時にパラメータがおかしくなるのだろうか、と思ってしまい、HTMLの文法の問題とはなかなかわからなかったので、同様の問題に当たった方の参考になれば。


…というか今さらjQueryIE7/8/9のみで起こる問題とか、参考になる方少ないでしょうけども…