| @写真

moon

iPhone 7 にしたら結構カメラの画質良くなって割と満足してる。フィルターかけることの喜び知ってしまって VSCO で遊んでる。フィルターかけるとオリジナルとフィルター済みの2パターンの画像ファイルできてしまう。 SNS にアップロードするときはフィルターかけたやつを上げたいけど記録用に自分のフォトライブラリに残すやつはフィルターかかってないやつがよい。そういう運用してるとフィルターかかってるやつとかかってないやつの両方がごちゃごちゃにフォトライブラリに残ったりして管理をどうするかで悩んでる。

一眼レフは相変わらず D90 使ってる。結構ボロボロになってきてて買い換えたいのだけど先立つものがなく買い換えられていない。グリップのラバーゴムを交換したり、キットレンズの VR 18-200 の修理をしたりしながら何とか使ってる。やっぱり iPhone のカメラとは別格の写真が撮れる。特に Nikon 純正の単焦点を付けて撮ったときは良いのが撮れる。フルサイズ機への買い換えを考えたこともあるけど、最近はぶっ壊れるまでとことん D90 を使おうかなと思ってる。ただ D90 は動画がよくない。音声はモノラルで解像度低いしフレームレートも低くオートフォーカスもなし。動画の画質は iPhone 5 くらいの頃から iPhone に負けている。 Apple TV でテレビに映して再生すると一昔前の YouTube みたいな画質になる。

一眼レフには Mac 側のストレージを圧迫するという問題点がある。いま使ってる MacBook Pro は 2009 年モデルでかなり古い。初期搭載の HDD から 512GB の SSD に変えているけど RAW がストレージを使いまくってて残り 20 GB 程度しか空きがなく Xcode のアップデートも行えないような状態。そもそも RAW 撮りやめてしまっても良いのではと思い始めた。 RAW 現像する暇ないし Aperture の開発は停止されて RAW 現像できるソフトがそもそも手元にない。実はカメラの撮って出し JPEG でも十分満足できている。結構良い画質で撮れてそれを Keenai (旧 Eye-Fi )でさっとスマートフォンに取り込んで SNS に上げたり iCloud 共有したりすることの方が自分のカメラの使い方としてプライオリティ高い。

プロでもハイアマチュアでもない普通の個人はどういうフォーマットで写真を撮って保存するのがベストなのだろうか。 RAW で撮っとけば後々後悔することはあるまいと思っていたけど、ストレージの値段が劇的に安くなってきてるわけでもない(ローカルストレージは安くなったが、クラウドストレージに同期しながら使おうとすると結局コスト高い)し、デバイス間の転送速度の問題もある。 2017 年のベストな写真管理方法知りたい。

| @技術/プログラミング

puma はメモリ食い

puma にして喜んでたけど二時間後くらいに NewRelic で様子を見てみたらメモリ 90% 以上消費してて swap ファイルもめっちゃでかいのができてて暴発一歩手前になってた。

一旦 puma をシングルモードからクラスターモードに変えて puma_worker_killer を導入し、 worker を定期的に再起動するようにした。ただ restart 後のメモリ増加傾向は変わらない。

引き続き NewRelic で様子を見ていると Python2 が一番メモリを食っている。このブログは Syntax Highlighting に Python の Pygmentspygments.rb 越しに使っている。これまで unicorn の worker プロセス 2 個で動かしていたときには最大でも Python のプロセスは 2 個で事足りてたけど、 puma にして 16 スレッド( puma のデフォルト)が起ち上がるので、 16 個の Python プロセスが起動していたっぽい。

これは意味ないなということで以前利用したことがあった Pygments の Ruby 移植版である rouge に変えてみることにした。こちらは Ruby なのでスレッドが別プロセスを起動してメモリ爆発ということにはならないと思われる。

puma_worker_killer と Pygments 置き換え後半日様子を見たが、これでメモリ消費量爆発ということはなくなった。

sidekiq でスレッドを増やしたときにも似たようなことを経験したけど、 Ruby から外部のプロセスを起動するようなプログラムを書いているときにマルチスレッドで処理をさせると、 Ruby のプロセスは増えなくても外部のプロセスがめっちゃ増えてそのせいでメモリあっぷあっぷになったり CPU の使用率が高まったりする。マルチスレッドプログラミングのご利用は計画的に。

異なるワーカー間でセッションを継続できない?

puma をクラスターモードにしたことで別の問題も発生した。なんとセッション情報が異なるプロセス間で共有されていないっぽい。なのでログインしてすぐに再ログインを求められるようになった。これは不便。原因を調査するには時間がかかりそうだったのでシングルモードに戻してみることにする。

まとめ

  1. デフォルトの 16 スレッドでは puma は unicorn に比べてメモリを沢山消費する可能性がある
  2. Ruby から外部のプロセスを起動するような処理に注意
  3. puma のクラスターモード( master worker モデル)でセッション情報がリセットされる問題に遭遇

追記

セッション情報が異なるプロセス間で共有されていない

これはちゃんと設定があって、 puma.rb に preload_app! を記述すればよかった。

If you're running in Clustered Mode you can optionally choose to preload your application before starting up the workers. This is necessary in order to take advantage of the Copy on Write feature introduced in MRI Ruby 2.0. To do this simply specify the --preload flag in invocation:

# CLI invocation
$ puma -t 8:32 -w 3 --preload

If you're using a configuration file, use the preload_app! method, and be sure to specify your config file's location with the -C flag:

$ puma -C config/puma.rb

# config/puma.rb
threads 8,32
workers 3
preload_app!

-- https://github.com/puma/puma#clustered-mode

これでプロセス間でセッション情報が共有されるようになった。

| @技術/プログラミング

ブログの管理画面にファイルアップロード機能をつけてみた。 GitHub の Issue みたいにドラッグアンドドロップでアップロードしてくれる。こんな感じ。

file upload demo.gif

いまのところ埋め込みフォーマットは Markdown にしか対応してない。

Lokka は heroku とか PaaS を使うことを前提に作られているのでファイルをアップロードする機能が提供されてこなかった( heroku は永続的なファイル書き込みができない)。 heroku で動かしている人でも使えるように Amazon S3 に上げるようにしてみた。 AWS のアカウント作成とかが必要なのでレンタルサーバーに設置してある WordPress にファイルアップロードするのに比べたら敷居が高いけど、設定するのは1回だけだし全くアップロードする手立てがなかったこれまでに比べたら劇的に快適になるはず。自分で使ってみたけどめっちゃ便利になった。

ファイルアップロード、ちゃんと作るなら Paperclip みたいにファイルのチェックを厳密に行なわないと危ないと思うけど、管理画面から上げるの前提なのでチェックなしで雑にアップロードできるようになっている。 AWS の token 系の設定画面を作ったら Lokka 本体に Pull Request 出します。

なお一つ前の記事でアプリケーションサーバーを puma に変更 - portal shit!というのを書いたけど、このファイルアップロード機能がうまく動かなかったので unicorn を捨てたのだった( pow で動かしている環境や bundle exec rackup して WEBRick で起動しているサーバーではちゃんとアップロードできる)。なぜか unicorn で multipart/form-data な POST リクエストを rack アプリケーションに適切に渡すことができず EOFError が発生してしまう。

仕事で unicorn で動いてるサーバーにファイルアップロードする仕組み入れたことは何度かあるけどこんなエラーになったことはなかったのになぁ。謎い。

| @技術/プログラミング

ブログのアプリケーションサーバーには unicorn を使っていたのだけど puma に変えてみた。

2011 年から使っていたようなので丸 5 年は unicorn を使っていたことになる。 puma はスレッドセーフにしないと死ぬ、ということは聞き及んでいたのでおそるおそる変えてみたけどいまのところ問題なさげ。

手元で確認したときには scss のコンパイルで怪しい雰囲気があった(特定ページにアクセスしたあとレスポンスを返さなくなってしまう)ので 0.12.7 だった compass のバージョンを 1.0.3 に上げてみたところ問題が解決した。結果オーライということで深追いはしてないけど、リポジトリを見に行ってみたらメンテナンス停止しているようだった。最近はフロントエンドは JavaScript でやるのが当たり前ですもんね。

Lokka のフロントエンドは 2010 年頃ナウかった Ruby ベースの技術が満載で最近の傾向とは異なるので、フロントエンドで利用してる gem がメンテされておらず Ruby のバージョンアップ時のボトルネックとなってくることが考えられる。この前 Ruby 2.4.0 で動くか素振りしてみたけどダメだった。padrino-helpers 、 slim 、 haml 、 coffeescript (この機能は自分が追加した)あたりとどういう風に折り合いを付けていくか考えないといけない。

| @散財

10 年くらい前に買ったナイキの靴、靴紐が切れたので新しい紐を買って付け替えた。キャタピランとかいうやつでゴムのいぼいぼが付いてて、靴を履くときに緩めなくても伸びるし履いたら足のかたちに合わせて勝手に締まる。

この靴はハイカットで上の方を緩めても下の方まで緩みが伝播せず脱ぎ履きがかなり面倒な靴だった。それがキャタピランを導入したことで脱ぎ履きが楽になり、おまけにちょうどいい具合に勝手に締められるので紐をきつくしすぎて足が痛いということにもならない。

キャタピランに変えた状態で一日歩いてみたけどすごく履き心地が良くなって最高だった。両足セットで 980 円くらいしたけど靴が生まれ変わるので元を取った感ある。

| @Mac/iPhone

JR東日本エリア在住ではないのだけど iPhone 7 を買って Suica の設定をしたので試してみたくなり iPhone 7 で電車に乗ってみた。JR九州の改札も福岡市地下鉄の改札もちゃんと通れた(当たり前)。ただ IC カードに比べて反応が少し遅くて「もしや使えないのでは?」と挙動不審になってしまった。もう少し右往左往してたら駅員に声かけされたと思う。買い物するときみたいにダブルタップして Wallet を呼び出してから通過したけど改札通過時にはそれをする必要はなくてただかざせばよいっぽい。

改札を通過するとすぐに iPhone に通知が来て残額がいくらかわかる。また移動中は「現在乗車中です」みたいな表示が出る。こういう通知はコンビニとかで買い物をしたときも来るけどめっちゃ便利。 一度 iPhone の Suica で金を払う体験をしてしまうと、レシートを見たり券売機で照会をしないと残高がいくらかわからない旧来型の IC カードには戻れなくなってしまう。カパカパ式ケースに iPhone と電子マネーを挟むのとは圧倒的にユーザー体験が違っていて、残高のリアルタイム通知が来て、乗車ステータスの確認ができ、残高が不足している場合はその場でチャージすることができる。

おサイフケータイ使ってた人たちは 10 年前にすでにこういう便利な体験してたのだろうか。自分は携帯は Motorola や Ericsson 、 NOKIA などの洋物携帯ばかり使ってたので今さらながら携帯電話に決済機能がくっつくことの便利さを実感した。これは本当に便利な機能だと思う。

| @技術/プログラミング

Pinboard にブックマークしたらはてなブックマークに同期するやつを作った。

なんで Pinboard を使うのか、どうしてはてブに一本化しないのかというと、洋物のサービスを使うときに Pinboard の方が使いやすいから。 IFTTT に Pinboard 連携機能はあってもはてブ連携機能はないし、 Delibar や Reeder や ReadKit も Pinboard には対応しているけどはてブには対応してない。あと Pinboard は広告出ないしホッテントリ的なものもないので気がついたら Amazon で買い物してたとかホッテントリの海に溺れてた、ということも起こらない。とはいえはてブのコメントでキャッキャッウフフはしたい。なのでブックマークするときは両方にしたい。パソコンの Chrome からブックマークするときは Taberareloo でクロスポストできるのだけど、最近 iPhone からブックマークすることが増えて(iPhone 進化し過ぎて仕事するときしかパホコン使わなくなった) Pinboard とはてブにそれぞれブックマークするのがだるかった。そういうわけで Pinboard をメインにしつつはてブに同期を試みた。

はてなスタッフの aereal さんが作ってるはてブ API 用の gem (aereal/hatena-bookmark-restful: A client library for Hatena::Bookmark RESTful API)あって使わせてもらったのだけど、これはそのままだと使えない。タグが複数あるケースに対応してない& User-Agent を送らないので API 側から 401 Unauthorized が返ってくる。なので雑にモンキーパッチした。

はてなブックマークの API は tag を 10 個まで設定できるけど、以下のように Request Body が Encode されるのを期待しているっぽい。

"comment=&tags=ruby&tags=http&url=https%3A%2F%2Fgithub.com%2Flostisland%2Ffaraday"

この様な Request パラメーターを Ruby で表現すると以下のような Hash になると思う。

params = {
  comment: '',
  tags: ['ruby', 'http'],
  url: 'https://github.com/lostisland/faraday'
}

Hash は当然のことながら同じキーを複数持つことはできないから、 tags は配列として表現される。これをこのまま Faraday (Ruby の HTTP クライアント。上述の gem でも使われてる)に渡して Request Body を生成するとエラーになってしまうのだった。

最近の Faraday には encode option が追加されて FlatParamsEncoder というのを選べるようになってた。こいつを使うと配列を value に持つ Hash をシリアライズしたときに tags=foo&tags=bar みたいな形式にしてくれる。加えて User-Agent も載せるようにもした。こんな感じ。

class Hatena::Bookmark::Restful::V1
  def create_bookmark(bookmark_params)
    res = connection.post("/#{api_version}/my/bookmark") {|req|
      req.params = bookmark_params
    }
    attrs = JSON.parse(res.body)
    bookmark = Bookmark.new_from_response(attrs)
  end

  private

  def connection
    @connection ||= Faraday.new(url: 'http://api.b.hatena.ne.jp/') do |conn|
      conn.request :url_encoded
      conn.options.params_encoder = Faraday::FlatParamsEncoder
      conn.request     :oauth, {
        consumer_key:    @credentials.consumer_key,
        consumer_secret: @credentials.consumer_secret,
        token:           @credentials.access_token,
        token_secret:    @credentials.access_token_secret
      }
      conn.headers['User-Agent'] = 'Hatena::Bookmark::Restful Client'
      conn.adapter Faraday.default_adapter
    end
  end
end

なおはてブの API はリクエストパラメーターが不正なとき 400 Bad Request を返すのではなく 401 Unauthorized を返す。のみならず User-Agent なしのリクエストに対しても 401 を返す。一方で OAuth ヘッダーが不正なときは 400 を返す。原因の切り分けがむずかしくなるので、リクエストパラメータが不正なときは 400 を返して欲しいし認証できないときは 401 を返して欲しい。

追記 2017-05-22 18:34:38

作者の aereal さんに気づいてもらってパッチを取り込んでもらったんだけど、なんかやっぱりタグありの記事をブックマークしようとするとエラーになるっぽい。相変わらず 401 Unauthorized が返ってくる…。