目次
はじめに
10ANTZのエンジニアGM担当している@mine-masahiroです。
10ANTZではクラウド環境としてGoogle Cloud Platform(GCP)を採用しています。
そして、マイクロサービスのデータベースとしてはGoogle Cloud Spannerを採用しています。
MySQLなどのデータベースのORMは多いですが、Cloud Spannerのgolang製ORMはなかなかありませんでしたので、今回SSORM(Simple Spanner ORM)を公開することにしました。
github
https://github.com/10antz-inc/ssorm
SSORMについて
Google Cloud Spannerのデータ操作言語(DML)とミューテーションは Cloud Spanner の 2 つの API が存在します。
ミューテーションとDMLの違いについて、DML とミューテーションの比較が参考になります。
簡単に説明すると
- DML
INSERT、UPDATE、DELETE の各ステートメントを使用してデータベース テーブル内のデータを操作できます。 - ミューテーション
データベース内のさまざまな行やテーブルに対して、Cloud Spanner によってアトミックに適用される一連の操作(挿入、更新、削除)を表します。
SSORMがサポートしているAPI
SSORMは下記の理由で現在DMLのみ対応しています。
同じトランザクションで DML とミューテーションを混在させない観点から、Mutation を使用して行った変更が、トランザクションを commit するまで後続のステートメントで参照できないため、トランザクションの最後にのみ書き込みをバッファリングするのが理想的なので、SSORMは現在DMLのみ対応しています。初期Mutation対応を行いましたが、実装するエンジニアが混乱しないようにMutationは外しました。
SSORMがサポートしている機能一覧
SSORMのtag一覧
- ssorm_key:”primary” //primary key
- ssorm_key:”delete_time” //soft delete時に使用
- ssorm_key:”create_time” //insert時に使用
- ssorm_key:”update_time” //update時に使用
- ssorm_key:”ignore_write” //insert、update対象から外す際に使用
サンプル
- Model
123456789101112type Singers struct {SingerId int64 `ssorm_key:"primary"`FirstName string `spanner:"FirstName"`LastName spanner.NullStringTestTime spanner.NullTime `spanner:"TestTime"` //NULL を許容する場合必ず、spanner.NullTimeを指定することTestSpannerTime spanner.NullTime `spanner:"TestSpannerTime"`TagIDs []spanner.NullString `spanner:"TagIds"`Numbers []int64 `spanner:"Numbers"`DeleteTime spanner.NullTime `spanner:"DeleteTime" ssorm_key:"delete_time"`CreateTime time.Time `spanner:"CreateTime" ssorm_key:"create_time"`UpdateTime time.Time `spanner:"UpdateTime" ssorm_key:"update_time"`} - Insert
- Model
12insert := Singers{}_, err = ssorm.Model(&insert).Insert(ctx, txn)
- Model
- Update (Model and Columns and Params)
- Model (where ssorm_key:”primary”で更新を行う)
12update := Singers{}_, err = ssorm.Model(&update).Update(ctx, txn) - Columns
123update := Singers{}columns := []string{"LastName", "FirstName", "Numbers"}count, err := ssorm.Model(&update).UpdateColumns(ctx, txn, columns) - Params
123update := Singers{}params := map[string]interface{}{"TagIds": []spanner.NullString{{StringVal: "a3eb54bd-0138-4c22-b858-41bbefc5c052", Valid: true}, {StringVal: "a3eb54bd-0138-4c22-b858-41bbefc5c053", Valid: true}}}count, err := ssorm.Model(&update).Where("SingerId > ?", 13).UpdateParams(ctx, txn, params)
- Model (where ssorm_key:”primary”で更新を行う)
- Find
- Model
12var singers []*Singerserr := ssorm.Model(&singers).Where("SingerId in (?)", []int{12, 13, 14}).Find(ctx, txn)
- Model
- First
- Model
12singer := Singers{}err := ssorm.Model(&singer).First(ctx, txn)
- Model
- Count
- Model
12345var (singer *Singerscount int64)err := ssorm.Model(singer).Count(ctx, txn, &count)
- Model
- Delete (Model and Where)
- Model (where ssorm_key:”primary”で削除を行う)
12delete := Singers{}_, err = ssorm.Model(&delete).DeleteModel(ctx, txn) - Where
12delete := Singers{}_, err = ssorm.Model(&delete).Where("SingerId = ?", 23).DeleteWhere(ctx, txn)
- Model (where ssorm_key:”primary”で削除を行う)
- SubQuery
- Model
12345678910111213141516171819202122232425262728293031type Singer struct {SingerId int64 `ssorm_key:"primary"`FirstName stringLastName stringAlbums []*AlbumsConcerts []*ConcertsDeleteTime spanner.NullTime `spanner:"DeleteTime" ssorm_key:"delete_time"`}type Albums struct {SingerId int64AlbumId int64Title stringDeleteTime spanner.NullTime `spanner:"DeleteTime" ssorm_key:"delete_time"`}type Concerts struct {SingerId int64ConcertId int64Price int64DeleteTime spanner.NullTime `spanner:"DeleteTime" ssorm_key:"delete_time"`}// FirstsubSingers := Singer{}err := ssorm.Model(&subSingers).TableName("Singers").AddSub(Albums{}, "SingerId = ?", 12).AddSub(Concerts{}, "SingerId = ?", 12).First(ctx, txn)//Findvar subSingers []*Singererr := ssorm.Model(&subSingers).Where("SingerId > ?", 12).TableName("Singers").AddSub(Albums{}, "").AddSub(Concerts{}, "SingerId > ?", 12).Find(ctx, txn)
- Model
- SoftDelete
- ssorm.SoftDeleteModelを使用して、Insert Update Find First Count Delete SubQueryを実行
12345678910err := ssorm.SoftDeleteModel(&singers).Find(ctx, txn)err = ssorm.SoftDeleteModel(&subSingers).Where("SingerId > ?", 12).TableName("Singers").AddSub(Albums{}, "").AddSub(Concerts{}, "SingerId > ?", 12).Find(ctx, txn)err = ssorm.SoftDeleteModel(&singers).Where("SingerId = 13").Find(ctx, txn)err = ssorm.SoftDeleteModel(insert).Where("SingerId in (?)", []int{12, 13, 14, 15}).Count(ctx, txn, &count)_, err = ssorm.SoftDeleteModel(&insert).Insert(ctx, txn)_, err = ssorm.SoftDeleteModel(&insert).Update(ctx, txn)_, err = ssorm.SoftDeleteModel(&insert).DeleteModel(ctx, txn)err = ssorm.SoftDeleteModel(&singer).Where("SingerId = ?", 25).First(ctx, txn)_, err = ssorm.SoftDeleteModel(&insert).Where("SingerId = ?", 25).DeleteWhere(ctx, txn)_, err = ssorm.Model(&insert).DeleteModel(ctx, txn)
- ssorm.SoftDeleteModelを使用して、Insert Update Find First Count Delete SubQueryを実行
まとめ
SSORMはtagを指定することで、primary key、create_time、update_time、delete_timeを自動判断し、SoftDelete機能を実現しています。
現在はArray型を対応しており、Struct型は今後対応進めていきたいと思います。またtagの種類を追加することで、Indexなども対応していきたいと思います。
SubQueryを対応しており、テーブルJoinは対応していません、下記の理由で現時点では対応予定はありません。
cloud-spanner-でインターリーブテーブルを高速に取得する
何か要望などがあればぜひGitHubのIssueなどからお願いします。