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

     1  package pool
     2  
     3  import (
     4  	"context"
     5  
     6  	sdkClient "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
     7  	apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
     8  	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
     9  	oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
    10  	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/transformer"
    11  	"go.uber.org/zap"
    12  	"go.uber.org/zap/zapcore"
    13  	"google.golang.org/grpc/codes"
    14  	"google.golang.org/grpc/status"
    15  )
    16  
    17  type logger interface {
    18  	log(level zapcore.Level, msg string, fields ...zap.Field)
    19  }
    20  
    21  type PrmObjectPutClientCutInit struct {
    22  	PrmObjectPut
    23  }
    24  
    25  func (c *clientWrapper) objectPutInitTransformer(prm PrmObjectPutClientCutInit) (*objectWriterTransformer, error) {
    26  	cl, err := c.getClient()
    27  	if err != nil {
    28  		return nil, err
    29  	}
    30  	var w objectWriterTransformer
    31  
    32  	w.it = internalTarget{
    33  		client:  cl,
    34  		prm:     prm,
    35  		address: c.address(),
    36  		logger:  &c.clientStatusMonitor,
    37  	}
    38  
    39  	key := &c.prm.key
    40  	if prm.key != nil {
    41  		key = prm.key
    42  	}
    43  
    44  	w.ot = transformer.NewPayloadSizeLimiter(transformer.Params{
    45  		Key:                    key,
    46  		NextTargetInit:         func() transformer.ObjectWriter { return &w.it },
    47  		MaxSize:                prm.networkInfo.MaxObjectSize(),
    48  		WithoutHomomorphicHash: prm.withoutHomomorphicHash,
    49  		NetworkState:           prm.networkInfo,
    50  		SessionToken:           prm.stoken,
    51  	})
    52  	return &w, nil
    53  }
    54  
    55  type objectWriterTransformer struct {
    56  	ot  transformer.ChunkedObjectWriter
    57  	it  internalTarget
    58  	err error
    59  }
    60  
    61  func (x *objectWriterTransformer) WriteHeader(ctx context.Context, hdr object.Object) bool {
    62  	x.err = x.ot.WriteHeader(ctx, &hdr)
    63  	return x.err == nil
    64  }
    65  
    66  func (x *objectWriterTransformer) WritePayloadChunk(ctx context.Context, chunk []byte) bool {
    67  	_, x.err = x.ot.Write(ctx, chunk)
    68  	return x.err == nil
    69  }
    70  
    71  // ResObjectPut groups the final result values of ObjectPutInit operation.
    72  type ResObjectPut struct {
    73  	Status apistatus.Status
    74  	OID    oid.ID
    75  	Epoch  uint64
    76  }
    77  
    78  // Close return non nil result in any case. If error occurred, the result contains only buffer for further reusing.
    79  func (x *objectWriterTransformer) Close(ctx context.Context) (*ResObjectPut, error) {
    80  	ai, err := x.ot.Close(ctx)
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  
    85  	if ai != nil {
    86  		x.it.res.Epoch = ai.Epoch
    87  		if ai.ParentID != nil {
    88  			x.it.res.OID = *ai.ParentID
    89  		}
    90  	}
    91  	return &x.it.res, nil
    92  }
    93  
    94  type internalTarget struct {
    95  	client               *sdkClient.Client
    96  	res                  ResObjectPut
    97  	prm                  PrmObjectPutClientCutInit
    98  	useStream            bool
    99  	address              string
   100  	logger               logger
   101  	resolveFrostFSErrors bool
   102  }
   103  
   104  func (it *internalTarget) WriteObject(ctx context.Context, o *object.Object) error {
   105  	putSingleImplemented, err := it.tryPutSingle(ctx, o)
   106  	if putSingleImplemented {
   107  		return err
   108  	}
   109  
   110  	it.logger.log(zapcore.DebugLevel, "putSingle not implemented, trying put as stream", zap.String("address", it.address))
   111  
   112  	it.useStream = true
   113  	return it.putAsStream(ctx, o)
   114  }
   115  
   116  func (it *internalTarget) putAsStream(ctx context.Context, o *object.Object) error {
   117  	cliPrm := sdkClient.PrmObjectPutInit{
   118  		CopiesNumber: it.prm.copiesNumber,
   119  		Session:      it.prm.stoken,
   120  		Key:          it.prm.key,
   121  		BearerToken:  it.prm.btoken,
   122  	}
   123  
   124  	wrt, err := it.client.ObjectPutInit(ctx, cliPrm)
   125  	if err != nil {
   126  		return err
   127  	}
   128  	if wrt.WriteHeader(ctx, *o) {
   129  		wrt.WritePayloadChunk(ctx, o.Payload())
   130  	}
   131  	res, err := wrt.Close(ctx)
   132  	if res != nil {
   133  		it.res.Status = res.Status()
   134  		it.res.OID = res.StoredObjectID()
   135  		it.res.Epoch = res.StoredEpoch()
   136  	}
   137  	return err
   138  }
   139  
   140  func (it *internalTarget) tryPutSingle(ctx context.Context, o *object.Object) (bool, error) {
   141  	if it.useStream {
   142  		return false, nil
   143  	}
   144  	cliPrm := sdkClient.PrmObjectPutSingle{
   145  		CopiesNumber: it.prm.copiesNumber,
   146  		Key:          it.prm.key,
   147  		Session:      it.prm.stoken,
   148  		BearerToken:  it.prm.btoken,
   149  		Object:       o,
   150  	}
   151  
   152  	res, err := it.client.ObjectPutSingle(ctx, cliPrm)
   153  	if err != nil && status.Code(err) == codes.Unimplemented {
   154  		return false, err
   155  	}
   156  
   157  	if err == nil {
   158  		id, _ := o.ID()
   159  		it.res = ResObjectPut{
   160  			Status: res.Status(),
   161  			OID:    id,
   162  			Epoch:  res.Epoch(),
   163  		}
   164  		if !it.resolveFrostFSErrors && !apistatus.IsSuccessful(it.res.Status) {
   165  			return true, apistatus.ErrFromStatus(it.res.Status)
   166  		}
   167  		return true, nil
   168  	}
   169  	return true, err
   170  }