Compare commits

...

15 commits

Author SHA1 Message Date
74e11b3cf3
[#1586] objsvc: Allow to send search response in multiple messages
All checks were successful
DCO action / DCO (pull_request) Successful in 37s
Tests and linters / Run gofumpt (pull_request) Successful in 34s
Vulncheck / Vulncheck (pull_request) Successful in 1m10s
Build / Build Components (pull_request) Successful in 1m55s
Pre-commit hooks / Pre-commit (pull_request) Successful in 2m5s
Tests and linters / Staticcheck (pull_request) Successful in 2m19s
Tests and linters / Tests (pull_request) Successful in 2m50s
Tests and linters / Lint (pull_request) Successful in 3m6s
Tests and linters / gopls check (pull_request) Successful in 3m4s
Tests and linters / Tests with -race (pull_request) Successful in 3m24s
Previously, `ln` was only set once, so search has really worked for
small number of objects.

Fix panic:
```
panic: runtime error: slice bounds out of range [:43690] with capacity 21238
goroutine 6859775 [running]:
git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object.(*searchStreamMsgSizeCtrl).Send(0xc001eec8d0, 0xc005734000)
        git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object/transport_splitter.go:173 +0x1f0
git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object/search/v2.(*streamWriter).WriteIDs(0xc000520320, {0xc00eb1a000, 0x4fd9c, 0x7fd6475a9a68?})
        git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object/search/v2/streamer.go:28 +0x155
git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object/search.(*uniqueIDWriter).WriteIDs(0xc001386420, {0xc00eb1a000?, 0xc0013ea9c0?, 0x113eef3?})
        git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object/search/util.go:62 +0x202
git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object/search.(*execCtx).writeIDList(0xc00011aa38?, {0xc00eb1a000?, 0xc001eec9f0?, 0xc0008f4380?})
        git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object/search/exec.go:68 +0x91
git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object/search.(*execCtx).executeLocal(0xc0008f4380, {0x176c538, 0xc001eec9f0})
        git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object/search/local.go:18 +0x16b
```

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-12-28 09:23:54 +03:00
f527556735
Revert "[#1492] metabase: Ensure Unmarshal() is called on a cloned slice"
All checks were successful
Tests and linters / Run gofumpt (pull_request) Successful in 4m40s
DCO action / DCO (pull_request) Successful in 5m19s
Vulncheck / Vulncheck (pull_request) Successful in 5m54s
Build / Build Components (pull_request) Successful in 6m36s
Pre-commit hooks / Pre-commit (pull_request) Successful in 6m35s
Tests and linters / Staticcheck (pull_request) Successful in 6m58s
Tests and linters / Tests (pull_request) Successful in 7m10s
Tests and linters / Lint (pull_request) Successful in 7m19s
Tests and linters / gopls check (pull_request) Successful in 7m22s
Tests and linters / Tests with -race (pull_request) Successful in 8m29s
This reverts commit 8ed7a676d5.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-12-26 16:25:13 +03:00
23b426ec4a
[#1584] go.mod: Update sdk-go
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-12-26 15:37:21 +03:00
7e3f2fca47 [#1563] tree: Wrap only ChainRouterError erros with ObjectAccessDenied
All checks were successful
Vulncheck / Vulncheck (pull_request) Successful in 3m25s
Pre-commit hooks / Pre-commit (pull_request) Successful in 4m26s
Tests and linters / gopls check (pull_request) Successful in 4m32s
Tests and linters / Tests with -race (pull_request) Successful in 7m7s
Tests and linters / Run gofumpt (pull_request) Successful in 8m50s
DCO action / DCO (pull_request) Successful in 9m21s
Build / Build Components (pull_request) Successful in 10m18s
Tests and linters / Tests (pull_request) Successful in 10m40s
Tests and linters / Staticcheck (pull_request) Successful in 10m52s
Tests and linters / Lint (pull_request) Successful in 11m20s
* Such wrapping helps to differentiate logical check errors and server internal
  errors.

Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
2024-12-16 16:19:04 +03:00
25ed595354 [#1563] object: Wrap only ChainRouterError erros with ObjectAccessDenied
* Such wrapping helps to differentiate logical check errors and server internal
  errors.

Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
2024-12-16 16:18:58 +03:00
fb2f40aea1 [#1563] ape: Introduce ChainRouterError error type
Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
2024-12-16 16:18:51 +03:00
15b4288d80
[#1548] metabase: Check if EC parent is removed or expired
All checks were successful
Vulncheck / Vulncheck (pull_request) Successful in 2m32s
Pre-commit hooks / Pre-commit (pull_request) Successful in 3m24s
Tests and linters / Run gofumpt (pull_request) Successful in 3m27s
DCO action / DCO (pull_request) Successful in 3m45s
Tests and linters / gopls check (pull_request) Successful in 4m3s
Build / Build Components (pull_request) Successful in 4m23s
Tests and linters / Staticcheck (pull_request) Successful in 4m52s
Tests and linters / Tests (pull_request) Successful in 5m29s
Tests and linters / Lint (pull_request) Successful in 5m44s
Tests and linters / Tests with -race (pull_request) Successful in 6m40s
Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
2024-12-11 10:42:12 +03:00
6ea8f2b23c
[#1548] engine: Rename parent -> ecParent
Parent could mean split parent or EC parent. In this case it is EC parent only.

Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
2024-12-11 10:41:32 +03:00
47f12f8440
[#1548] policer: Do not replicate EC chunk if object already removed
Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
2024-12-11 10:41:00 +03:00
edc1824c23
[#1544] go.mod: Update sdk-go
All checks were successful
DCO action / DCO (pull_request) Successful in 1m48s
Tests and linters / Run gofumpt (pull_request) Successful in 1m48s
Tests and linters / Staticcheck (pull_request) Successful in 3m8s
Vulncheck / Vulncheck (pull_request) Successful in 3m9s
Build / Build Components (pull_request) Successful in 3m45s
Pre-commit hooks / Pre-commit (pull_request) Successful in 4m5s
Tests and linters / Lint (pull_request) Successful in 4m28s
Tests and linters / Tests (pull_request) Successful in 4m32s
Tests and linters / gopls check (pull_request) Successful in 4m44s
Tests and linters / Tests with -race (pull_request) Successful in 5m17s
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-12-06 12:50:39 +03:00
cf48dfd55e [#1524] tree: Make check APE error get wrapped to api status
All checks were successful
Tests and linters / Run gofumpt (pull_request) Successful in 2m11s
DCO action / DCO (pull_request) Successful in 2m26s
Vulncheck / Vulncheck (pull_request) Successful in 2m37s
Pre-commit hooks / Pre-commit (pull_request) Successful in 2m55s
Build / Build Components (pull_request) Successful in 3m21s
Tests and linters / gopls check (pull_request) Successful in 3m25s
Tests and linters / Staticcheck (pull_request) Successful in 3m55s
Tests and linters / Lint (pull_request) Successful in 4m36s
Tests and linters / Tests (pull_request) Successful in 5m1s
Tests and linters / Tests with -race (pull_request) Successful in 6m4s
Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
2024-12-04 09:57:46 +03:00
274ac61236 [#1524] ape: Make APE checker return error without status
Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
2024-12-04 09:57:41 +03:00
892542d6e3 [#1532] node: Allow to omit metabase.path if shard is disabled
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-12-03 13:11:16 +00:00
af3d6368b0 [#1530] cli: Keep order for required nodes in the result of object nodes
All checks were successful
Tests and linters / Run gofumpt (pull_request) Successful in 1m54s
DCO action / DCO (pull_request) Successful in 2m23s
Vulncheck / Vulncheck (pull_request) Successful in 2m34s
Pre-commit hooks / Pre-commit (pull_request) Successful in 3m2s
Build / Build Components (pull_request) Successful in 3m11s
Tests and linters / gopls check (pull_request) Successful in 3m26s
Tests and linters / Staticcheck (pull_request) Successful in 3m49s
Tests and linters / Lint (pull_request) Successful in 4m34s
Tests and linters / Tests (pull_request) Successful in 4m57s
Tests and linters / Tests with -race (pull_request) Successful in 6m28s
Signed-off-by: Anton Nikiforov <an.nikiforov@yadro.com>
2024-12-03 11:45:52 +03:00
36fe470956 [#1530] node: Keep order for equal elements when sort priority metrics
Signed-off-by: Anton Nikiforov <an.nikiforov@yadro.com>
2024-12-03 11:45:38 +03:00
27 changed files with 229 additions and 99 deletions

View file

@ -51,7 +51,7 @@ var listContainersCmd = &cobra.Command{
var prm internalclient.ListContainersPrm var prm internalclient.ListContainersPrm
prm.SetClient(cli) prm.SetClient(cli)
prm.Account = idUser prm.OwnerID = idUser
res, err := internalclient.ListContainers(cmd.Context(), prm) res, err := internalclient.ListContainers(cmd.Context(), prm)
commonCmd.ExitOnErr(cmd, "rpc error: %w", err) commonCmd.ExitOnErr(cmd, "rpc error: %w", err)

View file

@ -1,15 +1,12 @@
package object package object
import ( import (
"bytes"
"cmp"
"context" "context"
"crypto/ecdsa" "crypto/ecdsa"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"slices"
"sync" "sync"
internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client" internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
@ -507,7 +504,6 @@ func isObjectStoredOnNode(ctx context.Context, cmd *cobra.Command, cnrID cid.ID,
} }
func printPlacement(cmd *cobra.Command, objID oid.ID, objects []phyObject, result *objectNodesResult) { func printPlacement(cmd *cobra.Command, objID oid.ID, objects []phyObject, result *objectNodesResult) {
normilizeObjectNodesResult(objects, result)
if json, _ := cmd.Flags().GetBool(commonflags.JSON); json { if json, _ := cmd.Flags().GetBool(commonflags.JSON); json {
printObjectNodesAsJSON(cmd, objID, objects, result) printObjectNodesAsJSON(cmd, objID, objects, result)
} else { } else {
@ -515,34 +511,6 @@ func printPlacement(cmd *cobra.Command, objID oid.ID, objects []phyObject, resul
} }
} }
func normilizeObjectNodesResult(objects []phyObject, result *objectNodesResult) {
slices.SortFunc(objects, func(lhs, rhs phyObject) int {
if lhs.ecHeader == nil && rhs.ecHeader == nil {
return bytes.Compare(lhs.objectID[:], rhs.objectID[:])
}
if lhs.ecHeader == nil {
return -1
}
if rhs.ecHeader == nil {
return 1
}
if lhs.ecHeader.parent == rhs.ecHeader.parent {
return cmp.Compare(lhs.ecHeader.index, rhs.ecHeader.index)
}
return bytes.Compare(lhs.ecHeader.parent[:], rhs.ecHeader.parent[:])
})
for _, obj := range objects {
op := result.placements[obj.objectID]
slices.SortFunc(op.confirmedNodes, func(lhs, rhs netmapSDK.NodeInfo) int {
return bytes.Compare(lhs.PublicKey(), rhs.PublicKey())
})
slices.SortFunc(op.requiredNodes, func(lhs, rhs netmapSDK.NodeInfo) int {
return bytes.Compare(lhs.PublicKey(), rhs.PublicKey())
})
result.placements[obj.objectID] = op
}
}
func printObjectNodesAsText(cmd *cobra.Command, objID oid.ID, objects []phyObject, result *objectNodesResult) { func printObjectNodesAsText(cmd *cobra.Command, objID oid.ID, objects []phyObject, result *objectNodesResult) {
fmt.Fprintf(cmd.OutOrStdout(), "Object %s stores payload in %d data objects:\n", objID.EncodeToString(), len(objects)) fmt.Fprintf(cmd.OutOrStdout(), "Object %s stores payload in %d data objects:\n", objID.EncodeToString(), len(objects))

View file

@ -41,6 +41,10 @@ func IterateShards(c *config.Config, required bool, f func(*shardconfig.Config)
c.Sub(si), c.Sub(si),
) )
if sc.Mode() == mode.Disabled {
continue
}
// Path for the blobstor can't be present in the default section, because different shards // Path for the blobstor can't be present in the default section, because different shards
// must have different paths, so if it is missing, the shard is not here. // must have different paths, so if it is missing, the shard is not here.
// At the same time checking for "blobstor" section doesn't work proper // At the same time checking for "blobstor" section doesn't work proper
@ -50,10 +54,6 @@ func IterateShards(c *config.Config, required bool, f func(*shardconfig.Config)
} }
(*config.Config)(sc).SetDefault(def) (*config.Config)(sc).SetDefault(def)
if sc.Mode() == mode.Disabled {
continue
}
if err := f(sc); err != nil { if err := f(sc); err != nil {
return err return err
} }

View file

@ -18,6 +18,22 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestIterateShards(t *testing.T) {
fileConfigTest := func(c *config.Config) {
var res []string
require.NoError(t,
engineconfig.IterateShards(c, false, func(sc *shardconfig.Config) error {
res = append(res, sc.Metabase().Path())
return nil
}))
require.Equal(t, []string{"abc", "xyz"}, res)
}
const cfgDir = "./testdata/shards"
configtest.ForEachFileType(cfgDir, fileConfigTest)
configtest.ForEnvFileType(t, cfgDir, fileConfigTest)
}
func TestEngineSection(t *testing.T) { func TestEngineSection(t *testing.T) {
t.Run("defaults", func(t *testing.T) { t.Run("defaults", func(t *testing.T) {
empty := configtest.EmptyConfig() empty := configtest.EmptyConfig()

View file

@ -0,0 +1,3 @@
FROSTFS_STORAGE_SHARD_0_METABASE_PATH=abc
FROSTFS_STORAGE_SHARD_1_MODE=disabled
FROSTFS_STORAGE_SHARD_2_METABASE_PATH=xyz

View file

@ -0,0 +1,13 @@
{
"storage.shard": {
"0": {
"metabase.path": "abc"
},
"1": {
"mode": "disabled"
},
"2": {
"metabase.path": "xyz"
}
}
}

View file

@ -0,0 +1,7 @@
storage.shard:
0:
metabase.path: abc
1:
mode: disabled
2:
metabase.path: xyz

32
go.mod
View file

@ -8,7 +8,7 @@ require (
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0
git.frostfs.info/TrueCloudLab/frostfs-locode-db v0.4.1-0.20240710074952-65761deb5c0d git.frostfs.info/TrueCloudLab/frostfs-locode-db v0.4.1-0.20240710074952-65761deb5c0d
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20241112082307-f17779933e88 git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20241112082307-f17779933e88
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20241107121119-cb813e27a823 git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20241226115718-82e48c8a634d
git.frostfs.info/TrueCloudLab/hrw v1.2.1 git.frostfs.info/TrueCloudLab/hrw v1.2.1
git.frostfs.info/TrueCloudLab/multinet v0.0.0-20241015075604-6cb0d80e0972 git.frostfs.info/TrueCloudLab/multinet v0.0.0-20241015075604-6cb0d80e0972
git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240814080254-96225afacb88 git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240814080254-96225afacb88
@ -27,7 +27,7 @@ require (
github.com/klauspost/compress v1.17.4 github.com/klauspost/compress v1.17.4
github.com/mailru/easyjson v0.7.7 github.com/mailru/easyjson v0.7.7
github.com/mr-tron/base58 v1.2.0 github.com/mr-tron/base58 v1.2.0
github.com/multiformats/go-multiaddr v0.12.1 github.com/multiformats/go-multiaddr v0.14.0
github.com/nspcc-dev/neo-go v0.106.3 github.com/nspcc-dev/neo-go v0.106.3
github.com/olekukonko/tablewriter v0.0.5 github.com/olekukonko/tablewriter v0.0.5
github.com/panjf2000/ants/v2 v2.9.0 github.com/panjf2000/ants/v2 v2.9.0
@ -40,15 +40,15 @@ require (
github.com/ssgreg/journald v1.0.0 github.com/ssgreg/journald v1.0.0
github.com/stretchr/testify v1.9.0 github.com/stretchr/testify v1.9.0
go.etcd.io/bbolt v1.3.10 go.etcd.io/bbolt v1.3.10
go.opentelemetry.io/otel v1.28.0 go.opentelemetry.io/otel v1.31.0
go.opentelemetry.io/otel/trace v1.28.0 go.opentelemetry.io/otel/trace v1.31.0
go.uber.org/zap v1.27.0 go.uber.org/zap v1.27.0
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
golang.org/x/sync v0.7.0 golang.org/x/sync v0.10.0
golang.org/x/sys v0.22.0 golang.org/x/sys v0.28.0
golang.org/x/term v0.21.0 golang.org/x/term v0.27.0
google.golang.org/grpc v1.66.2 google.golang.org/grpc v1.69.2
google.golang.org/protobuf v1.34.2 google.golang.org/protobuf v1.36.1
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
@ -119,15 +119,15 @@ require (
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0 // indirect
go.opentelemetry.io/otel/metric v1.28.0 // indirect go.opentelemetry.io/otel/metric v1.31.0 // indirect
go.opentelemetry.io/otel/sdk v1.28.0 // indirect go.opentelemetry.io/otel/sdk v1.31.0 // indirect
go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.24.0 // indirect golang.org/x/crypto v0.31.0 // indirect
golang.org/x/net v0.26.0 // indirect golang.org/x/net v0.33.0 // indirect
golang.org/x/text v0.16.0 // indirect golang.org/x/text v0.21.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect
lukechampine.com/blake3 v1.2.1 // indirect lukechampine.com/blake3 v1.2.1 // indirect
rsc.io/tmplfunc v0.0.3 // indirect rsc.io/tmplfunc v0.0.3 // indirect

BIN
go.sum

Binary file not shown.

View file

@ -42,7 +42,7 @@ func benchmarkExists(b *testing.B, shardNum int) {
for range b.N { for range b.N {
var shPrm shard.ExistsPrm var shPrm shard.ExistsPrm
shPrm.Address = addr shPrm.Address = addr
shPrm.ParentAddress = oid.Address{} shPrm.ECParentAddress = oid.Address{}
ok, _, err := e.exists(context.Background(), shPrm) ok, _, err := e.exists(context.Background(), shPrm)
if err != nil || ok { if err != nil || ok {
b.Fatalf("%t %v", ok, err) b.Fatalf("%t %v", ok, err)

View file

@ -7,8 +7,11 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/internal/testutil" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/internal/testutil"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test" cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -84,3 +87,47 @@ func TestStorageEngine_Inhume(t *testing.T) {
require.Empty(t, addrs) require.Empty(t, addrs)
}) })
} }
func TestStorageEngine_ECInhume(t *testing.T) {
parentObjectAddress := oidtest.Address()
containerID := parentObjectAddress.Container()
chunkObject0 := testutil.GenerateObjectWithCID(containerID)
chunkObject0.SetECHeader(objectSDK.NewECHeader(
objectSDK.ECParentInfo{
ID: parentObjectAddress.Object(),
}, 0, 4, []byte{}, 0))
chunkObject1 := testutil.GenerateObjectWithCID(containerID)
chunkObject1.SetECHeader(objectSDK.NewECHeader(
objectSDK.ECParentInfo{
ID: parentObjectAddress.Object(),
}, 1, 4, []byte{}, 0))
tombstone := objectSDK.NewTombstone()
tombstone.SetMembers([]oid.ID{parentObjectAddress.Object()})
payload, err := tombstone.Marshal()
require.NoError(t, err)
tombstoneObject := testutil.GenerateObjectWithCID(containerID)
tombstoneObject.SetType(objectSDK.TypeTombstone)
tombstoneObject.SetPayload(payload)
tombstoneObjectAddress := object.AddressOf(tombstoneObject)
e := testNewEngine(t).setShardsNum(t, 5).prepare(t).engine
defer func() { require.NoError(t, e.Close(context.Background())) }()
require.NoError(t, Put(context.Background(), e, chunkObject0, false))
require.NoError(t, Put(context.Background(), e, tombstoneObject, false))
var inhumePrm InhumePrm
inhumePrm.WithTarget(tombstoneObjectAddress, parentObjectAddress)
_, err = e.Inhume(context.Background(), inhumePrm)
require.NoError(t, err)
var alreadyRemoved *apistatus.ObjectAlreadyRemoved
require.ErrorAs(t, Put(context.Background(), e, chunkObject0, false), &alreadyRemoved)
require.ErrorAs(t, Put(context.Background(), e, chunkObject1, false), &alreadyRemoved)
}

View file

@ -71,21 +71,21 @@ func (e *StorageEngine) put(ctx context.Context, prm PutPrm) error {
// In #1146 this check was parallelized, however, it became // In #1146 this check was parallelized, however, it became
// much slower on fast machines for 4 shards. // much slower on fast machines for 4 shards.
var parent oid.Address var ecParent oid.Address
if prm.Object.ECHeader() != nil { if prm.Object.ECHeader() != nil {
parent.SetObject(prm.Object.ECHeader().Parent()) ecParent.SetObject(prm.Object.ECHeader().Parent())
parent.SetContainer(addr.Container()) ecParent.SetContainer(addr.Container())
} }
var shPrm shard.ExistsPrm var shPrm shard.ExistsPrm
shPrm.Address = addr shPrm.Address = addr
shPrm.ParentAddress = parent shPrm.ECParentAddress = ecParent
existed, locked, err := e.exists(ctx, shPrm) existed, locked, err := e.exists(ctx, shPrm)
if err != nil { if err != nil {
return err return err
} }
if !existed && locked { if !existed && locked {
lockers, err := e.GetLocked(ctx, parent) lockers, err := e.GetLocked(ctx, ecParent)
if err != nil { if err != nil {
return err return err
} }

View file

@ -1,7 +1,6 @@
package meta package meta
import ( import (
"bytes"
"context" "context"
"fmt" "fmt"
"time" "time"
@ -21,7 +20,7 @@ import (
// ExistsPrm groups the parameters of Exists operation. // ExistsPrm groups the parameters of Exists operation.
type ExistsPrm struct { type ExistsPrm struct {
addr oid.Address addr oid.Address
paddr oid.Address ecParentAddr oid.Address
} }
// ExistsRes groups the resulting values of Exists operation. // ExistsRes groups the resulting values of Exists operation.
@ -37,9 +36,9 @@ func (p *ExistsPrm) SetAddress(addr oid.Address) {
p.addr = addr p.addr = addr
} }
// SetParent is an Exists option to set objects parent. // SetECParent is an Exists option to set objects parent.
func (p *ExistsPrm) SetParent(addr oid.Address) { func (p *ExistsPrm) SetECParent(addr oid.Address) {
p.paddr = addr p.ecParentAddr = addr
} }
// Exists returns the fact that the object is in the metabase. // Exists returns the fact that the object is in the metabase.
@ -82,7 +81,7 @@ func (db *DB) Exists(ctx context.Context, prm ExistsPrm) (res ExistsRes, err err
currEpoch := db.epochState.CurrentEpoch() currEpoch := db.epochState.CurrentEpoch()
err = db.boltDB.View(func(tx *bbolt.Tx) error { err = db.boltDB.View(func(tx *bbolt.Tx) error {
res.exists, res.locked, err = db.exists(tx, prm.addr, prm.paddr, currEpoch) res.exists, res.locked, err = db.exists(tx, prm.addr, prm.ecParentAddr, currEpoch)
return err return err
}) })
@ -90,10 +89,21 @@ func (db *DB) Exists(ctx context.Context, prm ExistsPrm) (res ExistsRes, err err
return res, metaerr.Wrap(err) return res, metaerr.Wrap(err)
} }
func (db *DB) exists(tx *bbolt.Tx, addr oid.Address, parent oid.Address, currEpoch uint64) (bool, bool, error) { func (db *DB) exists(tx *bbolt.Tx, addr oid.Address, ecParent oid.Address, currEpoch uint64) (bool, bool, error) {
var locked bool var locked bool
if !parent.Equals(oid.Address{}) { if !ecParent.Equals(oid.Address{}) {
locked = objectLocked(tx, parent.Container(), parent.Object()) st, err := objectStatus(tx, ecParent, currEpoch)
if err != nil {
return false, false, err
}
switch st {
case 2:
return false, locked, logicerr.Wrap(new(apistatus.ObjectAlreadyRemoved))
case 3:
return false, locked, ErrObjectIsExpired
}
locked = objectLocked(tx, ecParent.Container(), ecParent.Object())
} }
// check graveyard and object expiration first // check graveyard and object expiration first
st, err := objectStatus(tx, addr, currEpoch) st, err := objectStatus(tx, addr, currEpoch)
@ -216,7 +226,7 @@ func getSplitInfo(tx *bbolt.Tx, cnr cid.ID, key []byte) (*objectSDK.SplitInfo, e
splitInfo := objectSDK.NewSplitInfo() splitInfo := objectSDK.NewSplitInfo()
err := splitInfo.Unmarshal(bytes.Clone(rawSplitInfo)) err := splitInfo.Unmarshal(rawSplitInfo)
if err != nil { if err != nil {
return nil, fmt.Errorf("can't unmarshal split info from root index: %w", err) return nil, fmt.Errorf("can't unmarshal split info from root index: %w", err)
} }

View file

@ -1,7 +1,6 @@
package meta package meta
import ( import (
"bytes"
"context" "context"
"fmt" "fmt"
"time" "time"
@ -112,7 +111,7 @@ func (db *DB) get(tx *bbolt.Tx, addr oid.Address, key []byte, checkStatus, raw b
// check in primary index // check in primary index
data := getFromBucket(tx, primaryBucketName(cnr, bucketName), key) data := getFromBucket(tx, primaryBucketName(cnr, bucketName), key)
if len(data) != 0 { if len(data) != 0 {
return obj, obj.Unmarshal(bytes.Clone(data)) return obj, obj.Unmarshal(data)
} }
data = getFromBucket(tx, ecInfoBucketName(cnr, bucketName), key) data = getFromBucket(tx, ecInfoBucketName(cnr, bucketName), key)
@ -123,13 +122,13 @@ func (db *DB) get(tx *bbolt.Tx, addr oid.Address, key []byte, checkStatus, raw b
// if not found then check in tombstone index // if not found then check in tombstone index
data = getFromBucket(tx, tombstoneBucketName(cnr, bucketName), key) data = getFromBucket(tx, tombstoneBucketName(cnr, bucketName), key)
if len(data) != 0 { if len(data) != 0 {
return obj, obj.Unmarshal(bytes.Clone(data)) return obj, obj.Unmarshal(data)
} }
// if not found then check in locker index // if not found then check in locker index
data = getFromBucket(tx, bucketNameLockers(cnr, bucketName), key) data = getFromBucket(tx, bucketNameLockers(cnr, bucketName), key)
if len(data) != 0 { if len(data) != 0 {
return obj, obj.Unmarshal(bytes.Clone(data)) return obj, obj.Unmarshal(data)
} }
// if not found then check if object is a virtual // if not found then check if object is a virtual
@ -185,7 +184,7 @@ func getVirtualObject(tx *bbolt.Tx, cnr cid.ID, key []byte, raw bool) (*objectSD
child := objectSDK.New() child := objectSDK.New()
err = child.Unmarshal(bytes.Clone(data)) err = child.Unmarshal(data)
if err != nil { if err != nil {
return nil, fmt.Errorf("can't unmarshal child with parent: %w", err) return nil, fmt.Errorf("can't unmarshal child with parent: %w", err)
} }
@ -219,7 +218,7 @@ func getECInfoError(tx *bbolt.Tx, cnr cid.ID, data []byte) error {
objData := getFromBucket(tx, primaryBucketName(cnr, make([]byte, bucketKeySize)), key) objData := getFromBucket(tx, primaryBucketName(cnr, make([]byte, bucketKeySize)), key)
if len(objData) != 0 { if len(objData) != 0 {
obj := objectSDK.New() obj := objectSDK.New()
if err := obj.Unmarshal(bytes.Clone(objData)); err != nil { if err := obj.Unmarshal(objData); err != nil {
return err return err
} }
chunk := objectSDK.ECChunk{} chunk := objectSDK.ECChunk{}

View file

@ -1,7 +1,6 @@
package meta package meta
import ( import (
"bytes"
"context" "context"
"errors" "errors"
"fmt" "fmt"
@ -195,7 +194,7 @@ func iteratePhyObjects(tx *bbolt.Tx, f func(cid.ID, oid.ID, *objectSDK.Object) e
} }
return b.ForEach(func(k, v []byte) error { return b.ForEach(func(k, v []byte) error {
if oid.Decode(k) == nil && obj.Unmarshal(bytes.Clone(v)) == nil { if oid.Decode(k) == nil && obj.Unmarshal(v) == nil {
return f(cid, oid, obj) return f(cid, oid, obj)
} }

View file

@ -247,7 +247,7 @@ func selectNFromBucket(bkt *bbolt.Bucket, // main bucket
var ecInfo *objectcore.ECInfo var ecInfo *objectcore.ECInfo
if objType == objectSDK.TypeRegular { if objType == objectSDK.TypeRegular {
var o objectSDK.Object var o objectSDK.Object
if err := o.Unmarshal(bytes.Clone(v)); err != nil { if err := o.Unmarshal(v); err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
isLinkingObj = isLinkObject(&o) isLinkingObj = isLinkObject(&o)
@ -413,7 +413,7 @@ func (db *DB) iterateOverObjectsInContainer(ctx context.Context, tx *bbolt.Tx, p
var ecInfo *objectcore.ECInfo var ecInfo *objectcore.ECInfo
if prm.ObjectType == objectSDK.TypeRegular { if prm.ObjectType == objectSDK.TypeRegular {
var o objectSDK.Object var o objectSDK.Object
if err := o.Unmarshal(bytes.Clone(v)); err != nil { if err := o.Unmarshal(v); err != nil {
return err return err
} }
isLinkingObj = isLinkObject(&o) isLinkingObj = isLinkObject(&o)

View file

@ -1,7 +1,6 @@
package meta package meta
import ( import (
"bytes"
"context" "context"
"encoding/binary" "encoding/binary"
"errors" "errors"
@ -121,9 +120,15 @@ func (db *DB) put(tx *bbolt.Tx,
return PutRes{}, errors.New("missing container in object") return PutRes{}, errors.New("missing container in object")
} }
var ecParentAddress oid.Address
if ecHeader := obj.ECHeader(); ecHeader != nil {
ecParentAddress.SetContainer(cnr)
ecParentAddress.SetObject(ecHeader.Parent())
}
isParent := si != nil isParent := si != nil
exists, _, err := db.exists(tx, objectCore.AddressOf(obj), oid.Address{}, currEpoch) exists, _, err := db.exists(tx, objectCore.AddressOf(obj), ecParentAddress, currEpoch)
var splitInfoError *objectSDK.SplitInfoError var splitInfoError *objectSDK.SplitInfoError
if errors.As(err, &splitInfoError) { if errors.As(err, &splitInfoError) {
@ -314,7 +319,7 @@ func updateSplitInfoIndex(tx *bbolt.Tx, objKey []byte, cnr cid.ID, bucketName []
return si.Marshal() return si.Marshal()
default: default:
oldSI := objectSDK.NewSplitInfo() oldSI := objectSDK.NewSplitInfo()
if err := oldSI.Unmarshal(bytes.Clone(old)); err != nil { if err := oldSI.Unmarshal(old); err != nil {
return nil, err return nil, err
} }
si = util.MergeSplitInfo(si, oldSI) si = util.MergeSplitInfo(si, oldSI)

View file

@ -18,7 +18,7 @@ type ExistsPrm struct {
// Exists option to set object checked for existence. // Exists option to set object checked for existence.
Address oid.Address Address oid.Address
// Exists option to set parent object checked for existence. // Exists option to set parent object checked for existence.
ParentAddress oid.Address ECParentAddress oid.Address
} }
// ExistsRes groups the resulting values of Exists operation. // ExistsRes groups the resulting values of Exists operation.
@ -74,7 +74,7 @@ func (s *Shard) Exists(ctx context.Context, prm ExistsPrm) (ExistsRes, error) {
} else { } else {
var existsPrm meta.ExistsPrm var existsPrm meta.ExistsPrm
existsPrm.SetAddress(prm.Address) existsPrm.SetAddress(prm.Address)
existsPrm.SetParent(prm.ParentAddress) existsPrm.SetECParent(prm.ECParentAddress)
var res meta.ExistsRes var res meta.ExistsRes
res, err = s.metaBase.Exists(ctx, existsPrm) res, err = s.metaBase.Exists(ctx, existsPrm)

View file

@ -6,6 +6,9 @@ import (
containersvc "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/container" containersvc "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/container"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/container" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/container"
containerGRPC "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/container/grpc" containerGRPC "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/container/grpc"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
) )
// Server wraps FrostFS API Container service and // Server wraps FrostFS API Container service and
@ -80,3 +83,7 @@ func (s *Server) List(ctx context.Context, req *containerGRPC.ListRequest) (*con
return resp.ToGRPCMessage().(*containerGRPC.ListResponse), nil return resp.ToGRPCMessage().(*containerGRPC.ListResponse), nil
} }
func (s *Server) ListStream(_ *containerGRPC.ListStreamRequest, _ grpc.ServerStreamingServer[containerGRPC.ListStreamResponse]) error {
return status.Error(codes.Unimplemented, "method ListStream not implemented")
}

View file

@ -11,7 +11,6 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/netmap" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/ape" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/ape"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain" apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
@ -104,14 +103,7 @@ func (c *checkerCoreImpl) CheckAPE(prm CheckPrm) error {
if found && status == apechain.Allow { if found && status == apechain.Allow {
return nil return nil
} }
err = fmt.Errorf("access to operation %s is denied by access policy engine: %s", prm.Request.Operation(), status.String()) return newChainRouterError(prm.Request.Operation(), status)
return apeErr(err)
}
func apeErr(err error) error {
errAccessDenied := &apistatus.ObjectAccessDenied{}
errAccessDenied.WriteReason(err.Error())
return errAccessDenied
} }
// isValidBearer checks whether bearer token was correctly signed by authorized // isValidBearer checks whether bearer token was correctly signed by authorized

View file

@ -0,0 +1,33 @@
package ape
import (
"fmt"
apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
)
// ChainRouterError is returned when chain router validation prevents
// the APE request from being processed (no rule found, access denied, etc.).
type ChainRouterError struct {
operation string
status apechain.Status
}
func (e *ChainRouterError) Error() string {
return fmt.Sprintf("access to operation %s is denied by access policy engine: %s", e.Operation(), e.Status())
}
func (e *ChainRouterError) Operation() string {
return e.operation
}
func (e *ChainRouterError) Status() apechain.Status {
return e.status
}
func newChainRouterError(operation string, status apechain.Status) *ChainRouterError {
return &ChainRouterError{
operation: operation,
status: status,
}
}

View file

@ -1,10 +1,19 @@
package ape package ape
import ( import (
"errors"
checkercore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/common/ape"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
) )
func toStatusErr(err error) error { func toStatusErr(err error) error {
var chRouterErr *checkercore.ChainRouterError
if !errors.As(err, &chRouterErr) {
errServerInternal := &apistatus.ServerInternal{}
apistatus.WriteInternalServerErr(errServerInternal, err)
return errServerInternal
}
errAccessDenied := &apistatus.ObjectAccessDenied{} errAccessDenied := &apistatus.ObjectAccessDenied{}
errAccessDenied.WriteReason("ape denied request: " + err.Error()) errAccessDenied.WriteReason("ape denied request: " + err.Error())
return errAccessDenied return errAccessDenied

View file

@ -162,13 +162,13 @@ func (s *searchStreamMsgSizeCtrl) Send(resp *object.SearchResponse) error {
var newResp *object.SearchResponse var newResp *object.SearchResponse
for ln := uint64(len(ids)); ; { for {
if newResp == nil { if newResp == nil {
newResp = new(object.SearchResponse) newResp = new(object.SearchResponse)
newResp.SetBody(body) newResp.SetBody(body)
} }
cut := min(s.addrAmount, ln) cut := min(s.addrAmount, uint64(len(ids)))
body.SetIDList(ids[:cut]) body.SetIDList(ids[:cut])
newResp.SetMetaHeader(resp.GetMetaHeader()) newResp.SetMetaHeader(resp.GetMetaHeader())

View file

@ -85,7 +85,10 @@ func TestContainerNodesCache(t *testing.T) {
}) })
t.Run("the error is propagated", func(t *testing.T) { t.Run("the error is propagated", func(t *testing.T) {
var pp netmapSDK.PlacementPolicy var pp netmapSDK.PlacementPolicy
require.NoError(t, pp.DecodeString("REP 1 SELECT 1 FROM X FILTER ATTR EQ 42 AS X")) r := netmapSDK.ReplicaDescriptor{}
r.SetNumberOfObjects(1)
r.SetSelectorName("Missing")
pp.AddReplicas(r)
c := placement.NewContainerNodesCache(size) c := placement.NewContainerNodesCache(size)
_, err := c.ContainerNodes(nm(1, nodes[0:1]), cidtest.ID(), pp) _, err := c.ContainerNodes(nm(1, nodes[0:1]), cidtest.ID(), pp)

View file

@ -202,7 +202,7 @@ func sortVector(cfg *cfg, unsortedVector []netmap.NodeInfo) ([]netmap.NodeInfo,
metrics: m, metrics: m,
} }
} }
slices.SortFunc(nm, func(a, b nodeMetrics) int { slices.SortStableFunc(nm, func(a, b nodeMetrics) int {
return slices.Compare(a.metrics, b.metrics) return slices.Compare(a.metrics, b.metrics)
}) })
sortedVector := make([]netmap.NodeInfo, len(unsortedVector)) sortedVector := make([]netmap.NodeInfo, len(unsortedVector))

View file

@ -281,6 +281,8 @@ func (p *Policer) adjustECPlacement(ctx context.Context, objInfo objectcore.Info
} }
chunkIDs[ch.Index] = ecInfoChunkID chunkIDs[ch.Index] = ecInfoChunkID
} }
} else if client.IsErrObjectAlreadyRemoved(err) {
restore = false
} else if !p.cfg.netmapKeys.IsLocalKey(n.PublicKey()) && uint32(idx) < objInfo.ECInfo.Total { } else if !p.cfg.netmapKeys.IsLocalKey(n.PublicKey()) && uint32(idx) < objInfo.ECInfo.Total {
p.log.Warn(ctx, logs.PolicerCouldNotGetObjectFromNodeMoving, zap.String("node", hex.EncodeToString(n.PublicKey())), zap.Stringer("object", parentAddress), zap.Error(err)) p.log.Warn(ctx, logs.PolicerCouldNotGetObjectFromNodeMoving, zap.String("node", hex.EncodeToString(n.PublicKey())), zap.Stringer("object", parentAddress), zap.Error(err))
p.replicator.HandleReplicationTask(ctx, replicator.Task{ p.replicator.HandleReplicationTask(ctx, replicator.Task{

View file

@ -9,8 +9,10 @@ import (
"fmt" "fmt"
core "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container" core "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container"
checkercore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/common/ape"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/refs" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/refs"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
cidSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" cidSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto" frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
@ -62,7 +64,22 @@ func (s *Service) verifyClient(ctx context.Context, req message, cid cidSDK.ID,
return fmt.Errorf("can't get request role: %w", err) return fmt.Errorf("can't get request role: %w", err)
} }
return s.checkAPE(ctx, bt, cnr, cid, op, role, pubKey) if err = s.checkAPE(ctx, bt, cnr, cid, op, role, pubKey); err != nil {
return apeErr(err)
}
return nil
}
func apeErr(err error) error {
var chRouterErr *checkercore.ChainRouterError
if !errors.As(err, &chRouterErr) {
errServerInternal := &apistatus.ServerInternal{}
apistatus.WriteInternalServerErr(errServerInternal, err)
return errServerInternal
}
errAccessDenied := &apistatus.ObjectAccessDenied{}
errAccessDenied.WriteReason(err.Error())
return errAccessDenied
} }
// Returns true iff the operation is read-only and request was signed // Returns true iff the operation is read-only and request was signed