kazuk は null に触れてしまった

C# / .NET 系技術ネタ縛りでお送りしております

月別アーカイブ: 10月 2010

Code Contracts あったまいー&あほー


こんなインターフェースにね。

    [ContractClass(typeof(BatchContract<>))]

    public interface IBatch<T>

    {

        bool Initialize(IBatchHost host);

        IBatchHost Host { get; }

        bool Initialized { get; }

        void Process(IEnumerable<T> items);

    }

こんな契約を書いて

    [ContractClassFor(typeof(IBatch<>))]

    public abstract class BatchContract<T> : IBatch<T>

    {

        public bool Initialize(IBatchHost host)

        {

            Contract.Requires<ArgumentNullException>( host!=null);

            Contract.Ensures(Initialized == Contract.Result<bool>());

            Contract.Ensures(Host == host);

            Contract.EndContractBlock();

            return true;

        }

        public IBatchHost Host { get; private set; }

        public void Process( IEnumerable<T> items )

        {

            Contract.Requires<ArgumentNullException>(items!=null);

            Contract.ForAll(items, _ => _ != null);

        }

        public bool Initialized {

            get; private set; }

    }

こうやって呼ぶ。

        [TestMethod]

        public void TestMethod1()

        {

            var host = new Mock.MockBatchHost();

            var batch = new Mock.MockBatch();

            batch.Initialize(host);

        }

実装がこうだと

        public bool Initialize(IBatchHost host)

        {

            return true;

        }

CodeContracts: BatchFrameworkTest: Run static contract analysis.

CodeContracts: BatchFrameworkTest: Static contract analysis done.

  elapsed time: 207ms

CodeContracts: BatchFrameworkTest: Validated: 33.3%

CodeContracts: BatchFrameworkTest: Total methods analyzed 12

CodeContracts: BatchFrameworkTest: Total time 2.460sec. 205ms/method

MockBatch.cs(14,13): warning : CodeContracts: ensures unproven: Initialized == Contract.Result<bool>()

IBatch.cs(28,13): warning :   + location related to previous warning

MockBatch.cs(14,13): warning : CodeContracts: ensures unproven: Host == host

IBatch.cs(29,13): warning :   + location related to previous warning

Test.dll(1,1): message : CodeContracts: Checked 3 assertions: 1 correct 2 unknown

すごいよ、Host プロパティ設定してないとか、チェックしてくれてるよ。

実装がこうだと

        public bool Initialize(IBatchHost host)

        {

            throw new NotImplementedException();

        }

CodeContracts: BatchFrameworkTest: Run static contract analysis.

CodeContracts: BatchFrameworkTest: Static contract analysis done.

  elapsed time: 276ms

CodeContracts: BatchFrameworkTest: Validated: 100%

CodeContracts: BatchFrameworkTest: Total methods analyzed 12

CodeContracts: BatchFrameworkTest: Total time 2.204sec. 183ms/method

Test.dll(1,1): message : CodeContracts: Checked 1 assertion: 1 correct

CodeContracts: BatchFrameworkTest: 

  elapsed time: 279ms

うひ、「つくってねーよ」が契約OKですかー

例外投げた時点で契約通りの挙動じゃないってのは解るんですが、これはちと「あほー」かなと。

ちゃんとテストを書いて動かそう。そうしないと未実装は Code Contracts では検出できないぜ!

ちなみに ReSharper 使いの人は ReSharper –> Window –> TODO Explorer で NotImplementedExceptionを投げている所を TODOとして検出してくれるよ。

(JetBrains の回し物だ、間違いない)

トランザクションを減らすという考え方


Windows Azure Storage Service を用いた在庫管理の設計 – The First Virtue – Site Home – MSDN Blogs

自分はこうしないなー等と思ってみた。

 

そもそもの「注文処理では、この在庫管理テーブルの商品から在庫量を読み取り、必要な注文数が確保できるかどうかを確認します。」の前提が自分の思う所とは食い違うって感じ。

 

歴史的な流れとして、システムのアーキテクチャで一貫性をどこが提供してきたでしょうか。

長らく使われてきた RDBMS に至る流れとしては基本的にはストレージが一貫性を提供するという物です。

「ストレージに保存されたデータは正である」と言っても良いでしょう。

そのストレージをいかに一貫して更新するか、出来るだけ競合を発生させない様にすると共に、発生したとしてアトミックな更新をどうやって提供するのかはストレージが原則として使われてきたわけです。

 

そして、ストレージが一貫性を提供するという考え方の基本は「マシンがいつ落ちるか解らない」という前提に立っているのです。マシンが落ちたとしてもデータを保持し続けるのがストレージの役目ですから。

さて、この前提ははたして現在でも正しいのでしょうか?楽観的悲観的どっちにしてもトランザクションを使って一貫性を提供する理由の第一の前提がこれです。

マシンがいつ落ちるか解らないという様な危うさはもう過去の話でしょう。(まぁ、数万台いればなんかしら落ちてるのは当然でしょうが、1台が頻繁に落ちるなんてのは無いというか、それ修理に出そうぜの世界です。)

 

前提はもう変わった、過去の話だとして考えるならどうでしょう。

1000個を売るのにWebサーバ(アプリケーションサーバ)が4台居たとして、それぞれに250個売らせれば良いなら各アプリケーションサーバに250個づつ売れという4トランザクションで1000個の物は売れます。

100個割り当てて30個売った時点でアプリケーション(の実行サーバが)落ちたとしての障害復旧を考えるなら売上のトランザクションデータを舐めれば70個売れてるって事は解るし、マシンが落ちてからの復旧の手順としてそれを織り込んでおけば1個単位が売れる事での在庫処理トランザクションは必要無いって事です。

受注を書きこむというトランザクションは当然にしっかりしている必要がありますが、そこが担保されているなら在庫処理トランザクションは割り当てられた個数売り切るまではアプリケーションサーバに割当たってるって事だけ知っててそれをカウントダウンすれば済む話です。マシンローカルというか、オンメモリの物だったら Interlocked でオペレーションすれば少なくともアトミック性や一貫性なんて物は自動的に提供されてるよねって話。

障害復旧で過去の全受注を舐めなきゃいけないとなると大きな話でWorkerRoleで多並列で処理してとか大きな事になりますが、在庫をアプリケーションサーバから割り当ててからの一定範囲を、そのアプリケーションサーバでとった受注を舐めるなんて別に大した話では無いでしょう。

 

Application_End で在庫を中央でのストレージベースの管理系に返す、とる時はがばっと取る、がばっと取ったというログレコードをちゃんと残して障害復旧時に舐める範囲を限定できるようにする。個別の受注時にはちゃんと受注明細を残す。これさえ満たせば受注明細を残すのは元々変わらないとしても在庫に関してはいちいちトランザクションを細かくする必要なんてありません。

一貫性を提供するのはどこの何か、安定して動く電源が確保されるならばそれはメモリで何の問題がありますか?電源的なトラブルとかで終了処理もまともにできない様なアプリケーションの落ち方はめったにないよね、あったとしてストレージで一貫性が提供される物をある程度舐めれば済む範囲であればそれを元に障害復旧できるよね。これが満たされる物であれば楽観的同時実行制御の様なトランザクション自体を消す事はできるし、トランザクションすなわち並行性に対する競合点ですので、消せばその分性能は上がるわけですよね。

 

てな感じで考える足がかりを作るとその先は結構トランザクションを消せるし減らせるっていうヨタでございました。

MS10-070 をインストールすると携帯向けのURL セッションが通らない


表題のとおりなんですが、皆さまご注意ください。

この現象は URL セッションで使われるセッションキーが URL 中に埋め込まれる時に MS10-070での暗号化方式の変更により IIS の下回りで動いているカーネルモードHTTPハンドラ(HTTP.sys)でのURLの規制値に引っかかってしまう事で発生します。

通常、携帯向けで URL session を使うと URL は以下の構造を持ちます。

http:// ホスト.ドメイン / セッションキー / サイト内URL

このセッションキーはご覧の通りで / に挟まれる区間ですので、その長さは KB 820129 IIS 用の Http.sys レジストリ設定 で解説されている UrlSegmentMaxLength によって最大長が制限されます。そして、この長さを超える場合には bad request となります。

HTTP.sys によってリクエストが不正として扱われる為、基本的に IIS W3C Log にも出力されず(Bad RequestなんだからMulformed Requestの可能性もあり、ログに出す項目のどこまで信頼できるか解らない&そもそもIISにリクエストが渡されないからIISログに乗り様がない)、アプリケーションをいっくら疑ってもその手前で弾き飛ばされるので厄介で、IISログや、イベントログをベースにエラー監視しているとすりぬけます(弊社のお客様サイトにおいて、数日間携帯での買い物ができない状態というのに気付かなかった罠、もうしわけございません。)

んで、UrlSegmentMaxLength がいくつなら引っかからないのさって事になると思いますが、弊社実績値で MS10-070適用以前に226バイトのキーが MS10-070の適用で 296バイトに伸びたという事で、少なくとも70バイト程度は増加すると言えます。むやみに大きな値を指定するとカーネルモードにデカイバッファを持っていかれたり、通常の制限値に依存してる他の何かがバッファオーバーフロー攻撃にさらされる危険も考えられるので無意味に大きな値を設定する事はお勧めできませんが、通常運用では既定値260の倍で520もあれば十分ではないでしょうか。

なお、この設定値の変更後はシステムの再起動が必要となります。IISの再起動ではカーネルモード HTTP.sys の再ロードおよび設定の再読み込みはされないっぽいです。

という訳で、携帯系で利用される Web サイトを開発、運用されている皆さま、ご注意ください。

追記:

この blog の内容は弊社で起こった問題事象を MS サポートに問い合わせ解決に至るまでの結果を元に弊社内での事象を含めて記述させて頂いております。
MSサポートの迅速な対応に感謝すると共に、経緯結果についての公開を承諾頂いた事に感謝します。

TFS 2010 & MSF Agile プロセステンプレートで「製品計画.xlsm」がうまく動かない時の対処方法


TFS2010 MSF Agile でプロセスを開始したら通常、チームエクスプローラーから「製品計画.xlsm」を使って当初計画を入力します。

image

この「製品計画.xlsm」がうまく動かないと大抵の人は困るでしょう。それに対する対処方法です。

リボンからマクロで、マクロ画面を出します。

image

編集をクリックしてマクロエディタを出し LocalizableStrings のVisible を

image

2-xlSheetVeryHiddenから、-1 – xlSheetVisible に変更します。

image

LocalizeableStrings のシートが見える様になりますので、これを編集します。

image

編集するのはB列のFields の内容になります。

image

Fields列の最初の「フィールド」を選択し、削除、上方向にシフトします。

「ストーリー ポイント」の次のセルに「状態」を入れます。

image

LocalizeableString の Visible を 2-xlSheetVeryHidden に戻してマクロエディタを閉じます。

このまま製品計画.xlsmを保存してあげましょう。

これにより、製品計画.xlsm がちゃんと動くようになり、バーンダウンチャートとかもきちんと表示されるはずです。

プロセステンプレートをいじる場合には、プロセステンプレートマネージャからダウンロードで取得した上で

MSF for Agile Software Development v5.0

→ Windows SharePoint Services

→ Shared Documents

→ Project Management

→ Product Planning.xlsm

にこのファイルの元ネタがありますので、これを修正しましょう。

以上、ハッピーな開発を! see you! (最後のノリがなぜか変)