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 }