Hatena::ブログ(Diary)

あどけない話

2014-12-04

HTTP/2 Frame test case の使い方

これは HTTP2 Advent Calendar 2014の4日目の記事です。

日本のHTTP/2 コミュニティでは、相互接続性を検証するためにHPACK test caseを作成し、好評を得ました。現在は、Frame test caseの作成に取り組んでいます。この記事では、Frame test caseの使い方について説明します。

正常系

現時点では、JSONフォーマットが確定していません。テストケースは、山本がとりあえず作ったものを個人のリポジトリに置いています。この記事がきっかけとなって、フォーマットの議論や他のテストケースの作成が進むと嬉しいです。

たとえば、正常なデータフレームは、こんな感じです。

{
    "error": null,
    "wire": "0000140008000000020648656C6C6F2C20776F726C6421486F77647921",
    "frame": {
        "length": 20,
        "frame_payload": {
            "data": "Hello, world!",
            "padding_length": 6,
            "padding": "Howdy!"
        },
        "flags": 8,
        "stream_identifier": 2,
        "type": 0
    },
    "draft": 14,
    "description": "normal data frame"
}

"error" が null なので、正常だと分かります。"wire"がネットワーク上に流れるフレームであり、16進数表記になっています。まず、"wire"データをパースして、"frame"と一致するか確かめましょう。もちろん、"frame" というJSONオブジェクトをあなたのコードで使われているデータ表現に変える必要がありますよ。

これは、正常なデータですので、逆も確かめます。すなわち、"frame"からフレームをビルドして、"wire" と一致しているか確かめます。

異常系

たとえば、異常なフレームはこんな感じです。

{
    "error": [
        6
    ],
    "wire": "0080000008000000020648656C6C6F2C20776F726C6421686F77647921",
    "frame": null,
    "draft": 14,
    "description": "data frame with frame size error"
}

これは "error" が null でないので、異常なフレームだと分かります。"error" には、可能性のあるエラーコードが(もれていなければ)すべて含まれています。"wire" 部分をパースし、返って来たエラーコードが "error" の中にあるか確かめましょう。

今後の課題

  • みんなで使ってみて JSON フォーマットを確定させましょう
  • みんなでテストケースを増やしましょう
  • それらをhpack-test-caseにマージしましょう

おまけ

なお、余談ですが、現在の仕様では "HTTP2.0" ではなく "HTTP/2" もしくは "HTTP2" が正しい名称です。

2014-06-25

Emacs 24.3/24.4 on Mac のフォント設定

Emacs で一番難しいのはフォントの設定です。特に Mac では地獄のように難しいです。とうわけで、Emacs 24.3 と来る Emacs 24.4 でうまくフォントを使うための設定を公開しておきます。

なお、Mac では素の Emacs を使ってはいけません。Emacs Mac port を使いましょう。パッチを当てるのは面倒なので、早く github なんかで公開されるといいですね。

ちなみに、素の Emacs を Dock から起動すると PATH を引き継がないので、はまります。Emacs Mac port なら PATH を引き継いでくれます。

フォントの設定

以下をお好みに合わせて変えて .emacs などに入れて下さい。

;; 以下はフレームの設定
(defvar my-frame-parameters
  '((height . 40)
    (width . 80)
    (top . 0)
    (left . 0)
    (foreground-color . "white")
    (background-color . "black")
    (cursor-color . "white")
    (mouse-color . "white")
    (tool-bar-lines . nil)))

(when (memq window-system '(x mac ns))
  (setq frame-title-format '(multiple-frames "%b" ("" invocation-name)))
  (setq default-frame-alist my-frame-parameters))

;; 以下が Mac 用のフォント設定
(when (memq window-system '(mac ns))
  (global-set-key [s-mouse-1] 'browse-url-at-mouse)
  (let* ((size 14)
	 (jpfont "Hiragino Maru Gothic ProN")
	 (asciifont "Monaco")
	 (h (* size 10)))
    (set-face-attribute 'default nil :family asciifont :height h)
    (set-fontset-font t 'katakana-jisx0201 jpfont)
    (set-fontset-font t 'japanese-jisx0208 jpfont)
    (set-fontset-font t 'japanese-jisx0212 jpfont)
    (set-fontset-font t 'japanese-jisx0213-1 jpfont)
    (set-fontset-font t 'japanese-jisx0213-2 jpfont)
    (set-fontset-font t '(#x0080 . #x024F) asciifont))
  (setq face-font-rescale-alist
	'(("^-apple-hiragino.*" . 1.2)
	  (".*-Hiragino Maru Gothic ProN-.*" . 1.2)
	  (".*osaka-bold.*" . 1.2)
	  (".*osaka-medium.*" . 1.2)
	  (".*courier-bold-.*-mac-roman" . 1.0)
	  (".*monaco cy-bold-.*-mac-cyrillic" . 0.9)
	  (".*monaco-bold-.*-mac-roman" . 0.9)
	  ("-cdac$" . 1.3)))
  ;; C-x 5 2 で新しいフレームを作ったときに同じフォントを使う
  (setq frame-inherited-parameters '(font tool-bar-lines)))

;; 以下の設定はお好みで
(setq resize-mini-windows nil)
(setq mouse-drag-copy-region t)

あわせて読みたい

2014-06-16

来る Emacs 24.4 を Mac で安定させる

もうすぐリリースされる Emacs 24.4 を Mac で使うと、ほんとうにイライラします。

なぜなら、

が頻発するからです。NEWS を読んでいて、以下を見つけました。

** New Core Text based font backend for Mac OS X 10.5 and newer.
To use the old font backend, use the following on the command line:
  % defaults write org.gnu.Emacs FontBackend ns
GNUstep and Mac OS X 10.4 use the old font backend.

新しいフォントバックエンドを使うのが問題かもしれないと思い、defaults を変え古いバックエンドで暮らししてみました。すると、嘘のように安定しました。

新しいフォントバックエンド用のコードが落ち着いて、設置を元に戻したくなったら、

 % defaults delete org.gnu.Emacs FontBackend

とすればよいようです。

2014-04-15

複数のGHCを共存させる

GHC 7.8.1 がリリースされ、type family がうまく扱えない問題が発覚したため、すぐに GHC 7.8.2 がリリースされました。このおかげで Yesod が、うまくビルドできるようになりました。しばらくして、GHC 7.8.3 もリリースされる気配があります。また、一ヶ月を目処に Haskell Platform 2014.2 が出るようです。

こうなると、GHC をうまく共存させてくれる Haskell Platform 2014.2 のリリースを待とうかと思うかもしれませんが、そんな必要はありません。GHC 自体に複数のバージョンと共存できる仕組みがあるからです。

GHC 7.8.2 を試したい人は、気軽にインストールしましょう。GHC 7.8.3 や Haskell Platform 2014.2 がリリースされたら、GHC 7.8.2 は消せばいいのです。ここでは、後で消しやすくするためのコツを紹介します。対象は、Unix 系の OS です。Windows のことは、誰かに任せます。

インストール

GHC 7.8.2のダウンロードサイトから、必要なバイナリを取ってきて展開します。以下、Mac の例:

% wget http://www.haskell.org/ghc/dist/7.8.2/ghc-7.8.2-x86_64-apple-darwin-mavericks.tar.xz
% tar zxvf ghc-7.8.2-x86_64-apple-darwin-mavericks.tar.xz
% cd ghc-7.8.2

ここで、単に configure を実行すると、/usr/local の下にインストールする設定になります。これでだと、インストールするファイルが散らばって、後から消しにくくなります。そこで、例えば /usr/local/ghc-7.8 というディレクトリの下にインストールするように決めます。

% ./configure --prefix=/usr/local/ghc-7.8

次に /usr/local/ghc-7.8 というディレクトリを作ります。僕は、このディレクトリを自分の所有にしています。root の所有でもいいですが、make install するときに sudo を使わないといけません。おかしなことは起りませんが、不安な人は自分の権限でインストールするのがいいでしょう。GHC 自体が自分の所有になっていても、GHC を壊した経験は僕にはありません。

% sudo mkdir /usr/local/ghc-7.8
% sudo chown あなたのアカウント /usr/local/ghc-7.8
% sudo make install

PATH に /usr/local/ghc-7.8/bin を追加すれば、GHC 7.8.2 のインストールは完了です。

削除

削除したくなったら、3つのディレクトを消します。

~/.ghc や ~/.cabal 自体は消してはいけません。こうすれば、~/.cabal/bin 以下のコマンドは残ります。コマンドは、静的リンクされているので、引き続き使えます。ただし、alex や happy のように lib に設定ファイルを置いているコマンドはうごかなくなるので、インストールし直す必要があります。

応用

この方法は、何も GHC 本体を消したい場合だけではなく、cabal でインストールしたユーザライブラリがおかしくなったときにも使えます。~/.ghc と ~/.cabal 以下のサブディレクトリを消せば、一から世界を構築できるようになります。

2014-03-01

RSSリーダ BazQux と DNS キャッシュ

BazQux(バズクックス)は、Google Reader代替として密かに注目されている RSS リーダです。実装と運用を一人でやっている Vladimir Shabanov さんによると、BazQux のウリは、

  • 高速である
  • ブログのコメントも表示できる
  • 複数のビューがある
  • モバイルに対応している

などらしいです。

BazQux のフロントエンドは、Ur/Web で記述されたコードから生成された JavaScript、バックエンドは Haskell だそうです。高速なのは、Haskell のおかげであると Vladimir さんは言っています。我々が開発している HTTP エンジンの Warp も使われているそうです。

現在、僕は Haskell 用の HTTP/2 ライブラリの作成に取り組んでおり、必要な技術を調べている過程で、redditでの議論のことを思い出しました。今回、よくよく読んでみると、僕が実装している DNS ライブラリを使っているが、ADNS (のラッパーである hsdns)と比べて安定しているものの、ドメインを検索できないことがあると書かれていました。

僕がDNSライブラリを書いたのは、不安定なADNSを見限ったためなので、安定しているのは当然として、ドメインの検索率が悪い点は気になりました。そこで、Vladimir さんにメールを書いたところ、すぐに返事が返ってきました。彼のコードと実際のドメインリストを見せてもらったところ、

  • DNSライブラリの使い方が間違っている
  • MVarを多用していて性能が悪そう (Vladimir は、そこはボトルネックではないと言っている)
  • HashMapで持っているキャッシュを定期的に枝刈りしてないのでメモリをたくさん使いそう

ということが分かりました。

奮い立ってしまった僕は、使ってもらえるか分かりませんでしたが、おもむろに concurrent-dns-cache というライブラリを作り始めました。設計のポイントは以下の通り。

  • DNSライブラリを正しく使う
  • セマフォを STM で実装する
    • 高並行な状況では MVar よりよいはず
  • キャッシュは Priority Search Queue で持つ
    • 優先度に指定した時刻で枝刈りできる。もちろん、ドメイン名で検索もできる。
  • ドメイン名を ShortByteString として扱う
    • ByteString は pinned オブジェクトなので、メモリが断片化する。このため Vladimir さんは Text を使っていたが、ShortByteString の方がメモり利用効率がよい
  • 高速な検索を実現するためにキーの第一要素としてハッシュ値を持つ

concurrent-dns-cache は、めでたく BazQux に採用され、以前よりもドメインの検索率が高く、少ないメモリで安定して動いているようです。高性能で高並列で安定しているネットワークコードをすばやく実装できるのが Haskell のよい点の一つですね。

今回の経験は HTTP/2 の実装にも役立ちそうでよかったし、現場の人と議論できたのはのは楽しい経験でした。

今少し悩んでいることは2つ。

一つ目:キーの定義は以下のようになっています。

type Hash = Int

data Key = Key !Hash            -- making lookup faster
               !ShortByteString -- avoiding memory fragmentation
               -- Haskell 2010 says: Derived comparisons always
               -- traverse constructors from left to right.
               deriving (Eq,Ord,Show)

実際のキーをハッシュ値と一緒にくるむことで、(MapからHashMapへの変更とは違って)コンテナの構造を変えることなく、検索速度を高速にします。みんな使いたいとは思いますが、ライブラリにするには小さすぎるので、どうしようかなぁという感じです。まだベンチマークを取っていないのですが、本当に有用そうなら小さくてもライブラリにするかもしれません。

二つ目:Priority Search Queue の本家本元のライブラリである PSQueueには、ある優先順位以上の要素を取り出す API はあるのですが、要素を取りさらった木を返す API はありません。そこで、僕は GHCソースコードから PSQ.hs をコピーしました。PSQueue の API はあまりよくないし、PSQ.hs はキーと優先順位の型が固定です。両者の API はまっく違うので、PSQueue を PSQ.hs で置き換えてバージョンアップするということもできません。

そもそも、Priority Queue がヒープの別名であることを知らない人は多く混乱の元なので、search-heap という名の別ライブラリでリリースしようかなぁ、どうしようかなぁ、という感じです。