From 2f50004047a5de6b15a3c4644d29b8616bc3680a Mon Sep 17 00:00:00 2001 From: Denis Kirillov Date: Thu, 24 Aug 2023 18:00:08 +0300 Subject: [PATCH 1/4] [#197] Update SDK Signed-off-by: Denis Kirillov --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f3a7512..17237a0 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230802075510-964c3edb3f44 git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6 - git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230821090303-202412230a05 + git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20231003164722-60463871dbc2 github.com/aws/aws-sdk-go v1.44.6 github.com/bluele/gcache v0.0.2 github.com/go-chi/chi/v5 v5.0.8 diff --git a/go.sum b/go.sum index b1099ce..33888e8 100644 --- a/go.sum +++ b/go.sum @@ -44,8 +44,8 @@ git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 h1:FxqFDhQYYgpe41qsIHVOcdzSV git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0/go.mod h1:RUIKZATQLJ+TaYQa60X2fTDwfuhMfm8Ar60bQ5fr+vU= git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6 h1:aGQ6QaAnTerQ5Dq5b2/f9DUQtSqPkZZ/bkMx/HKuLCo= git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6/go.mod h1:W8Nn08/l6aQ7UlIbpF7FsQou7TVpcRD1ZT1KG4TrFhE= -git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230821090303-202412230a05 h1:OuViMF54N87FXmaBEpYw3jhzaLrJ/EWOlPL1wUkimE0= -git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230821090303-202412230a05/go.mod h1:t1akKcUH7iBrFHX8rSXScYMP17k2kYQXMbZooiL5Juw= +git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20231003164722-60463871dbc2 h1:PHZX/Gh59ZPNG10JtTjBkmKbhKNq84CKu+dJpbzPVOc= +git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20231003164722-60463871dbc2/go.mod h1:t1akKcUH7iBrFHX8rSXScYMP17k2kYQXMbZooiL5Juw= git.frostfs.info/TrueCloudLab/hrw v1.2.1 h1:ccBRK21rFvY5R1WotI6LNoPlizk7qSvdfD8lNIRudVc= git.frostfs.info/TrueCloudLab/hrw v1.2.1/go.mod h1:C1Ygde2n843yTZEQ0FP69jYiuaYV0kriLvP4zm8JuvM= git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 h1:M2KR3iBj7WpY3hP10IevfIB9MURr4O9mwVfJ+SjT3HA= -- 2.40.1 From 4fc23ffa99bc504aa667b7acdac8af3437705166 Mon Sep 17 00:00:00 2001 From: Denis Kirillov Date: Fri, 25 Aug 2023 13:06:43 +0300 Subject: [PATCH 2/4] [#197] Disable homomorphic hash for PUT Disable TZ hash for PUT if it's disabled for container itself Signed-off-by: Denis Kirillov --- api/data/info.go | 15 ++++++++------- api/handler/handlers_test.go | 11 ++++++----- api/layer/container.go | 6 ++++-- api/layer/frostfs.go | 11 ++++++++++- api/layer/frostfs_mock.go | 10 +++++++--- api/layer/object.go | 1 + api/layer/versioning_test.go | 9 +++++---- internal/frostfs/authmate.go | 6 +++++- internal/frostfs/frostfs.go | 11 ++++++++--- 9 files changed, 54 insertions(+), 26 deletions(-) diff --git a/api/data/info.go b/api/data/info.go index 8530a7f..2949ccd 100644 --- a/api/data/info.go +++ b/api/data/info.go @@ -22,13 +22,14 @@ const ( type ( // BucketInfo stores basic bucket data. BucketInfo struct { - Name string // container name from system attribute - Zone string // container zone from system attribute - CID cid.ID - Owner user.ID - Created time.Time - LocationConstraint string - ObjectLockEnabled bool + Name string // container name from system attribute + Zone string // container zone from system attribute + CID cid.ID + Owner user.ID + Created time.Time + LocationConstraint string + ObjectLockEnabled bool + HomomorphicHashDisabled bool } // ObjectInfo holds S3 object data. diff --git a/api/handler/handlers_test.go b/api/handler/handlers_test.go index 2302303..b8c5227 100644 --- a/api/handler/handlers_test.go +++ b/api/handler/handlers_test.go @@ -213,7 +213,7 @@ func createTestBucket(hc *handlerContext, bktName string) *data.BucketInfo { } func createTestBucketWithLock(hc *handlerContext, bktName string, conf *data.ObjectLockConfiguration) *data.BucketInfo { - cnrID, err := hc.MockedPool().CreateContainer(hc.Context(), layer.PrmContainerCreate{ + res, err := hc.MockedPool().CreateContainer(hc.Context(), layer.PrmContainerCreate{ Creator: hc.owner, Name: bktName, AdditionalAttributes: [][2]string{{layer.AttributeLockEnabled, "true"}}, @@ -223,10 +223,11 @@ func createTestBucketWithLock(hc *handlerContext, bktName string, conf *data.Obj var ownerID user.ID bktInfo := &data.BucketInfo{ - CID: cnrID, - Name: bktName, - ObjectLockEnabled: true, - Owner: ownerID, + CID: res.ContainerID, + Name: bktName, + ObjectLockEnabled: true, + Owner: ownerID, + HomomorphicHashDisabled: res.HomomorphicHashDisabled, } sp := &layer.PutSettingsParams{ diff --git a/api/layer/container.go b/api/layer/container.go index e86dcb7..a9c9590 100644 --- a/api/layer/container.go +++ b/api/layer/container.go @@ -59,6 +59,7 @@ func (n *layer) containerInfo(ctx context.Context, idCnr cid.ID) (*data.BucketIn } info.Created = container.CreatedAt(cnr) info.LocationConstraint = cnr.Attribute(attributeLocationConstraint) + info.HomomorphicHashDisabled = container.IsHomomorphicHashingDisabled(cnr) attrLockEnabled := cnr.Attribute(AttributeLockEnabled) if len(attrLockEnabled) > 0 { @@ -122,7 +123,7 @@ func (n *layer) createContainer(ctx context.Context, p *CreateBucketParams) (*da }) } - idCnr, err := n.frostFS.CreateContainer(ctx, PrmContainerCreate{ + res, err := n.frostFS.CreateContainer(ctx, PrmContainerCreate{ Creator: bktInfo.Owner, Policy: p.Policy, Name: p.Name, @@ -134,7 +135,8 @@ func (n *layer) createContainer(ctx context.Context, p *CreateBucketParams) (*da return nil, fmt.Errorf("create container: %w", err) } - bktInfo.CID = idCnr + bktInfo.CID = res.ContainerID + bktInfo.HomomorphicHashDisabled = res.HomomorphicHashDisabled if err = n.setContainerEACLTable(ctx, bktInfo.CID, p.EACL, p.SessionEACL); err != nil { return nil, fmt.Errorf("set container eacl: %w", err) diff --git a/api/layer/frostfs.go b/api/layer/frostfs.go index 26f7352..01f8ece 100644 --- a/api/layer/frostfs.go +++ b/api/layer/frostfs.go @@ -43,6 +43,12 @@ type PrmContainerCreate struct { AdditionalAttributes [][2]string } +// ContainerCreateResult is a result parameter of FrostFS.CreateContainer operation. +type ContainerCreateResult struct { + ContainerID cid.ID + HomomorphicHashDisabled bool +} + // PrmAuth groups authentication parameters for the FrostFS operation. type PrmAuth struct { // Bearer token to be used for the operation. Overlaps PrivateKey. Optional. @@ -114,6 +120,9 @@ type PrmObjectCreate struct { // Enables client side object preparing. ClientCut bool + + // Disables using Tillich-Zémor hash for payload. + WithoutHomomorphicHash bool } // PrmObjectDelete groups parameters of FrostFS.DeleteObject operation. @@ -162,7 +171,7 @@ type FrostFS interface { // // It returns exactly one non-zero value. It returns any error encountered which // prevented the container from being created. - CreateContainer(context.Context, PrmContainerCreate) (cid.ID, error) + CreateContainer(context.Context, PrmContainerCreate) (*ContainerCreateResult, error) // Container reads a container from FrostFS by ID. // diff --git a/api/layer/frostfs_mock.go b/api/layer/frostfs_mock.go index 32eac88..e46f8b2 100644 --- a/api/layer/frostfs_mock.go +++ b/api/layer/frostfs_mock.go @@ -29,6 +29,10 @@ type FeatureSettingsMock struct { clientCut bool } +func (k *FeatureSettingsMock) BufferMaxSizeForPut() uint64 { + return 0 +} + func (k *FeatureSettingsMock) ClientCut() bool { return k.clientCut } @@ -114,7 +118,7 @@ func (t *TestFrostFS) ContainerID(name string) (cid.ID, error) { return cid.ID{}, fmt.Errorf("not found") } -func (t *TestFrostFS) CreateContainer(_ context.Context, prm PrmContainerCreate) (cid.ID, error) { +func (t *TestFrostFS) CreateContainer(_ context.Context, prm PrmContainerCreate) (*ContainerCreateResult, error) { var cnr container.Container cnr.Init() cnr.SetOwner(prm.Creator) @@ -141,14 +145,14 @@ func (t *TestFrostFS) CreateContainer(_ context.Context, prm PrmContainerCreate) b := make([]byte, 32) if _, err := io.ReadFull(rand.Reader, b); err != nil { - return cid.ID{}, err + return nil, err } var id cid.ID id.SetSHA256(sha256.Sum256(b)) t.containers[id.EncodeToString()] = &cnr - return id, nil + return &ContainerCreateResult{ContainerID: id}, nil } func (t *TestFrostFS) DeleteContainer(_ context.Context, cnrID cid.ID, _ *session.Container) error { diff --git a/api/layer/object.go b/api/layer/object.go index b9cde71..3f7dec0 100644 --- a/api/layer/object.go +++ b/api/layer/object.go @@ -460,6 +460,7 @@ func (n *layer) objectDelete(ctx context.Context, bktInfo *data.BucketInfo, idOb func (n *layer) objectPutAndHash(ctx context.Context, prm PrmObjectCreate, bktInfo *data.BucketInfo) (uint64, oid.ID, []byte, error) { n.prepareAuthParameters(ctx, &prm.PrmAuth, bktInfo.Owner) prm.ClientCut = n.features.ClientCut() + prm.WithoutHomomorphicHash = bktInfo.HomomorphicHashDisabled var size uint64 hash := sha256.New() prm.Payload = wrapReader(prm.Payload, 64*1024, func(buf []byte) { diff --git a/api/layer/versioning_test.go b/api/layer/versioning_test.go index 4476978..d59a3b9 100644 --- a/api/layer/versioning_test.go +++ b/api/layer/versioning_test.go @@ -153,7 +153,7 @@ func prepareContext(t *testing.T, cachesConfig ...*CachesConfig) *testContext { tp := NewTestFrostFS(key) bktName := "testbucket1" - bktID, err := tp.CreateContainer(ctx, PrmContainerCreate{ + res, err := tp.CreateContainer(ctx, PrmContainerCreate{ Name: bktName, }) require.NoError(t, err) @@ -177,9 +177,10 @@ func prepareContext(t *testing.T, cachesConfig ...*CachesConfig) *testContext { ctx: ctx, layer: NewLayer(logger, tp, layerCfg), bktInfo: &data.BucketInfo{ - Name: bktName, - Owner: owner, - CID: bktID, + Name: bktName, + Owner: owner, + CID: res.ContainerID, + HomomorphicHashDisabled: res.HomomorphicHashDisabled, }, obj: "obj1", t: t, diff --git a/internal/frostfs/authmate.go b/internal/frostfs/authmate.go index a53babb..2e9e423 100644 --- a/internal/frostfs/authmate.go +++ b/internal/frostfs/authmate.go @@ -57,12 +57,16 @@ func (x *AuthmateFrostFS) CreateContainer(ctx context.Context, prm authmate.PrmC basicACL.AllowOp(acl.OpObjectHead, acl.RoleOthers) basicACL.AllowOp(acl.OpObjectSearch, acl.RoleOthers) - return x.frostFS.CreateContainer(ctx, layer.PrmContainerCreate{ + res, err := x.frostFS.CreateContainer(ctx, layer.PrmContainerCreate{ Creator: prm.Owner, Policy: prm.Policy, Name: prm.FriendlyName, BasicACL: basicACL, }) + if err != nil { + return cid.ID{}, err + } + return res.ContainerID, nil } // GetCredsPayload implements authmate.FrostFS interface method. diff --git a/internal/frostfs/frostfs.go b/internal/frostfs/frostfs.go index ba66be8..d410e7b 100644 --- a/internal/frostfs/frostfs.go +++ b/internal/frostfs/frostfs.go @@ -106,7 +106,7 @@ var basicACLZero acl.Basic // CreateContainer implements frostfs.FrostFS interface method. // // If prm.BasicACL is zero, 'eacl-public-read-write' is used. -func (x *FrostFS) CreateContainer(ctx context.Context, prm layer.PrmContainerCreate) (cid.ID, error) { +func (x *FrostFS) CreateContainer(ctx context.Context, prm layer.PrmContainerCreate) (*layer.ContainerCreateResult, error) { if prm.BasicACL == basicACLZero { prm.BasicACL = acl.PublicRWExtended } @@ -137,7 +137,7 @@ func (x *FrostFS) CreateContainer(ctx context.Context, prm layer.PrmContainerCre err := pool.SyncContainerWithNetwork(ctx, &cnr, x.pool) if err != nil { - return cid.ID{}, handleObjectError("sync container with the network state", err) + return nil, handleObjectError("sync container with the network state", err) } prmPut := pool.PrmContainerPut{ @@ -150,7 +150,10 @@ func (x *FrostFS) CreateContainer(ctx context.Context, prm layer.PrmContainerCre // send request to save the container idCnr, err := x.pool.PutContainer(ctx, prmPut) - return idCnr, handleObjectError("save container via connection pool", err) + return &layer.ContainerCreateResult{ + ContainerID: idCnr, + HomomorphicHashDisabled: container.IsHomomorphicHashingDisabled(cnr), + }, handleObjectError("save container via connection pool", err) } // UserContainers implements frostfs.FrostFS interface method. @@ -244,6 +247,8 @@ func (x *FrostFS) CreateObject(ctx context.Context, prm layer.PrmObjectCreate) ( prmPut.SetPayload(prm.Payload) prmPut.SetCopiesNumberVector(prm.CopiesNumber) prmPut.SetClientCut(prm.ClientCut) + prmPut.WithoutHomomorphicHash(prm.WithoutHomomorphicHash) + prmPut.SetBufferMaxSize(prm.BufferMaxSize) if prm.BearerToken != nil { prmPut.UseBearer(*prm.BearerToken) -- 2.40.1 From d0a5880bfa71fad08f71ef017055e371b6053e9d Mon Sep 17 00:00:00 2001 From: Denis Kirillov Date: Fri, 25 Aug 2023 13:07:59 +0300 Subject: [PATCH 3/4] [#197] Configure buffer max size for PUT Signed-off-by: Denis Kirillov --- api/layer/frostfs.go | 3 +++ api/layer/layer.go | 1 + api/layer/object.go | 1 + cmd/s3-gw/app.go | 15 +++++++++++++++ cmd/s3-gw/app_settings.go | 5 +++++ config/config.env | 2 ++ config/config.yaml | 2 ++ docs/configuration.md | 10 ++++++---- 8 files changed, 35 insertions(+), 4 deletions(-) diff --git a/api/layer/frostfs.go b/api/layer/frostfs.go index 01f8ece..0f3388f 100644 --- a/api/layer/frostfs.go +++ b/api/layer/frostfs.go @@ -123,6 +123,9 @@ type PrmObjectCreate struct { // Disables using Tillich-Zémor hash for payload. WithoutHomomorphicHash bool + + // Sets max buffer size to read payload. + BufferMaxSize uint64 } // PrmObjectDelete groups parameters of FrostFS.DeleteObject operation. diff --git a/api/layer/layer.go b/api/layer/layer.go index 69e23cd..c94e4d6 100644 --- a/api/layer/layer.go +++ b/api/layer/layer.go @@ -48,6 +48,7 @@ type ( FeatureSettings interface { ClientCut() bool + BufferMaxSizeForPut() uint64 } layer struct { diff --git a/api/layer/object.go b/api/layer/object.go index 3f7dec0..f3cb7c2 100644 --- a/api/layer/object.go +++ b/api/layer/object.go @@ -460,6 +460,7 @@ func (n *layer) objectDelete(ctx context.Context, bktInfo *data.BucketInfo, idOb func (n *layer) objectPutAndHash(ctx context.Context, prm PrmObjectCreate, bktInfo *data.BucketInfo) (uint64, oid.ID, []byte, error) { n.prepareAuthParameters(ctx, &prm.PrmAuth, bktInfo.Owner) prm.ClientCut = n.features.ClientCut() + prm.BufferMaxSize = n.features.BufferMaxSizeForPut() prm.WithoutHomomorphicHash = bktInfo.HomomorphicHashDisabled var size uint64 hash := sha256.New() diff --git a/cmd/s3-gw/app.go b/cmd/s3-gw/app.go index daf5338..adece92 100644 --- a/cmd/s3-gw/app.go +++ b/cmd/s3-gw/app.go @@ -85,6 +85,7 @@ type ( defaultXMLNSForCompleteMultipart bool bypassContentEncodingInChunks bool clientCut bool + maxBufferSizeForPut uint64 } maxClientsConfig struct { @@ -189,6 +190,7 @@ func newAppSettings(log *Logger, v *viper.Viper) *appSettings { settings.setBypassContentEncodingInChunks(v.GetBool(cfgKludgeBypassContentEncodingCheckInChunks)) settings.setClientCut(v.GetBool(cfgClientCut)) settings.initPlacementPolicy(log.logger, v) + settings.setBufferMaxSizeForPut(v.GetUint64(cfgBufferMaxSizeForPut)) return settings } @@ -217,6 +219,18 @@ func (s *appSettings) setClientCut(clientCut bool) { s.mu.Unlock() } +func (s *appSettings) BufferMaxSizeForPut() uint64 { + s.mu.RLock() + defer s.mu.RUnlock() + return s.maxBufferSizeForPut +} + +func (s *appSettings) setBufferMaxSizeForPut(size uint64) { + s.mu.Lock() + s.maxBufferSizeForPut = size + s.mu.Unlock() +} + func (s *appSettings) initPlacementPolicy(l *zap.Logger, v *viper.Viper) { defaultPolicy := fetchDefaultPolicy(l, v) regionMap := fetchRegionMappingPolicies(l, v) @@ -589,6 +603,7 @@ func (a *App) updateSettings() { a.settings.useDefaultNamespaceForCompleteMultipart(a.cfg.GetBool(cfgKludgeUseDefaultXMLNSForCompleteMultipartUpload)) a.settings.setBypassContentEncodingInChunks(a.cfg.GetBool(cfgKludgeBypassContentEncodingCheckInChunks)) a.settings.setClientCut(a.cfg.GetBool(cfgClientCut)) + a.settings.setBufferMaxSizeForPut(a.cfg.GetUint64(cfgBufferMaxSizeForPut)) } func (a *App) startServices() { diff --git a/cmd/s3-gw/app_settings.go b/cmd/s3-gw/app_settings.go index 7d8f266..6034598 100644 --- a/cmd/s3-gw/app_settings.go +++ b/cmd/s3-gw/app_settings.go @@ -149,6 +149,8 @@ const ( // Settings. cfgSetCopiesNumber = "frostfs.set_copies_number" // Enabling client side object preparing for PUT operations. cfgClientCut = "frostfs.client_cut" + // Sets max buffer size for read payload in put operations. + cfgBufferMaxSizeForPut = "frostfs.buffer_max_size_for_put" // List of allowed AccessKeyID prefixes. cfgAllowedAccessKeyIDPrefixes = "allowed_access_key_id_prefixes" @@ -528,6 +530,9 @@ func newSettings() *viper.Viper { v.SetDefault(cfgPProfAddress, "localhost:8085") v.SetDefault(cfgPrometheusAddress, "localhost:8086") + // frostfs + v.SetDefault(cfgBufferMaxSizeForPut, 1024*1024) // 1mb + // kludge v.SetDefault(cfgKludgeUseDefaultXMLNSForCompleteMultipartUpload, false) v.SetDefault(cfgKludgeCompleteMultipartUploadKeepalive, 10*time.Second) diff --git a/config/config.env b/config/config.env index 3609af3..4e6b4c7 100644 --- a/config/config.env +++ b/config/config.env @@ -127,6 +127,8 @@ S3_GW_CORS_DEFAULT_MAX_AGE=600 S3_GW_FROSTFS_SET_COPIES_NUMBER=0 # This flag enables client side object preparing. S3_GW_FROSTFS_CLIENT_CUT=false +# Sets max buffer size for read payload in put operations. +S3_GW_FROSTFS_BUFFER_MAX_SIZE_FOR_PUT=1048576 # List of allowed AccessKeyID prefixes # If not set, S3 GW will accept all AccessKeyIDs diff --git a/config/config.yaml b/config/config.yaml index 0539720..6a25957 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -152,6 +152,8 @@ frostfs: set_copies_number: [0] # This flag enables client side object preparing. client_cut: false + # Sets max buffer size for read payload in put operations. + buffer_max_size_for_put: 1048576 # List of allowed AccessKeyID prefixes # If the parameter is omitted, S3 GW will accept all AccessKeyIDs diff --git a/docs/configuration.md b/docs/configuration.md index af0dd29..4862594 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -509,12 +509,14 @@ header for `PutObject`, `CopyObject`, `CreateMultipartUpload`. frostfs: set_copies_number: [0] client_cut: false + buffer_max_size_for_put: 1048576 # 1mb ``` -| Parameter | Type | SIGHUP reload | Default value | Description | -|---------------------|------------|---------------|---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `set_copies_number` | `[]uint32` | yes | `[0]` | Numbers of the object copies (for each replica) to consider PUT to FrostFS successful.
Default value `[0]` or empty list means that object will be processed according to the container's placement policy | -| `client_cut` | `bool` | yes | `false` | This flag enables client side object preparing. | +| Parameter | Type | SIGHUP reload | Default value | Description | +|---------------------------|------------|---------------|---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `set_copies_number` | `[]uint32` | yes | `[0]` | Numbers of the object copies (for each replica) to consider PUT to FrostFS successful.
Default value `[0]` or empty list means that object will be processed according to the container's placement policy | +| `client_cut` | `bool` | yes | `false` | This flag enables client side object preparing. | +| `buffer_max_size_for_put` | `uint64` | yes | `1048576` | Sets max buffer size for read payload in put operations. | # `resolve_bucket` section -- 2.40.1 From e007465b2772a7efefeb506fecc646f3c56847db Mon Sep 17 00:00:00 2001 From: Denis Kirillov Date: Fri, 25 Aug 2023 13:11:57 +0300 Subject: [PATCH 4/4] [#197] Update CHANGELOG.md Signed-off-by: Denis Kirillov --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3cb3bee..cec578a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ This document outlines major changes between releases. - Implement chunk uploading (#106) - Add new `kludge.bypass_content_encoding_check_in_chunks` config param (#146) - Add new `frostfs.client_cut` config param (#192) +- Add new `frostfs.buffer_max_size_for_put` config param and sync TZ hash for PUT operations (#197) ### Changed - Update prometheus to v1.15.0 (#94) -- 2.40.1