月別アーカイブ: 2013年2月

Posterousが2013年4月30日に終了するので色々面倒なことになった

こんにちは、Posterousでブログを開設して失敗したと思っている おがた (@xtetsuji) です。

Posterousが2013年4月30日に終了することが決定しました。アナウンスあり (どうせこれ自身もPosterousブログだし、無くなるだろうからウェブ魚拓)。ITmediaでニュースにもなりました

posterous-is-closing-down

しかし昨年3月にTwitterに買収されてから明らかに開発が滞って、Twitterアカウントでも連日障害報告をしていたり、いたるところでサービス終了を匂わせてはいたのですが、やはり終わってしまうのかという感じです。ユーザ視点で見ると買収も良し悪しですね。Twitter大好きなのでなんともいえない気分です。Posterousの遺伝子がTwitterに受け継がれることを祈るばかりです。

さて、Posterousからブログを引っ越す必要があるわけですが、上記のリンクからも分かる通り、バックアップをZIPファイルで取得できるようです。

posterous-blog-backup-interface

バックアップを予約して、ブログの規模にもよりますが、程なくしてバックアップは完了します。「所定のメールアドレスにメールするね」って書いてありますがこなかった。クソ…;。

で、ダウンロードしたZIPをほどくのですが、以下のような構造になっていました。

ogata@langdechat:~/Downloads/space-3273122-xtetsuji-222e60b8d71f5c7095fab6dd8ea7723d$ ls -l
total 1152
-rw-r--r--@  1 ogata  staff   10342  2 16 14:30 exports.css
-rw-r--r--@  1 ogata  staff     925  2 16 14:30 head.xml
drwxr-xr-x   3 ogata  staff     102  2 16 14:30 image/
-rw-r--r--@  1 ogata  staff    4303  2 16 14:30 index-2.html
-rw-r--r--@  1 ogata  staff     718  2 16 14:30 index-3.html
-rw-r--r--@  1 ogata  staff    4555  2 16 14:30 index.html
drwxr-xr-x  51 ogata  staff    1734  2 16 14:30 posts/
-rw-r--r--@  1 ogata  staff  550443  2 16 14:30 wordpress_export_1.xml

MT形式でのインポート形式への対応ファイルがあるかなーと期待していましたが、どうもそんな考えは甘かったようです。いちおうindex.htmlを開けばそれだけで完結した形式で閲覧はできるので、これ一式をどこかのウェブサーバに持っていけば閲覧は引き続きできると思います。

wordpress_export_1.xml ってのは WordPress へのインポートファイルなのかと思いましたが、中を見たらマルチバイト文字列が化け化けで使いものになりませんでした。これには失笑。posts/ 以下にある XML と HTML、こちらはUTF-8形式で文字化けもせず各記事が1ファイルになって保存されていました。ここから復旧するしかないでしょうね。

数が少なければ、新しいブログに一つ一つ手で入れていくっていうのもアプローチとしてはアリかと思いますが、画像関連が面倒くさい。

ちょっとこれらを著名な日本のブログサイトやブログシステムにインポートするスクリプトを書くことになりそうです。大した仕事では無いと思いますが。少なくとも「MT形式」にさえしてしまえば後はどうにでもなるでしょう。

私は幸い、独自ドメイン post.tetsuji.jp をPosterousに紐付けて使っていました。なので誘導に関しては比較的融通がきくだろうし検索サイトの誘導も比較的容易だと思うのですが、Posterous のサブドメイン ***.posterous.com でブログやっていた人は「住所変更」もできず悲しいことになりそうです。これはPosterous側がposterous.comのドメインでどうアフターケアするか見守るしかありません。

新しいブログ先はどうするか?今回の件で私はブログサイトの永続性に大いに疑問を持ち、正直懲りました。無料のブログサイトでは永続性は期待できない、有料のも疑心暗鬼になっています。本当は自分でホスティングするのが面倒だし自分のオペレーション的な信頼性も低いかなと思って当初他のブログサイトPosterousにホスティングしていたのですが、ちょっともう無いかなと。面倒でも自分でWordPressかMovableTypeを立てるよ、って感じです。

「日本で何年も続いているブログサイトあるよ?」とか「Tumblrは業界最大手だし大丈夫じゃない?」って反応もあるかとは思いますが、前者は前者で無料はちょっと…;有料のもコストがかかる。後者は個人的にリブログという仕組みが肌にあわないというか著作権的に大丈夫なのって気になって使えない。

Posterousの創業者が永遠に終了しないブログサービスを作るって言っているようですが、自分の信頼が地に堕ちたことに気づいていないらしい。

Posterousのバックアップアーカイブを別形式に変換できるようなスクリプトが書けたらまたお知らせします。もしくはリクエストがありましたら Twitter @xtetsuji までお気軽にお寄せください。自分に出来る限りなんとかします。

mod_perlだけで重い処理を行うにはどうすればよいか

自称mod_perlエバンジェリストの おがた (@xtetsuji) です。

2012年のYAPCで「mod_perl王にオレはなる!」とか言っておいて、mod_perl業がおろそかになってしまって非常に恐縮です。最近は別の事に熱中していて…;という言い訳は置いといて、そろそろ本腰を入れてmod_perlしていきます。

Twitterを見ていたらこんなツイートがありました。

[tweet https://twitter.com/_sugarsan/status/301911501659320320]

これに対して @mod_perl_info

[tweet https://twitter.com/mod_perl_info/status/302079473338175488]

…;と返しましたが、mod_perlだけでこういう問題を解決するにはどうすればよいでしょう。

まずは system($cmd.’&’) 作戦から。

mod_perl の Perl CGI エミューレート環境 (Apache::Registry, ModPerl::Registry ファミリー) では、コードはある種のコンパイルをされ、特殊な内部パッケージ名の handler という名前のサブルーチンの中のコードとなります。mod_perl は一度メモリ上に作った特殊な内部パッケージの handler というサブルーチンを繰り返し呼び出すことでコストを抑えています。

mod_perlのPerl CGIエミュレート環境ということで、CGI.pm か CGI::Simple を使っていると仮定すると、system($cmd.’&’) されるのは print $cgi->header(); を行って、必要なHTMLなりテキストなりのアウトプットが完了した時点だと思います。そうでなければユーザに完全なレスポンスを返さずに長時間作業に入ることになってしまい、ユーザはブラウザの前でイライラすることでしょう。

これで問題解決…;と言えるかもしれませんが、いくつかの問題もはらんでいます。

  • この system($cmd.’&’); が呼び出されるアクセスが大量にやってきた場合、全ての子の数(たとえばprefork MPMの場合、MaxClientsの数)以上のアクセスが $cmd の長時間処理にかかりっきりになったら、それ以上のHTTP応答ができなくなる。
  • ユーザに返したいレスポンスを全て返した(HTMLであれば”</html>”までとか)としても、ブラウザはそのHTTPリクエストのレスポンスをたぶん待ち続けるはず。ブラウザがHTMLドキュメントの終了を推測できてDOM解析を完了できたり、KeepAlive等のタイムアウト設定で接続が切れることで、表向きは問題無いように見えるけど、処理としてはエレガントではない。

これをmod_perlだけで解決するにはどうすればよいでしょうか。

ネタ元ツイートの方は exec が CORE::exit と同等 (Apacheの子プロセス自体を終了させる) と言及していますが、実際の exec の挙動は一般的に「現在のプロセスを他のプロセスで置き換える」事です。execはexitと違い、mod_perl側が *CORE::GLOBAL::exec をいじるなどのサポートは行っていないので、元のexecそのものの挙動になります。preforkされた該当の子httpdは $cmd で定義されたコマンドに置き換わり、親から見たら子は突然消えたことと同じことになり、子が設定値的に足りなくなれば親が充足してくれます。ただ、以下の問題があります。

  • mod_perlハンドラhandler中で正規の処理を終わらせていないので、クライアントから見たら接続先のHTTPサーバが突然見えなくなるという乱暴な事になる (大きな問題ではないと思いますが)
  • ログ処理フェーズへ子プロセスが進めないので、当然ながらログが書かれない

前者は少々乱暴でも良いとは思いますが、ログが書かれない事を望むウェブ開発者はいないでしょう。

これら全てをmod_perlだけで穏当に解決するにはどうすればよいか。

まずmod_perlというかApacheは、レスポンスを返すフェーズの後続が以下であることを復習しましょう。

  • レスポンスフェーズ
  • ログ処理フェーズ
  • クリーンナップフェーズ
  • 次の処理待ち…;

長時間かかる仕事 $cmd を実行するのであれば、レスポンスフェーズをきちんと終わらせた後、ログ処理フェーズでログを書いてもらい、$cmd の実行はクリーンナップフェーズで行うのが良いでしょう。大量に来るのであればクリーンナップフェーズで exec して子httpd を $cmd にして、親に「子が親離れした」と思わせてもよい。クリーンナップフェーズであれば既にログも書いているので、exec で子が「いなくなっても」問題は無いでしょう。

それは以下のようなコードで実現できると思います。mod_perl の Registry ファミリーを使った Perl CGI エミュレーションスクリプト中を想定。

my $r = $cgi->r # $cgi is instance of CGI.pm or CGI::Simple
    or warn "non mod_perl environment";
if ( !$r ) { # Pure CGI
    system($cmd.'&'); # Pure CGI の場合は諦めてここで実行
} elsif ( $r->can('register_cleanup') ) { # mod_perl1
    $r->register_cleanup(sub { system($cmd.'&'); }); # exec($cmd) でも OK
} elsif ( $r->can('pool') { # mod_perl2
    $r->pool->cleanup_register(sub { system($cmd.'&'); }); # exec($cmd) でも OK
}
else { die "unknown"; }

もしくはmod_perl環境でもわざとレスポンスフェーズの末尾でsystem()をして、それに掛かった時間をログに載せるという手もあります。

my $r = $cgi->r;
my $start_time = time();
system($cmd.'&');
my $end_time = time();
$r->notes->set( cmd_proctime => $end_time - $start_time ) if $r;

Apche の LogFormat ディレクティブで %{cmd_proctime}n を使って参照できます。

$r->notes->set( cmd_proctime => $value ); でなく $ENV{cmd_proctime} = $value; として LogFormat ディレクティブで %{cmd_proctime}e として参照することもできますが、これはmod_perlではバッドノウハウ。この子プロセスが次に受けたリクエストがこのsystem()処理を行なわいスクリプトでも比較的永続的なプロセスである子は環境変数を持ち越してしまうので、前回の値がかかれてしまう。Pure CGIはそもそもApacheノートや環境変数でmod_log_configに値を渡すことはできない(これについて以前FastCGIでどうすべきかを論じた記事がありますが、それがPure CGIでも参考になると思います)。

mod_perlでも、Apacheの処理フェーズなどを駆使してここまで曲芸ができるという例でした。

上記コードは全て知識を元にした推測で書いています。実証実験はしていません。何か問題があればTwitter等でリプライください。

もっと汎用的にこの問題に取り組みたいということであれば、ジョブキューを使いましょう。例えば「第10回 ジョブキューで後回し大作戦―TheSchwartz,Qudo,Q4M(3):Perl Hackers Hub|gihyo.jp …; 技術評論社」や、書籍「モダンPerl入門」を参照すると良いと思います。

JALマイルの還元率

こんにちは、普段は陸マイラーのおがた (@xtetsuji) です。

ポイント大好き。普段は近所のサークルKサンクスでEdyやカルワザポイントをためて、Tポイント(これは個人的に貯めづらい)、Pontaポイントなども貯めています。一度セッティングすると意識せずにポイントがたまって、後で数千円の「お小遣い」がもらえるのが嬉しい。逆に意識してやっちゃうと「ポイントを貰うために買い物をする」という本末転倒なことになりかねないので、意識しないフロー作りがポイントかもしれません。

ではポイント制度の代表格である航空券のマイルはどこかというと、私はJALです。Edy使っているならANAのほうがいいとか言われるんですが、もともと地元帯広にJASしか航路がなくてJASが買われてJALになって、最近になってANAやAirDoの便がとかち帯広空港に就航しても惰性でJALです。一度大きく傾きましたが、まぁ悪い会社じゃないと思いますよ。ずいぶん以前に株主優待目当てに株買おうと思って買わなかった事がありましたが、今思い出しても冷や汗が出ます。やっぱり自分に株や投機事は無理。

通常は飛行機に乗るともらえるマイルですが、普段の生活でもマイルを貯めることができて、そういう人のことを「陸マイラー」(おかまいらー)といいます。私もクレジットカードをJALカードにして公共料金等の支払いをそこに集約することで、毎月そこそこのマイルがたまっていきます。

陸マイラーの方法についてはプロがたくさんいますので、検索してみるとたくさん出てくると思います。挙句、マイルを貯めるためだけに飛行機に乗る修行僧という人達もいるくらい。

ここでは「ここでは○○円の買い物につき1マイルもらえる」といった話は省略します。純粋に既に手に入っているマイルが1マイルあたり何円かといった還元率について考えたいと思います。

(※「還元率」って言葉で正解かな。ちょっと心配。)

金券や電子マネーに交換する場合

JALはWAONと提携しているのでJALマイルをWAONポイントに変えることができます

  • 10000マイルで10000WAONポイント(100000円)
  • [キャンペーン時]10000マイルで12000WAONポイント(12000円)

キャンペーンの時を考慮しても、1円/1マイル〜1.2円/1マイルですね。

WAONポイントもそうですが、多くのポイントシステムでは1円/1ポイントです。これを下回ることはない。逆に言えば1ポイントあたりどれだけ多くのお金にするかが鍵となってきます。

他にもJAL提携店舗で使えるJALクーポンに交換することもできますが、こちらも10000マイルで12000円のようです。1.2円/1マイル。詳しくはJALクーポンマイルを参照。

パートナークーポンに交換する場合

パートナークーポン特典を参照。

数が結構あったり、古いものがなくなったり新しいものが出てきたり、移り変わりが激しいので、個別の比較はしませんが、大体1マイルあたり1円〜2.5円が相場。中には2000マイルで10000円相当(5円/1マイル)というのもありましたが、「安い」といっても別に欲しくもないものに交換する必要は無いでしょう。

パートナー特典に交換する場合

パートナー特典を参照。

基本的に、SuicaやPASMO、そしてPonta等の1円/1ポイントのサービスに交換するサービスです。

全部観たわけではないですが、ほぼ10000マイル→10000ポイントという相場のようです。つまり1円/1マイル。交換手数料を取られないだけマシといったところでしょうか。普段飛行機に乗らない陸マイラーだけど鉄道はよく利用するといった人には良いかもしれませんが、あまり還元率は得とは言えなさそうです。

JALとっておきの逸品に交換する場合

JALとっておきの逸品を参照。

一見相場が分からない色々な品物が10000マイルから交換できます。例えば10000マイルで交換できるものに10000円の価値があるか?ちょっと分かりませんでしたが、価格.comやAmazonを見れば最安店で10000円以下で手に入りそうな気もします。

本当に欲しいものがあるのか、といった点もポイント。無駄なマイル出費は控えたい。特に家電については型落ちですぐ値段も下がるので割りに合わない(1マイルあたり1円以下)こともありそう。

良くてせいぜい2〜3円/1マイルくらいではないでしょうか。私はあまり期待していない部分です。

フライトで使う

航空会社のマイルというポイントを貯めているのだから、頻繁でなくとも飛行機を利用する人が貯めているのがマイルだと思います。私も出張等で頻繁に飛行機には乗りませんが、年数回の帰省で必ず飛行機に乗ります。

国内線、国際線、提携航空会社と色々ありますが、ここでは一番需要が高そうな国内線について計算してみようと思います。マイル早見表も参考にしてください。

今回は国内線の中でもさらに一番需要が高いB区間について考察してみます。ドル箱路線と言われている羽田と札幌・福岡を往復する人は多いでしょう。片道利用と往復利用がありますが、明らかに往復利用のほうが割が良いのでそちらのみを考察します。

B路線の往復に必要マイル数は

  • 通常15000マイル
  • JALカード会員14000マイル
  • ディスカウントマイル期間12000マイル

ディスカウントマイル期間は閑散期に行われているものです。

ここで重要なのは「航空券を早く買えるか」。JALの場合は約1ヶ月前までに購入すれば先得割引といって航空券が半額程度になります。だいたい片道20000円弱。往復で40000円弱。そうなると通常マイルで換算するとだいたい40000/15000=2.7円/マイルくらい。驚くべき数字でもありません。

逆に直前に買う場合には片道30000円弱、往復で60000円弱になるので60000/15000=4円/マイルと結構割安になります。ただマイル枠という座席枠があるので、これも早く申し込まないと通常座席は空いているけどマイル枠の座席は無いという事態にも陥いるでしょう。

年末年始や大型連休の場合、片道は40000円、往復で80000円ほどに値段が跳ね上がって、これをマイルで交換するとさぞかし還元率が良いと思ってしまいますが、こういう時は確実にマイル航空券の対象外期間です。

それでも上記での、他の商品や他のポイントへ交換するよりも割が良いので、全然詳しい計算はしていませんが、交換できるのであればJALマイルは航空券に交換するのが良いのではないかというのが結論です。

閑散期に早く予約ができて先得割引がきけば、片道15000円、往復30000円程度でB区間を往復できます。そうなると1.5円/マイルと、それほど魅力的な数字ではなくなってしまいますが、こういうときにお金に余裕があるならキャッシュで乗ってさらにマイルを貯めるというのも手ではないでしょうか。

国際線や他の提携航空路線については私もよく分からないので計算していないのですが、他の商品や他のポイントに交換するよりはやはり還元率は良いのかもしれません。

国内線は事前に航空券を先得割引で買って細々とマイルを貯めつつ、陸マイラーでもマイルを貯めて、たまったマイルは先得割引が使えない時期に移動が決まったときや、海外旅行の楽しみなどに温存しておくというのも良いでしょう。

皆さんのマイル運用術についても知りたいです。Twitter @xtetsuji 等へご意見おきかせください。

モバイルSafariでアドレスバーを非表示にするscrollTo(0,1)はバッドノウハウ

昔からスマートフォンは iPhone ユーザの おがた (@xtetsuji) です。iPhone 3G→iPhone 4→iPhone 4S→iPhone 5←イマココ。

パソコンやMacで使っているブラウザがGoogle Chromeなので、同期が便利とかの理由で最近はiPhoneでのウェブ閲覧に標準のモバイルSafariではなくGoogleがリリースしたChrome for iOSを使っているのですが、最近よくアクセスするサイトでは以下のような現象が起こります。

  • ブックマークからアクセスする
  • ページが表示されたらすぐ目的の下方向にスワイプでスクロールする
  • Chromeのページ読み込み進捗バーが消えてページ読み込みが完了する
  • スクロールしていたのにページのトップに戻される

これって、モバイルSafariのアドレスバーを非表示にしたいがためのバッドノウハウなんですよね。Googleで「モバイルSafari アドレスバー (消す OR 隠す)」とかで検索するといっぱい出てくる。

要するに「ページ読み込み(DOM構築)が完全に完了した後に下に1pxスクロールすれば隠せるからJavaScriptでそれをやっちゃえ」っていう発想。ただChromeもそうだけど、モダンなブラウザはページ読み込みが完全に完了する以前からページの一部を表示しはじめる。だから、ブックマークからアクセスしてページが表示されたと思ってスクロールしていると、特にiPhoneはCPUがパソコンより遅いとかネットワークが遅いやらの関係で読み込み完了までの時間がかかるページだと、せっかく下にスクロールしたのに上に戻されるという結果になる。そもそも自分が使っているブラウザはChromeなのでアドレスバー隠すとか全く関係ないし、余計な事に巻き込まれて迷惑なことこの上なし。

検索してみるとjQueryバージョンとか色々あると思うけど、世間で主に流通しているシンプルなJavaScriptコードは以下のようなものだと思う。

window.onload = function() {
    setTimeout(function(){window.scrollTo(0,1);}, 100);
}

なんで0.1秒間を置くのかかよく分からない。このあたりもバッドノウハウたるゆえんじゃないかな。

告白しますが、恥ずかしながら自分も昔これを書いたことあります。昔気にならなかったのは、モバイルSafariを使っていたというよりは、ページレンダリングも何もかも遅くて気にならなかったんじゃないかなと思う。

もしコレを本当にやりたいのであれば、スクロールしていない(Y座標が0である)ことを確認してから行うべき。

window.onload = function() {
    if(window.scrollY == 0)
        setTimeout(function(){window.scrollTo(0,1);}, 100);
}

上記は想像でコード書いたので上手く動くかはわからない。if付け足しただけだし、エラーにはならないと思うけど。

Googleで検索して真っ先に出てきた「千と千尋のアドレスバー隠し – スタジオ・アルカナ技術ブログ」でもこれについては言及していなかった。

本当にアドレスバーから解放されたいのであれば、ハードルは高いけど自分でiPhoneアプリを作るか、もしくは必要なページ遷移を全てAjaxで行うようにしてホームアイコンに置かせてアドレスバーを消すmeta要素を入れるといったほうが良いように思う。後者は典型的なAjaxウェブアプリをiPhoneアプリに見立てる方法として検索すれば色々情報が出てくるので、興味があったら検索してみると良いと思う。

同じこと思っている人はいるだろうと思っていたけど、とりあえず一つ見つけた。

Chrome for iOSを使って、このバッドノウハウが入ったよく使うサイトを快適に使うにはどうしたらいいだろう。開発元に連絡するしかないかなぁ、などと考えている今日この頃でした。