タイトルのまんまです。
話の前提として、以下のパッケージを使っているものとします。
google.golang.org/appengine/datastore
SDK組み込みの appengine/datastore
や Cloud Datastore用の cloud.google.com/go/datastore
については言及しませんが、多分似たような設計になってると思うので流用可能なテクではないでしょうか。
モチベーション
GAE/J+Slim3とかだとKindのSchemaの変更は大変気軽にできました。
新規追加のプロパティはデフォルト値になりますし、削除したプロパティはインスタンスに変換される時に無視されるからです。
一方、Goは違います。
Entityに存在するプロパティに対応するフィールドがstructにないと、エラーになります。
このため、GAE/Goでは不要になったプロパティを削除できず、ずっと残す運用をしていました。
具体的に以下のような感じです。
type Sample struct {
ID int64 `goon:"id" datastore:"-"`
Name string
}
これを以下のように変更して適当に検索かけて使ってるところ消す。
type Sample struct {
ID int64 `goon:"id" datastore:"-"`
DeprecatedName string `datastore:"Name"`
}
もしくは、 datastore.PropertyLoadSavor
を使って読み出し時に該当のプロパティを抜いてから変換します。
Goの場合 ps = ps.Filter(p => p.Name != "Name")
みたいなコードが書きづらいのでひたすらめんどくさいです。
何も考えずにプロパティ削りたい。
対応
structに対し、 datastore.PropertyLoadSavor
を実装します。
まずは今まで書いていたであろう実装を示しておきます。
func (entity *Foo) Load(p []datastore.Property) error {
if err := datastore.LoadStruct(entity, p); err != nil {
return err
}
// 何かやったりする
return nil
}
func (entity *Foo) Save() ([]datastore.Property, error) {
// 何かやったりする
p, err := datastore.SaveStruct(entity)
if err != nil {
return nil, err
}
return p, nil
}
これを以下のように変更します。
func (entity *Foo) Load(p []datastore.Property) error {
err := datastore.LoadStruct(entity, p)
if fmerr, ok := err.(*datastore.ErrFieldMismatch); ok && fmerr != nil && fmerr.Reason == "no such struct field" {
// ignore
} else if err != nil {
return err
}
// 何かやったりする
return nil
}
func (entity *Foo) Save() ([]datastore.Property, error) {
// 何かやったりする
p, err := datastore.SaveStruct(entity)
if err != nil {
return nil, err
}
return p, nil
}
これだけでOKです。
これで大丈夫な理由
We don't return early, as we try to load as many properties as possible.
It is valid to load an entity into a struct that cannot fully represent it.
That case returns an error, but the caller is free to ignore it.
要するに、 datastore.LoadStruct
した時に *datastore.ErrFieldMismatch
が返ってきても途中で処理打ち切ったりせずにお尻まで処理してからエラー返してるので無視してもデータが壊れてることはないゾ!という話。
Reasonの値をチェックしているのは若干神経質な気もするので、おおらかな性格の人はReasonはみなくても多分大丈夫なんじゃないでしょうか。
感想
無視するために独自の datastore.LoadStruct
実装しなきゃかなと思って結構手を動かしてたんだけどある時 datastore.ErrFieldMismatch
の存在とソレに関わる処理を見つけて無駄な作業だった事を知ってしまいたいへん悲しい(有益な発見
とりあえず、これから本番環境に突っ込んでみる予定です。