読者です 読者をやめる 読者になる 読者になる

システム障害で消耗してるあなたに:失敗から学ぶための取り組み「Failure teaches Success」

こんにちは!広告エンジニアのレオです。最近、システム障害を起こしていますか?クックパッドも例外ではないです。毎月、何かしらのシステムに何かしらの障害が起きてしまいます。その際、早く気づき、速やかに対応することによって被害を最小限に留めるように努めます。そして、システムやデータを正常な状態に復旧させます。

正常な状態に戻した段階では対応はまだ完了していません。問題の本当の原因は何なのか、またその再発をどうやって防止するかを考えて手を打つまでは、障害の対応が完了したといえません。予防しない限り、また同じ過ちを繰り返すことになってしまいます。

失敗は成功のもと

根本原因分析、そして再発防止は大事な作業ですが、とても難しい作業です。クックパッドでは、これらを少しでもやりやすくするために、ルールと仕組みをまとめています。この仕組みを「Failure teaches Success」(略してFtS)と名づけています。直訳すると、「失敗は成功のもと」です。失敗の中には学びがあることを意識したネーミングです。失敗を共有することでみんなで学んで、同じ問題が発生しないようにすることを目指します。

FtSのルール

  • 問題が発生したら、チームで共有して、再発防止策を話しあう
  • "Failure teaches Success"の報告を作成する
  • 再発防止策を実施する
  • 他のチームと共有して、他の対策が必要か、よりよい対策ができないかについて話しあう

根本原因分析と再発防止策の手助けとして、考え方やアドバイスをまとめたテンプレートを用意しています。

FtS報告のテンプレート

## タイトル
- トラブルの内容を簡潔に表現するタイトルをつける

## 概要
- トラブルでおこった現象、影響範囲、経緯(必要に応じて)、技術的要因を簡潔に記述する
- 影響範囲については、技術的知識がない人であっても理解できる用語で記述する
- 技術的要因についてはトラブルに直接関係する事項だけを記載する

## 発生原因
- トラブルが発生した原因を記載する
    - 技術的要因ではなく、なぜその技術的要因を発生させてしまったのかを記載する

## 再発防止策
- チーム、全社で実施する行動だけを書く
    - 技術的要因の発生を技術的に防止できるなら、その対策をおこなう
    - 「気を付ける」「注意する」といった主観的な防止策は書かない
    - ルールが実施されていないのであれば、その原因を探ったうえで対策する
- うっかりミスや知識不足であっても、それを防止するための対策をおこなう
- 実施済みの項目と実施予定の項目を分ける
    - 実施予定についてはいつ実施するのかを記載する
        - 実施予定のものを実施したら、再発防止策の適用完了日に追記する

## 再発防止策の適用完了日
- 各対策の実施日を記入する
- 実施予定の再発防止策については、適用次第に追記する

FtSの考え方

技術的要因と根本原因は違う

システム障害等の問題が発生した場合、直接的な原因がソフトウェアやハードウェアの不具合や設定ミスであることが多いでしょう。しかし、原因は技術的要因だけではありません。仕組みや環境から由来する原因も考えられます。なぜなぜ分析などを用いて、発生原因を見つけ出します。

たとえば、こんな具合に障害を分析します。

  • データベースにテーブルが存在しなかったため、サイトが表示されなかった
    • なぜテーブルが存在しなかったのか?
  • 担当者がDROP TABLEを誤って実行した
    • なぜ誤ってDROP TABLEを実行したか?
  • 担当者が本番環境を開発環境だと誤認した
    • なぜ本番環境を開発環境だと誤認したか?
  • 作業端末で本番環境と開発環境を区別する方法がない
    • もしかして、それが根本原因かもしれない?(仕組みに問題があることを発見)
    • そもそも区別する必要があるのか?(別の疑問)
  • 開発環境と本番環境で同じ手順で作業するため
  • 開発環境と本番環境は同じ権限でアクセスできるため(複数の解答が考えられる)

それぞれの段階で複数の疑問を思い浮かぶこともありますし、原因は1つだとは限りません。

個人ではなく、チームと会社

根本原因の追求は、個人の過ちの追求ではありません。人は確率的に間違えるのが自然ですから、個人を責めても価値は生まれません。チームや会社が提供する環境・プロセスで対策・防止すると効果的です。これはとても大事なことです。

問題の直接的な原因が個人のミスであっても、ただ「気をつける」「注意する」だけでは何も変わりません。人間ですから、「気を抜ける」「不注意になる」ことは必ず発生するので、仕組みで人的要因を緩和して再発防止を目指します。

  • 手順・チェックリスト・ルールを整備する
  • ツールを導入する
  • 外部に委託する
  • 手順を自動化する
  • 作業自体をなくす

ルールのコンプライアンス問題

ルールを追加して、手順を整備するなど、人間の行動を改善するような再発防止策は費用対効果が高いこともあります。しかし、ルールや手順はいつの間にか複雑になりすぎて、ついつい忘れられていきます。「ルールを実施しなかった」といったミスの発生原因になりやすいです。ルールが実施できていなければ、ルールに何故従えないかを探って対策を行います。

よくあるルールを実施しない理由

  • 手順、ルールが多すぎる
  • ルール項目の実施が難しく、時間がかかる
  • 手順項目が退屈
  • 無駄な項目が多い
  • 手順、ルールの存在が知られていない

「対策しない」という選択

完全に対策するのが難しい問題、または非常にレアな問題は敢えて「対策しない」という選択もあります。費用対効果の問題ですが、事業への影響とリスク許容を理解した上で取る選択肢です。

再発しないように対策しなくても、該当問題の検知・計測を自動化することをおすすめします。事業や環境の変化によって、費用対効果の計算が変わりますので、再発した際に速やかに対策して再評価することができます。

人間は過ちを繰り返す

人間は必ず過ちを繰り返します。それは仕方のない現象ですから、同じ組織で同じ過ちを繰り返さないように、失敗から学ぶ仕組みを構築して対策しましょう。

Spotlight 検索に iOS アプリのコンテンツを表示させる

 こんにちは。検索・編成部の中村です。いよいよ来月は WWDC 2016 が開催されますね。どんな発表があるか今から楽しみです。本エントリでは、Core Spotlight APIs を使用してアプリ内のコンテンツを Spotlight 検索に表示させる方法について解説していきます。

Spotlight 検索

 Spotlight 検索はホーム画面を右や下にスワイプして表示します。画面上部の検索窓からアプリ内のコンテンツを検索でき、ヒットした項目をタップするとアプリが起動して目的のコンテンツが表示されます。この仕組を利用してユーザーは素早く目的を達成できます。数多くアプリをインストールしているユーザーは、アプリの検索に利用しているのではないでしょうか。

f:id:nkmrh:20160520094836p:plain

 iOS クックパッドアプリ(v16.3.0.0 以降)では、特売情報を掲載している店舗が Spotlight 検索にヒットします。店舗をタップすると、アプリが起動して目的の店舗ページが表示されます。また、右端の矢印をタップすると地図アプリが起動し店舗の位置を表示します。

f:id:nkmrh:20160520094824p:plain

f:id:nkmrh:20160520094843p:plain

 このように Spotlight 検索に表示するには、コンテンツのメタデータをあらかじめ OS にインデックスさせておく必要があります。以降はその具体的な実装方法を紹介します。

Search API

 Spotlight 検索にコンテンツをインデックスさせるには3つのアプローチがあります。

  • NSUserActivity

NSUserActivity を使用する方法です。NSUserActivity は Handoff の実装にも使用するクラスです。ユーザーが閲覧したコンテンツをインデックスする際はこのクラスを使用します。パブリックコンテンツの場合は、メタデータを Apple のサーバに送信することで、検索結果ランキングを向上させることもできるようです。

  • Core Spotlight APIs

Core Spotlight Framework を使用する方法です。アプリに保存されているコンテンツをインデックスする際に使用します。

  • Web markup

同じコンテンツがWebサイトにある場合、ウェブページにコンテンツのメタデータをマークアップしておきます。Apple のウェブクローラにインデックスさせることで Spotlight 検索や Safari の検索結果に表示されます。

Core Spotlight APIs In Batch Mode

 冒頭で紹介した店舗情報の例で店舗情報はアプリに保存されているため、Core Spotlight APIs を使用しています。以降は Core Spotlight APIs を使用したバッチインデックスの実装を紹介します。

// 1
let attributeSet = CSSearchableItemAttributeSet(itemContentType: kUTTypeContent as String)

if let image = UIImage(named: "thumb") {
    let thumbnailData = UIImagePNGRepresentation(image)
    // 2
    attributeSet.thumbnailData = thumbnailData
}

// 3
attributeSet.title = "title (best to limit your title to 90 characters)"

// 4
attributeSet.contentDescription = "content description (best to limit your description to 300 characters)"

// 5
attributeSet.keywords = [title, contentDescription]

attributeSet.identifier = "1234"

// 6
attributeSet.contentURL = NSURL(string: @"http://sample/1234")

// 7
attributeSet.latitude = xx.xxxxxx
attributeSet.longitude = xx.xxxxxx
attributeSet.supportsNavigation = 1

// 8
attributeSet.phoneNumbers = ["xxxxxxxxxxx"]
attributeSet.supportsPhoneCall = 1

// 9
attributeSet.languages = ["ja", "en"]

// 10
let domainIdentifier = "com.core-spotlight-search-sample"
let searchableItem = CSSearchableItem(uniqueIdentifier: attributeSet.identifier, domainIdentifier: domainIdentifier, attributeSet: attributeSet)

let searchableIndex = CSSearchableIndex(name: "com.core-spotlight-search-sample-searchable-index")

// 11
searchableIndex.fetchLastClientStateWithCompletionHandler({ (clientState, error) in
    searchableIndex.beginIndexBatch()
    
    // 12
    searchableIndex.indexSearchableItems(searchableItems, completionHandler: nil)
    
    // 13
    let clientState = NSKeyedArchiver.archivedDataWithRootObject(NSDate())
    searchableIndex.endIndexBatchWithClientState(clientState, completionHandler: nil)
})
  1. 引数の itemContentType にはコンテンツの種類に応じた Uniform Type Identifier を指定します。Uniform Type Identifier については Uniform Type Identifier Overview を参照してください。
  2. コンテンツに関連したサムネイル画像を指定します。アプリのアイコン画像は避けてください。(指定しない場合は自動的にアプリのアイコン画像が表示されます)画像が正方形であれば 180 x 180 pixels を用意します。横長の画像は横 180 pixels に調整され、縦長の画像は縦 270 pixels に調整されます。詳細はこちらの Provide a thumbnail image that captures the item in a relevant and appealing way. に記載されています。
  3. タイトルはデバイスの横幅より長い場合はトランケートされます。90 文字以内に収めるといいようです。
  4. 説明も長いものはトランケートされます。300 文字以内に収めるといいようです。(タイトルは最大1行、説明は2行表示できます)
  5. 検索キーワードを指定します。コンテンツに直接関係のないものは避けてください。
  6. 対応するWebサイトがある場合はそのURLを指定します。
  7. 緯度・経度を指定し supportsNavigation を有効にすると、矢印アイコンが表示されます。タップすると地図アプリが起動します。
  8. 電話番号を設定し supportsPhoneCall を有効にすると、電話アイコンが表示されます。タップすると電話をかけることができます。(表示できるのは地図・電話のどちらか1つです)
  9. コンテンツに含まれている言語を指定します。
  10. コンテンツのドメインIDを設定します。ドメインIDを指定してまとめて削除する API も用意されています。
  11. 前回実行したバッチインデックスの状態を取得できます。ここで取得した情報をもとに、次にインデックスするものを決めることができます。
  12. アイテムをインデックスします。
  13. 引数の clientState には 250 バイトまでの情報が保存できます。今回の例では日付を保存しています。

 以上が Core spotlight APIs を使用したバッチインデックスの実装です。

Batch Index On Background Task

 このバッチインデックスはバックグランドタスクと組み合わせて使用すると、ユーザーのタスクを邪魔をせずにアプリ内のコンテンツをインデックスさせることができます。Github にサンプルプロジェクトがありますので、参考にしていただけると幸いです。

終わりに

 いかがでしたでしょうか。Spotlight 検索を実装してみると、アプリの種類やインデックスするコンテンツの内容によっては思いのほか有用な機能を作ることができるかもしれません。是非試してみてください。

参考

App Search Programming Guide

ImageMagickのピクセルキャッシュとリソース制限

こんにちは、成田(@mirakui)です。今日はみんな大好き ImageMagick チューニングのお話です。

2016/5/13 に公開された、いわゆる ImageTragick と呼ばれる脆弱性では、 policy.xml というファイルを更新するという workaround が紹介されていたのは記憶に新しいと思います。

この policy.xml は、今回の workaround のようにファイルタイプを制限するだけではなく、画像の縦横ピクセル数、利用するメモリやディスクのサイズなどを制限することができます。 Web サービスなどでユーザのアップロードした画像を ImageMagick で変換する場合、このようなリソース制限を適切に行うべきでしょう。

そこで今回は policy.xml によるリソース制限方法を紹介します。

前提

特に明記しない限り、2016/05/14 現在の 6 系における最新開発版である ImageMagick 6.9.4-2 の仕様を基準にしています。

基本の書式

policy.xml の基本の書式は以下のとおりです。

<policymap>
  <policy domain="resource" name="temporary-path" value="/tmp"/>
  <policy domain="resource" name="memory" value="256MiB"/>
  <policy domain="resource" name="map" value="512MiB"/>
  <policy domain="resource" name="width" value="8KP"/>
  <policy domain="resource" name="height" value="8KP"/>
  <policy domain="resource" name="area" value="128MB"/>
  <policy domain="resource" name="disk" value="1GiB"/>
  <policy domain="resource" name="file" value="768"/>
  <policy domain="resource" name="thread" value="2"/>
  <policy domain="resource" name="throttle" value="0"/>
  <policy domain="resource" name="time" value="120"/>
  <policy domain="system" name="precision" value="6"/>
  <policy domain="cache" name="shared-secret" value="replace with your secret phrase"/>
  <policy domain="coder" rights="none" pattern="EPHEMERAL" />
  <policy domain="coder" rights="none" pattern="HTTPS" />
  <policy domain="coder" rights="none" pattern="MVG" />
  <policy domain="coder" rights="none" pattern="MSL" />
  <policy domain="coder" rights="none" pattern="TEXT" />
  <policy domain="path" rights="none" pattern="@*" />
</policymap>

ImageTragick 脆弱性の workaround では domain="coder" の設定だけを書いたと思いますが、それ以外にも上記のような設定項目があります。

なお、「基本の」と書きましたが、設定できる項目はこれで全てです。

以下のように、コマンドラインで現在の設定が確認できます。

$ identify -list resource
Resource limits:
  Width: 100MP
  Height: 100MP
  Area: 25.181GB
  Memory: 11.726GiB
  Map: 23.452GiB
  Disk: unlimited
  File: 768
  Thread: 12
  Throttle: 0
  Time: unlimited

本記事では、domain="resource" で指定できるリソース制限について紹介します。

ピクセルキャッシュが消費するリソース

policy.xmldomain=resource で示されている「リソース」というのは、具体的にはピクセルキャッシュの記憶領域を指します。

The Pixel Cache - ImageMagick: Architecture

ピクセルキャッシュは、1ピクセルを表現する PixelPacket 構造体を、画素数の分だけ並べた配列です。 ImageMagick は内部的にこのピクセルキャッシュで画像を表現しています。 画像を処理する場合には、このピクセルキャッシュの領域を確保するためにメモリやディスクといったリソースを消費することになります。

typedef struct _PixelPacket
{
  Quantum
    blue,
    green,
    red,
    opacity;
} PixelPacket;

QuantumQ16 でビルドした場合(デフォルト)は 2 バイト、Q8 の場合は 1 バイトです。

つまり、Q16 でビルドした ImageMagick において横 400 px、縦 300 px のピクセルキャッシュのサイズは以下のように求めることができます。

ピクセルキャッシュサイズ
  = width * height * sizeof(PixelPacket)
  = 400 * 300 * (4 * 2)
  = 960,000 [bytes]

リサイズ処理におけるピクセルキャッシュの利用例

ImageMagick の画像処理においてどのようなサイズのピクセルキャッシュが作られるかを説明します。

例として、以下のように convert コマンドで横 6,000、縦 4,000 ピクセルの JPEG 画像を 300x200 に縮小する場合のピクセルキャッシュ領域について考えます。

$ convert -debug All src.jpg -resize 300x200 dst.jpg

この場合、内部的には 3 サイズのピクセルキャッシュが作られます。

  1. 6000x4000 (183.1 MiB: 入力画像の展開用ピクセルキャッシュ)
  2. 6000x200 (9.155 MiB: リサイズ処理のためのピクセルキャッシュ)
  3. 300x200 (469 KiB: 出力画像用ピクセルキャッシュ)

メモリリソース上でのリサイズ処理では、この3つが同時にメモリ上に作られるため、合計 192.7 MiB のメモリリソースが消費されます。 つまり、6000x4000 ピクセルの画像をメモリ内で 300x200 にリサイズする場合には、メモリリミットを最低でも 192.7 MiB より大きく設定する必要があります。これがメモリリソースリミットです。

ちなみに、以下のブログ記事で紹介されているように JPEG の size ヒントを与えることによって、上記の例の場合は、リソース消費を 192.7 MiB を 4.463 MiB まで抑えることができました。

本当は速いImageMagick: サムネイル画像生成を10倍速くする方法 - 昼メシ物語

$ convert -debug All -define jpeg:size=300x200 src.jpg -resize 300x200 dst.jpg

policy.xml によるリソース制限

ピクセルキャッシュはメモリリソースを消費すると書きましたが、正確には、リソースは以下の3種類があります。

  • memory: メモリ
  • map: メモリマップドファイル
  • disk: ディスク

この記事の本題である policy.xml でのリソース制限というのは、ピクセルキャッシュが消費するこれらのリソースを制限する、という意味です。

これらのリソースの挙動と制限について、policy.xml に沿って説明します。

なお各リソースには、対応する環境変数が存在します。もし対応する環境変数が定義されている場合は、policy.xml の値よりも環境変数の値が優先されます。 また、コマンドラインツールで -limit memory 256MiB -limit map 512MiB のようにリソースリミットを指定することもできます。この場合、環境変数よりもコマンドラインオプションの値が優先されます。

memory, map, disk

ピクセルキャッシュを作ることができるリソースには以下の3種類があり、それぞれ容量のリミットが設定されています。

リソース名 対応する環境変数 デフォルト値
memory MAGICK_MEMORY_LIMIT システムのメモリサイズ [bytes]
map MAGICK_MAP_LIMIT システムのメモリサイズ * 2 [bytes]
disk MAGICK_DISK_LIMIT unlimited [bytes]

ピクセルキャッシュは通常、メモリ上に作られます。

もしメモリのリソースリミット以上のサイズのピクセルキャッシュを作ろうとした場合、メモリマップドファイルが使われます。

さらにメモリマップドファイルのリソースが不足している場合は、ディスクに作られます。 ImageMagick のユーザなら、/tmp/magick-xxxxx というような名前の一時ファイルを見たことがあるかもしれません。これがディスクリソースに作られたピクセルキャッシュです。

以上の 3 リソースの制限値は、policy.xml では以下のように記述します。

  <policy domain="resource" name="memory" value="256MiB"/>
  <policy domain="resource" name="map" value="512MiB"/>
  <policy domain="resource" name="disk" value="1GiB"/>

area

メモリ利用の制限には、memory の他にも area という値があります。

  <policy domain="resource" name="area" value="128MB"/>
リソース名 対応する環境変数 デフォルト値
area MAGICK_AREA_LIMIT システムのメモリサイズ * 2 [bytes]

area リミットは、メモリに作ることを許す最大のピクセルキャッシュサイズです。

areamemory とよく似ていますが、意味はやや異なります。

memory リソースは複数回ピクセルキャッシュが作られると、都度消費されるものです。そしてピクセルキャッシュが不要になったときに解放されます。 例えば一連の処理で 100 KiB のピクセルキャッシュが 3 つ作成される場合、memory リミットは 300 KiB より大きい必要があります。

それに対して area は消費されるリソースではなく、メモリに作ることを許すピクセルキャッシュのサイズに対するリミットです。同様の例の場合は、area リミットは 100 KiB より大きければ十分です。

ピクセルキャッシュ作成時における areamemorymapdisk の関係を擬似コードで表すと以下のようになります。

if 作りたいピクセルキャッシュのサイズ < areaリミット &&
   作りたいピクセルキャッシュのサイズ < memoryリソース残量
  memoryリソース残量を消費してピクセルキャッシュを作成
elsif 作りたいピクセルキャッシュのサイズ < mapリソース残量
  mapリソース残量を消費してピクセルキャッシュを作成
elsif 作りたいピクセルキャッシュのサイズ < diskリソース残量
  diskリソース残量を消費してピクセルキャッシュを作成
elsif 分散ピクセルキャッシュサーバ※が有効
  分散ピクセルキャッシュサーバ上でピクセルキャッシュを作成
else
  エラー
end

この擬似コードからも分かるように、リソースのリミットが設定されていれば、リミットを超えた変換が走る前に失敗させることができ、リソースは消費されずに済みます。

※なお、分散ピクセルキャッシュサーバ(distribute-cache)については本題から外れるので詳しい説明を省きます。公式ドキュメント の "Distributed Pixel Cache" の項を御覧ください。

width, height

作成されるピクセルキャッシュの横、縦の長さに対して制限をかけることができます。

  <policy domain="resource" name="width" value="8KP"/>
  <policy domain="resource" name="height" value="8KP"/>
リソース名 対応する環境変数 デフォルト値
width MAGICK_WIDTH_LIMIT 214.7 MP (Q16の場合。Q8なら429.5MP)
height MAGICK_HEIGHT_LIMIT 214.7 MP (Q16の場合。Q8なら429.5MP)

なお、ImageMagick 6.9.4-1 までは、width リミットで指定した値が height リミットとしても使われてしまうというバグがあります。

この記事を書くためにソースコードを読んでいたらそのバグを発見したので、下記のプルリクエストを送ったところ、すぐにマージしていただくことができました。6.9.4-2 では直っていると思われます。

Fix typo s/width/height/ in resource.c (ImageMagick-6 branch) by mirakui · Pull Request #199 · ImageMagick/ImageMagick

その他

  <policy domain="resource" name="temporary-path" value="/tmp"/>
  <policy domain="resource" name="file" value="768"/>
  <policy domain="resource" name="thread" value="2"/>
  <policy domain="resource" name="throttle" value="0"/>
  <policy domain="resource" name="time" value="120"/>
リソース名 対応する環境変数 デフォルト値
temporary-path MAGICK_TEMPORARY_PATH, MAGICK_TMPDIR $TMPDIR の値( /tmp など)
file MAGICK_FILE_LIMIT ulimit -n の 3/4
thread MAGICK_THREAD_LIMIT OpenMPの最大スレッド数。OpenMP無効時は 1
throttle MAGICK_THROTTLE_LIMIT 0 [microseconds]
time MAGICK_TIME_LIMIT unlimited [seconds]

それぞれの値の意味は以下です。

  • temporary-path
    • /tmp/magick-xxxxxx のように、ピクセルキャッシュがファイルとして展開される際のディレクトリ
  • file
    • ピクセルキャッシュをディスク上で同時に展開できる最大個数。
  • thread
    • OpenMP で並列処理を行う最大スレッド数。一般的に、画像のリサイズ程度の処理では並列処理をしないほうが高速であることが多いです。並列処理を無効化する方法はいくつかありますが、この値を 1 にすることでも実現できます。
  • throttle
    • 並列処理を行う際、CPU 負荷を下げるための設定です。単位はマイクロ秒で、これが大きいほどピクセルキャッシュの走査処理を遅くし、負荷を下げることができるようです。ただし、私たちは並列処理を使ってないため詳細な性能は確認していません。
  • time
    • ピクセルキャッシュの走査処理におけるタイムアウト時間を指定します。単位は秒です。

リソース制限のチューニング例

ユーザからアップロードされた画像をオンラインでリサイズするというユースケースについて考えます。

この場合、もしかしたらユーザは巨大な画像をアップロードするかもしれません。 ファイルサイズが小さくても縦横のサイズが大きい画像というものを作ることは可能です。しかし ImageMagick でそれを愚直にピクセルキャッシュとして展開してしまうと、メモリやディスクが埋め尽くされる事になりかねません。

このようなユースケースの場合、私のおすすめは以下のとおりです。

  • memory を、そのプロセスが使っていい最大の容量にする
    • area の指定でもいいと思います。前述の通り似たような役割なので、areamemory どちらかが書いてあれば事足りると思います。
    • アップロードされうる画像の最大の width, height が分かっている場合は事前に convert -debug All オプションで表示されるデバッグログを見て、必要なリソース容量を見積もるのをおすすめします。
    • ピクセルキャッシュの容量を減らしたい場合は、JPEG 画像なら JPEG size hint を利用したり、ImageMagick を Q8 (--with-quantum-depth=8)でビルドしたりすると良いでしょう。
  • diskmap0B にする
    • ユースケースによりますが、普通のスマホやデジカメで撮ったような写真であれば、オンメモリで処理できずにディスク I/O が走るような巨大リサイズは何らかの異常である可能性が高いと思います。こういった場合はそもそもディスクにピクセルキャッシュを書かせず、即エラーにしてしまう方が可用性にとって良いでしょう。
  • width, height リミットはデフォルトのまま
    • 画像面積に対する制限がしたければ、 memory もしくは area だけで十分に役割が果たされるためです。そもそも前述の通り、width のリミットが height としても使われてしまうというバグが 6.9.4-1 まであるので、それを理解したうえで使う必要があります。width, height の制限をかけたかったら、policy.xml ではなく、別途 identify コマンドなどで調べたうえでアプリケーションから制限するのが現状では良さそうです。
  • thread1
    • 前述の通り、並列処理を無効にしたほうが画像リサイズは速いからです。
    • なお --disable-openmp--without-threads オプションをつけてビルドされた ImageMagick の場合は、そもそも並列処理は無効になっているのでここを変更する必要はありません。

まとめると、下記のような設定があれば十分でしょう。

<policymap>
  <policy domain="resource" name="memory" value="4GiB"/> <!-- 容量は環境に合わせて調整 -->
  <policy domain="resource" name="map" value="0B"/>
  <policy domain="resource" name="disk" value="0B"/>
  <policy domain="resource" name="thread" value="1"/> <!-- 並列処理が有効なビルドの場合 -->
</policymap>

まとめ

本記事では ImageMagick におけるピクセルキャッシュの仕組みと、そのリソース制限について紹介しました。

今回紹介した内容の中には公式ドキュメントを読んだだけでは分からない仕様が含まれているので、皆様のチューニングのお役に立てれば幸いです。

/* */ @import "/css/theme/report/report.css"; /* */ /* */ body{ background-image: url('http://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527163350.png'); background-repeat: repeat-x; background-color:transparent; background-attachment: scroll; background-position: left top;} /* */ body{ border-top: 3px solid orange; color: #3c3c3c; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; line-height: 1.8; font-size: 16px; } a { text-decoration: underline; color: #693e1c; } a:hover { color: #80400e; text-decoration: underline; } .entry-title a{ color: rgb(176, 108, 28); cursor: auto; display: inline; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; font-size: 30px; font-weight: bold; height: auto; line-height: 40.5px; text-decoration: underline solid rgb(176, 108, 28); width: auto; line-height: 1.35; } .date a { color: #9b8b6c; font-size: 14px; text-decoration: none; font-weight: normal; } .urllist-title-link { font-size: 14px; } /* Recent Entries */ .recent-entries a{ color: #693e1c; } .recent-entries a:visited { color: #4d2200; text-decoration: none; } .hatena-module-recent-entries li { padding-bottom: 8px; border-bottom-width: 0px; } /*Widget*/ .hatena-module-body li { list-style-type: circle; } .hatena-module-body a{ text-decoration: none; } .hatena-module-body a:hover{ text-decoration: underline; } /* Widget name */ .hatena-module-title, .hatena-module-title a{ color: #b06c1c; margin-top: 20px; margin-bottom: 7px; } /* work frame*/ #container { width: 970px; text-align: center; margin: 0 auto; background: transparent; padding: 0 30px; } #wrapper { float: left; overflow: hidden; width: 660px; } #box2 { width: 240px; float: right; font-size: 14px; word-wrap: break-word; } /*#blog-title-inner{*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-position: left 0px;*/ /*}*/ /*.header-image-only #blog-title-inner {*/ /*background-repeat: no-repeat;*/ /*position: relative;*/ /*height: 200px;*/ /*display: none;*/ /*}*/ /*#blog-title {*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-image: url('http://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527172848.png');*/ /*background-repeat: no-repeat;*/ /*background-position: left 0px;*/ /*}*/