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
『AIに負けない』より『AIと遊ぶ』」〜ワクワクが最強のテスト・QA学習戦略_公開用
odan611
1
330
AIに障害切り分けを全部やってもらった。 。 。 。
estie
0
330
「勝手に広まる」人気 AI エージェントを爆速で作ろう!(AWS Summit Japan 2026講演資料)
minorun365
PRO
10
2.8k
ご挨拶「10周年を迎える共創ラボのこれまでとこれから」
iotcomjpadmin
0
170
AWS Summit Japan 2026の振り返りと2027へ向けて / AWS Summit Japan 2026 Recap and Prospects for 2027
kaminashi
1
170
從觀望到全公司落地:AI Agentic Coding 導入實戰 — 流程整合與安全治理
appleboy
1
470
Tech-Verse 2026_Keynote
lycorptech_jp
PRO
0
120
AIDLC_ヤフーショッピングの取り組み
lycorptech_jp
PRO
0
420
AI Agentをシステムに組み込む前にゆるく向き合ってみる
hayama17
0
190
IaC コードを資産へ:AWS CDK 社内ライブラリと横断展開 / aws-summit-japan-2026
gotok365
10
1.7k
知見・人・API・DB・予算 ─ ナイナイ尽くしだった人事データ整備 with dbt、5年間の学び
ken6377
1
130
「軸足」は 固定しなくていい - 熱量と強みで描く、しなやかなキャリアの形
kakehashi
PRO
1
320
Featured
See All Featured
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
38
2.9k
Art, The Web, and Tiny UX
lynnandtonic
304
22k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
32
2.9k
Data-driven link building: lessons from a $708K investment (BrightonSEO talk)
szymonslowik
1
1.1k
How to Align SEO within the Product Triangle To Get Buy-In & Support - #RIMC
aleyda
2
1.6k
Navigating Weather and Climate Data
rabernat
0
260
Un-Boring Meetings
codingconduct
0
330
The browser strikes back
jonoalderson
0
1.3k
Measuring Dark Social's Impact On Conversion and Attribution
stephenakadiri
2
230
SEO Brein meetup: CTRL+C is not how to scale international SEO
lindahogenes
1
2.8k
Fireside Chat
paigeccino
42
4k
Impact Scores and Hybrid Strategies: The future of link building
tamaranovitovic
0
320
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みたいなコマンドベース