業務でGoogle Cloud Strage にObjectをコピーするときの挙動について調べたのでまとめておきます。
GCSでのコピーは cloud.google.com/go/storage 配下にある copy.go に処理の中身が書いてあります。
実際にGCSでコピーをするときには以下のようなコードを書きます。
※ サンプルコードなので適当です。
client, _ := storage.NewClient(ctx) defer client.Close() srcObj = client.Bucket(srcBucketName).Object(srcObjName) distObj = client.Bucket(distBucketName).Object(distObjName) // copier の生成 copier := distObj.CopierFrom(srcObj) // copy の実行 attr, err := copier.Run(ctx) if err != nil { // copy失敗 -> (1) fmt.Errorf("copy error. err: %v", err) } // copyが成功したら生成される Object Attributes return attr
ここで (1) の copy時にエラーが発生したときにcopy途中だったオブジェクトはどうなるのか?ということが気になったので実際の copy 処理を行なっている Run
メソッドの中を見ました。
// Run performs the copy. func (c *Copier) Run(ctx context.Context) (attrs *ObjectAttrs, err error) { ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Copier.Run") defer func() { trace.EndSpan(ctx, err) }() if err := c.src.validate(); err != nil { return nil, err } if err := c.dst.validate(); err != nil { return nil, err } // Convert destination attributes to raw form, omitting the bucket. // If the bucket is included but name or content-type aren't, the service // returns a 400 with "Required" as the only message. Omitting the bucket // does not cause any problems. rawObject := c.ObjectAttrs.toRawObject("") for { res, err := c.callRewrite(ctx, rawObject) if err != nil { return nil, err } if c.ProgressFunc != nil { c.ProgressFunc(uint64(res.TotalBytesRewritten), uint64(res.ObjectSize)) } if res.Done { // Finished successfully. return newObject(res.Resource), nil } } }
新しいオブジェクトにCopyが完了した段階で if res.Done {}
の分岐に入ってきてこの時だけcopyした結果をインスタンス化して返しているのでそもそもCopyが行われてる最中に失敗したら Copy 対象は中途半端な状態で残ることなくerrが返されるだけになります。
途中で失敗してもゴミが残らないように作られてました。