Skip to content

Instantly share code, notes, and snippets.

@ryogrid
Last active December 21, 2024 06:33
Show Gist options
  • Save ryogrid/a5b719bbfa4dbffd2683d587d60fcb0b to your computer and use it in GitHub Desktop.
Save ryogrid/a5b719bbfa4dbffd2683d587d60fcb0b to your computer and use it in GitHub Desktop.
SamehadaDBのTODO
■SamehadaDBのTODO
・【済】TransactionManager?のBeginとCommitを現在のexecutorのテストケースではどう暫定実装しているのか確認
・【済】executor系のテストやloggingのテストでTransactionManagerのBeginとCommitが呼び出されるようにする
・【済】TransactionManagerクラスとTransactionクラスの実装
・【済】"// TODO(student): add logging here" と cpp実装でコメントされている箇所のif文をSamehadaDBのgoのコードベースにも加える
・【済】logging/recoveryの実装
 -【済】C++実装版の非同期I/Oの仕組みをチェックしておく(別のBusTubフォークを参照する形かな)
 -【済】flush thread が logging に必須であるか確認し、必要なら対応する
 -【済】recovery_test のテストケースのセットアップをSamehadaInstanceを使う形にしたが、他のテストケースと比較して、
     必要な処理が行われているかを確認・対応
 -【済】remove, delete のところをコメントアウトしたのでよろしく対応する
 -【済】他のtestがNGになったので直す
 -【済】 ConstructTupleメソッドのポーティング
 -【済】RedoTestでDiskManagerImplのShutdown内の処理でpanicする原因を調べる
 -【済】(logging/recoveryの実装に必要であれば)TransactionManagerのwrite_set周りの実装をする
    => 必要か微妙だが、Abort時のロールバックに必要なので実装しておく
 -【済】write_set周りの対応 at TableHeap, Transaction, TransactionManager
 -【済】PageのGetLSNとSetLSNの実装
 -【済】BusTubの中にあるお役立ちコメントの転記
 -【済】DiskManagerのWriteLog(Flushで使用)とReadLog(Redo, Undoで使用)の実装
 -【済】TupleのSerializeTo (AppendRecordで使用)と TupleのDeserializeFrom(DeserializeRogRecordで利用)の実装
 -【済】LogManagerのFlushとAppendLogRecordの実装
 -【済】LogRecoveryのDeserializeRogRecord、Redo、Undoの実装
 -【済】TablePageのApplyDeleteの実装
 -【済】Redo と Undo のテストケースがpassするか確認(現状、テストがスタックして終了しない)
       -【済】スタックしたりクラッシュせずにテストケースが完走するようにする
          (デシリアライズがちゃんと行われていそうなことは確認した。少なくとも各レコードのヘッダデータは)
       -【済】Undo用のトランザクションごとのマップは複数要素を持てなくて大丈夫なのか?
          => LSNのチェーンがあるので大丈夫
       -【済】Commitのレコード入力されてもAppendLogRecordやDeserializeLogRecordで無視されているがいいのか?
       (Redoメソッドには、Commit時のレコードを考慮したコードが存在する)
          => ヘッダだけのレコードが追加されているので問題ない
       -【済】Undoでタプルの削除をキャンセルする時や、Redoでタプルを挿入する時はRIDをタプルにセットしておいた方がいいような
          => 少し自信がないがセットするように修正した
       -【済】TestRedoのpass
       -【済】TestUndoのpass
        -【済】なぜかログ種別がCOMMITのログレコードがRedoフェーズで見つかっていて、それだと
            まずいはずなので、原因を調査
-【済】tupleにRIDが設定されていないように見えるがそれだとイテレーションすらできないのでは・・・?
     しかしTestSimpleInsertAndLimitExecution、TestSimpleInsertAndSeqScanWithPredicateComparisonあたりは
     パスしている。確認しておいた方がいいかも。
    => 少し自信がないが、Insertする時に設定してしまうようにした
・【済】snapshotのポーティング
・【済】Indexのポーティング(Hash Index)
 -【済】Indexクラス、LinearProbeHashTableIndexクラスのポーティング
 -【済】LinearProbeHashTableのkeyの型を丸っとbyteスライスにする(ハッシュとる時もbyteスライスを入力にとらせる)
 -【済】Hash Indexを利用する Plan NodeとExecutorの実装
  - 【済】indexのデータをどこか適切なクラスが保持して executorなりから触れるようにする
       (今の実装だとページとしてストレージに保存されるがリカバリはしないので、DB復旧時は該当ページをクリアするとか
        しないといけない予感)
 -【済】テストケースの実装(ひとまず版)
 -【済】同じキーで複数タプルある場合でも動作するか確認(テストケースの拡張)
 -【不要】abortでロールバックする場合、UndoでInsertを取り消す場合に対応するインデックスも削除する
      ↑ひとまず、理由に関わらず再起動時はインデックスは再構築、というポリシにしたので不要
 -【サスペンド】インデックスの再構築(存在しなかった場合の構築も含む) ← DBMSの体をとる形にした時で良いことにする
・【済】個別のクエリで、各々異なるテーブルにアクセスできるようになっているか確認
・【済】ページのdeallocateへの対応 ← DeletePageという名前で存在していた
・【済】タプルの削除への対応
 -【済】削除処理自体の実装
-【済】 "TODO: (SDB) need implement (if is deleted)" なところの実装
-【済】loggingの確認、abort時のrollback, recoveryの実装
 -【済】DeleteExecutor、DeletePlanNodeの実装
 -【済】executorのテストケース(insert, delete, selectionのミックス要)の実装と、それがPASSすることの確認
 -【済】リカバリのテストケースのdeleteを加える形での拡張と、それがPASSすることの確認
・【済】LinearProbeHashTableIndexのInsertやDeleteが決め打ちで最初のインデックスデータを対象としているのを修正して、
      複数カラムがインデックスを持てる。というか、predicateが1項だけでイコール指定である前提であれば、よろしく
      適切なインデックスを使えるようにする。(selectionはHashScanIndexExecutorの場合のみ)
・【済】タプルの更新への対応
 -【済】要修正箇所の確認
 -【済】TablePage と TableHeapのUpdateTupleメソッドのポーティング
 -【済】Abort時のrollback処理のケア
 -【済】logging/recovery周りのケア
 -【済】plan node と executor の設計・実装
 -【一旦断念】インデックスが存在する場合のケア
  ↑★Abort時は触ったカラムのインデックスを再構築する、という仕様にすることにして、実装は後回し。Index周りの設計の見直しも必要かもしれない★
 -【済】テストケースの実装(updateのplan実行、Abort時のロールバック、リカバリテストケースの拡張)
・【済】join操作(BusTubで実装されていたHash Join)への対応
 -【済】先にテストケースのポーティングをやっておく
 -【済】EvaluateJoinメソッドの実装
 -【たぶん済】Boolean型への対応?
 -【済】一通り書けたのでデバッグ
・【済】predicateでイコール以外の比較演算子を使えるようにする(select, delete, updateのみ利用可能。joinは対応しない)
・【済】concurrency系の実装
 -【済】rwlatchもしくはlatchを使う必要があるとコメントされているクラスの対応
 -【済】フィールドアクセスがアトミックでないければらないとコメントされている箇所の対応
 -【済】pageのロックが必要だが行われていない箇所がないか複数CPP版を比較して、あれば対応
 -【済】(略)とコメントされていないがrwlatchもしくはlatchを使う必要があるクラスの対応
  要チェック対象
  - (不要)DiskManagerImpl
  - (済)Catalog
  - (不要)TableMetadata
  - (済)LockManager
  - (追加箇所無し)LockManagerを使うべきところのチェック
  - (不要)Column
  - (不要)Schema
  - (不要)Page
  - (不要)HashTableHeaderPage
  - (不要)HashTableBlockPage
  - (不要)Tuple
  - (追加箇所無し)ClockReplacer
   - (不要)circularList
    - (不要)node
 -【済】(略) とコメントされていないがフィールドアクセスがアトミックでなければらない箇所の対応
      というか、フィールドへの値加算なんかがアトミックでさえあれば、オブジェクトのロックが必要
      ないところは上のタスクと併せて対応する感じで
注: 上記のロック箇所のチェックは https://github.com/ryogrid/SamehadaDB/commit/2d859c250545a0f36d93e5f661c2fc24a622f8d8
  時点での実装を前提としたものである
 -【済】ログバッファのFlush処理の排他について確認・対応(commit時、バッファフル時の呼び出しについて)
 -【済】並列でのTransaction実行のテストケース実装
      (SS2PLが実装されていればpreventionやdetectionは不要。ただし、どの方法でもAbortされるTransactionは発生し得る)
・【済】並行トランザクションへの対応の修正で2つほど既存テストケースがfailするようになってしまったので対応する
・【不要と判断】deadlock prevention, detectionの実装
        理由: Strong Strict 2phase Locking(SS2PL)を採用したが、その場合
           デッドロックは発生せず、また、SS2PL以外のデッドロックが発生
           しうるロック戦略の方がパフォーマンスが良いとは必ずしも言えない
           ということであったため
・【済】データ型の拡充(とりあえずFloat型のサポート)
・【済】Aggregation系の実装(Count, Max, Min。Group by、Having)
 -【済】一通りの実装
 -【済】Group by、Havingが無い条件でのテスト
 -【済】Group by、Havinを用いるケースでテスト
・【済】TestTestTableGeneratorがTEST1_SIZE定数(テーブルに挿入するタプル数)が元々の値である1000(現在は20にしてある)
    だとfailするので、同テストケースで利用しているテスト用テーブル生成のユーティリティー関数を見直す
    (タプルのinsert処理を行うTableHeapのバグか、利用しているSeqScanExecutorのバグの可能性もあり)
・【済】インデックスと併せて使えない、かつ、join (Hash join)では使えないという前提での predicateでの複数項サポート。
    論理演算子はAND, OR両方に対応。結局のところはシーケンシャルスキャンでのマッチでしか使えないということ。
・【済】update処理で指定カラムのみ更新できるよう修正(現在は全カラム上書きしていまう)
・【済】update処理の結果、データが元々タプルが存在したページに収まらなくなる場合にabortするだけで何の
    リトライ処理等も行われないので実装する
・【済】Order by の実装(ひとまず、最終的に返す結果にのみ指定可能とする)
・【済】NULL型への対応
  -【済】主要な箇所の対応
  -【済】テストケースの実装(カラムの値として設定できることのみ確認)
  -【済】ついでに、Bool型をカラムに持つタプルのinserttion、selectionと、predicateでBOOL型が指定できるか確認
      テストケースの作成含む。
  -【済】predicateでNULL値を指定可能とする
  -【済】 テストケースの実装(上の実装箇所について)
・【済】フロントエンドの実装(ひとまずSQL文字列でクエリ発行できる組み込みDBに仕立てる?)
 -【済】SQLをシンタックスに対応したASTに落とすライブラリを試してみて、その上で設計検討
 -【済】SQL文をASTに落とすメソッドの実装
 -【済】ASTをtraverseしてPlanNodeのツリーを構築するための情報に落とすVisitorの実装
  -【済】SELECT(最低限の構成要素)
  -【済】CREATE TABLE
  -【済】INSERT(単一レコード)
  -【済】INSERT(複数レコード)<- 単一レコード用の実装でカバーできた
  -【済】WHERE句の複数項対応(括弧指定にも対応できているはず、NOTはサポートせず)
  -【済】DELETE
  -【済】UPDATE
  -【済】SELECT specifying *(ワイルドカード)
  -【済】SELECT with JOIN
  -【済】SELECT using aggregation系関数
  -【済】LIMIT句
  -【済】OFFSET句
  -【済】ORDER BY句 (複数カラム指定可)
  -【済】IS NULL / IS NOT NULL
  -【済】CREATE TABLE での インデックス指定
  -【済】BinariOpExpressionでpredicateなどを表現する際の仕様を一部変更(AST処理コードとユニットテストの修正が必要)
  -【済】実装したtraverse処理群のユニットテストを書く
 -【済】 PlanNodeのツリーをExecutionEngineに実行させる処理
 -【済】 ExecutionEngineが返した結果をValueオブジェクトの2次元配列に変換して返すようにする
 -【済】ASTから落とした情報からPlanNodeのツリーを構築する処理
  -【済】SELECT(最低限の構成要素)
  -【済】CREATE TABLE
  -【済】INSERT(単一レコード)
  -【済】INSERT(複数レコード)
  -【済】SELECT specifying *(ワイルドカード)
  -【済】SELECTでワイルドカードでなくカラム名を指定した場合にバグっているので修正する
  -【済】WHERE句の複数項対応(括弧指定にも対応できているはず、NOTはサポートせず)
  -【済】SELECT with JOIN(2テーブルまで)
   -【済】WHERE句無し
   -【済】WHERE句有り
  -【済】DELETE
  -【済】UPDATE
/* 要対応
  - IS NULL / IS NOT NULL (リテラル指定箇所でのNULL値指定の対応も含む?)
  - SELECT using aggregation系関数
  - LIMIT句
  - OFFSET句
  - ORDER BY句 (単一カラム対応のみ)
  - HAVING句
  - CREATE TABLE での インデックス指定
  - NULL値が存在する場合のJOINのユニットテストを書く
*/
 -【済】組み込みDBとして使えるようにする
     -【済】呼び出し側は各カラムの型を知っている前提で interface{} を要素とする2次元配列として返す
     -【済】DBオブジェクト作成時に必要であれば既存DBファイルのロード、クラッシュしてもいなくてもRedo・Undo。
         復元後はログファイルをクリア(最大のLSNは引き継ぐ必要あり)
     -【今回はパス】SQL文のサニタイズ?(今回は必要であったとしても、Webフレームワークのところでやらせればよい)
     -【済】利用してよいメモリサイズを指定させるようにする(その値から最大フレーム数を設定する)
     -【済】定期的にスナップショットを実行するスレッドをDBオブジェクト作成時に立てる
     -【済】 WebフレームワークがSamehadaDBのオブジェクトを触る際の排他制御他の実装
         (SamehadaDBオブジェクトがまだスレッドセーフになっていないので)
 -【済】公開モジュールとして利用できるようにする(やり方は確認済み)
 -【済】Webアプリ側のXSS対策?
 -【済】バックエンドに組み込みSamehadaDBを利用したWebアプリのデモを公開する
今回のデモアプリの範囲であれば大丈夫かな・・・
/*
     - クラッシュした際の影響範囲を局所化する(SQLパーサライブラリの処理結果を受け取って以降は
      スレッドを立てて処理させる。Golangのそのあたりの挙動の調査の必要あり。SS2PLなロックは大丈夫か?
      ラッチはとりっぱなしになる?)
*/
 - 公開メソッドをスレッドセーフにして、並行トランザクション実行を可能とする
  (ロック取得周りでAbortした場合にリトライする処理の実装も必要)
・【済】B+Tree Indexについてお勉強
・【済】オンメモリ(ページや永続化は無視)で動作するSkipList・SkipList Indexの実装
    (なお、オンメモリ版SkipList Indexは設計の検討のために書いたもので、動作確認まではしていない)
・【済】Skip List Indexの大枠と重要箇所の設計(KeyにValue含めちゃう法も採用を検討)
・【済】ひとまずシングルスレッド前提でのSkipList Indexの実装(起動時の復元処理は、HashIndex同様、Executorや
      その少し下層のあたりのつくり上対応できない箇所があるが、そこをのぞいては対応可能な形にしておきたい)
 -【済】ページをノードとしてその中にエントリ(Key, Valueのペア)を持つ形のSkipListをオンメモリな形で実装
      (ページのデータ領域へのシリアライズ・デシリアライズなどは行わず、オブジェクトのフィールドへの
       アクセスで対応するページ内情報へのアクセスを模す形で実装)
  -【済】Insert, Delete, Get, Iteratorがざっくり動作するところまでの実装
  -【済】Insert, Delete, Get を混ぜたファジングテストを実装して実施。検出した不具合は修正
  -【済】Delete対応のためにノードにバックリンクを設けた設計を変更し、削除対象フラグを設け、
      Insert、Delete、Get等でのノード探索で突き当たった際に、突き当たった際にリンクの
      繋ぎ変えを行い、全 レベルの繋ぎ変えが終わったら削除するような設計に変更する
      (削除対象フラグの立ったエントリがしばらく残り続けるため、エントリ探索時に余計な
       処理が発生するが許容する)
      ※元の設計だと、並行トランザクションに対応しようとした際にデッドロックを避けられなく
        なる可能性が高いため、変更が必要
 -【サスペンド】Hash Indexを起動時に復元できるようにする(SkipiList Indexのオンディスク化設計でミスしないため)
  -【済】 ヘッダバケットの情報がページ内にシリアライズされているか確認
   => 問題無し
  -【済】ヘッダページを更新の度にFlushする(現状のextendしない仕様なら作成時のみでよい)
  -【済】リカバリの時にブロックページを更新したらFlushしないといけない気がするが、どうやって今のI/FでページIDを得ればよいか・・・
    => Fetchしたページはバッファが溢れればその契機に、そうでなければリカバリ完了時にFlushされるのでそれでOK
    (再起動前に未flushでもリカバリの中でリプレイされれば、Flushされるし、ブロックページは中身が0パディングされていれば
     問題ない。が、NewPage呼び出し時に0パディングされた領域は作られていない模様)
  -【済】NewPage(その先でAllocatePageが走る)する際にDBファイルにはゼロクリアした該当するページの領域を作成するよう修正
     (場合によってはデータ用ページでもDiskManagerImplのWritePageやReadPageが失敗し得たはず)
  -【済】カタログ情報から辿って各インデックスのヘッダページにたどり着けるようにする
  -【済】Hash Indexで利用しているページがちゃんとClock Replacerなどで管理されているのか確認(pinだunpinをちゃんとやってるいるか)
     => 問題無し
↓復元は一旦あきらめる方向に方針転換
 (雑にやることも難しく、結局、インデックスデータについてlogging/recoveryを実装しないといけない)
/*
   -【×】インデックス用のページのFlushと対応するタプルを含むページのFlushが同期してないとおかしなことになる?
      => 現状の設計だとyes
   -【x】データ用ページでRedoでのタプルの重複が起きたりはしないようになっている?
   ↑ログ作成時にページに設定されたログのLSNとの比較によりページがFlush済の場合、一度ログからの復元が行われてログのLSNが
   ページに設定されている場合(つまりログの時点までページの内容が永続化されていることが保証されている場合)は、同じ操作が
   重複されて行われることはない
    => データ用ページがFlush済みの場合に、Redo/Undoの中でインデックスデータの操作をリプレイすることは今の設計では不可ということ・・・
  - レコードにテーブルのOID(テーブルID)とカラム番号の情報を含めるようにしないとダメそう(CRUD的な各メソッドに渡す引数を増やす必要あり)
  - LogRecoveryクラスからカタログ情報を参照できるようにする(Redo・Undoを行う際にインデックスのオブジェクトにアクセス可能にする)
*/
  -【済】インデックス用ブロックページのcache out/inで問題が起こらないことを確認
      => 実装を見る限りは問題無し
  -【済】DB再起動時に呼び出されるインデックスの再構築処理を実装
     (元の実装だと毎回新たなページを規定数アロケートしてしまっている)
  -【済】ユニットテスト書きとデバッグ
 -【作業中】Skip List Indexのオンディスク対応
  -【済】ヘッダページ、ブロックページのフィールドアクセスをメソッド呼び出しに全て置き換える
  -【済】ヘッダページ、ブロックページのページレイアウトを決める
   - 【済】ヘッダページ
       => 明にフィールドのシリアライズ/デシリアライズは行わず、フレーム上のメモリ領域に構造体をマップするだけとした(Hash Indexのヘッダページと同じ)
   -【済】ブロックページ
    -【済】forwardフィールドを固定長の配列にできよう、当該データにアクセスしている箇所を修正できるか確認・対応
       => 確認・修正の上、固定長の配列にした。リグレッションテストもpassした
    -【済】maxEntryフィールドを見ている箇所はダミーの定数値を見ているのと同じなので、置き換えてしまう
    -【済】SkipListHeaderPageをページとして確保した領域に配置するように修正。リグレッションテストもpass
         (再構築可能とする際には、得たページIDをSkipList Indexを保持するColumnクラスに設定できるようにし、
         復元時にはそのページIDを引数にページのフェッチを行うこと。また、ヘッダページからリスト全体で確保した
         ページのリストも辿って得られるようにしなければならない。各ページ領域の不要なデータをクリアするため...
         しかし、最初にまとめて確保するHash Indexと異なり、クリアはできたとしても、再構築時やそれ以降に
         再度ブロックページ(ノード)を作成するとなった時に、過去の動作時に割り当て済みのページが余っていれば
         使うようにする、というのはスマートに実装できるだろうか・・・。というか、ブロックページがFlushされている
         保証は無いのでForwardを辿っていくことで全てのページを辿れる保証もないな。。。
         BufferPoolManager~DiskManagerのあたりでDeallocateしたページを再利用できるようにする修正をこの機会
         に入れた方が、急がば回れのような気もする・・・。が、今のページ0番はカタログ情報が永続化されていて、他の
         情報は何も入っていないので、別種の情報を加えるのはやりにくい感。index用のファイルを用意してそれを毎回削除
         しつつ、レコード用とは別のBPMを使わせればいい???しかしインデックス用のBPMを使うというのもコードの構成
         がムダに複雑化する感)
         ↑括弧内の話は続く作業には影響しない、ことにした
    -【済】ページレイアウトのFIX
    -【済】レイアウト通りデータ配置する際に必要になる定数群をTablePageクラスを参考に定義する
  -【済】情報を参照/更新するためのメソッド群をページのメモリ領域にシリアライズ/デシリアライズするものにする(ブロックページのみ)
  -【済】各種CRUD用のメソッド等でノードを辿る際に、アクセス先のページをフェッチしてキャストしてとするように修正
  -【済】ダミー定数で定められたエントリ数しかノードに詰めずにsplitしている、かつ、非効率なエントリ削除・挿入をしている、という状態で
      TestSkipListMixがpassする状態にする
  -【済】ノードをスプリット(ノードの新規作成も行われる)しなければならない際の処理ロジックなど
        DUMMY_MAX_ENTRY定数を参照している箇所を正式な実装に修正
  -【済】sizeとoffsetをuint16で保持するように修正)
  -【済】非効率なエントリ削除・挿入をしている箇所を修正(ヘッダ側のメモリ領域のスライドが必要)
  -【済】全てのForwardが処理できた削除マーク付きノードに対しPageのDeallocateを呼び出す処理を追加する(現状、再利用されはしないが・・・)
  -【済】dbインスタンスのFinalizeメソッドをShutdownにリネームし、全てのdirtyなページをFlushする処理を追加
  -【済】Fuzzerが実行できるようにする
     => 無言failは独自Assert系関数の実装がそもそも適切でなかったためであったので修正
  -【済】Redoでログされていた処理の再実行が起きたか否かでgracefulかを判断するようにし、それによって再構築か復元か、
      を変えるようにする
  -【済】カラム情報の中で張られているインデックスの種類を持てるようにし、それらをシリアライズ/デシリアライズの項目としても追加
     (スキーマ作成時のカラム情報指定のところで、インデックスの種類を設定できるようにしなければならない)
  -【済】インデックスがSkipList Indexな場合の再起動時のインデックス情報の復元(or再構築)を実装する
     (割り当て済みのページの再利用は今回は対応しない。また、SkipList Indexを利用可能なExecutorおよびPlanNodeの対応も先送り)
  -【済】パーサライブラリのIndex種指定の対応状況の確認
      => 作成するインデックスの名前を省略するとパースエラーになるようだが、以下はパースしてASTを吐くところまでは動作することを確認した
"CREATE INDEX testTbl_index USING hash ON testTbl (name);"
       インデックス名を省略するとパースエラーという点はポスグレ互換と言えないかもしれないが、(限定的な)ポスグレ互換のNW I/Fを提供
       しようと考えた場合でも十分ごまかしの効く範囲・・・だと思われる
       ただし、これまで存在しなかったテーブル定義の更新という形にならざるをえないので、そこは新規のルートを作成する必要あり。
  -【済】AToMP本のconcurrent skip list (Mutex使う版)のアルゴリズムが適用できるようにコードを書き替えてみる
      (適用が可能かの検証的な意味も含む)
  -【済】書き換えたロジックのデバッグ(ホップ数が log N 程度に収まっているかもdebug printで確認しておきたい)
  -【済】SkipList用のFuzzerのテストケースで仕様外のパラメータを与えないようにして、動かしてみる
     => Fuzzerのバグか何かで fuzzing process hung or terminated unexpectedly: exit status 2 とかなってしまって、
       その時のパラメータを試すもpassという感じだったので、Fuzzerはひとまず置いておく。
       fuzz: elapsed: 15m3s, execs: 1140 (0/sec), new interesting: 23 (total: 211)
       という程度まで実行してpassしていたようなので、SkipListのコアロジックの部分については問題ないだろう。
  -【先送り】Fuzzerのテストケースを長時間動かしても問題ないように、メモリ上で動作する仮想ディスクを用意する
  -【済】keyTypeをVarcharとした版のTestSkipListMixテストケースを作成して動作を確認する
  -【済】TestSkipListMixとTestSkipListMixSの2つのテストケースをinterfaceやジェネリクスを使って1つにまとめる
      => 共通化したので、Float型の場合のテストケースも加えた
  -【済】SkipListHeaderPage を XXXBlockPageと同様にメンバーはシリアライズして、Pageオブジェクトをキャストして扱う形にする
        (そうでないと、ページ毎のMutexを用意できないので。ついでにマルチスレッド対応のためにLSNも記録できるようにする)
  -【済】SkipListBlockPageでLSNを記録できるようにする
・【済】SkipList Indexの並行トランザクション対応
 -【済】FindNodeをロックをとりながら、かつ、コーナーノードのLSNを記憶して進むように書き換える(更新系の場合は、レベル1ではWロックをとる)
 -【済】イテレータおよび参照系のみが動作する前提でのIteratorの並列アクセス対応(なお、更新系が入る場合は、ロックマネージャに範囲ロックを実装、
     しそれで事前にロックをとった上で、呼び出すといった処理の追加実装が必要になる)
 -【済】 新規ノードの作成、もしくはノード削除が必要だった場合、該当の処理を行う前に全てのコーナーノードと更新系操作の対象ノードに更新が
       かかってないことを全てのロックを手放して、再度経由したコーナーノードのWロックと対象ノードのロックをとり、更新カウンタの値に
       変化がないかでチェックしてから行うように修正する。ロックの解放は更新系操作が完了したあとでまとめて行う。
       (Mutexがリエントラントではないので、同一のノードについて重複してロックをとらないようにする考慮が必要)
 -【済】上記のチェック結果がNGだった場合は、持っているロックを全て解放して、そもそもの操作自体をリトライするよう修正する
 (現在のSkipListクラスのRemoveやInsertで返り値を見て成功するまでリトライ、とすれば良いと思われる)
 -【済】チェック時のロック獲得およびチェックを行うメソッドと、チェックが成功して更新操作を行った後にまとめてロックの解放を
     を行うユーティリティメソッドの実装が空なので埋める
 -【済】既存のテスト(逐次)がpassするか確認
 -【後回し】Unpin後もしくはロック解放後にオブジェクトにアクセスしている(PageIDを取得するメソッドを呼び出している)箇所のチェック
      (GCにメモリ解放を任せるBPMの設計上、動きはするがお行儀よくないので)
 -【済】並行アクセスを行うテストケースを作成し、Get, Remove, Insertが混ざった場合でデッドロックを起こさず、期待した値が返るか確認する
  -【済】TestSkipListParallelSimpleInteger (InsertとGetを1要素ずつする処理を奇数と偶数で2スレッドに分けて同時に走らせる)
  -【済】TestSkipListParallelSimpleInteger2 (InsertとGetを処理を奇数と偶数で2スレッドに分けて同時に走らせる。上のものと異なりInsertが全て終わってからGetを行う)
  -【済】TestSkipListParallelSimpleInteger3stride
  -【済】TestSkipListMixParallelInteger (一つのパラメータではpass)
  -【済】GolangのFuzzerが謎に落ちる問題の対策になるかもというところも兼ねて、オンメモリでストレージをエミュレートするDiskManagerを実装してみる
  -【済】実ファイルシステムでないとfailするタイプのテストだけVirtualDiskManagerを有効にしていても実ファイルシステムが利用されるよう、フラグ操作するコードを
      テストケースに追加する
  -【済】FuzzSkipListMixIntegerの24hランニング
  -【済】FuzzSkipLIstMixVarcharの12hランニング
  -【済】fuzzerでの1パラメータでの実行時間の1sec制限を変更できないか調べる
      => 標準のものは無理のようであった
  -【済】TestSkipListMixParallelIntegerを書いてpass
      (いい感じにsplitや新規ノードの作成が起こるようにする)
  -【済】TestSkipListMixParallelVarcharを書いてpass
  -【済】TestSkipListMixParallelVarcharのopTimesを大きな値にして、12hぐらいのランニングで
      Unpin漏れのメモリリークのようなことが起きていないか確認する
  -【断念】TestSkipListMixParallelXXXXのFuzzerかそれに類するテスト版を用意しpassさせる
      => 1パターン 1sec timeout制限がどうにもならなかった
  -【済】複数スレッドでのアクセスでスケールするかベンチマークをとる
      => BPMの改善によりある程度スケールすることが確認できた
  -【済】更新系が入ってもスケールするよう、必要なタイミングだけWLockに切り替える仕組みで改善を試みる
     => 若干の効果はあった
  -【済】UnpinPageの呼び出しをロック解放の前にするよう変更してみて(それが本来正しいので)、ランニングで問題が出ない
   か確認して、問題無ければ正規コードとして入れる(FetchとUnpin以外でDecPinCountを呼んだりするようにしたので
   そうしないとまずい気がする)
  -【済】上の改善の修正でレースコンディションを作りこんだようなので修正
  -【済】試しにUnpinPage、DecPinOfPage, IncPinOfPage の中身をコメントアウトした状態でベンチをとって性能劣化の程度がどう変わるか見る
  -【後回し】Iteratorのための範囲ロックの実装と、それを前提にしたIteratorの実装修正
  -【済】(並行SkipList単体で)Iteratorを並行アクセス時でも動作可能とする
        ↑phantom readに関する考慮は入れない
       -【済】TestSkipListMixParallelStrideVarcharがデグレでfailしていたがテストケース側の問題だったので修正
       -【済】TestSkipListMixParallelStrideAddedIteratorVarcharを追加しpass  
  -【済】Indexを用いたポイント・レンジスキャンで汎用的に利用できるExecutorの実装
   -【済】IndexインタフェースにIteratorメソッドを追加。その時に返すための型としてIndexIteratorといったインタフェースを追加
   -【済】ポイントスキャン用ExecutorのSkipListIndexで利用した場合のテストケースの実装とそれによるテスト
   -【済】レンジスキャン用のPlanNodeとExecutorの実装
    -【済】実装するExecutorでPredicateのチェックも行うよう設計・実装変更
    -【スキップ】IndexがSkipList Index の時に 開始および終了レンジに math.MinInt32 の値が来たら、nil指定に置き換える
   -【済】逐次アクセスでのテスト
   -【済】Abort時のIndexデータrollbackの実装(しっかりとしたテストは未)
   -【済】並列アクセスでのテスト
    -【済】Indexデータrollbackのテストケース作成とそれによるテスト
     -【済】子Executorから受け取ったデータを用いてDelete, Update を行えるよう既存のExecutorを拡張する
     -【済】SkipList Indexを使ったレコード読み込みを行うExecutorと、拡張したDelete/UpdateのExecutorで
         各々の組み合わせた際の動作をテストしつつ、Abort時の動作もテストするテストケースを実装し、
         同テストケースによるテストを行う
    -【済】並列にDelete、Update、Insert、Select(SkipList Indexを用いたポイント・レンジスキャン)を行う
        テストケースを作成し、同テストケースによるテストを行う
     -【済】あれこれ
     -【済】balanceのカラムの方の重複チェックも行うようにする
     -【済】口座間の資金移動に対応するテストロジックを実装する
     -【済】テストケース側の考慮不足で操作の失敗や重複した値を入れてしまうといったことがないかレビュー
     -【済】テストケース最後でのassertionの処理を実装する
     -【済】各々のPlanNode作成用関数を実装する
     -【済】テストケース(資金移動の部分だけactive)が通るようにする (INTEGER、直列スキャン&SkipList Index)
     -【済】テストケース(全てActive)が通るようにする(INTEGER、直列スキャン)
     -【済】テストケース(全てActive)が通るようにする(INTEGER、SkipList Index)
     -【済】テストケース(Random Update以外Active)が通るようにする(VARCHAR、SkipList Index)
     -【済】テストケース(Random Update以外Active)が通るようにする(VARCHAR、直列スキャン)
     -【済】テストケース(全てActive)が通るようにする(VARCHAR、SkipList Index)
      -【済】Updateのロールバックとlogging/recoveryで、Update時にridが変化したケースにも対応できるようにする
     -【済】テストケース(全てActive)が通るようにする(VARCHAR、直列スキャン)
     -【済】TestSkipListPrallelTxnStrideVarcharでSkip ListのInsert時、まれに key duplicationが起きているのはテストのせいか?DB側の実装のせいか?
     -【済】SkipListでかなり大きなVarcharなキー(ex:ページサイズの半分程度)が来た時にsplitして納めるルートの修正
     -【済】Floatなカラムを持つ場合でもSkipList Indexを用いた並列Txn実行が動作するようにする
        (※InsertやUpdateでキーの重複が起きるとfailするが、Floatのルートが問題ないことだけ確認できればよいのでそれは許容するものとした)
     -【済】SkipList Index x Varcharで14hランニング
・【済】SkipList Indexでのジャイアントロック(UpdateEntryとScanKey)が本当に必要か再検討・検証(必要なかった)
・【済】スキャン(レンジスキャン、フルスキャン)時に、where句によるフィルタリングにヒットしなかったレコードのRロックを保持し続ける
    必要があるのか確認(今はしている)。なお、Wロックされていないかのチェックは必要なはず。
    => 再度同一テーブルにアクセスして、別txnに書き換えられたところを読んでしまうとRepeatable Readが満たせなくなるので、
       そういうケースを検出する仕組みが入っていない限りとらないとダメ
・【済】ClockReplacerの無駄なロックを取り除く
・【済】UnpinPageのための無駄なロックを取り除く
・【済】Unpin時にノードのロックを持ってない箇所をチェックし解放の順序を入れ替える
・【済】typed-nil問題が起きうるコード個所を見直す(warmingとか出てる?)
・【済】SkipList(単一キー&キー重複非対応)の状態で、これを見れば同様のものが実装可というレベルの詳細度
    でドキュメントを作成して、技術記事などの形で公開する
・【済】上のドキュメントの英訳版をGitHub Pagesで公開して、SamehadaDBのREADME.mdからリンクする
・【済】SkipListな並列txnのテストケースで最短の文字列長を1にするとテストがfailするのがなぜか確認しておく
   (現状のテストケースの重複チェックが期待通り機能していないという認識を前提とすれば分かるが、そうでない場合はおかしい)
    => 現状のコードでは最短長1でもテストは問題なくpassした。従って最短長は1に戻した。
・【済】SkipList Indexをキーの重複があっても動作可能なように拡張を行う
 -【済】設計をまとめる(送金テストケースでレコードデータとインデックスデータの不整合は起こらないか?)
   ・特定のKeyに紐づいているValueをまとめて消す処理を既存の処理の上で手抜きにやるとどうなる?ロックとの絡みでどうにかなる?
    横でInsertが走った場合はファントム潰し的な話でどうしようもないとして。
    => txn分離レベルとか以前にレコードデータとインデックスの不整合が起こりうるかを考える必要があって、それについては
      特定のKeyのレコードをまとめて削除、だとかも一個ずつロックがとれるか確認しながら行う形に実装されるはずで、
      というか、そもそも、Indexに対してそのような操作をする必要のあることはないはずなので用意する必要も無い気がする。
      => ロックがあれば、元々存在するファントムが起こり得るというのと同様の問題しかないはずなので、上述の手抜き実装
        でいって良いと判断
 -【済】実装
 -【済】重複対応版のSkipList Index単体および同SkipList Indexを用いた重複したカラム値が存在する場合の簡易なテスト
 -【済】送金txn+αトランザクションの並列実行のテストケースの実装とそれを用いたテスト
 -【済】balanceの値をハッシュ値で決めるようにしたが問題無く動くようになったがVarcharのテストケースで確認する
 -【済】わざと重複を生じさせるようにする
   (挿入時に一定数、同一の内容のレコードを追加するとかで良い気がする。それであればselect時のチェックも容易であるし。
    collectNumMaybeへの反映も忘れずに)
 -【済】 TestKeyDuplicateSkipListPrallelTxnStrideVarcharのopTimesを90000にしてのロングランニング
・【済】組み込みDBとして利用した際に、実ディスクを使うようにしておく(本来のデフォルト)とクエリの
    実行結果がバグる(SPAなTODOアプリのデモは少なくともおかしくなっている)ので調査
    (チェックポイントを挟んでからリカバリを行うようなテストケースを追加して確認?)
・【済】新たにリストが空しか返ってこない状態になる不具合を検出。リカバリなどを行わなくても発生する。
    チェックポインティングが走ると内部的にデッドロックなどが起きる?(しかし、入力フォームを持った
    Webページは返ってくる。ただし、入力フォームでアイテム追加を試みても追加されず、サーバ側のログを
    見ると、該当するリクエストが飛んできた旨の出力が無い。レスポンスを返した後に表示されるもので、
    その前にブロックしてしまうと表示されない・・・とか?)
    /todo/も内部で/itemsというAPIを読んでいるので、DBにアクセスする種のAPI呼び出しがブロックして
    返ってこない状況になっていると考えると、説明はつきそう
・【不要だった】Updateのログ形式がridが変化した場合を考慮したものになっていないので修正する
        => WriteRecordはnew_rid発生時にAbortでのロールバック時にインデックス情報の更新が必要な都合から、
          Updateのレコード1つにまとめるということをしたが、現時点での実装ではリカバリが必要な場合は
          インデックスはゼロから再構築するため同様の考慮は必要ない。従って、リカバリにおいては、
          基本的には現在の実装で作成されているApplyDeleteとInsertの2つのログがそのまま処理されれば
          問題は生じない。
・【済】Undoの中でのUpdateのロールバックでnew_ridが発生した場合の考慮が未実装なので対応する
・【済】UpdateでRIDが変化したレコードが発生したトランザクションが未コミットの状態でクラッシュ
    した場合にリカバリが行えるかテストする
・【済】リカバリ処理のUndoフェーズの中のUpdate操作のUndoでレコードのRIDが変化することがあった場合に
     期待通りリカバリが行えるかテストする
/* 【済】
・updateでRID変更が起きた時にデータの整合性が崩れないようにするためのWrite Recordへの操作まとめ
 - 通常時のupdate呼び出しで変更が起きた場合 => 基本的にはAbort処理で元に戻るはずなので、この変更自体が起きたことで書き換えなどを行う必要はない。
                        (他のトランザクションやIndexの存在を考えた時に行うべき処理は通常ルート・Commit・Abortのいずれにおいても、
                         まだ検討必要かもしれないが、CommitやAbortで自txnが操作したものを反映・ロールバックするという観点では
                         WriteRecord自体については何もしなくて良い)
 - Abort処理の中のupdate呼び出しで変更が起きた場合
  - RID変更の有無に関わら、ず以下のようなRIDの読み替えの仕組みをAbort関数の中に実装する
   - 読み替え用mapを用意する。ridへのアクセスは左のmap内に該当エントリがあれば変換し、なければそのままという形で扱うことにする。
   - UpdateTupleの呼び出しで、本来ロールバックすべきRIDに戻せずに別のRIDになった場合は、本来RID -> 変わってしまったRIDの変換のエントリを追加する。
    ただし、この時、ロールバック元RIDにmapで変換をかけていた場合は、既存のエントリを丸っと本来RID(元と同一) -> (さらに)変わってしまったRID で置き換える
   - 他の操作のロールバックでは読み替え用マップの更新操作は行わない
   - (DELETEのロールバック時にRIDの読み替えを行った場合、対象のレコードに削除マークがついていないはずなので、その考慮が必要???
     そのケースだけUpdateTupleを呼びださないといけない・・・・・・?←同一txnでDELETEしたtupleをUPDATEすることはないので考慮不要)
     ↑読み替えた場合、元々削除マークがついていたレコードを扱う時におかしなことは起きたりしないだろうか・・・?
      ↑ロールバック先が元々削除マークがついていなければならない状態であるケースは起こり得ない。ロールバック元については上記同様に
       DELETEしたタプルをUPDATEしているケースは起こり得ない。UPDATEで削除マークをつけていた場合は発行順の後ろからロールバック
       している中でUPDATEのロールバックで削除マークが外されるので問題ない。     
*/
・【断念】キャッシュアウト時にBPMでページのWLatchをとっているのが無くても問題ないようであれば取り除く
 (BPMがI/Oの間排他されてしまうので)
  => どうせ、キャッシュインのI/Oを待たなければならないし、試してみるとバグったので取り除かない。
   (キャッシュアウトは非同期I/Oを行うことで効率化は図れるかもしれないが、バグらないようにどうすれば
    よいかは調査が必要) 
・【済】Optimizer実装に向けた前準備
 -【済】調査
 -【済】設計
・【済】オプティマイザ周りの設計やりつつスケルトンを一通り書く
 -【ひとまず済】統計情報回り
 -【ひとまず済】オプティマイザのモジュール
 -【ひとまず済】追加・修正が必要なPlanとExecutor(Selection, Projection、NestedLoopJoin、IndexJoin。既存のものだとHashJoin)
・【済】オプティマイザのスケルトンに動かないまでも、修正を入れて行けば動くというコードを一通り書く
 - 【ひとまず完】BestScan関数
         => ひとまず書きあがった。内部で呼び出している関数の実装が終わっているわけではない。
 -【ひとまず完】BestJoin関数
      => ひとまず書きあがった。内部で呼び出している関数の実装まで終わっているわけではない。
 -【済】TODOコメントの整理
    (今回の修正関連だと分かる識別子を入れ、情報を精緻化する。これにより残実装を俯瞰的に 把握可能とする)
・【済】オプティマイザをテストケースが通った状態にする
 -【済】カラム情報周り(今の実装だとカラム名の文字列のみを基本的に扱う想定で、テーブル名が必要な場合はカラム名のフィールド
     に無理やり持たせているが、そのままだとオプティマイザの実装的にNGかもしれない。そもそもColumnクラスがテーブル情報
     を持つようになっていないし)
     => Columnクラスのカラム名フィールドにテーブル名を持たせる内部仕様に修正した。
       対処としてはCreateTable呼び出し時、同フィールドにテーブル名を結合することで、どうにかした。
       おそらく、これでいけるはず。
       (CreateTableを経ていないColumnインスタンスはインスタンス作成時に指定したカラム名しか入っていないので注意。
       特に、ouput schemaを構築する処理など)
       扱うテーブルの中にカラム名の重複がなければ、クエリ中でテーブル名をプレフィックスとしてつけても
       つけなくても動作する想定(オプティマイザの実装以前から存在したコード含む)
 -【済】Executor・Plan周り
  -【済】NestedLoopJoinの実装
  -【済】IndexJoinの実装
  -【済】 HashJoinの修正
  -【済】 Selectionの修正
  -【済】Projectionの修正
 -【済】Expression周り(同じxxxExpressinというクラスでもパーサの出力データの構造体がメンバに持つものはExpression interfaceの
    実装ではない。オプティマイザの中では同様に扱えた方がよいかもしれない。オプティマイザに入力する時点で同種のものは
    バックエンドで利用しているExpression interfaceを実装したものに変換をかけておくという設計も現実的な落としどころとして検討
    の余地はありそう)
 -【ひとまず済】統計情報周り
  -【済】statistics.go 内のコードの実装
  -【済】コアロジックの中でSelectionのつけ忘れの箇所があるらしいので対処
  -【済】statistics.go 内以外の統計情報関連のTODOコメントつけてあるところを埋める
 -【済】BestScan関数のテストケースでのDB内データ他の入力データのセットアップ処理
 -【済】TODOコメントのところでデバッグ開始するのに対処しておかないとなとこ
 -【済】BestScan関数のテストケースを通す
 -【済】BestJoin関数のテストケースを通す(=オプティマイズ処理)
・【済】余計なProjectionが走らないようにする
・【済】フロントエンドからオプティマイザを通してクエリを実行できるようにする
 -【ひとまず済】TableStatisticsクラスやそれに付随するクラスを使って統計情報をよしなに管理するためのコードを既存
       コードベースに入れ込む
       => ひとまず、定期的に統計情報をアップデートするスレッドを走らせるようにした
 -【ひとまず済】SQLでindexを持つカラムを作れるようにするか、原則SkipList Index有りにする
         => デフォルトで SkipList Index を全カラムが持つという仕様とすることにした
 -【済】オプティマイザの前処理にクエリ中のカラム指定がテーブル名をプレフィックスとして含まない場合に、付与する
       処理を加える。曖昧性があるクエリであればエラーを返すようにする
      (シングルテーブルの場合に既存の実装で動作するのであれば、それで構わない)
 -【済】ON句を指定しているパターンを通すための、QueryInfoの書き換え
      (ON句で指定されているの内容を、WHERE句の条件にANDで付加するだけでOKのはず)
-【済】アスタリスク対応
 -【済】DELETE、UPDATEでも通せるようにする(アスタリスク指定としてQueryInfoを最初に書き換えてしまえばよい?)
 -【済】オプティマイザを通すプランナを実装して、クエリがオプティマイザの対処可能なクエリであればそちらの
      プランナを利用するようにする
   -【済】SELECT
   -【済】DELETE
   -【済】UPDATE
・【済】組み込みDBとして使用する際に並列トランザクション実行を可能にする
 -【済】スレッドセーフでない箇所の洗い出しと、スレッドセーフ化
 -【済】CCプロトコルの都合でAbortしたトランザクションのリトライ処理の設計と実装
・【済】ロギングがActiveか、と、(ロックでの)排他制御がActiveか、が1つのフラグで表現される
    実装になってたのを、2つのフラグ(排他制御の方はTransactionオブジェクト毎に持たせる形)に分離
・【済】不要なコメントアウトコードを一掃
・【済】Insert時のページスキャンの最適化
・【済】REST I/Fのサーバの実装
・【済】RETT I/FでサーバにアクセスするSPA(Web)の簡易なクライアントの実装
・【済】デモサーバのリカバリ後の統計情報更新スレッドの処理でクラッシュが繰り返された現象の確認
    => 現状、不明
・【済】HashIndex内でのページ番号保持もSkipList Index同様に16bitから32bitに修正
    => 実装の都合上、keyのサイズとvalueのサイズが両方64bitになり、合算値が64bit から 128bitとなった。
・【済】copy関数利用箇所で仕様の誤認識の上で実装されている箇所がないかチェック
  => Tuple、tmp_tuple(Hash Joinのとこ)、loggingでの利用箇所あたりでいくつか怪しいところがあったので要精査
   => 特に問題のある個所は無かった
・【済】RESTサーバの最大ワーカ数の確認
    => リクエストを受ける部分では特に何のリミットもかけずにハンドラ関数を呼び出すようであった
・【済】サーバをシグナルでgracefulに終了させられるようにする
・【済】glacefulに終了したか否かをログファイルが削除されているか否かで判断できるようにして、それに応じてIndexデータの
    再構築をするか否かを決定するようにする。
   (今のRedo/Undoが起きたかどうかでの判定は、厳密には正しくないはず)
・【済】ASTを処理する際の文字列リテラルの取得に不具合があるので修正する(半角スペースを含む文字列が正しく処理できない)
・【ページデータのWriteのみとして済】ストレージのWriteでDirect I/Oを行うよう引数設定などする(OSのディスクキャッシュへのメモリコピーを回避)
                   (ログの方は可変長であるため、書き込みサイズが可変で指定できないとどうしようもないのでひとまず対応しない)
・【済】Update時のロールバック時の考慮回りの処理が汚いのでデザインを見直してリファクタリング
・【済】Update処理のリファクタリングでダミータプルを入れるようにしたが、メタデータのサイズを考慮して入れるか入れないか判断する必要がありそう
・【済】上の修正に不備があったので対処
・【済】上の修正に不備があったので対処(インデックスが利用されていた際にdirty readが起こる場合があった)
・【済】GitHub Actionのワークフローでのテストの際に、VirtualMemoryの利用をOFFに書き換えて、実ディスクでテストが走るようにする
・【済】deallocateしたぺージを再利用できるように修正
・【済】Materializationの(ための仕組み?)の実装
    => HashJoinExecutorで利用されていたタプル群を一時的にDBファイル内のページに格納し、適宜取り出す仕組みを
      他のクラスでも利用できる形にくくりだした
・【済】sync.Poolクラスを使って一度確保したPageオブジェクトのバイト列領域のメモリ領域を使いまわすようにして性能改善
・【作業中】B-link treeなインデックスの追加 (検証用リポジトリ: https://github.com/ryogrid/sametree)
 - 【済】hmarui66/blink-tree-goをSamehadaDBのバッファプールを利用させる形で動作させられるかの検証
  - 【済】逐次アクセス
  - 【済】並行アクセス
 - 【済】バッファマネージャ統合実装の改善・再確認(メモリリークしないか、など)
 - 【済】イテレーション用インタフェースの追加
      (元から存在するメソッドを用いてイテレーションを行うコードは既に書けて動いている)
 - 【作業中】blink-tree-goコアのバッファマネージャ管理のページID(別形式のデータとして管理を委譲している形。IDは独自採番)とSamehadaDBでのページIDの変換マップの内容がせいぜい500エントリ程度しか永続化不可のため対処
  (プログラムを一度終了させた後に、復元可能とするための仕組みの話。クラッシュ時はインデックスごと再構築するしかない)
  - 【済】復元までの一連の処理
  -【済】永続化に利用したページの解放
 - 【済】close時のblint-tree-goの方のバッファマネージャで空きページとなっているIDの永続化と再起動時の解法
  (これをやらないとclose時に空きページであったページが、再起動後再利用されずWritePageもされないため、SamehadaDB側でもDBファイルに利用されない領域が生まれてしまう)
  - 【済】複数ページにまたがってた場合も含む永続化と復元処理の実装
  - 【済】永続化が単一ページに収まった場合のテストケースの作成とその通過
  - 【済】テストケースの複数ページにまたがる場合への拡張とその通過
 - 【済】SamehadaDBに限らず、バッファプールでページ管理しているDBMSがblink-tree-goをライブラリとして利用できるよう、インタフェースを切る?
・【済】blink-treeをコンテナとして利用したインデックスの実装
 - 【済】並行トランザクションのテストケース(銀行の口座間の送金を模したもの)のpass (Integer、Float)
 - 【済】VarcharでBTreeIndexが指定された場合は、キーサイズが50バイトになるようにシリアライズしてからコンテナに渡す
 - 【済】50バイトになるようにシリアライズしたもののパディングの0の要素を取り除けるようにする
 - 【済】ロングランニングでの確認
 - 【済】サーバ終了時のインデックスのメタデータ書き出し処理のキック
 - 【済】上のキック処理が入って問題ないかのテストケースの追加
・フロントエンドから利用した際のTRANSACTION文のサポート
・デフォルトでSkipListインデックスが張られるようになっているSamehadaDBクラス経由でのテーブル作成の仕様を、無しも含めて任意に指定できるようにする
 (ASTを解析して作る構造体でインデックスの種類を持てるようにする必要もあり)
・pgproto3を試してみる(ポスグレ互換I/F実装のためのパッケージ)
 github.com/jackc/pgx/v5/pgproto3
 https://www.postgresql.jp/document/8.0/html/protocol-flow.html
・TableHeap::GetTupleでIndex経由のアクセスをしにいった時に削除済み(コミット済み)
 だった場合にtxnをAbortさせているが、単に無視させるようにする?
 (UpdateでRIDが変化した場合のインデックスの更新タイミングの都合で、他のtxnに見えてしまっている
  模様)
・Hash IndexにもUpdateをアトミックに行うようにする修正を加える
・indexのエントリにもタプルのMarkDeleteとApplyDeleteのように削除マーカを用意しないとDirty Readが起きる
 (なお、これを実装すれば、Update時のジャイアントロックは不要になるはず)
・スナップショット実施用スレッドの処理に再利用可能ページに関するログについての考慮を追加
・トランザクション用以外のスレッドにContextを使うようにリファクタリング
・Flush中およびログ書き出し中のクラッシュへの考慮
(チェックサム情報のページ内への追加と、リカバリ時のチェックサム値の確認、直接DBファイルに書かないようにする?チェックポインティング時のデータを残しておく?)
・直列スキャナを用いた際にハロウィン問題が起こり得る件の対処(Updateでレコードの位置を後方に移した時など)
・クエリ結果全てが得られるまで待たずに、ストリームで順次レコードを返すことができる形のI/Fの実装
 (Planツリーの実行結果を返すメソッドの箇所の対応、その先のフロントの対応の2つがある。
  テストの時などにラクなので、まとめてリストで返すI/Fもそれはそれで残しておいて良い気もする)
・グループコミットの実装
 参考: https://qiita.com/kumagi/items/54d5a010bf3f9076ac8b
・ARIESの実装
・SkipList Indexの再構築および復元のテストケースの作成
・indexデータのlogging/recovery
 => 基本的にはエントリを追加もしくは削除した時にページIDとキー(SkipList Indexではキーの重複はない)を記録したログ
   を残して、LSNをページに書き込めばよい。リカバリする時は通常のレコードデータと同様にLSNの大小を見て、適用するか
   否かを判断すれば良い。
   ただ、これだけだけだと以下のケースへ対応できないのでそれぞれ対処する必要がある。
   ・トランザクションが未完の状態でクラッシュしていた場合
     => Undoフェーズでのロールバックが必要だがその判断が上に記述したログの情報からだけでは分らない。
       txn IDも併せてログに含めてあれば、レコードのログといっしょくたに扱うようにすることでどうにか
       なりそうだが、現状、SkipListBlockPageの中の各メソッドにTransactionオブジェクトは渡っていない・・・
   ・Splitが起きた場合への対処
     => Splitが起きた場合は、その操作自体のログを残す必要があるはず。なぜなら、エントリのあるページが
       変わってしまうので。
       ログに残す情報としては、キーレンジ xxxx ~ yyyy が、ページID A番からB番に移った、というような
       ところが分かる形になっていればよい・・・はず?
       この場合、リカバリ時は、splitしたページをマージするというのは無茶な気がするので、ページ番号の読み替え
       テーブルを用意するような形になるのではないかと思われる。(UpdateでRIDが変わっていた場合のロールバックでの
       対処と同じようなやり方になるかと)
       もう一つは、1エントリずつ削除と追加のログを残すという方法。これであれば、読み替えテーブルは不要にできそうだが、
       効率はすごく悪そうな感じがする。特にログを残す際のオーバヘッドがつらい、か。
・バッファサイズが小さくとも、スワップアウトできるVictimページが存在しないためにpanic、という
 状況にならないよう制御する
 (Fetchでnilを返して、そのトランザクションをアボートさせ・・・ても解放されるフレームは限定的
  な気がする。SkipList Indexが複数ページのピンを同時に立てることがあるので、そこでまとめて解放
  される形にできればこの方針でもいけるかもしれない。そうでなければ、空きフレームが一定数以下に
  なったらFetchをブロックさせれば良い・・・?というか、Victimページが見つからない時はBPMのラッチを
  離してsleepでブロックさせるとかすればよいのか・・・?)
・ロックとってるのにアトミック操作で変数アクセスしている箇所がパフォーマンス的にムダなので修正
・複数テストケースの(オンメモリ仮想ファイルでの)並列実行を可能にする
 - 実ファイルでないと実施できないテストケースだけオンメモリ仮想ファイル利用の例外としているところを、
  例外としなくて済むようオンメモリ仮想ファイルの実装を拡張し、それに合わせてテストケースを修正する
  (DBを構成するオブジェクトを削除しても、テスト中はなんちゃって仮想ファイルシステム上に仮想ファイルが
  残っていて、再度Openして利用できるようにする)
 - 乱数seedの設定をテストケースローカルで閉じるようにして、(並列txnのテストでなければ)再現性が担保され
  るようにする(今は各々がグローバルの乱数seedを変更してしまっている)
・フロントから利用する際にエラー発生時の返り値も定義しておき、適宜返すようにする
 (現状、math.MinInt32がSkipList Indexの張られているカラムで値として使用された場合など)
・BPMのFetchPageとUnpinPageの性能改善(内部的にMutexをより細粒度にとるようにできないか?とか)
 => BPMがボトルネックになっていることがSkipListで確認されているが、それは他の箇所でも起こることだと思われる
 => 異なる箇所であればファイルの並列読み書きも可能なようなのでそこらもおこなえるようにしたい
・ベンチマーク的なものの整備と測定
・オプティマイザのエンハンス
 - predicateでのOR対応
・phantom readを回避可能なよう並行制御を改善する
・戦いは続く
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment