今日もまた踏みました

非定期更新:主に何とも言えない事態にあった際に更新しています

Windows 10で電源ケーブルを抜くとプログラムが終了するという事案が発生

とりあえずですが、タスクスケジューラを利用していない方は該当しません…
また、初期のバグっぽいので、改善される可能性は大きいです

発生条件は、タスクスケジューラに登録されている電源の部分、赤枠で囲ってますが、「コンピュータの電源をバッテリに切り替える場合は停止する」の部分にチェックが入っている場合に発生します

f:id:itrident_kumakawa:20150806142049j:plain

Windows 10にアップグレードした方が主にこの現象に嵌ると思いますが、Windows 8.1までであれば、「コンピュータを AC 電源で使用している場合のみタスクを開始する」のチェックが外れている場合、「コンピュータの電源をバッテリに切り替える場合は停止する」のチェックのON・OFFに関わらず、OFFの扱いとなります

それが、Windows 10になり、独立して認識してしまっている様です

 

ですので、下記の様に両方のチェックを外しておく事でこの現象は解決します

f:id:itrident_kumakawa:20150806142054j:plain

 

PHP の try...catch で Exception がキャッチされない事案が発生

とりあえず結論を知りたいという技術者の方へ

とりあえず

catch (Exception $e)

catch (¥Exception $e)

に変更してリトライしてみてください。
それでもダメな場合は、エラーの内容を確認してみてください

PHP は throw した例外以外は catch しません

例えば 0 の除算とか… オブジェクトが null でしたとかは catch しないです
そちらの場合は、 set_error_handler を使う必要があります

まずは大丈夫なパターンから

このパターンは問題なくキャッチされる

<?php
class TestException extends Exception { }

class Test
{
  public function goTest()
  {
    try
    {
      echo "### Start ###¥n";
      throw new TestException('Test Exception!');
      echo "### End ###¥n";
    }
    catch (Exception $e)
    {
      echo "### Catch ###¥n";
      echo $e->getMessage() . "¥n";
    }
  }
}

$test = new Test();
$test->goTest();

 実行結果

 ### Start ###
### Catch ###
Test Exception!

正しくキャッチされている

似ているけどキャッチされないパターン

上記のソースに namespace を付けるとキャッチされなくなる

<?php namespace sample;
class TestException extends ¥Exception { }

class Test
{
  public function goTest()
  {
    try
    {
      echo "### Start ###¥n";
      throw new TestException('Test Exception!');
      echo "### End ###¥n";
    }
    catch (Exception $e)
    {
      echo "### Catch ###¥n";
      echo $e->getMessage() . "¥n";
    }
  }
}

$test = new Test();
$test->goTest();

 実行結果

### Start ###
PHP Fatal error:  Uncaught exception 'sample¥TestException' with message 'Test Exception!' in test.php:11
Stack trace:
#0 test.php(23): sample¥Test->goTest()
#1 {main}
  thrown in test.php on line 11

とまあ、キャッチされない

原因

当たり前は当たり前なんだけど…

catch (Exception $e)

と記載すると、 catch は sample¥Exception で待ち構えているため、 ¥Exception を継承した sample¥TestException はキャッチしない
なので namespace を付けた場合は、「 catch (¥Exception $e) 」としておくか、 use で Exception を宣言しておく必要がある様だ
他の言語に慣れていると、 Exception で全部キャッチできそうな気になるので気を付けておきたい
最も実害が無いので、常に ¥Exception にしておいても良いかも知れない

Apache を Proxy にしていたら Shift JIS 系のサイトが文字化けするという事案が発生

発生した理由

直接接続した場合のレスポンスヘッダ

Content-Type: text/html

Proxy 経由した場合のレスポンスヘッダ

Content-Type: text/html; charset=UTF-8

Content-Type に UTF-8 が追加されている…

疑う余地も無くこれが原因と断定した

 

まあ、PHP でアプリケーション作ってた Apache に無理やり Proxy 機能を追加したので仕方なかったですけどね…

 

対処

httpd.conf の Proxy ディレクティブに下記を追加

AddDefaultCharset Off

全体に影響しても良い場合は httpd.conf 内の AddDefaultCharset を変更すれば良いとは思うのだけど、今回は Proxy の場合のみ影響を出したかったの でProxy ディレクティブ内に記載とした

 

C# から SSH を使って MySQL を使う時に MySQL への再接続が遅いという事案が発生

とりあえず結論を知りたいという技術者の方へ

ConnectionString に下記を加えて変化が無ければここに答えはありません
頑張って次を探してください

Pooling=False

これで解決したものの上司への説明が欲しい場合は…

MySQL Connector / Net は IIS などで利用する事を前提にしているのか、Connection Pool  の機能が標準で ON になっている。そのため、MySQL をクローズしたのみでは切断されておらず、再接続の時にポートの解放待ちが発生するため、結果として再接続が遅くなる

とダメ元で言ってみてください…

 

開発ツールと利用ライブラリ

今回は下記を利用して問題が発生しました
Visual Studio 2010
SSH : SSH.NET Library - Home
MySQL : MySQL :: Download Connector/Net

 

本題はここから

お客様より、こちらで作成したアプリが「お昼休みに手を放してると午後に登録しようとするとエラーで落ちるんだよね」と言われた

MySQL を利用している WEB システムのメンテナンスプログラムを C# で拵えて利用して頂いていたのだが、お客様より上記のような話があがってきた
仕方ないので修正を加える事となったのだが、今回は SSH を利用して MySQL へ接続するという仕組みとなっていた。落ちているのは SSH なのか MySQLなのか…そこから考えなければならないのだが、そんなに頻繁にアクセスする類でもないので、検索や登録などのボタンアクションの開始で接続し、終了で切断する様に修正を加えた

結果、検索を 2 回連続でクリックしたら 2 回目が異常に遅いという現象が発生した

ちょっとトレースしてみたところ、再接続のタイミングで謎のウェイトが発生している事がわかった。そう言えば誰かがそんな現象が出るって言ってた気がする…
という事で追跡の旅は始まる

切断したのに切断されないという現象

MySqlConnection にて作成された connection に対して Close() と Dispose() を呼び出したが MySQL 側でウォッチしている限り切断されていない
何か切断手順が間違っているのかなとリファレンスをなめるが、それらしき答えは発見できなかった

今回は SSH 経由で接続してる事もあり、何か意外な事実があるのではと、SSH 側も調査を開始、まずはポートフォワードを行うための ForwardedPortLocal の切断処理をチェック Stop() を呼べている。だが MySQL は切断されない

それではと、SSH の切断も確認する。 Disconnect() と Dispose() を呼べている。だが MySQL は切断されない

Dispose してるから普通に考えたらありえないけど、オブジェクト生きてたりするのかなと、接続と切断の部分を中心に、ソースコードを洗い直してみたが、それらしき場所は見つからなかった

諦めて実証実験用のソースコードを作成

プログラム本体では、想定外の事があってもわかりにくいので、SSH 経由で MySQL へ接続するだけのサンプルを作成し、それを使って再現するかの実験を開始した

こんな感じで作成した

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Renci.SshNet;
using MySql.Data.MySqlClient;

namespace SSHTest
{
    class Program
    {
        static void Main(string[] args)
        {
            SshClient client = null;
            try
            {
                string host = "192.168.xxx.yyy";
                int port = 22;
                string user = "user";
                string sshPasswd = "password";
                string keyFile = @"keyfile.idrsa";

                PrivateKeyFile pkfile = new PrivateKeyFile(keyFile, sshPasswd);
                client = new SshClient(host, port, user, pkfile);
                client.Connect();

                uint dbPort = 3306;
                string server = "127.0.0.1";
                uint localPort = 13306;
                string dbUser = "dbuser";
                string dbPassword = "dbpassword";
                string dbName = "dbname";

                ForwardedPortLocal forward = new ForwardedPortLocal(server, localPort, "127.0.0.1", dbPort);
                client.AddForwardedPort(forward);
                forward.Start();  // ①

                var connectStr = "server=" + server + ";";
                connectStr += "port=" + localPort.ToString() + ";";
                connectStr += "user=" + dbUser + ";";
                connectStr += "password=" + dbPassword + ";";
                connectStr += "database=" + dbName + ";";

                var connection = new MySqlConnection(connectStr);
                connection.Open();  // ②

                connection.Close();  // ③
                connection.Dispose();  // ④
                forward.Stop();  // ⑤
                client.Disconnect();  // ⑥
                client.Dispose();  // ⑦
                Environment.Exit(0);  // ⑧
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
    }
}

結果… MySQL は切断されなかった
何か根本的な所に原因があるらしい

実際はこんな感じに動く

この仕組みの特徴的な所だが、SSH の接続完了後に ForwardedPortLocal がポートフォワードするために Listen する
今回は、127.0.0.1:13306 となる。MySQL Connector は、そのサーバー及びポートに対して接続を実施する。そのため接続先は 127.0.0.1:13306 となる

①の部分、「forward.Start(); 」を通ると、Forwardの準備が開始され
TCP 127.0.0.1:13306 0.0.0.0:0 LISTENING
となり、Listen される

②の部分、「connection.Open();」を通ると、MySQL へ接続され
TCP 127.0.0.1:13306 0.0.0.0:0 LISTENING
TCP 127.0.0.1:13306 127.0.0.1:zzzzz ESTABLISHED
となり、MySQLへの通信が開始される

③、④の MySQL に対する終了処理を実施しても変化なし
TCP 127.0.0.1:13306 0.0.0.0:0 LISTENING
TCP 127.0.0.1:13306 127.0.0.1:zzzzz ESTABLISHED

⑤の ForwardedPortLocal に対する停止処理を実施すると、Listenが終了し、
TCP 127.0.0.1:13306 0.0.0.0:0 LISTENING
TCP 127.0.0.1:13306 127.0.0.1:zzzzz FIN_WAIT_2
となる
この状態から⑥に移るまで、こちらの環境では30秒ほど必要だった

⑥の時点ではもうポートは閉じていた
TCP 127.0.0.1:13306 0.0.0.0:0 LISTENING
TCP 127.0.0.1:13306 127.0.0.1:zzzzz FIN_WAIT_2

⑦の Disconnect だが、ForwardedPortLocal を Dispose してしまうと落ちてしまうので、ForwardedPortLocal のDisposeを行わずに SSH Client を Disconnect した

ここで再確認できたのは、ForwardedPortLocal の Stop は Listen の終了だったらしく、それまでの接続を強制的に切断する事ではなかったらしい
また、時間がかかるポイントは、MySQLの切断ではなく、SSH で利用してる Forward ポートの解放だったようだ。切断の処理などを雑に作成していると再接続を行った際に待ち時間が発生するという理屈のようだ

本体のプログラムで感じた挙動と違うのは、本体側に接続ポートを転がす仕組みを入れていたためだった(ポートが握りっぱなしになる話を聞いて、この処理を入れたのだった…時間無かったんだよ…)。やはり再現確認用プログラムを作る習慣は大事だ

MySQL だけの再接続は実は早かった

今回の問題が発生した理由は、SSH までクローズしてしまっていた事だろう。ForwardedPortLocal を Stop せずに、そのまま MySQL を再接続する分には問題無く再接続できた
なら MySQL の再接続で問題無いじゃないとなる所なのだが、そうもいかない
切断されたのが SSH なのか MySQL なのかのハンドリングができなかったからだ
できなかった(方法が見つからなかった)ので、ボタンアクションで再接続という方向にシフトした。けど SSH ごと切断→再接続すると遅い。まだ何も解決していなかった

MySQL 側の設定を再確認

そもそもの所に頭を戻す。そう、そもそも③(Close)か④(Dispose)の時点で MySQL が切断されていないのが気持ち悪い。Dipose してるのに消えないとか何者よ…と思いながらリファレンスを参照し、設定項目の漏れを探す
ない、全然見つからない。そもそも設定できる項目少ないんだよね。この子
他に何か無かったっけと思い、Connection String にも設定項目がある事を思い出して、MySQL Connector で利用できる設定項目を眺める

MySQL connection strings - ConnectionStrings.com

ここのサイトを眺めていたら、とても気になる一文を発見
「Recycle connections in pool」
ようやく攻略の糸口を見つけた。クライアントアプリケーションとして作成していたので、完全に頭から離れていたが、そんな人居たよね Connection Pool さん
もしかしてデフォルトで ON になってるの?と思い、OFFにする設定を探す

有りました「Pooling=False」試しに入れてみるとようやく狙った通りに動いた

ありがた迷惑な感じではあるけれど、サーバーで利用する事を考えれば、接続を再利用する設定がデフォルトなのは当たり前と言えば当たり前なのか
SSH 経由の場合に、Forward ポートを握りっぱなしにされて困るとか、レアケースよね。けどまあ、実例のサイトも多いので、それなりに使ってる人居ると思うけど

とりあえず MySQL Connector をサーバーで使わない時は、Pooling=False を入れると頭に刻んだ

Google には繋がるが Yahoo には繋がらない

前に Google には繋がるが Yahoo には繋がらないという現象に対応した時の記録
時期は 2014 年の 8 月頃

 

お客様よりインターネットが見れなくなったという電話が入った
連絡を受けた内容を整理すると
・メールは受信できている
・インターネットは繋がらない
との事だった

不自然に思い、事前に用意しておいた TermViewer の QuickSupport を実行してもらった所、接続に成功した
※TeamViewer (http://www.teamviewer.com/) は遠隔操作のアプリケーション

とりあえず「インターネットは繋がらない」は正確な情報ではなかったらしい

Internet Explorer を開いてみると、デフォルトが Yahoo Japan になっているのだが「503」で落ちていた
ちなみに自分の PC で Yahoo Japan は開けたので Yahoo は落ちていない

この時点で、下記の状況となる
DNS は使えている(メール OK 及び TeamViewer OK )
・インターネットそのものには繋がっている
・そもそも Yahoo そのものが 503 になる事は考えにくい

その後、何気なく Google を開いたら、Google は見れた。Yahoo は見れないのに…
とはいえ、おかげで原因の見当がついてきた

Google と Yahoo の違い、一番大きいのは Googlehttps, Yahoo は http だという事
SSL で暗号化されたされた通信は通さざるを得ないと考えるのであれば、通信途中にあるフィルタリング系(ウィルスチェックやマルウェアチェックなど)が怪しい
ブラウザのプラグインやアドインの場合だと SSL 通信でも動作する可能性が高いが、中継する機器などの場合は、SSL 通信はスルーしてるはず
何とかできるのであれば、そもそも SSL 通信の覗き見が可能という事になるので

そう言えば FortiGate のライセンス更新した方が良いですか? を聞かれていたのを思い出したで、更新したかを確認した所、更新しなかったとの事
なので FortiGate の設定から HTTP のフィルタを外した所、Yahoo も正常に開くようになった

とりあえずライセンス更新してないからって、503 で落とすのはあんまりな気がします
FortiGate を使うのやめる時は、物理的に外す事が推奨の様だ

それにしても、Google さんの目指す全てのサイトは https にすべしが実装されると、FortiGate とか入れても全然対策にならないのでは…と思う今日この頃です

このご時世にInternet Explorer 11のみ派手にデザインが崩れる事案が発生

新しく作成しているサイトをテストしようと思ったら派手に壊れていた

今どき、どんな HTML + CSS になってるんだよと思われるかも知れない
関係者の名誉のために一言添えるのであれば、本来は Internet Explorer 11 でも問題なく表示されている。それがテストを実施しようと思った途端に壊れていたという状況となった

という事で追跡調査を行った

見た感じの壊れ方は、まるで CSS がまともにあたっていない感じだった、それもあり下記の内容を順次確認した

1. 他のブラウザでの確認 ( Firefox, Chrome ) ⇒ 正常に表示されていた
2. キャッシュの確認。キャッシュを全てクリア後に再表示。 Fiddler を使い、全てのファイルがダウンロードされているのも確認。 ⇒ 全て正常にダウンロードされていた
3. デザインのHTML ( こちらは正しく表示されている )と、読み込まれている CSS を比較 ⇒ 全く同じ
4. CSS の読み込み順序を同じに変更 ⇒ 変化なし
5. 読み込まれている JavaScript を全て停止 ⇒ 変化なし
6. HTML のタグを確認 ⇒ 全く同じ構成
7. 実は見てるファイルが違うのかと思い HTML を修正 ⇒ 変更される

いよいよ意味が分からなくなって来た所で、元のデザインにあり、作成後に入れ忘れている一文を入れてみた

<!--[if lt IE 9]>
<script src="http://html5shiv-printshiv.googlecode.com/svn/trunk/html5shiv-printshiv.js"></script>
<![endif]-->

ここで初めてデザインに変化が訪れた
入れてみたと言っておいてなんだが、変化がないはずだった… はずなのに、あれ? 変わってる? となった
Internet Explorere 11 では条件付きコメントは使えないはず…
それ以前に lt なのだから IE 9 以下のブラウザとなるけど 11 だし…
なのになぜ適用された? と思いつつようやく犯人の姿が見えてきた

思いもよらない伏兵は、こんな所に配置されていた

F12 を押して Developer Tools を表示したままにしていたのに、全く気が付かなかった
なぜ header タグが正しく認識されていないんだろう… 程度だった
よくよくブラウザの部分をみたら、「7」になっている。つまり Internet Explorer 7 での表示になっている

f:id:itrident_kumakawa:20150210195957p:plain
試しに edge に変更したところ、正常に表示された…とても悲しい

そしてどうしてこうなった

F12 を押して Developer Tools を終了した所、デザインが崩れる。結局の所 Internet Explorer 7 での表示に戻ったらしい
結論から言えば、デフォルト表示で互換表示になる理由があるようだ

何の気なしに Internet Explorer の互換表示設定を確認してみた

f:id:itrident_kumakawa:20150211035409p:plain

凄く気になる一文、「表示イントラネットサイトを互換表示でする
まさかと思いチェックを外してみた所、表示は正常に戻った

今回の現象は、Internet Explorer がアクセスした先はイントラネットだと解釈した事により、互換表示に切り替わってしまったという事になる

そして Internet Explorer のいうイントラネットサイトとは

見つかりました
http://support.microsoft.com/kb/884430/ja

: プロキシ構成が使用されない場合、Internet Explorer では、ホスト名にドット (.) が含まれない URL はローカル イントラネット ゾーンに割り当てられます。ドット (.) が含まれないホスト名は、ドットなし (ドットレス) ホスト名と呼ばれます。FQDN はインターネット ゾーンに割り当てられます。

だそうです
今回、名前ベースのバーチャルホストを使ってサイトを分けており、hostsに書いた名前にドット(.)は含まれていなかった。それが原因だったようだ
本番は IP Address で動かしていたので大丈夫だったらしい…

今日からhostsにサーバー名書く時は servername.vsie と名付ける事に決めた

OpenVPNで作成したVPNで大きなファイルのやり取りに失敗した

OpenVPNを使ってVPNを構築している場合に通信トラブルが発生した時の記録

 

OpenVPNを使って拠点間のVPNを下記の様な形で構築した

f:id:itrident_kumakawa:20150205031051p:plain

しばらく運用を実施していると、下記の様なトラブルが発生する様になった
pingは通る
vimを使うと止まる
・小さいサイズのファイルはcatできる
・大きいサイズのファイルはcatできない

原因はOpenVPNで扱う標準設定のパケットサイズが大きく、結果的にブラックホール現象の様な症状となっていた

色々と試してみたが、上記の構成の場合のパケットサイズは1280くらいが一番安定する

具体的には、confファイルに下記を追記する
tun-mtu 1280
fragment 1280
mssfix 1280