2023-06-27 08:53:53 +00:00
|
|
|
package client
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
|
2024-10-07 14:20:25 +00:00
|
|
|
buffPool "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/util/pool"
|
2023-07-26 11:38:43 +00:00
|
|
|
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
2023-06-27 08:53:53 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/transformer"
|
2023-07-06 14:06:17 +00:00
|
|
|
"google.golang.org/grpc/codes"
|
|
|
|
"google.golang.org/grpc/status"
|
2023-06-27 08:53:53 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func (c *Client) objectPutInitTransformer(prm PrmObjectPutInit) (*objectWriterTransformer, error) {
|
|
|
|
var w objectWriterTransformer
|
|
|
|
w.it = internalTarget{
|
|
|
|
client: c,
|
|
|
|
prm: prm,
|
|
|
|
}
|
2023-11-13 12:18:07 +00:00
|
|
|
key := &c.prm.Key
|
2023-09-07 08:53:49 +00:00
|
|
|
if prm.Key != nil {
|
|
|
|
key = prm.Key
|
2023-06-27 08:53:53 +00:00
|
|
|
}
|
|
|
|
w.ot = transformer.NewPayloadSizeLimiter(transformer.Params{
|
|
|
|
Key: key,
|
2023-07-07 12:34:31 +00:00
|
|
|
NextTargetInit: func() transformer.ObjectWriter { return &w.it },
|
2023-09-07 08:53:49 +00:00
|
|
|
MaxSize: prm.MaxSize,
|
|
|
|
WithoutHomomorphicHash: prm.WithoutHomomorphHash,
|
|
|
|
NetworkState: prm.EpochSource,
|
2024-03-25 07:35:17 +00:00
|
|
|
Pool: prm.Pool,
|
2023-06-27 08:53:53 +00:00
|
|
|
})
|
|
|
|
return &w, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type objectWriterTransformer struct {
|
2023-07-07 12:34:31 +00:00
|
|
|
ot transformer.ChunkedObjectWriter
|
2023-06-27 08:53:53 +00:00
|
|
|
it internalTarget
|
|
|
|
err error
|
|
|
|
}
|
|
|
|
|
|
|
|
func (x *objectWriterTransformer) WriteHeader(ctx context.Context, hdr object.Object) bool {
|
|
|
|
x.err = x.ot.WriteHeader(ctx, &hdr)
|
|
|
|
return x.err == nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (x *objectWriterTransformer) WritePayloadChunk(ctx context.Context, chunk []byte) bool {
|
|
|
|
_, x.err = x.ot.Write(ctx, chunk)
|
|
|
|
return x.err == nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (x *objectWriterTransformer) Close(ctx context.Context) (*ResObjectPut, error) {
|
2024-03-19 12:36:51 +00:00
|
|
|
if x.err != nil {
|
|
|
|
return nil, x.err
|
|
|
|
}
|
|
|
|
|
2023-07-05 12:50:33 +00:00
|
|
|
ai, err := x.ot.Close(ctx)
|
|
|
|
if err != nil {
|
2023-06-27 08:53:53 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2023-07-05 12:50:33 +00:00
|
|
|
|
2024-07-12 11:35:01 +00:00
|
|
|
if ai != nil {
|
|
|
|
x.it.res.epoch = ai.Epoch
|
|
|
|
if ai.ParentID != nil {
|
|
|
|
x.it.res.obj = *ai.ParentID
|
|
|
|
}
|
2023-07-05 12:50:33 +00:00
|
|
|
}
|
|
|
|
return x.it.res, nil
|
2023-06-27 08:53:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type internalTarget struct {
|
2023-07-06 14:06:17 +00:00
|
|
|
client *Client
|
|
|
|
res *ResObjectPut
|
|
|
|
prm PrmObjectPutInit
|
|
|
|
useStream bool
|
2023-06-27 08:53:53 +00:00
|
|
|
}
|
|
|
|
|
2023-07-07 12:34:31 +00:00
|
|
|
func (it *internalTarget) WriteObject(ctx context.Context, o *object.Object) error {
|
|
|
|
putSingleImplemented, err := it.tryPutSingle(ctx, o)
|
2023-07-06 14:06:17 +00:00
|
|
|
if putSingleImplemented {
|
2023-07-07 12:34:31 +00:00
|
|
|
return err
|
2023-07-06 14:06:17 +00:00
|
|
|
}
|
|
|
|
it.useStream = true
|
2023-07-07 12:34:31 +00:00
|
|
|
return it.putAsStream(ctx, o)
|
2023-07-06 14:06:17 +00:00
|
|
|
}
|
|
|
|
|
2023-07-07 12:34:31 +00:00
|
|
|
func (it *internalTarget) putAsStream(ctx context.Context, o *object.Object) error {
|
2023-06-27 08:53:53 +00:00
|
|
|
wrt, err := it.client.objectPutInitRaw(ctx, it.prm)
|
|
|
|
if err != nil {
|
2023-07-06 14:06:17 +00:00
|
|
|
return err
|
2023-06-27 08:53:53 +00:00
|
|
|
}
|
2023-07-07 12:34:31 +00:00
|
|
|
if wrt.WriteHeader(ctx, *o) {
|
|
|
|
wrt.WritePayloadChunk(ctx, o.Payload())
|
2023-06-27 08:53:53 +00:00
|
|
|
}
|
|
|
|
it.res, err = wrt.Close(ctx)
|
2023-11-13 12:18:07 +00:00
|
|
|
if err == nil && it.client.prm.DisableFrostFSErrorResolution && !apistatus.IsSuccessful(it.res.st) {
|
2023-07-26 11:38:43 +00:00
|
|
|
err = apistatus.ErrFromStatus(it.res.st)
|
|
|
|
}
|
2023-07-06 14:06:17 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-07-07 12:34:31 +00:00
|
|
|
func (it *internalTarget) tryPutSingle(ctx context.Context, o *object.Object) (bool, error) {
|
2023-07-06 14:06:17 +00:00
|
|
|
if it.useStream {
|
|
|
|
return false, nil
|
|
|
|
}
|
2023-09-07 08:53:49 +00:00
|
|
|
|
|
|
|
prm := PrmObjectPutSingle{
|
|
|
|
XHeaders: it.prm.XHeaders,
|
|
|
|
BearerToken: it.prm.BearerToken,
|
|
|
|
Session: it.prm.Session,
|
|
|
|
Local: it.prm.Local,
|
|
|
|
CopiesNumber: it.prm.CopiesNumber,
|
|
|
|
Object: o,
|
|
|
|
Key: it.prm.Key,
|
|
|
|
}
|
2023-07-06 14:06:17 +00:00
|
|
|
|
|
|
|
res, err := it.client.ObjectPutSingle(ctx, prm)
|
|
|
|
if err != nil && status.Code(err) == codes.Unimplemented {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err == nil {
|
2024-03-25 07:35:17 +00:00
|
|
|
it.returnBuffPool(o.Payload())
|
2023-07-07 12:34:31 +00:00
|
|
|
id, _ := o.ID()
|
2023-07-06 14:06:17 +00:00
|
|
|
it.res = &ResObjectPut{
|
|
|
|
statusRes: res.statusRes,
|
|
|
|
obj: id,
|
2024-07-12 11:35:01 +00:00
|
|
|
epoch: res.epoch,
|
2023-07-06 14:06:17 +00:00
|
|
|
}
|
2023-11-13 12:18:07 +00:00
|
|
|
if it.client.prm.DisableFrostFSErrorResolution && !apistatus.IsSuccessful(it.res.st) {
|
2023-07-26 11:38:43 +00:00
|
|
|
return true, apistatus.ErrFromStatus(it.res.st)
|
|
|
|
}
|
|
|
|
return true, nil
|
2023-07-06 14:06:17 +00:00
|
|
|
}
|
|
|
|
return true, err
|
2023-06-27 08:53:53 +00:00
|
|
|
}
|
2024-03-25 07:35:17 +00:00
|
|
|
|
|
|
|
func (it *internalTarget) returnBuffPool(playback []byte) {
|
|
|
|
if it.prm.Pool == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
var buffer buffPool.Buffer
|
|
|
|
buffer.Data = playback
|
|
|
|
it.prm.Pool.Put(&buffer)
|
|
|
|
}
|