日曜日, 1月 19, 2014

次世代Webアーキテクチャーの話 (CROSS2014を聞いて) このエントリーを含むはてなブックマーク


 CROSS2014の次世代Webセッションを見て来た。
セッションの前半で議論されていたプロトコル編はしっかりとした方向性が示されていたが、後半のアーキテクチャー編は現状の混沌とした話が多くて、方向性というか新しいビジョンを示すまではいけなかった印象だった。

それは、サーバのアーキテクチャーが成熟していることも理由の一つなのかもしれない。
しかし、アーキテクチャーこそ方向性を示すのが重要だろうと思うので、個人的に考えていることをまとめることにした。 

Webスケールを実現する技術とリアルタイムWebの方向性


  リアルタイムWebというわけではないが、密結合なプロトコルはことごとくインターネットで玉砕されてきた歴史がある。 

古くはCORBA IIOP、DCOMの失敗。それからSOAPの失敗。 
(ちなみに、IIOPのIはInternetで、当初は大規模なインターネットスケールで分散させようとしたことがうかがえる) 

これらは何でうまくいかなかったのか。 

それは、簡単に言ってしまえば、単一のマシンから複数のマシン、そして、インターネットへと拡張していく際に失うもの、つまり整合性や信頼性の問題を考慮してなかったからだと思う。(詳しくは補足を参照)

 IIOPにしてもSOAPにしてもライブラリのAPIのような形で提供して、それをインターネットを跨いで同期的に実行することを想定していた。
 
これらは整合性や信頼性の問題を解決することなしに、単一マシンのアーキテクチャーをそのままインターネットの信頼できないマルチ・マシン上に拡張してしまったのだ。 
(かつてIBMでCORBAのプロジェクトに多少からんでいた私は、インターネット上では通用しないことを嫌というほど味わった。早く気づくことができていたら(特にIBMは)莫大な投資をせずに済んだのに。) 

ところで、リアルタイムWebの方向性はどうなんだろうか。

将来においても、既存のWebアーキテクチャの延長線上にあるものなのか。それとも、別の違う方向を進むのか。 また、何かリアルタイムWeb技術が急速に進んで、既存のWebアーキテクチャーがそれに置き換わるというような話になるのか。 

クライアントとサーバが密に結合した形になってしまうと前述したようなトラウマが蘇ってくる。 

プロトコルの高速化によりある程度の適用範囲は広がるとは思うが、それでも局所的にしか解決できないだろう。 逆の言い方をするとリアルタイムWebを追求していくとNWやプロトコルの高速化でしか対応する術はないように思える。 

一方、既存のWebアーキテクチャというのは、http(s)とかプロトコルの話だけでなく、RESTなどを含むアーキテクチャスタイルの話が中心になる。

これは、permalink(URI)で示すリソースを操作する概念であり、基本的にステートレスである。 あらためて説明するまでもないが、RESTの「リソース」概念は次のようなものである。

・インターネットのすべてのものがリソースである。
・すべてのリソースは、URIによってアドレス可能である。
・リソースに対する操作は、HTTPの基本的なオペレーション、Get, Put, Post, Delete によって定義される

普通のWebサーバは基本的にはスタティックなホームページの配信を行い、動的なWebにしてもサーバで都度生成したものを返しているだけだ。

Webスケールといった言葉があるように、これはスケーラビリティに優れており、また、ネットワーク耐性にも優れている。

RESTは要はそのWebのアーキテクチャースタイルを壊さないようにWebサービスに応用したものである。 

また、キャッシュも重要な技術要素の一つである。これは、「誰か他の人のリソースを利用し、あるいは、他の誰かが、我々のリソースを利用することを認める」ような世界である。

Webスケールを実現できているのは、ブラウザキャッシュ、PROXY、あるいはCDNといったキャッシュ技術があるからといっても過言ではない。

他方で、ワンセグTVがフルセグTVより数秒のディレイがあっても気にする人はいないように、そもそも本当にリアルタイムである必要があるかという話がある。 

別のいい方をすれば、非同期的な更新で、eventuallyであっても十分ではないかということ。 例えば、一般的なWebアプリのユースケースを考えてみると、そこまでオリジナルにこだわる必要はなく、コピーの若干古い情報だとしても実用上問題はないケースも多い。むしろ、完璧に同じタイミングじゃないとまずいケースの方が少ないのではないか? 

これは妥協しろと言っているのに等しく、低レイテンシーを求めるリアルタイムWebとは相容れない考え方かもしれない。 

なので、個人的には、既存WebとリアルタイムWebは別の方向性をとり、リアルタイムWebが既存Webに置き換えられていくこともないだろうと考えている。

全体的な方向性としては、リアルタイムWebはそれはそれで盛り上がりつつも、既存Webのアーキテクチャー上をベースとして今後も進んでいくのではないだろうか。 

データのキャッシュ化を実現するためには


 一つの解として、HTMLなどがWebサーバやブラウザでキャッシュされるのと同様に、データにおいてもキャッシュさせるという考え方がある。 

そもそもデータは動的であり揮発性の高いものであるが、生成する際にバージョニングして静的に扱えるようにすることで、HTMLなどの静的コンテンツと同様、キャッシュを効かせることができればスケーラビリティは高くなる。 

バージョニングとは、例えば、データはレコード単位にURI+リビジョン番号を振ることでユニークであることを保証し、それが一致している限り同じデータとみなすような仕組みである。更新されるとURIは変化しないがリビジョン番号はカウントアップされる。 

正のデータはサーバ上にあって常に誰かによって更新されている。他のシステムはそのコピーを持ち、正のデータが更新されるとEventuallyに更新されることを前提に考える。ここでは遅延を許容することが肝要である。 

また、同じデータが複数のクライアントやサーバに散在している可能性も考慮し、更新する際は何回更新されても同じリビジョンであれば同じ結果になるようにする(べき等性を考慮する) 

ただし、データの整合性についてはシビアに考えなければいけない。これには、更新の際に元データのリビジョン番号が異なれば他者による更新があったということでエラーとするような楽観的排他の仕組み(Snapshot Isolation)で対応できる。

これにより、整合性の問題をWebのアーキテクチャースタイルを壊さないで解決できるようになる。 

去年だったか、permalinkの重要性は薄れて短縮リンクで十分という議論もあったが、整合性まで考えるとやはりpermalinkは重要であるように思う。短縮リンクはpermalinkのaliasという位置づけであろう。

サーバ負荷軽減のためには通信しないこと

 
 注意しなければならないのは、既に同じリビジョンのデータを持っているにもかかわらず、何度もサーバにリクエストを投げてしまうようなことである。これではキャッシュしている意味がない。 

CROSSセッションの議論でも、シングルページアプリケーションのデメリットとして、サーバへの通信回数が増えて負荷が大きくなるという指摘がなされていた。

「long pollなどNativeと同じで不必要な通信が多く発生するリスクがありしばしば作り方が問題になる。 初期化のタイミングがなくなる反面、サーバへの通信回数が増える。 最悪、ステータスコードを見ないでリトライするようなクライアントコードが書かれるリスクも考慮しないといけない」

シングルページだとパフォーマンス管理が難しくなったという意見であるが、そもそもサーバサイドのレンダリングを引きずられていると高速化は難しく、たとえデータバインディングに特化できたとしても、効率よくしないと今度はそれがパフォーマンスに悪影響を与えてしまう。

ステートレス性というか、キャッシュを利用してなるべく通信を発生させないようにする。また、発生しても必要最低限のデータに絞ることが大事である。

究極の形態がオフライン耐性をもったアプリであり、Offline Firstのような概念や方向性をもって作っていけば、おのずとNW効率のよいものが出来上がるのではないかと思う。 

クライアント・サーバ間のデータバインディング

 
 クライアント・サーバ間のデータバインディングは、具体的にはAngularJSに代表されるようなMVVMに加えてサーバとのデータバインド機能を追加するようなイメージとなる(図)


これまでの、クライアントからのリクエストによってサーバ上で動的にコンテンツが作られるスタイルから、基本的にクライアントとサーバが同じデータを参照しており、どちらかが更新したタイミングでお互いに通知されて同期されるような、クライアントとサーバとの双方向バインディングのスタイルに変化していくと思う。(リアクティブプログラミングという)

これからの時代、このようなクライアント・サーバ間のリアクティブプログラミングなどが求められると思うのだが、一歩間違えれば密結合の世界になって、スケールしなくなるのも懸念される。 

同期的な密結合ではスケールしないことは述べてきた通りであり、それは、XML/SOAPでもREST/JSONでも同じことだといえる。
 
実際、WebサービスのAPIは今ではREST/JSONが主流であるが密結合になってしまって残念なものも多く見られる。 

クライアント・サーバ間のデータバインディングでは、図にあるように、XHRでリクエストを受けつけるが、レスポンスについてはWebSocketで非同期更新する。

バージョニングされたデータが非同期更新されるのがポイントであり、WebSocketであっても同期的な密結合に陥ることはないのが特長である。 

あと、バリデーションの問題(サーバとクライアントでどうバリデーションするか)といった新たな課題もあるが、長くなるのでここでは触れない。

通信のデータ形式、それから、BaaS

 
 クライアント・サーバ間のデータバインディングでは、通信のデータ形式はXMLでもJSONでもMessagePackでも何でもいい。 むしろ同じように扱えるべきだと考える。 
データとは本質的には普遍的なものだから。

よく見かける、XML vs JSONとか、SOAP vs RESTなどの議論はたいへん不毛である。
データ表現を変えたところでアーキテクチャーが密結合であれば意味がない。

ちなみに、弊社のReflexWorksは様々なデータ表現に対応している。
 (先日、既存のReflexWorksシステムにおいて、XMLからMessagepack+Deflateに変更したところ処理速度が4倍(1034msから250ms)、データ量が2%(892,799bytesから21,255bytes)になった。なんと98%削減できたことになる。業務ロジックに変更はない。) 

現在、AngularJSのクライアントとReflexWorksサーバ間との非同期データバインディングが可能なBaaS、vte.cx engine(ぶいてっくす えんじん)を開発中である。

ソフトスキーマと項目単位に付与できるACLやグループ機能などのSNS機能もある。 

乞うご期待。 

(補足:http://stream.itmedia.co.jp/enterprise/pdf/pdf_16_1235461490.pdf より)
 

・整合性の問題
マルチ・プロセスからマルチ・マシンへ拡張するとき、我々は、状態を失う。「システム」のグローバルな状態というのは、虚構である。興味深い分散システムには、整合的な状態というものは存在しない。(Lamport:http://research. microsoft.com/users/lamport/pubs/pubs.html)分散OSのプロジェクトは、グローバルな状態を導入しようとしたが、大々的に失敗した。
しかし、障害の局所化(何かまずいことが起きても、システムの部分は生きのこる)を手に入れた。

・信頼性の問題
(インターネットの)信頼できないマルチ・マシンたちに拡張するとき、誰を信ずることが出来るか分からない難しい状況の中で、我々は信頼を失う。しかし、我々はスケール(webスケール、インターネットスケール)」を手に入れた。
 


火曜日, 1月 14, 2014

Web API認証について このエントリーを含むはてなブックマーク

 最近、Web APIの認証をどうすべきか考えている。

 例えば次のようなケースをどうするか。
 「既存のWebサイトがあり、既にユーザIDとパスワードによる認証によって、ブラウザでデータを提供している。 今回、この提供データをブラウザの画面ではなく、REST APIにて取得可能にしたい。 このデータはユーザ毎に取得可能な値が違うので、認証、または認可によって制限をかけたい。」

 ユーザーがブラウザからIDとパスワード(以下ID/PW)を使ってログインする方式を、そのままWeb APIにも適用しても安全なのだろうか。 Web APIの先にはスマホアプリやシェルスクリプトなどから直接ログインするものなどが考えられるが、安全かつシンプルに実装するにはどうしたらいいのだろうか。 

私はセキュリティの専門家ではないので間違った考え方をしている可能性もあるが、誰かの目に留まって助言いただけるかもしれないので、それを期待して今考えていることを率直に述べようと思う。

APIで安全な認証はそもそも作れるか

 
 そもそも認証については原則ブラウザを使わないとダメなんじゃないかと私は考えている。
 その理由は、ブラウザじゃないとサイトの安全性について確認することが困難だから。
ブラウザであれば、利用者はアドレスバーでドメイン名を確認できるし、https通信されていることやベリサインシールも確認できる。その逆にアドレスバーを隠してあるサイトは信用できないしパスワードを盗む偽サイトかもしれない。
 世の中を見渡すと、ほぼ全ての認証の基本になっているのがブラウザからのログインケースであるように思う。

 一方、API認証で、たとえID/PWで認証できるようなAPIを作ったとしても、ブラウザのようにサイトの信頼性を確認する手段がないため、利用者が安心して使えるものを作るのは難しいのではないかと思う。
 例えば、コマンドラインのプロンプトやスマホアプリの画面でID/PWを入力するアプリを作ったとすると、どこで認証するかをユーザは正しく認識する必要があるが、送信先URLに不正がないことなどを確認するのは難しいと思う。(※1)  
 ただ、利用者に毎回ID/PWを入れさせなくてもアクセス制御さえきちんとできれば十分というケースもあるだろう。このようなケースであれば後述するOAuth2.0や認証キーを使う方法などがある。

API認証のパターン


 認証(認可)の方法は、大きく分けて以下の3つがあると思う。 


1.ID/PWを送信するBasic認証やDigest認証(含むWSSE)  
2.(ブラウザの管理画面等で発行する)認証キーを使う方法  
3.OAuth2.0 アクセストークンを使う方法  

1.のBasic認証APIでID/PWをハードコードして使うケースはよくありがちで、このような安易なやり方はセキュリティ上まずいのはわかる。 APIはプログラムから利用されることを前提として作られているため,PWリスト攻撃/辞書攻撃を試みるクラッカーにとって攻撃に役立つ便利な機能になり格好なものになることは容易に想像できる。

 2.の認証キーを使う方法では、まず管理者がログインして画面を開き、そこに表示されている認証キーを使ってAPIからアクセスする。MBaaSでもよく使われており、ID/PWを入力しないため安全な方法といえる。

 3.のOAuth2.0では、アクセストークンをAPIの認証キーとして使う。アクセストークンを入手する際に最低一回は必ず、正規のWebのログイン画面で認証する必要がある。 

DropboxUploader(https://github.com/andreafabrizi/Dropbox-Uploader)はbashでOAuth2.0を使っている。(しかし実際に導入してみるとわかるのだが設定がちょっと面倒くさい) 

OAuth2.0は「認可」である。これを使うということは、つまり、認証はAPIでは行わないことと同意となる。認証キーを使う方法も、Authorizationヘッダに付与するOAuth2.0 Bearerも同様に「認可」の一部であると考えられる。認可はユーザによって正しく認証されていることが前提であり、この方式を使えばパスワードが罠サイトに送信されるような心配もない。 

2.と3.は認証と認可を別々に分けることでリスクを減らすという考え方で、これらを採用できるのであれば特に問題はないし、また冒頭に挙げたケースもこれで解決できるかもしれない。 OAuthのアクセストークンを使えばAPIで認証やる意味はないという意見もアリだと思う。ということで、OAuth2.0などの認可の話はここで終わり

 問題は、1.のような「認証」をAPIでやってよいかどうかということである。  

次にこの認証の論点に絞ってもう少し掘り下げてみる。

APIの認証をやらなくなった現状


 Google Data APIには以前、Client Login方式 (https://developers.google.com/accounts/docs/AuthForInstalledApps)というのがあったが今は廃止されてOAuth2.0に移行している。 

かつてのClientLogin方式はID/PW認証ができてとても便利だったのを覚えている。しかし、しばしばCAPTCHAロックが発生するため、アプリでID/PW固定で使うのは困難であった。結局、Googleは不正ログイン対策のためID/PW方式を諦めざるを得なかったのだと思う。(廃止理由:http://adwords-ja.blogspot.jp/2013/09/adwords-api-clientlogin.html) 

GoogleのClientLoginの件もしかり、昔流行ったWSSEにしても、 https://www.google.co.jp/search?q=WSSE で検索してもわかるように、多くのサービスがOAuth2.0に移行している現状がある。これは、リピート攻撃に弱くて筋が悪いと言われているWSSEを廃止したいことや、Webの認証機能に一元化したいという背景があるように思う。また、前述したような、ユーザがどこで認証するかを正しく認識する必要があることや、認証と認可を分けて考えればそもそもAPIに認証が不要だったというケースも多いのかもしれない。

でも、よく考えてみると、WebのログインフォームからはID/PWが送信されているわけで、API認証を廃止したところでログインフォームの動作をAPIでエミュレートすることが可能である。むしろそれを禁止する方が難しく、例えば、ログインページをスクレイピングしてtokenを切り取るなどすれば大概はエミュレートできる。(ただ、ワンタイムトークンなど発行することで難しくすることはできる) 

ブラウザでできることをプログラムから実行するのがなぜまずいのか。 今のところ、ログインフォームのようにID/PWをうまく保存する方法がないとか、不正ログイン対策のためのCAPTCHAをプログラムでハンドルするのが難しい、ぐらいしか説明が見つからない。

ログインフォームなどのセキュリティ強化策


 ID/PWを送信するAPI認証がセキュリティ上まずいとはいったものの、そもそもログインフォームのエミュレートを防げないのであればAPI認証の非公開にかかわらず意味がないし、結局はログインフォーム自体のセキュリティを強化する必要があるように思う。 

例えば、生PWを送信するリスクをどう考えるか。 ブラウザのログインフォームにしてもAPIにしても認証を行う際には必ずID/PWを必要とするが、生のPWを送信するときには細心の注意が必要である。(かつて、URLパラメータにPWを含めてしまうとRefererに機能によってリンク先に送出されることがあったように) 
またSSLで暗号化していてもログに書かれてしまうことで漏洩の危険性が高まるため可能であれば生で送信はやめた方がよいと思う。 

そこで、Basic認証は使わずにDigest認証やWSSEのようにパスワードをハッシュ化(※2)することでパスワードそのものの推定を難しくすることを考えてみる。これで万一漏洩した際にも同じパスワード使ってるサービスへの影響が少なくなるというメリットが生じる。

 個人的には、(今さらかよ!と思うかもしれんが)WSSEをハッシュ化して送信する方法で十分だと考えている。ただし、サーバではリピート攻撃に耐えうるようにWSSEをワンタイム化する。 そのまま保存してしまうとハッシュ化パスが事実上のパスワードになってしまうため、PWをさらにハッシュ化(または暗号化)したものを保存する。 生パスワードを知らないので漏れることも悪用(生パスワードを保存しておいて、他のシステムの認証にも使ってしまうようなこと)される心配もないためユーザに対して安心感は増すと考えられる。 また、初回だけWSSEを送って認証に成功したらサービス側で有効なトークン(セッションID)を発行してクライアントは以後毎回それを送るというブラウザでのセッションクッキーと同等の仕組みにすると毎回パスワードを入力しなくてもよくなるのでユーザビリティが増す。

これを実装するのに、Javascriptでパスワードをハッシュ化してXHRで送信することになると思うが、CSRF対策も考える必要がある。 
http://matome.naver.jp/odai/2133794394551702001 
http://utf-8.jp/public/20131114/owasp.pptx (P24-25に対策が載っている) 

不正ログイン対策まで考えるのであれば、CAPTCHAの他にリスクベース認証や二要素認証などの防御対策の他、不正アクセスがないかチェックするためのログイン履歴機能なども必要になってくるだろう。つまり、API認証だけのセキュリティを考えるのではなく、ログインフォームを含む認証全体の機能についてよく考えないといけないという話になる。

APIKey+WSSE


 最後に、どうしてもAPIで認証したいという場合の簡易的な方法について思いついたことを紹介する。 

これは正規のIdpからお墨付き(APIKey)をもらうことでログイン認証できる権限を得る方法である。 
具体的には、WSSEのこれまでのPasswordDigest生成ロジックを改良したもので、管理画面から入手したapikeyを先頭にくっつけてsha256にしてbase64エンコードするだけ。(注:元のWSSEはsha1だった。WSSEはワンタイムである必要がある) 
 PasswordDigest = base64(sha256(APIKey.base64_decode(Nonce).Created.Password)) 
 ID/PW以外にsecretのAPIkeyを付けることで2要素認証的な意味でセキュリティを強化できる。 また、アプリごとにトークンを使い分けることや、(不正ログインなどの)問題が発生した場合にキーを無効にできたり、より柔軟な運用が可能になる。複数回ログイン失敗で検知できる仕組みは必要だが、CAPTCHAなどのロックの仕組みは必要ないだろう。

 ただ、このようなオレオレ認証にはリスクが伴う。 
snapchatの事件(http://www.thread-safe.com/2014/01/46m-usernames-phone-numbers-leaked-by.html) も起きたばかりなので慎重に考えていきたいところである。


(2016/1/6 追記) BaaS vte.cxでは結局、こんな感じで整理した。=> セキュリティについて

(※1) もしクローズドなHTTPクライアントだとしたらどうやってSSLチェックしてホスト証明したと示せるのかが問題になる。まずはクライアント自体が信頼できるものであることを証明できないといけない。詳しくはこちらの資料(http://www.slideshare.net/shunsuketaniguchi520/ssl-28267938)のP7-を参照。適切なサーバ証明書の設定により接続先のサーバの正当性は検証できるが、スマホアプリにはアドレスバーがないものが多く、利用者が能動的に接続先や接続プロトコル(https)を確かめるのが困難である。

(※2) Saltが無いハッシュでは生パスワードが推測可能になるという危険があります。sha256(APIkey+nonce+日時+パスワード)のnonceがソルトに相当します。さらにDBに保存するときにストレッティングして保存、または暗号化します。

(追記)
@ritouさんからご意見いただいた。感謝です。



ごもっとも。
ちょっと、Resource Owner Password Credentialsについて調べてみます。

https://github.com/applicake/doorkeeper/wiki/Using-Resource-Owner-Password-Credentials-flow によれば、

以下のリクエストをOAuth Providerに投げると、

{
  "grant_type"    : "password",
  "username"      : "user@example.com",
  "password"      : "sekret",
  "client_id"     : "the_client_id",
  "client_secret" : "the_client_secret"
}

以下のように、access_tokenが返ってくるので、

{
  "access_token": "1f0af717251950dbd4d73154fdf0a474a5c5119adad999683f5b450c460726aa",
  "token_type": "bearer",
  "expires_in": 7200
}

あとは、Authorizationヘッダに"Bearer " + access_token
をAPI実行時のヘッダに付ければいけます。

PS. しかし、これはPWが生で送られている。パスワードを送信してたらログに書くとかろくでもないことをする人が出てくるという経験則がある。
オレオレ認証作ったやつには税金かけろみたいな話( http://togetter.com/li/617280 )が聞こえてきて(;゜Д゜)ガクブル だしなあ。困った。

月曜日, 9月 16, 2013

Flashをミッションクリティカルシステムで使ってよいものか このエントリーを含むはてなブックマーク

 9月11日に出たFlash Playerのセキュリティパッチ(v11.8.800.168)に不具合があり、ニコニコ動画、サイボウズ、Samsungなどで大規模障害が発生した。
 Sumsungによれば、一日あたり$600,000の損失で、少なくとも10万人に影響が出ているとのこと。 
Daily estimated damage is $600K. This is from the customer’s quote - just one company but we're now having calls from many other companies.Samsung's groupware (mySingle) is using Flash as its UI and more than 100,000 people in Samsung are having problem.
 14日に緊急修正パッチ(v11.8.800.174)が出たが、これにも不具合が残っており、Googleストリートビューが見れなくなるなど、16日現在も影響が続いている。 弊社のReflexWorksもFlashを使った印刷機能があり、影響が出ている。

原因となっている問題の箇所

 168で私が把握した不具合はExternalInterface関係で以下の2つ。 詳しくは、bugbase を参照    
  • IE8以下でFlashの関数をJSから呼ぶことが出来ない
    • FLASHオブジェクトは見つかるが内部の関数を呼ぼうとすると「is not a function」でエラー発生
  • Flash -> JSコール時のパラメータが文字化け
 174で新たに発見したのが以下の不具合
  • Flash -> JSコール時の戻り値が文字化け 
  暫定対応  

 少なくとも174であればFlashの内部関数は呼べるので、Flash -> JSコール時において、エンコード/デコードすれば問題を回避できる。 参考:http://site.oukasei.com/?p=79

Adobeの対応のまずさ

不具合Flashがもたらす破壊力は大量破壊兵器に匹敵するものである。 
もちろん、ストリートビューが見れなくてもすぐに困ることはない。被害者であるGoogleもそれほど騒いでいるように見えないのは、これを機にIEからChromeへの乗り換えを期待しているんじゃないか、という邪推もある。
   しかし、インターネット上であっても、ミッションクリティカルな業務は当たり前になり、Flashを使った業務アプリも多数存在している。それがFlashの不具合のせいで日々の業務を行えずに大変な事態になってしまったのだ。 
 Adobe社は、今回のセキュリティパッチを、9・18 サイバー攻撃への対応に間に合わせようと相当急がれたんだと思う。不具合をパラメータの部分だけ直してリリースしてしまい、戻り値の部分は直さずに見過ごしてしまった。つまり、テストもろくにせずに出していたことがバレて世界中に恥をさらすことになってしまったのだ。不具合に対応しているはずの緊急パッチにも不具合があったなんて前代未聞である。不具合は2バイト文字の文字化けという単純なものであり、英語圏の方々は発見できなかったことは想像できるにせよ、対応がお粗末すぎて空いた口が塞がらない。


 Adobeさん、お願いだからテストしてくれ!リソースを減らしすぎてテスターいないなら私に声をかけてくれ! 
 HTML5に印刷コントロールさえあれば私もFlashなんて使わないのにと心底思う。  
 これじゃ、皆に見限られてもしょうがないよ。

<追記>

  •  この不具合(BUG3630443)は11.8.800.156 (beta)で発見されていたにもかかわらず修正されずにリリースされたもよう。9・11同時多発テロに合わせて攻撃するつもりだったのか!?
  • 9/18 test build が出たもよう https://sendnow.acrobat.com/Receive.aspx?i=hZcy1XNdqp6Qn57eviBpWw
  • 9/24 こっそりFIX版である11.8.800.175が出たようだが何の説明もない。 http://blog.livedoor.jp/fs00r612/archives/51960262.html
  • 9/25 昨日オープンになった11.9.900.96(Beta)に同じBugが残っていることが判明。これがリリースされるとまた大騒ぎになる。┐(´-`)┌ http://labs.adobe.com/downloads/flashplayer.html 
  • 9/25  11.9.900.110 で修正されていることを確認。ε-(^、^; ほっ.
  • 10/10 Win8+IE10+Flash(11.9.900.117)で不具合発生。なんでこんな変更を突然勝手にやるんだ!(`⌒´♯)
    • niconico動画のxsplit broadcasterが動かない状態に(http://com.nicovideo.jp/community/co1050862?zeromypage_nicorepo)

日曜日, 12月 23, 2012

【BPStudy#64】RDBじゃだめな理由を2つほど このエントリーを含むはてなブックマーク

皆さん、昨日はお疲れ様でした。 また、運営していただいたBeProudの皆さん、ありがとうございました。

10人くらいだったら完全非公開にしようと思ったんだけど、30人くらい来てくれたので公開することにしました。 UST録画はこちら



BDBの代わりにRDBのインスタンスを複数配置するんじゃだめなの?というのはよく聞かれる質問だけど、昨日も当然のように質問された。

いつもうまく答えられなくて歯がゆいけど、それは、O/Rマッパーも必要なくなって、RDBを導入管理する必要がないから。だけど、そこはやっぱり伝わんなかったかな~。

O/Rマッパー無くしてソフトスキーマで


開発の4割がマッピングといわれ、 それに、テーブル設計、正規化、SQL、チューニングなど、 ほとんどがデータベースまわり。要は、これを全否定したいわけ。 直接オブジェクトを格納するわけだからO/Rマッパは不要になる。

 昔、EUC(Enduser computing)とか言ってたっけ。 
Lotus Notesでアプリ開発したことある人ならわかると思うけど、 RDB不要な感じでスキーマを自由に作れる開発環境は、(流行ってるかどうかは別として)、珍しいものでも何でもなく、一度経験するとその高い生産性に慣れてしまって、もう元には戻れなくなる。RDBは大変な負荷だったんだと気づく。

最近では Google App Engineや最近ではBaaSといったWebサービスもソフトスキーマで、アプリケーション内で自由に項目を設定できる

ACIDのDどうすんの?

   だって、APサーバごとにRDB導入するわけにはいかないでしょう? だから普通はDBサーバのインスタンスを分けようという話になる。でもそれだけでスケールするんだったら苦労なんてしないわけ。 
 結局、AP側にキャッシュを入れようという話になって、今だとRedisが使われるじゃないですか。で、永続化すればI/Oが詰まるので、どう溜めるかをよく考えないといけなくなる。つまり、ACIDのDをどうするか。トランザクションリカバリーとか、むしろ障害対策を頑張って作る必要があるんだけど、皆さんどんなふうに解決しているんだろうね。Append only fileで頑張ってる? まあ、2.4で非推奨になったVirtual Memoryを自力で作るぐらいの覚悟があれば大丈夫なんだろうけど。
 一方のBDBは、DBMSと同等のACIDがあるので、トランザクションログからのリカバリもDBMSと同じようにできる。単純なログファイルに書かれるだけだから、スケールアウトして複数ノードになったとしても運用はチョー楽チンだ。

BDBは、他のKVSと比較して特別に速いもんじゃないし、高機能というわけじゃない。
でも、APのプロセスで動作する一つのjarがあって、処理自体は当然メモリー内で実行されるからキャッシュ技術と同等のパフォーマンスぐらいは出せる。また、APサーバ内だけで完結するからDBサーバは実質的になくても動作する。この辺りが魅力かな。

ついでにスクリプト言語も(ry


もうそろそろ、サーバはデータを返すだけでよくないか? 

Javaなんかダセーって、一方で、HTMLタグが混在したスクリプト言語のソースを堂々とgithubで晒している。そして、パフォーマンスを競って、サーバのレンダリングを一所懸命チューニングしている。 どうかと思うよ。

twitterの例はあるとは思うけど、レンダリングはクライアントでもう十分だろ?

こんな感じで、乱筆すみませんでした。

みなさん、よいお年を。

火曜日, 12月 11, 2012

ReflexWorks開発における選択と集中の話 このエントリーを含むはてなブックマーク


何事も選択と集中が大事です。時間もお金も人も無限にあるんだったら何も苦労しません。私たちは、ReflexWorksを作るにあたって何を自作すべきか、あるいは他を利用する方がいいのか、色々悩みながら開発してきました。

標準化技術は採用すべきでしょうし、車輪の再発明は避けるべきでしょう。それは当然です。私たちも無い機能は作るし既にあれば利用させていただくという方針で基本的にはやっています。

かつてJava全盛の時代は楽でした。単にJavaの標準化技術を後追いすればよかったからです。でも、今はなかなか思うようにいきません。

10年ぐらい前からJavaの動きに鈍くなり、後追いだけでは対応できなくなってきました。
今盛んに訴えられている、JavaScriptやThin Server Architectureの重要性の話なんて(私には)4年ぐらい前のメッセージに聞こえます。

ReflexWorksもThin Server Architectureが基本ですが、既に大規模事例があり1年以上前から運用されています。
ということはどういうことか。システム構築開始は2年前で、ReflexWorksの開発はもっと前ということです。

今、JavaのThin Server Architectureに影響を受けた人が、これからフレームワーク作ってシステム構築して運用したとして、評価されるまであと何年かかるのでしょう?そのときデビューして果たして競争力はあるのでしょうか。(ReflexWorksに競争力があるとは到底思ってませんが・・)

JSONにしても2006年時点では自作するしかありませんでした。シリアライザもXML pull parserもXStreamを改造して使っています。
でも、これは当時の事情でしかたなくです。今ではご存知のようにJAX-RSなどのJavaの標準APIがあります。でも遅いんです。

今更、それに準拠するためだけにフレームワークを修正することは、少なくとも私たちにとっては無意味です。
それは、ReflexWorksフレームワークのテストには数年、数百人月という膨大な工数が費やされており、高い品質を維持しているからです。
品質は不具合の数だけでなくパフォーマンスやセキュリティなど総合的な評価が大事です。いくらJava標準といっても品質が高いとは限りません。

テストがきちんと実施され本番でも期待通りに動作している。これが最も価値ある状態であり、これでやっと他のお客様にも自信をもって最高のパフォーマンスを提供できるのです。

一方で、そのクオリティをさらに高める努力をしていくつもりです。継続的に投資していきます。

例えば、MessagePack対応。

ReflexWorksのボトルネックである、通信とシリアライザと永続化の3つの大きな要素のうち、通信とシリアライザについてMessagePackを採用することで大きな改善を見込めます。

残る永続化の部分ですが、私たちはKVSをゼロから作るなんて無謀なことはいたしません。世の中にある優れたKVSを利用しようというスタンスです。
現在はOracle BDB JEと、Google App EngineのDatastoreの2つを採用しています。(選択できます)

Oracle BDB JEを採用している理由は信頼性です。いい意味で枯れており、情報が豊富で、何よりOracleの技術サポートを受けられます。(保守契約が必要)
FAQを見ていただけるとわかりますが、トランザクションログのリカバリやトラブルシューティングも大変わかりやすく運用も楽です。

BerkeleyDB JE FAQ

トランザクションはロック方式ですが、現在はそれほど大きなボトルネックになってはいません。(他の改善効果に比べて低い)

パフォーマンスに優れたKVSは他にもいろいろありますが、トランザクションをサポートしており、かつ安心して利用できるという点で、BDB JEには一日の長があると考えています。

とはいえ、私が知らないだけかもしれません。他に信頼性に絶対的自信のあるKVSがあればぜひご紹介ください。
ちなみに、次の候補としては、Amazon DynamoDBを検討しております。



月曜日, 12月 10, 2012

バーチャルがリアルに語ります。本物のクラウドを。 このエントリーを含むはてなブックマーク


BPStudy#64の集まりが悪いので決めました。 少人数だからできる本音トーク。資料も上げたりしません。tweetもやりません。ふっふっふ。 プライベート・クラウドは良くないアイデアっていうけれど本当は凄いこと。規模の経済性だけでは語れない、本当のプライベートクラウドのメリットは確かにある。それを僕は今回のプロジェクトで体験した。 この感動を来てくれた皆に伝えたい。

300万トランザクション/日を処理するスケールアウト型オンライン帳票システムの仕組み



Agenda(案) ※内容が変わる可能性があります
  • 分散KVSについて
    • なぜ分散KVSを採用したか
      • RDBのインスタンスを複数持つんじゃだめなの?
      • RedisやMongoDB、Cassandra、HBaseじゃだめなの?
    • 分散KVSを業務アプリで使うには
  • トランザクション処理
  • スケーラビリティ
    • シャーディングのPros/Cons
  • パフォーマンス
    • 本当のボトルネックはどこか
    • Thin Server Architecture
      • 動的コンテンツをサーバで作成しない
      • 帳票作成においてサーバに負荷をかけない
  • 可用性
    • ミッションクリティカル。でも実際は・・。
    • 実際に障害に遭遇した話
  • まとめ
    • 全体最適解としてのプライベートクラウド

土曜日, 12月 01, 2012

ajn22のBTで発表してきました このエントリーを含むはてなブックマーク


ajn22、皆さんお疲れさまでした! 

また大変盛り上がって楽しかったですね〜。ajnのこの独特の楽しい雰囲気の源は一体なんだろうといつも思います。単なる事例紹介とは違って、秒間最大2000reqを処理する大規模なサービスだったり、SharededCounterの議論だったり、機械学習だったり、あるいは、クライアントMVCやリアクティブプログラミングといったことを、皆さん独自の視点や発想で研究実践され、そしてそれが惜しげもなく披露される。それは刺激的です。 

そりゃ、勉強も大事ですよ。並列分散トランザクション技術やリアルタイム処理、関数型言語・・・、いろいろ勉強しないといけない。でも、それらは実際に使われてナンボなわけで、そこからは実践的なプラクティスはなかなか生まれてこない。

ところがajnでは実際に数億のトランザクションを運用していてCounterがボトルネックなんですよ〜という話が出てきて議論が始まり、そこで、実はShardedCounter がいまだにベストプラクティスということを知るわけ。過去議論したSkiplistとか、Memcacheの利用、あるいはロックフリー技術とか駆使して何とか解決できればいいのだけれど現状はまだまだという感じ。これは大きなテーマだから今後もぜひ研究していきたいところですね。私自身、モチベーションの原点を再発見した感じです。 

あと、DEMOを見ていただいた方にお願いです。

以下のFacebookページにもいいねしてあげてください。(昨日言い忘れてました)


公報一括ダウンロードサービス

以下にTwitterのまとめと僕の資料を置いておきます。
それでは。







金曜日, 8月 17, 2012

ぶいてく流 スケーラブルアプリの作り方 2012 (前編) このエントリーを含むはてなブックマーク

明日のクラウド温泉3.0@小樽の発表資料の前編(導入部分のみ)を公開しておきます。ちょっと詰め込みすぎて1時間以内に終わらないこと確実なので・・・。あらかじめ読んでおいていただけると嬉しいです。

日曜日, 7月 08, 2012

RESTに関する3つの間違い このエントリーを含むはてなブックマーク

楽観的排他制御を利用する非同期的なトランザクション実行であればスケーラビリティを損ねることなく2phase commitが可能である。これは、分散KVSにおけるスケーラビリティと一貫性の両立について で主張したように、同期的な2phase commitは密結合に誘導することになるため、矛盾するように思えるかもしれない。だがそんなことはない。
前半はまずこの話から入るが、後半ではRESTに関する間違いについて、3つほど思うところを述べたい。

楽観的排他制御と2phase commit

 reflexworksではFeedやEntry単位でatomicなトランザクション処理を行えるが2phase commitはサポートしていない。これを許すと密結合になってスケールしないからである。だが、これはあくまで同期的な処理の話であって、ネットワーク障害への耐性を考慮され、非同期処理やオフラインで使えるのであれば2phase commitでもスケールすると考えている。
 RESTにおける2phase commitについては、REST におけるトランザクションについて (Re: Web を支える技術) が詳しい。
トランザクションレイヤを RESTful に表現する手法は理解しておいて損がないものだと思います。具体的には、
  • トランザクションの開始は POST を使ったトランザクションリソースの生成
  • トランザクション開始後は POST ではなく PUT を使い、クライアントがリクエスト ID を指定することで、ネットワーク障害への耐性を確保
  • トランザクションのコミットも再送可能じゃないと困るので PUT
  • コミットの返り値を確認してからトランザクションリソースを DELETE
といったあたりが基本になると思います。
ポイントは、クライアントがリクエスト ID を指定する点。IDはPOSTで返却されたリソースIDになるが、楽観的排他制御のためには、論理シーケンス番号 (LSN)も同時に必要になるはずだ。
もう少し具体的に説明しよう。

reflexworksでは、LSNに該当するものはリビジョン番号であり、POST /buy でリソースURL(/buy/934085,1)が返却される。(この,1がリビジョン番号で更新の都度+1される)次に、 PUT /buy/934085,1 を実行。もし既に更新されていたらリビジョン番号不一致となるため、409 conflict(optimistic locking failed)が返る。DELETEも同じくリビジョン番号を指定して実行する。
前述の例では自分しか更新しない前提ではあるが複数回実行される可能性は考慮すべきところで、べき等性の保証が必要なのだが、この場合は楽観的排他エラーで「既に更新されています」というメッセージを出してあげる方が親切だと思われる。
ちなみに、同一リソースURLでリビジョン番号も同じであれば、システム全体で同じものであることが保証されている。これは、複数のノードに散在するデータの整合性を保証するためのreflexworksの基本的な考えにもなっている。(エンタープライズクラウドにおけるスケールアウトの現実解

また、POSTはリソース作成時にのみ使い、PUTは既存のリソース更新に使うのはrelfexworksも同様である。「 一般に、リソース作成時に PUT を使うべきでないとされる理由は、クライアント間で名前の競合が発生する可能性があるから」という理由はもちろんだが、CRUDの4つは明確に区別したいという理由もある。SQLにおいても、INSERTとUPDATEは区別されている。

RESTはステートレスでなければならないか

さて、ここからRESTの3つの間違いの話に入る。
ステートレスはRESTの原理原則であり、実際に疎結合にすることでシステムはスケールラブルになる。私は、ステートレスをスケーラビリティ確保の絶対条件と考え、reflexworksではセッションは使わない方針を貫いてはいた。そして、ログイン処理においてさえセッションIDは使わずに都度認証することを徹底していた。たしかにステートレスにするとスケールはする。だが、最近は度が過ぎるとかえって不自由な設計を招いてしまうことになることがわかってきて、逆にセッションはアリなんじゃないかと思うようになってきた。

はっきりいおう。私は間違っていた。


一つは、ユーザログイン管理において毎回認証を実行させる必要があり、擬似的なセッションIDを発行する必要があったこと。具体的には、認証に成功したら新たに時間制限をつけたcookieチケット(ワンタイムパスワード)を発行し擬似的なセッションIDで対応していた。また、そのチケットはあくまでログイン認証用であり、セッションオブジェクトは管理しないようにしていた。ユーザにログイン入力を強要させるわけでもなく、名前もStateless Session ID(SSID)にしていた。
 しかし、ユーザIDとワンタイムパスワードを含むトークンを毎回クライアントと通信してやりとりするのはセキィリティ上よろしくない。私は頑にステートレスを守る一方でセキュリティをないがしろにしていたわけだ。
 また、パフォーマンスも悪くなる。
前述した2phase commitでは、払い出したチケット(/buy/934085,1)は一時的な揮発性の情報であり、これらはむしろセッションオブジェクトかキャッシュメモリに置くべきである。こうすることで、パフォーマンスも格段によくなり、KVSとの間にメモリという緩衝材が入ることでI/O負荷も軽減できる。
というわけで、最近はRESTであっても必要最低限のセッションオブジェクトだけは認めてもいいと考えるになった次第である。実際に最新版のreflexworksでは標準のセッションオブジェクトが使えるようになっている。また、一時的なオブジェクトはなるべくキャッシュさせるような設計を推奨している。

リソースは本当にドメインロジックを必要としないか

2つ目の間違いはリソースとドメインロジックの置く場所についてである。
MVCの話のなかでも触れてはいるが、ドメインロジックはバウンダリだけでなくコントロールやエンティティの層においても構わない。



にもかかわらず、当初のreflexworksでは、リソースを特定するURLに対して単純なHTTPリクエストのCRUD操作(GET,PUT,POST,DELETE)しか用意していなかった。
これだと、例えばECサイトの金額チェックなどをサーバ側で行うことができない。金額を正確にチェックするにはサーバ側でマスター参照する必要があるはずである。たとえ認証を付けたところで、リクエストデータが正しいかどうかという話は別である。どんなに頑張ってクライアントでチェックしてもサーバ側では無条件では受付けられない。
だからといって、自由にサーバ側のインターフェースを作らせたくはない。
サービスを自由に作らせると、きっとこんな感じのインターフェースになってしまうだろう。

entry = getFoo(entry)
entry = updateFooByBar(entry)
・・・

これでは、サービス志向の二の舞になってしまう。せっかく、RESTでリソースを一意に定義できているのだから、こういう感じにしたい。

entry = Foo.service(entry)

 例えば、GET  /p/Foo?item=abc などと実行すると、/p servletが呼び出され、URLパラメータとentityにもとづいて処理されるといった感じになる。/p servletの中では、reflexContext.get("Foo");とすることで実際のリソースにアクセスできる。(もちろん、POST,PUT,DELETEメソッドもある)

ちなみに、/d/Fooに対して実行すると、/d servletが呼び出され、以前のバージョンと同様に、サービスは実行されずにリソースの内容がそのまま返される仕組みとなっている。(/dはdata、/pはproviderの頭文字)

最近では、単純にデータだけが提供される、BaaS(Backends as a Service)やDBaaS(Database as a Service)が流行っているが、本当にサービスのドメインロジックが必要ないか、よく検討して使うべきだろう。

イベントドリブンなアーキテクチャーは受け入れられるか

先日、MOVEが話題になった。
コントローラをOperationsとEventsに分けた方がいいという案だが、望まれなかった子にあるように、MVCの一つの解釈(あるいは間違えているの)であり、MVCを否定するものではない。
ただ、closureや、あるいは、Deferredのような非同期処理をなんとか中心に考えたいという気持ちもわからないでもない。

実は、reflexworksでもイベントドリブンな設計はいけると思った時期があった。実際にイベントをトリガーにサーバサイドJSを実行できる。でも開発者の多くが難しいと感じ、オーソドックスな方を好んだので、今は廃止しようと考えている。オーソドックスな方とは、前述したような単純な/p servletによるフィルターのような処理のことである。

単にユニークであることが使いやすいとは限らない。




また、コントローラをOperationsとEventsに分けるより大事だと思うのは、Modelをデータ(スキーマ)とドメインロジックに分けることである。スキーマは一貫させる一方でドメインロジックは各レイヤの最適な場所に配置させる。貧血症みたいなオブジェクトにはなってしまうが、それを恐れてはいけない。

以上が、RESTに関して私が犯した間違いの3点である。
だが、もしかしたら、こうやって原理原則を修正した結果、RESTのアーキテクチャーとは到底呼べないものになってしまっているかもしれない。だがそれでも構わない。なぜなら、これらは実務に落としたときに実際に問題となって一つ一つ解決してきたものだからである。

今流行のリーンスタートアップ流にいうなら、クールなアーキテクチャだと思って採用(Build)するのは結構なことだが、測定(Measure)して、学習(Learn)してから最終的に判断しないとだめである。それが本当に素晴らしいかどうかは、実務に落としたとき初めてわかるものだから。

<ご紹介>
reflexworks           <= 製品についてはこちら
reflexworksで分業開発しよう!  <= 開発案件募集中です


月曜日, 7月 02, 2012

KVS上でアプリを動作させるために必要なたった2つのこと このエントリーを含むはてなブックマーク

先の記事で、ReflexWorksにおけるデータ操作の方法について触れ、ツリー構造の仮想フォルダ管理とREST APIによるデータアクセスについて説明した。ここでは、もう少し掘り下げて、KVS上でアプリを動かすための条件とは何なのかについて考えてみたい。(たった2つのことといいながら長文失礼します)

SQL vs NoSQL: Battle of the Backends

タイムリーにも、Google IO でSQL vs NoSQL: Battle of the Backendsというセッションがあったようだ。ここでは、SQL(MySQL)とNoSQK(Datastore)を、Queries、Transactions、Consistency、Scalability、Management、Schemaの6つの項目について比較していて、前半の3つについてはSQLが、後半の3つについてはNoSQLが優れているという結果になっている。最後は両者同数でどっちもどっちだね、みたいな話になっているが、セリングトークも含まれるのでこれは真に受けて欲しくないとにかく、これまで主張してきた通り、トランザクションが増えるとRDBがボトルネックになり、オンライン処理ではそのうち限界になってくるはずだ。
 また肝心のパフォーマンスについては、「基幹系システムでCloud SQLは使えるか試してみた」を見てもわかるように、Datastoreとはまだ相当の差があるようだ。

とはいえ、丸山先生もおっしゃっているように、GAEでもMySQLが使えるようになったことは、まあ、それはそれで結構なことだとは思う。(ということでお茶を濁す)

アプリにKVSを意識させないことが大事

さて、今WebアプリといえばHTML5などのリッチコンテンツが主流である。クライアント上のネイティブアプリのように小気味好く動作することが求められている。またスマートデバイスではオフラインや非同期更新などもある。もはや、JSPやJSF、あるいはPHPでHTMLを動的に作るといった古いアーキテクチャーは通用しない。エンジニアは利用者に最大限のエクスペリエンスを与えようと頑張っており、UI/UXに多くの時間と労力をかけている。可能なかぎり画面だけに集中したいと思っているはずだ。なので、JavaScriptを中心としたフレームワークのムーブメントが今起きていると思う。

このような要求にもReflexWorksであれば対応できる。ReflexWorksは、疎結合を基本としているためクライアント開発者がUI/UXに専念できるように、なるべくサーバ開発を意識させないようにしている。クライアントシンプルなREST APIでサービスを呼び出すことだけ考えればよく、結果もJSON/XMLで取得できる。

KVSで業務アプリを動かす条件とは

冒頭で、GAEではDatastoreはよいがCloudSQLはビミョーであるといった。
一方、AWSのRDSやSQL Azureはまずまずのようだが、実は、Amazon DynamoDBや、Azure Storage Service(KVSの方)にもビミョーなところがある。

一つ目はトランザクション。
GAEではEG(エンティティグループ)のトランザクションが扱える。Azure Storage Serviceでも、同一テーブル、Partitionkeyのデータはトランザクション処理可能である。しかし、DynamoDBではトランザクションを扱えない。(ドキュメントによるとatomic counter程度はできそうだが・・)



http://www.slideshare.net/kentamagawa/amazon-dynamodb-11808513
クラウドのエンタープライズ利用を考えた場合、数msで返せるとか、数万qpsとかいっても単体のスループットであれば実はそれほど重要ではない。ブラウザ表示で数msと数百msの違いは感じられないので、単純にKVSパフォーマンス比較してもあまり意味はない。逆にどんなに高速でもトランザクション非サポートは痛い。トランザクション機能がないと結局は参照系など用途は限定されることになるため、単体性能は悪くてもノードを増やすことでリニアに性能向上できることの方がはるかに重要だ。たとえ数百msのレスポンスで数十tpsになったとしてもトランザクション機能とスケーラビリティは必須である

2つ目はセカンダリインデックス。
実はKVSであっても単純なkeyとvalueだけでなく高度な検索ができるようなものもある。例えば、GAEのDatastoreには、データがソートされて格納されている。そしてRANGE検索ができるため前方一致で検索できる。これはRDB(2次元)とKVS(1次元)の間の1.5次元の性質をもつというのだそうだ。(このあたりについては、kazunoriさんの記事:BigTableと分散KVS がとても参考になる)
キー検索以外にもプロパティ検索も欲しくなる。DynamoDBやAzure Storage Serviceでは、プロパティ検索は可能だが、GAEのproperty indexのような、Built-in indexが存在しないためプロパティ検索は全スキャンになってしまうようだ。これでは大量のデータ検索には使えない。




ReflexWorksにおける基本設計と検索について

元へ。クライアント開発では単なるJSON呼び出しなので、それほど苦労しないことは理解いただけたと思うが、サーバ側ではそれなりのサービスを準備するのが結構大変だと思われるかもしれない。だが、実はそんなことはない。以下に示すように、ReflexWorksはドキュメント指向でありORMは不要であり設計も単純明快だ。



ReflexWorksにおける設計の基本となるのはエンティティである。エンティティとは、簡単にいえば、一単位として扱われるデータのまとまりのこと。難しくいえば、貧血症をおこしたオブジェクトのこと(笑)。POJOでもJSONの連想配列でも何でもいい。とにかく、クラスやプロトタイプに複数の項目がプロパティとして付いているものをエンティティと呼んでいる。ただし、CSVのようにデータを列挙したものではなく、項目と値がペアになっておりまた親子関係も表現できる。

ReflexWorksでは、このエンティティにユニークなアドレス(URL)をつけることで、データ全体を階層管理している。そして、そのアドレスに対してREST APIで操作する。



BDBはセカンダリインデックスが使えるという(1.5次元の)KVSである。このセカンダリインデックスにより、様々なプロパティ検索やソートなどを高速に実行できる。

例) GET /d/{selfid}?{name}{=|.eq.|.lt.|.le.|.gt.|.ge.|.ne.}{value}&{name}{=|.eq.|.lt.|.le.|.gt.|.ge.|.ne.}{value}&...&l={n}


重要なことは、どんなに分散しても全体の階層管理は崩れず一貫した操作が可能になっているということ。RDBであればデータ量が増えるとテーブル分割などで頭を悩ませることになるが、ReflexWorksであればアプリはBDBのことを気にせず単にREST APIを実行すればよく、裏にあるノードの数なども気にする必要はない。

ReflexWorksにおけるトランザクション処理

トランザクションに関して説明する。
reflexworksでは、最小単位のentryにaliasという別名フォルダを複数付けることができ、またatomicな更新ができる仕組みを提供している。別名フォルダとは、/foo/doc1.xmlを/bar/doc1.xmlでもアクセス可能にする仕組みで、UNIXファイルシステムのシンボリックリンクだとおもっていただければ結構だ。


atomicity(原子性)、isolation(分離性)、consistency(一貫性)
  • (ATOMの)entryはトランザクションの最小単位であり原子性をもつ
    • つまり、RDBの1レコードに相当
  • alias(別名)の追加削除はentry内であるためatomicでありisolationが保たれる
    • 例えば、entryがフォルダ移動しても2重に見えたりはしない
  • 複数のentryはfeedで括られる
  • feedのPOST/PUTは1トランザクションで実行されるため、どれか一つでも失敗するとロールバックする(一貫性は保たれる)

例えば、本を買う例でいえば、本はカートに入れられるか、入れるに失敗するかでなければならない。あるいは、買うか買わないかである。




これは、フォルダをカートとみなせば、注文データ(Document)がフォルダ移動する問題に置き換えることができる。以下のように、異なるフォルダで同時に見えてはいけないし、どちらにもないという状態があってはいけない。




これを、reflexworksで実装すると以下のようになる。selfはこのドキュメントの実体があるフォルダを示す。(固定)一方のalternate(別名)のフォルダがfolder1からfolder2に更新されることで上記のフォルダ移動を実現する。entryの更新はatomicなので一貫性は保たれる。


最後に

KVS上でアプリを動作させるために必要なこととは、トランザクション処理とセカンダリインデックスの2つということを説明してきたが、あくまでこれはオンライン業務のアプリに関してであり、一貫性を必要としないような検索系のアプリなどは他のKVSを使ってさらに高速化できるだろう。あるいは、BDB以外でトランザクション処理とセカンダリインデックスをサポートするKVSがあるかもしれない。実はReflexWorksはBDBに特化したものではなく、他のKVS上でも動作するように、TaggingServiceとDatastoreはレイヤーを別けて作ってある。実際にGAE上でも動作するし、本当は、DynamoDBやAzure上でも動かしたいと思っている。 繰り返しになるが、ReflexWorksであれば、単純なRESTサービスのAPI実行になるため、下位のレイヤーがどのようなものになろうと開発者は意識する必要がない。また、こうすることでベンダーロックインを防ぐことにもなる。
 ちなみに、先日からFacebook上で開発案件を募集しはじめたので、あわせて紹介しておきたい。 


 ReflexWorksで分業開発をしよう! 
「HTML5や画面デザインなどをお得意とされている方は画面開発だけに集中し、サービスのことは考えなくて構いません。サービスの実装部分はJavaなので、サービス開発担当、もしくは弊社メンバーがお手伝いできます。お互いに得意なところを役割分担しながら分業開発できればいいなと考えています。」


金曜日, 6月 29, 2012

エンタープライズクラウドでも破壊的イノベーションを このエントリーを含むはてなブックマーク

昨日の記事はちょっとわかりにくかったと思うので補足したい。

RDB不要論

浅海先生よりコメントをいただいた。(ありがとうございます)

全くおっしゃるとおりで、「昔から普通にやってきたこと」で今でも大変有効な技術は多いと思う。ISAMやVSAMのKSDSにしても昔は実際にオンライン処理をやっていたわけだからできないはずがない。というより、もしかしたらRDBより向いているんじゃないか?というのが私の率直な印象。結局のところ、データの集計や分析では、わざわざRDBからデータ抽出してHadoopでバッチ処理するか、カラムナDBに入れ直すとかやっているわけである。実際、大規模トランザクションを処理するオンラインシステムでは「リレーショナル」機能はほとんど利用されておらず、まるでKVSのように使われているのが現状ではないだろうか。そう考えるとそもそもオンライン処理でリレーショナルは必要ないんじゃないかとさえ思えてくる。

それからもっと厄介なのがO/Rマッピングの件。オブジェクトの構造や関係性をリレーショナルモデルに効率よく変換できない問題(インピーダンス・ミスマッチ)があって、その結果作られた効率の悪いSQLが全体のパフォーマンスを劣化させてしまうという現状がある。
なので、やはり、O/Rマッピングなどの「癌」はアプリから排除すべきであり、そのためにはRDBではなくオブジェクトを直接ストアできるKVSを使うべきというのが私の意見である。それでやっと純粋にオブジェクトの世界だけでアプリを作れるようになるのだ。
 
また他方で、RDBはオンライン処理系から排除されてバッチ処理系の仲間になるわけだが、これは前述したとおり、カラムナDBにするか、いっそのことHBASEにしてしまうのがいいのではないかと思っている。そして、バックエンドのHBASEなどに対しては、今流行のFluentdを利用することで、うまくやれば、ほぼリアルタイムに更新可能になるかもしれない。ただし、データロスは許されないので信頼性の担保についてはもう少しよく検討する必要があるとは思うが。


これでいいのだ

データ操作と破壊的イノベーション

リレーショナル機能がないとしたら、データ操作をどうするかという問題が出てくる。RDBはなんだかんだいって、複合検索やソート、JOINといった、とても便利な検索が可能である。そして、これはアプリにとって大変重要で必要不可欠な機能でもある。

そこで、ツリー構造の仮想フォルダ管理とREST APIによるデータアクセスで解決することを考えた。実際にReflexWorksでは、以下のように、UNIXのファイルシステムのパスのようなイメージで直感的に操作でき、アクセス制御、共有設定、暗号化といった設定も可能である。例えば、GET /foo/bar/doc1.xml と実行すると、doc1.xmlを取得できる。(GETコマンドはリモートからのHTTPリクエストかローカルのAPIからのどちらからでも実行できる。また、doc1.xmlはATOM Entryを拡張したもので、様々な項目をユーザ自身で自由に追加できる。もちろんJSONも可能だ)ちなみに、GET /foo/bar とすると、barフォルダ配下のエントリが全てリストで取得でき、?item=hello* など、項目に検索条件を指定することも、前方一致検索、ソートなども可能だ。アプリで必要な機能はほぼ満たしており、リレーショナルを意識することなく、オブジェクトに集中して設計開発できるのは大変嬉しいところである。



RDB不要論とか偉そうに言っているが、実はこういった発想はGoogle AppEngineからいろいろ学んで得たものである。ひがさんの記事の破壊的イノベーションのような発想の転換があり、それから、コスト1/30でスケーラビリティの衝撃 で紹介したような実際にKVSで動作するスケーラブルなアプリが多数作られるにつれ、自信が確信に変わっていった・・。

CQRSとの比較

で、CQRSの話に移るが、恥ずかしながら、浅海先生から指摘されるまで知らなかった。
CQRSとは には以下のようにある。


CQRSは「Command Query Responsibility Segregation」の略語である。日本語で言えば「コマンドクエリ責務分離」もっと解りやすく言えば「更新処理と参照処理とに対する応答部分を明確に分離する」事である。構造としてSystemを次の四つに分類する。
  • Query(参照)
  • Command(更新)
  • 内部Event
  • 外部Event(公開Event)

これを読む限りにおいては、ReflexWorksはCQRSではないようである。
更新処理と参照処理はKVS内で完結するため分離ではなく同一である。KVS内であれば参照と更新を分けなくても高速に処理できる。どちらかというとRDBの負荷を軽減させるというよりRDBを無視して単独で動作するという表現に近い。また、MVCを否定しているわけではなく、MVCモデルは進化する で述べた以下の発想に基づいて設計されている。


ドメインサービスとしてのReflex BDB
Reflexの新3層アーキテクチャーにおけるドメインは、EntityCRUD操作以外の何ものでもないのだが、登録変更削除においては、「外から何をされてもデータの整合性が壊れない様にすること」がもちろん保証されなければならないし、検索においては、単なるPKによる検索だけではない点は補足すべきだろう。例えば、PK以外のKeyによる検索、大小比較、全文検索、Pagingといった検索などについては、要求仕様に応じて、それなりにドメインロジックを実装しなければならない。 まだ公表する段階ではないのだが、Private CloudのソリューションであるReflex BDBの次のバージョンにおいて、単なるKey/Valueの検索ではなく、ドメインサービスとして機能するようなものを作っている。内部的にはBDBなのでKey/Valueではあるが、GAEProperty Indexのような仕組みを追加することで、汎用的なKeyによる検索などができるようにするつもり。ちなみに、これはConsistent Hashと伝染プロトコルを使ったスケールアウトアーキテクチャーとなっており、Dynamoのようにそれぞれのノードを柔軟に追加削除できる。(予定)



この記事を書いたのが2009年10月で、完成してお客様事例になったのが2011年1月だから、まともに使えるようになるまで2年以上かかったことになる。現在、事例1 のお客様で
約100万件/日のトランザクション、約10万枚/日の伝票印刷を処理しているが平均500msのレスポンスを維持できている。(2012/10現在) 150万ops/日、99.96%が100ms以内のレスポンス
自分自身、その性能発揮ぶりに驚愕すると同時に、「クラウドを空想してあれこれ議論するよりも論より証拠。100の理論より1つの事例」ということをあらためて認識した次第である。

木曜日, 6月 28, 2012

エンタープライズクラウドにおけるスケールアウトの現実解 このエントリーを含むはてなブックマーク

企業のWebシステムの3-tierモデルでは、ロードバランサーを置くことで、複数のWebサーバとAPサーバに処理を分散させることが可能だが、ある程度の規模になると今度はDBがボトルネックとなりスケールしなくなる。 これを解決するにはどうすればいいのだろうか?ReflexWorksでは、DBMSに依存しないオンライントランザクションシステムの構築を可能にしている。そのポイントについて述べてみようと思う。

キャッシュ技術の限界

 DBMSのボトルネック解消手段としてよく使われるのがキャッシュ技術である。キャッシュ技術には、OSSのMemcachedや、JavaEE7のJCache、Oracle Coherence, WebSphere eXtreme Scaleといった商用製品などがあり、メモリキャッシュをAPサーバに置くことでDBのボトルネックを軽減させることができる。 なかには、トランザクションをサポートしているのもあり、読込だけでなく書込においても効果を発揮して、DBMSの負荷は大きく軽減できるようになる。

たしかに、キャッシュ技術を使うことで高速化(低レイテンシ)を実現できるが、一方で実は改善できるのは高速化した分だけ、という見方もある。頻繁に使うマスタ参照や数千tps以上といった大規模リクエストの緩衝機能として効果は見込めるものの、実際にキャッシュ読込のために最低1回はDBにアクセスしなければならず、永続化の問題(揮発性のためデータがロスする)やDBとの整合性の問題がある。また大規模なメモリが必要になるとコスト面でもメリットは少なくなる。つまり、キャッシュはあくまでキャッシュであり、リニアに無限にスケール拡張ができる技術とはいえないということである。

2008年当時、企業内クラウドは、最初は大手ベンダーによるキャッシュ目的で導入され、最後はアーキテクチャ変更をもたらす(参照)といったような楽観的な予測もあったが、いろいろ経験するにつれ、もう少し踏み込んだ発想をしないとブレークスルーはしないと私は考えるようになった。

主従逆転の発想

前述のように、キャッシュメモリをどんなに積んだって最後はRDBがボトルネックになるのは必然であり、これを解決するにはアーキテクチャー変更が伴うわけだが、だからといってRDBを否定してKVSで全部代用できるとは思えない。企業にはRDBのために特別に用意された耐久性に優れた高性能サーバが厳然と存在しており、今後もRDBがシステムの中心として君臨し続けることは間違いないからだ。Webの3-Tierアーキテクチャーも基本的には変わらないだろう。

しかし、オンライントランザクション処理に限っていうとトランザクション処理とデータストアの分離ができれば、スケールしないRDBを必ずしも使う必要はなくなる。つまり、単にRDBをデータの格納場所として使い、オンライン処理の基盤としては別のものを使うことで、RDBがボトルネックになるのを防ごうという発想である。

具体的には、キャッシュの発想をもう少し進化させたような感じで、APのKVS内だけでトランザクション実行を完結させ、RDBとはEventual に同期するような仕組みとすることで、RDBに負荷が集中することを防ぐ参照系(マスタ、トランザクションのREAD)はRDBへアクセスしなくてもKVS内で実行できるようにする。要はKVSだけでアプリの実行(READ,WRITE)ができるようにすること。これを実現するには、KVSでアプリのデータ操作をすべて賄える必要があり、ACIDトランザクション処理を確実に実行できかつ永続性を備えることは必須となる。

1.マスタ等をKVSに配置する。参照(READ)はKVS内で実行しRDBに参照しにいかない。
2.  トランザクションの参照(READ)はKVS内で実行する。RDBに参照しにいかない。
3.  トランザクションの更新(POST,PUT,DELETE)をKVS内で実行して保存する。非同期に更新結果をRDBに送信する。




この図を見てもわかる通り、実はWeb-AP-DBの3 Tierのアーキテクチャーは崩していない。ただAPサーバ内にKVSというトランザクション処理基盤を導入しているだけである。(3Tierというだけで企業にとってはずいぶん敷居の低いものに見える)

だがこうすると今度はRDBとの整合性が問題になる。RDBはEventualに更新されるためタイムラグが発生する。例えば、実際にはKVSで既に注文を受け付けているのに、RDBではまだ受け付けられてない状態になっているかもしれない。

実はここが最も重要なポイントで、整合性※などが問題ないように、うまく設計する必要がある。(※ 整合性と一貫性はどちらもConsistencyの意味で使われるが、私は、オンライン処理などの同期処理において矛盾の無いことを保証する場合に一貫性といい、非同期処理においてデータに矛盾の無いことを保証する場合に整合性といっている。この使い分けが正しいかどうかはわからない。)

1点目は、遅延を許容すること
 正のデータは常にKVS上にありリアルタイムに更新されているが、RDBは若干遅れて更新されることを前提に考える。オンラインの範疇はKVSまでとし、RDBから以降はバッチ処理と割り切ることができるかどうか。ここはeventually consistencyというと語弊があるので、単なる非同期更新と考えてもらえればと思う。

2点目は、整合性を保証するプロトコルとすること
 データはレコード単位に(システム全体で)ユニークかつバージョン(リビジョン)管理する。また同じデータが複数ノードに散在している可能性があり、RDBを更新する際は何回更新されても同じ結果になるようにする(べき等性)
 さらに、あるノード上のデータがRDB更新前にダウンした際にも、KVSのログ等からコミット分を抽出してRDBに反映できること。(トランザクションリカバリー)


ReflexWorksでは、データはユニークなIDとバージョン(リビジョン)番号で管理されており、整合性は常に保たれる。もともとスマホやChromeのオフラインアプリなどでネットワーク分断があっても整合性を保つために導入した概念だが、APとDB間を含むシステム全体で整合性を保証することにも利用している。






 
© 2006-2015 Virtual Technology
当サイトではGoogle Analyticsを使ってウェブサイトのトラフィック情報を収集しています。詳しくは、プライバシーポリシーを参照してください。