Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
go-sqlite3を使ってCloud Spannerエミュレーターを作ってみた / Clou...
Search
kazegusuri
February 10, 2020
Technology
6.8k
5
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
go-sqlite3を使ってCloud Spannerエミュレーターを作ってみた / Cloud Spanner emulator with go-sqlite3
kazegusuri
February 10, 2020
More Decks by kazegusuri
See All by kazegusuri
handy-spanner GCPUG
kazegusuri
4
2.1k
Open SKT: メルペイ開発の裏側 / builderscon tokyo 2019 Open SKT
kazegusuri
22
28k
Keep watching and extending features of gRPC
kazegusuri
3
2.7k
Testing with microservices in merpay
kazegusuri
10
11k
Real World Mercari API Architecture
kazegusuri
1
6.4k
gRPC and REST with gRPC in practice
kazegusuri
19
8.1k
Fluentdで始めるPrometheus / Prometheus Tokyo Meetup #1
kazegusuri
1
2k
GRPCの実践と現状での利点欠点 / Go Conference 2016 Spring
kazegusuri
44
32k
OutputとBufferedOutputの間の何か
kazegusuri
2
3.5k
Other Decks in Technology
See All in Technology
LLMにもCAP定理があるという話
harukasakihara
0
280
非定型業務をAI slackbotで自動化する ~ 社内要望を自動壁打ちするbotを作った ~/automating-ad-hoc-work-with-ai-slackbot
shibayu36
0
560
2026 TECHFRESH 畢業分享會 - 開發日常大解密!從領域驅動到企業級上線
line_developers_tw
PRO
0
590
「気づいたら仕事が終わっている」バクラクAIエージェント本番運用の裏側 / layerx-bakuraku-aie2026
yuya4
19
11k
Building applications in the Gemini API family.
line_developers_tw
PRO
0
2.7k
AAIFに入ってみた ~内から見えるコミュニティ動向~
sato4
0
120
FDE という解 ― 暗黙知と明示知をつなぐ、伴走型エンジニアリング ―
otanet
0
110
機械学習を「社会実装」するということ 2026年夏版 / Social Implementation of Machine Learning June 2026 Version
moepy_stats
4
1k
MCP Appsを作ってみよう
iwamot
PRO
4
390
あなたの AI ワークスペースに、 専門コーダーを連れてくる - Amazon Quick Desktop 最新情報
kawaji_scratch
1
130
AI Engineering Summit Tokyo 2026 AIの前に、やることがある 〜医療データ企業の4フェーズ〜
dtaniwaki
0
2.4k
やさしいA2A入門
minorun365
PRO
10
1.5k
Featured
See All Featured
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
194
17k
Leo the Paperboy
mayatellez
7
1.8k
Future Trends and Review - Lecture 12 - Web Technologies (1019888BNR)
signer
PRO
0
3.6k
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
The Limits of Empathy - UXLibs8
cassininazir
1
350
Unsuck your backbone
ammeep
672
58k
Building AI with AI
inesmontani
PRO
1
1.1k
Leveraging Curiosity to Care for An Aging Population
cassininazir
1
270
Documentation Writing (for coders)
carmenintech
77
5.4k
svc-hook: hooking system calls on ARM64 by binary rewriting
retrage
2
290
Claude Code のすすめ
schroneko
67
230k
A Tale of Four Properties
chriscoyier
163
24k
Transcript
go-sqlite3を使って Cloud Spannerエミュレータを つくってみた mercari.go #13
@kazegusuri • Merpay Backend Engineer • Architect Team 2015/11 2017/01
2018/01 SRE & Platform Platform Architect
トークの概要 handy-spannerの説明をしながら 01 SQLiteの話をして Goでどうやっているのか 03 02
Cloud Spanner • Google Cloud Platformが提供するフルマネージドデータベース ◦ スキーマ,トランザクション,強整合性,スケーラブル • Google
Cloud Client Libraryを使ってアクセス ◦ https://github.com/googleapis/google-cloud-go • gRPCでAPIを提供 ◦ gRPCの定義も公開されている ◦ https://github.com/googleapis/googleapis
handy-spanner • Cloud Spannerの(非公式)エミュレーター (Go) ◦ 現状では一番再現率が高いはず ◦ https://github.com/gcpug/handy-spanner ◦
https://speakerdeck.com/kazegusuri/handy-spanner-gcpug • SQLパーサーはmemefish (Go) ◦ Cloud SpannerのSQLを正確にパース可能 ◦ https://github.com/MakeNowJust/memefish • ストレージはSQLite
SQLite • "SQLデータベースエンジンを実装したインプロセスライブラリ" ◦ Full-Featured SQL ◦ Extensionによる機能拡張 • mattn/go-sqlite3
◦ https://github.com/mattn/go-sqlite3 ◦ Goのsqlite3用driver ◦ database/sqlから操作可能
Cloud Spannerエミュレーター • gRPCのAPIを実装してCloud Spannerの挙動を再現する • Google Cloud Client Libraryからアクセス先を切り替えて利用する
App Google Cloud Client Library Google Cloud Spanner handy-spanner SQLite
gRPC gRPC
Spannerの機能は大きく3つ • RPCによるデータの参照・更新 ◦ e.g. StreamingRead, Mutation(Commit) ◦ SQLiteのRead/Write(クエリ)に変換 •
SQLによるデータの参照・更新 ◦ e.g. ExecuteStreamingSql, ExecuteSql(DML) ◦ SQLiteのRead/Write(クエリ)に変換 • トランザクション管理 ◦ e.g. BeginTransaction, Commit, Rollback ◦ SQLiteのトランザクションとして管理
handy-spanner StreamingRead “SELECT FirstName, LastName FROM Singers WHERE FirstName =
‘foo’” SQLite SQLite SQL ReadRequest Table = Singers Columns = FirstName, LastName KeySet = ‘foo’ クエリ生成
handy-spanner ExecuteStreamingSql “SELECT * FROM Singers WHERE FirstName = ‘foo’”
SELECT * FROM WHERE Singers = FirstName ‘foo’ “SELECT * FROM Singers WHERE FirstName = ‘foo’” SQLite Spanner SQL AST SQLite SQL クエリ生成 クエリパース
何がSQLiteで難しいの? • SQLiteにないデータ型(配列, 構造体) • SQLiteにない操作の実現 (UNNESTなど) • Cloud Spannerの関数
• トランザクション
配列と構造体の実装 • SQLiteのJSON型を利用 • 配列はJSON ARRAY ◦ e.g. [1, 2,
3], ["foo", "bar"] • 構造体はJSON OBJECT ◦ ただしフィールドに順序があるのでkey, valueを配列で扱う ◦ e.g. {"keys": ["x", "y"], "values": [1, 2]}
配列と構造体は組み合わせ可能 • 配列の構造体の配列の構造体みたいなのが可能 ◦ もちろん配列の要素型や構造体のフィールドは任意 • 読み書き前に型の定義は正確にわかっている ◦ e.g. ARRAY<STRUCT<x
INT64, y ARRAY<STRING>>> • DataValue型 ⇔ JSONの相互変換を実現したい ◦ DataValueは型定義と値を保持したstruct []struct { x int64 y []string } type DataValue struct { Type DataType Value interface{} } type DataType struct { // INT64, STRING... }
JSONとの相互変換 • json.Marshalerとjson.Unmarshalerを実装する ◦ DataValue型からJSONへの変換処理をMarshaler ◦ JSONからDataValue型への変換処理をUnmarshaler • 型の定義を見ながら再帰的に展開していく func
(v *DataValue) MarshalJSON() ([]byte, error) { } func (v *DataValue) UnmarshalJSON([]byte) error { }
データベースから透過的に扱いたい • database/sql.Scannerとdatabase/sql/driver.Valuerを実装する ◦ Scannerはデータベースから値を取り出す(Scan)するとき ◦ Valuerはデータベースに値を書き込むとき • 実際の値(JSON)への変換はMarshal/Unmarshalにまかせる func
(v *DataValue) Value() (driver.Value, error) { b, err := v.MarshalJSON() if err != nil { … } return driver.Value(string(b)), nil } func (v *DataValue) Scan(src interface{}) error { s, _ := src.(string) return v.UnmarshalJSON([]byte(s)) }
関数 • Cloud Spannerには大量の関数がある ◦ 集計関数, 数学関数, 文字列関数, 配列関数, タイムスタンプ関数...
◦ https://cloud.google.com/spanner/docs/functions-and-operators • 全ての関数がSQLiteに存在する訳ではない ◦ ABS() など基本的なものはある ◦ 配列関数や, 文字列関数などほとんどの関数はない
関数 • リテラルやパラメータなどは入力値がわかるのでクエリ変換可能 • データベースの値に対する操作はできることに制限がある… SELECT DATE_ADD( DATE "2008-12-25", INTERVAL
5 DAY); SELECT DATE "2008-12-30"; クエリ変換 SELECT DATE_ADD( @date, INTERVAL 5 DAY); @date = "2008-12-25" SELECT DATE "2008-12-30"; クエリ変換 SELECT DATE_ADD( tbl.created_at, INTERVAL 5 DAY); クエリ変換 Spanner SQL SQLite SQL
SQLiteのデータを操作したい • SQLite上のデータはアプリケーションから直接操作できない ◦ 出力時にScanner, 入力時にValuerを通すことで加工はできる ◦ JSONのようにExtensionによりSQLite側に直接ロジックも追加可能 Extension SQLite
handy-spanner JSON driver Scanner Valuer
SQLiteのカスタム関数 • SQLiteのクエリからGoの関数が呼び出せる ◦ 任意の値を受け取って、任意の値を返す関数 Extension SQLite handy-spanner JSON driver
Scanner Valuer Function
カスタム関数の実装 • Goの関数の引数と戻り値がカスタム関数と一致 ◦ interface{}で任意の型も可能 ◦ 可変長引数も可能 • エラーも返せる SIGN()
関数の実装 SELECT SIGN(3) // => 1 SELECT SIGN(-2) // => -1
EXTRACT() 関数の実装 カスタム関数の実装
カスタム関数の登録 • SQLiteドライバーを再登録する ◦ github.com/mattn/go-sqlite3.SQLiteConnのRegisterFuncで関数を登録 • database/sql.Openで登録したドライバを指定
カスタム関数を使えば • DATE_ADDの機能をX_DATE_ADDと登録した場合 SELECT DATE_ADD( DATE "2008-12-25", INTERVAL 5 DAY);
クエリ変換 SELECT DATE_ADD( @date, INTERVAL 5 DAY); @date = "2008-12-25" クエリ変換 SELECT DATE_ADD( tbl.created_at, INTERVAL 5 DAY); クエリ変換 Spanner SQL SQLite SQL SELECT X_DATE_ADD( DATE "2008-12-25", INTERVAL 5 DAY); SELECT X_DATE_ADD( @date, INTERVAL 5 DAY); @date = "2008-12-25" SELECT X_DATE_ADD( tbl.created_at, INTERVAL 5 DAY);
カスタム関数を使えば • 仮想的にデータ型も定義可能なはず! • 例えば、位置情報型(Geometry) ◦ SQLiteには"(100, 200)"のような文字列で保存 ◦ 比較(=)などのオペレーターは関数に置き換える
SELECT tbl.pos1 = tbl.pos2 クエリ変換 SELECT GEO_EQUAL(tbl.pos1, tbl.pos2)
カスタム関数を使う場合の注意 • rows.Err()のチェックは必須 ◦ カスタム関数でエラーが発生時にScanやク エリはエラーにならない
時間があればトランザクションの難しさを… • SQLiteはスキーマレベル Lock ◦ 誰かがロックを取るの他の全員がロック待ちになる • Shared Cache Modeにするとテーブルレベル
Lock ◦ 誰かがテーブルを読み込むとRead Lock ◦ 誰かがテーブルを書き込むとWrite Lock ◦ Read Lock中はReadはできるがWriteはできない ◦ Write中は他の誰もReadできない • ずっとReadしているやつがいるといつまでもWriteできない…!
handy-spannerのトランザクション管理 • SQLiteのトランザクションがどのテーブルをどうロックしているか全部管理 • トランザクションの優先度をつける ◦ 必要に応じて任意のトランザクションを終了させる ◦ 通常ならデッドロックするケースでも動くようになる
まとめ • エミュレーションは面白い ◦ エミュレーション対象のことがわかる • SQLiteのハックは面白い ◦ Goの拡張だけでなくC拡張も使えばもっとできることは広がる •
開発デバッグ目的だけじゃなくプロダクション用途にも発展可能 ◦ S3互換ストレージのような ◦ CQL(Cassandra)とかJQL(Jira) みたいなクエリベース ◦ Redisみたいなコマンドベース