ぽにょろん

思いついたこととメモ

c#からFirebirdのDBにアクセスした後、Exeの終了が遅い。その2。

Firebird Embeddedを使用した後、プロセスの終了が遅い件ですが、
その後色々調べたところ、似たような件で悩んでいる人はいるようでした。

stackoverflow.com

ただ、解決にはいたっていないようです。
試しに FbConnection.ClearAllPools() がどれくらい効果あるのか試してみました。

結論から言えば、ConnectionをPoolしているときはそれなりに効果ありますが、
やはり3秒は超えられない。
なにかのタイムアウト待ちなのか、どうなのか。

環境

  • Firebird Embedded 2.5.4
  • FirebirdSql.Data.FirebirdClient 4.8.0

検証

前回利用したものの最後に、ClearAllPools と Connection作る時にPooling を true にする処理を追加。

static void Main()
{
    var i = 0;
    var max = 2000;
    while (i < max)
    {
        var connectionBuilder = new FbConnectionStringBuilder();
        //.....諸々
        connectionBuilder.Pooling = true;
        using (var connection = new FbConnection(connectionBuilder.ConnectionString))
        {
            connection.Open();
            connection.Close();
        }
        i++;
    }
    FbConnection.ClearAllPools();
}

FbConnection.ClearAllPools() を呼ぶ場合と呼ばない場合の2種類を用意して、時間計測。

f:id:kowill:20151012230435p:plain

FirebirdClient2 : FbConnection.ClearAllPools()を呼ぶ方。
FirebirdClient : FbConnection.ClearAllPools()を呼ばない方。

あまり厳密ではないですが、手元で何度かやってみたけど、結果は変わらず。

c#からFirebirdのDBにアクセスした後、Exeの終了が遅い

c#からFirebirdのDBにアクセスした後のExeの終了が遅いということがありまして、少し調べてみました。

結論が正しいのかわかりませんがメモとして残しておきます。
※検証甘いです。

環境

  • Firebird Embedded 2.5.4
  • FirebirdSql.Data.FirebirdClient 4.8.0

現象

諸事情で、A.exe → B.exe といった呼び出しが複数回あるようなアプリ構造がありました。
B.exeの終了が遅いので、全体の処理がとても緩慢な感じに。
ということで、ミニマムコードを。

static void Main()
{
    using (var connection = new FbConnection(接続文字列))
    {
        connection.Open();
        connection.Close();
    }
}

これだけで、私の環境だとMainを抜けた後に3秒程かかりました。
ConnectionをOpenしなければ1秒未満です。

分析

まずは時間を。

var process = Process.Start(@"D:\tmp\FirebirdClient\FirebirdClient.exe);
process.WaitForExit();

//ExitCodeでHHmmss形式で終了時間をReturnするようにして、
//DateTime.NowをHHmmss形式にしたので差分をとる。
var elapsed = Utility.Subtract60(DateTime.Now.ToIntTime(), process.ExitCode);

Console.WriteLine($"Path: {path} / Seconds: {elapsed}");

f:id:kowill:20150927232141p:plain

ProcessMonitorで見てみると、

f:id:kowill:20150927230852p:plain

もしかして、また君ですか。
fb_trace_xxxには時折苦しめられておるのですが(笑)

解決策(??)

動作を見ている限り、fb_trace_xxxやfb_Lock_xxxなどのTempデータはNetProvider経由の場合、プロセス終了時にクリアされているようでした。
DelphiXE + FireDAC だと接続切っただけでクリアされていたような気がするんだけどなぁ。

ってなわけで、

[DllImport("kernel32", SetLastError = true)]
private static extern bool FreeLibrary(IntPtr hModule);

手動でアンロードしてみる。

  • FirebirdSql.Data.FirebirdClient → × (そりゃそうだよね..)
  • fbembed.dll → ○ パリィ(°▽°)
static void Main()
{
    using (var connection = new FbConnection(接続文字列))
    {
        connection.Open();
        connection.Close();
    }
    UnloadDll(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), @"fbembed.dll"));
}
[DllImport("kernel32", SetLastError = true)]
private static extern bool FreeLibrary(IntPtr hModule);

public static void UnloadDll(string path)
{
    foreach (ProcessModule dll in Process.GetCurrentProcess().Modules)
    {
        if (dll.FileName.Equals(path, StringComparison.OrdinalIgnoreCase))
        { 
            FreeLibrary(dll.BaseAddress);
        }
    }
}

f:id:kowill:20150927233213p:plain

これで良いのだろうか(笑)
動作に問題はなさそうなんですが、確かなことが分からず。
とりあえず、これで妥協してみようかなぁ。

試しにサンプル上げてみた。

github.com

.NET Framework 4.5.2 でXmlReaderのDTD読み込みの挙動が変わっていた

以前、少しはまってしまったので、メモしておきます。
先に解決策書いておくと、ちゃんとXmlResolverのインスタンス作成しておきましょう!です。

問題点

TargetFramworkを4から4.6に上げた際に、DTDの外部実体参照が読み込まれなくなってしまっていた。
その時の読み込み部分。

var reader = XmlReader.Create(path, new XmlReaderSettings { DtdProcessing = DtdProcessing.Parse })  
<?xml version="1.0" encoding="utf-8" ?>  
<!DOCTYPE Samples[  
  <!ENTITY Sub1 SYSTEM "Body\Sub1.txt">  
]>  
<Samples>  
  <Title>あいうえお</Title>  
  <Body>&Sub1;</Body>  
</Samples>  

Titleは取れるが、Body部分が取れてこない。
f:id:kowill:20150923092528p:plain
試しに、TargetFramworkを変更してみると、

という結果に。

解決

ということで、おなじみのMSDN

XmlReader.Create メソッド (System.Xml)

書いてありました。

f:id:kowill:20150923092020p:plain

ありがとうMSDN

さらに原文の方を見ると、

f:id:kowill:20150923092023p:plain

はひ。そうですか、そうですよね。 という感じに。

というわけで、

var reader = XmlReader.Create(path, new XmlReaderSettings { DtdProcessing = DtdProcessing.Parse, XmlResolver = new XmlUrlResolver() })  

みたいにしてあげれば良さそう。

f:id:kowill:20150923092726p:plain

というわけで解決。 そもそもの構造が良くなさそうってのもありますが、良い勉強になりました(笑)

ソースサンプル

XmlReaderでDTD読み込み

Postfix, Dovecotのversionを取得する

そう何度もやるもんじゃないので、version確認する方法をメモ。

CentOSの場合

よく使うのはCentOSなので、ついでに記載。

cat /etc/redhat-release  
>>> CentOS Linux release 7.2.1503(Core)  

Postfixの場合

postconf | grep mail_version  
>>>mail_version - 2.10.1  
>>>milter_macro_v = $mail_name $mail_versino

Dovecotの場合

dovecot --version  
>>>2.2.10  

CentOS7でテスト用のメールサーバーを立てる

仕事でプログラムからMail送信する機能があったため、テスト用のメールサーバー立てました。
手順を残しておきたかったので、メモしておきます。
公開するような設定にはなっていないです。

検証環境

postfix Install

まずはPostfixのインストール。

yum install postfix  
cp -p /etc/postfix/main.cf /etc/postfix/main.cf_yyyymmdd

postfix Settings

設定は必要そうなものだけ。

/etc/postfix/main.cf

myhostname = mail.hoge.test  
mydomain = hoge.test  
ient_interfaces = all  
mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain  
home_mailbox = Maildir/  

自動起動

systemctl enable posrfix.service  

保存先

mkdir /etc/skel/Maildir  
chmod 700 /etc/skel/Maildir  

サービスの再起動

サービスを一応Reload。

service postfix reload  

dovecot Install

つづいてDovecotをインストール。

yum install -y dovecot  
cp -p /etc/dovecot/dovecot.conf /etc/dovecot/dovecot.conf_yyyymmdd  
cp -p /etc/dovecot/conf.d/10-mail.conf /etc/dovecot/conf.d/10-mail.conf_yyyymmdd  
cp -p /etc/dovecot/conf.d/10-auth.conf /etc/dovecot/conf.d/10-auth.conf_yyyymmdd  
cp -p /etc/dovecot/conf.d/10-ssl.conf /etc/dovecot/conf.d/10-ssl_yyyymmdd  

dovecot Settings

こちらも必要なところだけ。
sslとかも切っておく。

/etc/dovecot/dovecot.conf

protcols = imap pop3  

/etc/dovecot/conf.d/10-mail.conf

mail_location = maildir:~/Maildir  

/etc/dovecot/conf.d/10-auth.conf

disable_plaintext_auth = no  
auth_mechanisms = plain login  

/etc/dovecot/conf.d/10-ssl.conf

ssl = no    

自動起動

systemctl enable dovecot.service  

サービスの再起動

service dovecot reload  

Firewall

ファイヤーウォールは必要なポートを開けておく。

firewall-cmd --add-port=25/tcp --permanent  
firewall-cmd --add-port=110/tcp --permanent  
firewall-cmd --reload  

UserCreate

ユーザーは適当に追加。

useradd test  
passwd test  

最後に

これで完了かと思って、動作確認してみたら、うまくいかず。
一度、rebootしたらつながったので問題ないはず。