🉑

TypeScriptで簡易DBを実装する

2022/12/01に公開

はじめに

バックエンドエンジニアとして3年目に入ると、インフラ構築やDBREとの折衝など言語の知識だけでは対処できない業務が増えてきた、、、

そこでインフラや低レイヤについて知見を深めるべく、2か月前に読了した Let's Build a Simple DatabaseをTypeScriptで書き直しました。

作ったもの

実際の実装がこちら。

https://github.com/yasuaki640/tsqlite

SQLiteのようなファイルベースのDBで、userテーブルのinsertselectしかできません。

元ネタのPart 5(ディスクへの永続化)までの実装となっており、B-Treeの導入はスコープ外としました。

動作イメージ
$ npm start mydb.db

> tsqlite@0.0.0 start
> node build/src/main.js mydb.db

db > insert 1 mask eron@twitter.com
Executed.
db > insert 2 trump donald@fakenews.co.jp
Executed.
db > select
(1, mask, eron@twitter.com)
(2, trump, donald@fakenews.co.jp)
Executed.
出力されたバイナリファイル
00000000: 0100 0000 6d61 736b 0000 0000 0000 0000  ....mask........
00000010: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000020: 0000 0000 6572 6f6e 4074 7769 7474 6572  ....eron@twitter
00000030: 2e63 6f6d 0000 0000 0000 0000 0000 0000  .com............
00000040: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000050: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000060: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000070: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000080: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000090: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000100: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000110: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000120: 0000 0002 0000 0074 7275 6d70 0000 0000  .......trump....
00000130: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000140: 0000 0000 0000 0064 6f6e 616c 6440 6661  .......donald@fa
00000150: 6b65 6e65 7773 2e63 6f2e 6a70 0000 0000  kenews.co.jp....
00000160: 0000 0000 0000 0000 0000 0000 0000 0000  ................
...

学んだこと

車輪の再発明は楽しい

普段何気なくライブラリをダウンロードして使っていますが、その裏には地道なエラーハンドリングやバリデーション、メモリ管理など膨大な処理が隠れていることに気づきました。

例えば対話的な処理を実装するにも、使い慣れないNode.jsのAPIに詳しくなる必要があったり、SQLを地道に解析しバリデーションする必要があったり、、、言語機能やAPIをフル活用するため必然的に言語に詳しくなります。

意外だったのは、JS(Node.js)でDBを実装するという「さすがにそれは向いてないんじゃないの(笑)」みたいな技術選定だと、言語機能やあまり使わないようなAPIをフル活用するため必然的に言語に詳しくなります。

他言語への移植の学習効果

実装のきっかけとなった記事にも書かれていますが、何かしらのコードを理解する際、コピペやただ写経するだけだと理解が薄くなりがちでした。

これが他言語への移植となると、元ネタのコードを詳細に理解しさらにC言語とTypeScript(Node.js)両方のAPIについて詳しくなければなりません。

例えばファイルシステム回りのAPIについて、C言語のopen()とNode.jsのfs.open、NodeのCLI用便利メソッド、など両方のライブラリについて知見が深くなりました。

デメリットは時間がかかることです。

JavaScript(Node.js)でも意外とバイナリは扱える

JSはメモリに直接アクセスできませんが、Buffer.allocを使えば任意のサイズのバイナリを生成し疑似的なメモリとして扱うことができます。

またC言語だとlseek等の関数でファイルの読み書き位置を指定できますが、Nodeだとfs.readで読み取り開始位置を指定することができます。

上記のようにC言語のmallocopenに相当するAPIは、Node.jsに用意されてるものもあると感じました。

キャッシュの仕組み

元ネタの実装にはデータのキャッシュ機構が存在し、ページが要求されて初めてファイルストレージからメモリにマッピングする、デマンドページング的な処理を実装しています。

ここではファイルの全体ではなく必要な部分だけ読み込むなど少ないリソースで大量のデータを高速に扱う工夫が凝らされており、自身で実装しなおすことでキャッシュの仕組みを感じることができました。

終わりに

今回はTypeScriptで超簡素なDBを実装してみました。

普段使っている何気なく使っているライブラリを自作することは学びが多く、普段の開発において視点をちょっとだけ広く持てた気がします。

もしよろしければ一緒に勉強しませんか?

ご意見、間違いありましたらコメントお待ちしています!!!

GitHubで編集を提案

Discussion