コンテンツにスキップ

BinPack と互換性

高レベルテーブルはエンティティを BinPack でエンコードします。PackedTable はエンコード済みエンティティをエンジンの値として保存し、table prefix、primary-key bytes、index values、row-id metadata からキーを組み立てます。

補足
bool1 byte でエンコードします。
signed / unsigned integer固定幅でエンコードします。primary key と index では、必要に応じて順序を保つエンコードを使います。
float, double固定幅でエンコードします。index values は順序を保つ浮動小数点 byte 列を使います。
enumunderlying integer type でエンコードします。
std::string長さ付き byte 列。
std::string_view書き込み用アダプターのみ。復号が必要な永続フィールドには使わない方が安全です。
std::vector<T>, std::array<T, N>要素の型も対応している必要があります。
std::vector<uint8_t>長さ付き byte payload。
std::map<K, V>, std::unordered_map<K, V>key と value の型が対応している必要があります。
std::optional<T>値の有無を表す byte と、値がある場合のエンコード済み値。
std::pair<A, B>, std::tuple<Ts...>要素ごとにエンコードします。
aggregate structtrivially copyable な aggregate は memcpy fast path、それ以外は Boost.PFR のフィールド宣言順。
akkaradb::Ref<T>参照先 row id をエンコードします。
akkaradb::Immutable<T>内側の値をエンコードし、復号時に封印済みの値として返します。

エンティティのレイアウトはストレージ形式として扱います。テーブル名の変更、primary key の型変更、フィールド順序の変更、フィールド型の変更は migration として扱ってください。

BinPack は小さく直接的なエンコード形式です。自己記述的な schema migration format ではありません。

次の変更はストレージ migration として扱います。

  • テーブル名の変更
  • primary key の型変更
  • aggregate のフィールド順序変更
  • フィールド型の変更
  • 既存 aggregate からのフィールド削除
  • Ref<T> target の primary-key model 変更
  • foreign-key action で使う optional / null の意味変更

non-trivial な aggregate struct では、Boost.PFR が宣言順にフィールドを辿ります。

struct UserV1 {
uint64_t id;
std::string name;
uint32_t age;
};

途中にフィールドを追加すると、それ以降のバイトレイアウトが変わります。永続化済みデータでは、明示的なバージョン管理か管理された書き換えを使ってください。

field index はインデックス対象フィールドの値を index key にエンコードします。数値や浮動小数点フィールドは ordered scan のために順序を保つ byte 列を使います。Immutable<T> は内側の値、Ref<T> は参照先 row id を index 化します。

つまり index compatibility はフィールドのエンコード表現に従います。互換性のないフィールド変更の後は index rebuild が必要です。

管理された変更では、次の流れが扱いやすいです。

  1. 旧 struct で旧 table を開く。
  2. 行を scan する。
  3. 各行を新 struct に変換する。
  4. 新しい table name または一時 database に書き込む。
  5. 変換済みの行を書き込む前に index を登録する。
  6. 検証後に application code を切り替える。

小さな組み込みデータセットでは、バイト列をその場で変更するより全件を書き換える方が単純で安全なことが多いです。