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
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
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時代の開発フロー改革の ロードマップと実行 ─
starfish719
0
9.5k
Oracle AI Database@Azure:サービス概要のご紹介
oracle4engineer
PRO
6
1.9k
AIの性能が向上しても未解決な組織の重大問題は何か?/An Unsolved Organizational Problem in the Age of AI
moriyuya
3
570
新しいVibe Codingと”自走”について
watany
5
280
AIソロプレナー時代に2ヶ月で20人増員した事業創造会社の開発組織の話
miyatakoji
0
510
Oracle AI Database@AWS:サービス概要のご紹介
oracle4engineer
PRO
4
2.9k
地球に⽣きるAI —GeoAIと「中間領域」— / AI Living on Earth — GeoAI and the “Intermediate Layer” —
ykiyota
0
140
AI Engineering Summit Tokyo 2026 AIの前に、やることがある 〜医療データ企業の4フェーズ〜
dtaniwaki
0
2.4k
2026TECHFRESH畢業分享會 - 葬送的通靈師:化系統與用戶雜訊成行動訊號
line_developers_tw
PRO
0
590
Microsoft Build Keynoteふりかえり
tomokusaba
0
120
MIERUNE JCT 発表資料「宇宙から伊能忠敬ごっこ」
syuchimu
0
200
スキルと MCP ツール、責務をどう分けるか? AI が迷わないインターフェース設計の戦略
cdataj
1
870
Featured
See All Featured
How To Speak Unicorn (iThemes Webinar)
marktimemedia
1
480
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
254
22k
The Cult of Friendly URLs
andyhume
79
6.9k
Google's AI Overviews - The New Search
badams
0
1k
How to make the Groovebox
asonas
2
2.2k
Getting science done with accelerated Python computing platforms
jacobtomlinson
2
220
Lightning talk: Run Django tests with GitHub Actions
sabderemane
0
200
Hiding What from Whom? A Critical Review of the History of Programming languages for Music
tomoyanonymous
2
850
A Soul's Torment
seathinner
6
2.9k
30 Presentation Tips
portentint
PRO
1
320
Max Prin - Stacking Signals: How International SEO Comes Together (And Falls Apart)
techseoconnect
PRO
0
180
Unsuck your backbone
ammeep
672
58k
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みたいなコマンドベース