MAD Designer iconMAD

5. ドキュメントに含まれないもの:コンパニオンファイル

.madはデータセットではなく、定義です。レポートが表示するすべての内容は派生データであり、再生成可能であり、意図的にアーカイブの外に置かれています。ドキュメントの傍らには次の3種類のファイルが生成されます:

ファイル 出力元 格納内容
<hash>.maddb MADDataTableFast レポートの行データ
<hash>.mdcache MADDataTable (:354) そのデータ上の仮想テーブル群を記述するplist
<hash>{ih,lh}{w, h}.madac MADArrayCaching(MADReportFormatter :179, :183から供給) レンダリング済みページコンテンツ——レイアウト識別子とページサイズをキーとするSQLiteデータベース。ihはレポートインデックスキャッシュ、lhはレポートレイアウトキャッシュ

これら3つはすべて、ドキュメントごとにdataTableIdHashで命名された1つのディレクトリ内にまとめて配置されます。MADocument.m:286-300は、MADURLFactory newURLCacheFilesDirectoryForFilename:(MADURLFactory.m:47-49)が提供するディレクトリ(ドキュメント外のユーザーごと・デバイスごとの場所)からテーブルを構築します:

MADDataTableFast *datatable = [[MADDataTableFast alloc] initWithId:dataTableIdHash];
...
NSURL *cacheUrl = [self newUrlCacheForFilename:cacheName];
self.urlDataTableCache = cacheUrl;
[datatable loadFromCacheWithUrl:cacheUrl error:&error];

5.1 名前から想像されるものとは異なるファイルサイズ

実際のLibrary/Caches/配下のキャッシュディレクトリでの実測値は以下の通りです:

種類 ファイル数 合計サイズ
<hash>.maddb 1 816 KB
<hash>.mdcache 1 1.7 KB
<hash>{ih,lh}{...}.madac 6 約15 MB

.madacファイルはフォーマッタのページキャッシュです。ihおよびlhキーはMADReportFormatter.m:179および:183で生成され、<layoutIdentifier>ih<CGSize>および<layoutIdentifier>lh<CGSize>の形をとります({612, 792}はポイント単位のUS Letterサイズ)。つまり、キャッシュはレイアウト識別子とページジオメトリをキーとしており、これが§5.3で述べる2つの問題を引き起こす原因となっています。

80 KB未満のドキュメントに対し、レンダーキャッシュはドキュメントの約200倍に達します。大半を占めると予想されがちな行データは、ディレクトリ全体の6%未満にすぎません。

意図的にこのサイズになっている理由。 このサイズは設計上の帰結であり、最適化不足による偶発的なものではありません。開発者自身の説明(2026-10-01)による2つの理由は以下の通りです:

  1. 2パスレンダリング。 総ページ数やグループ化は、全行セットが手元に揃うまで確定できません。レポートフッター内のカウントグループであれば最後に合算できますが、レポートヘッダー内の集計値は最初のページをレイアウトする前に判明している必要があり、また行数が確定するまでページ番号を振ることもできません。そのため、結果セット全体を一度だけ取得してデバイス上に保持します。
  2. 将来のビューのための基盤としてのキャッシュ。 行データがローカルにあれば、ページ遷移のたびにデータベースへ再クエリを発行するウェブサイトとは対照的に、ソースへ再クエリすることなく同一キャッシュから同じデータの異なるビューを構築できます。この単一キャッシュの上にさらなる機能が計画されているため、単なる一時領域(スクラッチスペース)として扱うのは誤りです。

キャッシュを圧縮すること自体は可能ですが、現時点で優先すべき変更ではありません。

5.2 この設計がもたらす帰結

5.3 コンパニオンファイルにおいて未規定の事項

ディレクトリ名と格納処理は実装されていますが、仕様としては未規定の箇所があります。別プラットフォームの実装者が推測せざるを得ない3つのギャップは以下の通りです:

  1. レンダーキャッシュのキーがコンテンツではなくファイル名になっている(2026-10-01確認)。 MADReportFormatter -layoutIdentifier(MADReportFormatter.m:186)は_overrideIdentifier ? _overrideIdentifier.hash : _filename.hashを返します。つまりキーはドキュメントのファイル名とページサイズの組み合わせであり、読み込みパスではコンテンツの検証を一切行いません。MADArrayCaching -initializeDatabase(MADArrayCaching.m:141)は、acテーブルが存在しさえすれば既存のSQLiteファイルをそのまま再利用し、キーや読み込み処理のどこにもバージョン、タイムスタンプ、コンテンツハッシュを含みません。同じファイル名のままドキュメントを編集しても.madacファイルは無効化されず、ファイルが削除されるか別のページサイズでレンダリングされるまで古いページが返され続けます。

    2026-10-01の実測でもこの挙動と一致。 15:22にLondonドキュメントに対してヘッドレスの更新とエクスポートを実行した後、キャッシュディレクトリ内は以下の状態でした:

    ファイル サイズ 更新時刻 (mtime)
    <hash>.maddb 835,584 15:22
    <hash>.mdcache 1,644 15:22
    <layoutId>ih{612, 792}.madac 81,920 15:22
    <layoutId>lh{612, 792}.madac 737,280 15:22
    <layoutId>ih{0, 0}.madac 1,867,776 10:52
    <layoutId>lh{0, 0}.madac 4,333,568 10:52

    更新処理は必要なファイル——PDFのページジオメトリである{612, 792}(US Letter)のエントリ——のみを書き直し、残りはそのまま放置します。{0, 0}のエントリには手が付けられず、この2ファイルだけでディレクトリ全体(15 MB)のうち6.0 MBを占めています。{0, 0}は実際のジオメトリが確定する前のページサイズであるため、これら2ファイルはほぼ間違いなく計測パスの産物であり、ファイル名をキーとする方式のもとでは削除しない限り決して無効化されません。

  2. 何を削除してよいかの規定がない。 3種類とも再生成可能であるためすべて削除して安全なはずですが、他のファイルが一切ないディレクトリに0バイトの.maddbが存在するケースがあり、データが充填される前にディレクトリが作成されることが分かります。そのような空ディレクトリに意味があるのかどうかは規定されていません。
  3. .mdcacheのplistスキーマ。 現在は判明しています——これは結果テーブルのシリアライズされたスキーマであり、MADDataTableFast -copyPlistで書き込まれ、-loadWithPlist:で読み込まれます。トップレベルキーは、name、columnKeys(c<hash> IDの順序付き配列)、columns(c<hash>をキーとする辞書で、各要素はMADDataColumnのplist:caption、fieldName、identifier、type、nativeType、dataLength、dataPercision、isPrimaryKey、isNotNull、isAutoIncrement、defaultValue)、rowCounter、sortDescriptors、および高速テーブルのbulkInsertLoopです。defaultValueには番兵値~MTNull~(MADDataColumnNullSentinel、MADDataColumn.m:23)が使われます。なお、フォーマット上の永続的な癖(quirk)が2点あります。1つ目はdataPercisionというスペルミスがフォーマットに組み込まれていること、2つ目は~MTNull~番兵値がdocs/project/BUGS.md Track 1.5に記載されている"<null>" / "(null)"系とは別の第3のNULLマーカーであることです。

このページは英語原文の日本語翻訳です。内容に相違がある場合は、英語原文が優先されます。