Largeobject Reworks

数日前にTOASTメカニズムで書いたように、PostgreSQLの現在のLargeObjectの実装とTOASTメカニズムの仕組みは非常に似通っている。

まず、テーブル定義。TOASTテーブルとpg_largeobjectは非常に似通った構造を持っている。

pg_toast_%u (
    chunk_id        oid,
    chunk_seq       int4,
    chunk_data      bytea,
    unique(chunk_id, chunk_seq)
)

pg_largeobject(
    loid            oid,
    pageno          int4,
    data            bytea,
    unique(loid, pageno)
)

データ構造が似ているという事はやっている事も似通っており、利用者から長大なデータが渡されると、これを固定長のチャンク/ページに分割してこれを保存する。

ラージオブジェクトの場合、loread()やlowrite()を通じて、分割されたページ群の一部だけを参照/更新する事ができる。しかし、TOASTデータを参照/更新する場合には、分割されたチャンクを一度オンメモリで再構成してから利用者に返すため、1Gとか非常に大きなデータをBYTEA型で保持する事は現実的でない。

ラージオブジェクトのように、TOASTデータの一部だけを参照/更新するインターフェースを持つことは非常に有用な事である。

一方、TOASTデータの格納には幾つかモードがあり、単純にラージオブジェクトのインターフェースを持ってくるわけには行かない事情がある。

一つは、データの圧縮。TOASTデータをDBに書き込む場合、TEXT型やBYTEA型の場合は内部的に圧縮処理を行ってから書き込むため、ディスク消費を抑えられる反面、オフセット値からどのチャンクを参照すればよいのか特定できない。

もう一つは、TOASTデータのインライン化。TEXTやBYTEAを含むテーブルであっても、タプルの長さが十分に小さい時は、TOASTテーブルを利用せず、可変長データをタプルに押し込んでDBへと書き込みを行う。その場合、TOASTデータの一部を更新することは(タプル全体を再構成しなければ)不可能である。
従って、ラージオブジェクト風のインターフェースをTOASTデータに適用する場合には、データ型が以下の条件を満たしている必要がある。

  • 部分アクセスが可能なTOASTデータ型は、常にデータ圧縮を行わない
  • 部分アクセスが可能なTOASTデータ型は、常にTOASTテーブルを利用する
    • 但し、Read-Onlyならinline化されていてもOK

このような戦略はまだ実装されていないので、データのTOAST化を行う際に『常に非圧縮でTOASTテーブルを利用する』という戦略を用いるデータ型BLOBを提供するという方針で開発をすすめる。

ざっくり見た感じ、inv_read()相当の処理は既にTOASTの実装にあるので、あとはinv_write(), inv_truncate()相当の処理を入れて、ラージオブジェクトのディスクリプタとの関連付けをやるようにすれば、結構、簡単にできるように思える。