git.frostfs.info/TrueCloudLab/frostfs-sdk-go@v0.0.0-20241022124111-5361f0ecebd3/client/object_put_transformer.go (about)

     1  package client
     2  
     3  import (
     4  	"context"
     5  
     6  	buffPool "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/util/pool"
     7  	apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
     8  	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
     9  	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/transformer"
    10  	"google.golang.org/grpc/codes"
    11  	"google.golang.org/grpc/status"
    12  )
    13  
    14  func (c *Client) objectPutInitTransformer(prm PrmObjectPutInit) (*objectWriterTransformer, error) {
    15  	var w objectWriterTransformer
    16  	w.it = internalTarget{
    17  		client: c,
    18  		prm:    prm,
    19  	}
    20  	key := &c.prm.Key
    21  	if prm.Key != nil {
    22  		key = prm.Key
    23  	}
    24  	w.ot = transformer.NewPayloadSizeLimiter(transformer.Params{
    25  		Key:                    key,
    26  		NextTargetInit:         func() transformer.ObjectWriter { return &w.it },
    27  		MaxSize:                prm.MaxSize,
    28  		WithoutHomomorphicHash: prm.WithoutHomomorphHash,
    29  		NetworkState:           prm.EpochSource,
    30  		Pool:                   prm.Pool,
    31  	})
    32  	return &w, nil
    33  }
    34  
    35  type objectWriterTransformer struct {
    36  	ot  transformer.ChunkedObjectWriter
    37  	it  internalTarget
    38  	err error
    39  }
    40  
    41  func (x *objectWriterTransformer) WriteHeader(ctx context.Context, hdr object.Object) bool {
    42  	x.err = x.ot.WriteHeader(ctx, &hdr)
    43  	return x.err == nil
    44  }
    45  
    46  func (x *objectWriterTransformer) WritePayloadChunk(ctx context.Context, chunk []byte) bool {
    47  	_, x.err = x.ot.Write(ctx, chunk)
    48  	return x.err == nil
    49  }
    50  
    51  func (x *objectWriterTransformer) Close(ctx context.Context) (*ResObjectPut, error) {
    52  	if x.err != nil {
    53  		return nil, x.err
    54  	}
    55  
    56  	ai, err := x.ot.Close(ctx)
    57  	if err != nil {
    58  		return nil, err
    59  	}
    60  
    61  	if ai != nil {
    62  		x.it.res.epoch = ai.Epoch
    63  		if ai.ParentID != nil {
    64  			x.it.res.obj = *ai.ParentID
    65  		}
    66  	}
    67  	return x.it.res, nil
    68  }
    69  
    70  type internalTarget struct {
    71  	client    *Client
    72  	res       *ResObjectPut
    73  	prm       PrmObjectPutInit
    74  	useStream bool
    75  }
    76  
    77  func (it *internalTarget) WriteObject(ctx context.Context, o *object.Object) error {
    78  	putSingleImplemented, err := it.tryPutSingle(ctx, o)
    79  	if putSingleImplemented {
    80  		return err
    81  	}
    82  	it.useStream = true
    83  	return it.putAsStream(ctx, o)
    84  }
    85  
    86  func (it *internalTarget) putAsStream(ctx context.Context, o *object.Object) error {
    87  	wrt, err := it.client.objectPutInitRaw(ctx, it.prm)
    88  	if err != nil {
    89  		return err
    90  	}
    91  	if wrt.WriteHeader(ctx, *o) {
    92  		wrt.WritePayloadChunk(ctx, o.Payload())
    93  	}
    94  	it.res, err = wrt.Close(ctx)
    95  	if err == nil && it.client.prm.DisableFrostFSErrorResolution && !apistatus.IsSuccessful(it.res.st) {
    96  		err = apistatus.ErrFromStatus(it.res.st)
    97  	}
    98  	return err
    99  }
   100  
   101  func (it *internalTarget) tryPutSingle(ctx context.Context, o *object.Object) (bool, error) {
   102  	if it.useStream {
   103  		return false, nil
   104  	}
   105  
   106  	prm := PrmObjectPutSingle{
   107  		XHeaders:     it.prm.XHeaders,
   108  		BearerToken:  it.prm.BearerToken,
   109  		Session:      it.prm.Session,
   110  		Local:        it.prm.Local,
   111  		CopiesNumber: it.prm.CopiesNumber,
   112  		Object:       o,
   113  		Key:          it.prm.Key,
   114  	}
   115  
   116  	res, err := it.client.ObjectPutSingle(ctx, prm)
   117  	if err != nil && status.Code(err) == codes.Unimplemented {
   118  		return false, err
   119  	}
   120  
   121  	if err == nil {
   122  		it.returnBuffPool(o.Payload())
   123  		id, _ := o.ID()
   124  		it.res = &ResObjectPut{
   125  			statusRes: res.statusRes,
   126  			obj:       id,
   127  			epoch:     res.epoch,
   128  		}
   129  		if it.client.prm.DisableFrostFSErrorResolution && !apistatus.IsSuccessful(it.res.st) {
   130  			return true, apistatus.ErrFromStatus(it.res.st)
   131  		}
   132  		return true, nil
   133  	}
   134  	return true, err
   135  }
   136  
   137  func (it *internalTarget) returnBuffPool(playback []byte) {
   138  	if it.prm.Pool == nil {
   139  		return
   140  	}
   141  	var buffer buffPool.Buffer
   142  	buffer.Data = playback
   143  	it.prm.Pool.Put(&buffer)
   144  }