forked from TrueCloudLab/frostfs-node
Compare commits
75 commits
Author | SHA1 | Date | |
---|---|---|---|
6567ceaf13 | |||
1a738792c0 | |||
c84013a854 | |||
3cbff57535 | |||
ca8b01667f | |||
b88fe8c4a7 | |||
64900f87e1 | |||
8d499f03fe | |||
d2114759aa | |||
2075e09ced | |||
cf48069fd8 | |||
86264e4e20 | |||
100eb8b654 | |||
36fb15b9a4 | |||
e45382b0c1 | |||
5dd8d7e87a | |||
bc045b29e2 | |||
fc6abe30b8 | |||
a285d8924f | |||
410b6f70ba | |||
0ee7467da5 | |||
8c746a914a | |||
98308d0cad | |||
2d1232ce6d | |||
e65d578ba9 | |||
bf06c4fb4b | |||
56d09a9957 | |||
0712c113de | |||
48930ec452 | |||
f37babdc54 | |||
fd37cea443 | |||
e80632884a | |||
5aaa3df533 | |||
3be33b7117 | |||
29b4fbe451 | |||
0d36e93169 | |||
8e87cbee17 | |||
12fc7850dd | |||
dfe2f9956a | |||
e06ecacf57 | |||
64c1392513 | |||
dcfd895449 | |||
6730e27ae7 | |||
f7779adf71 | |||
f93b96c601 | |||
aed84b567c | |||
fe29ed043a | |||
4f9d237042 | |||
dfdeedfc6f | |||
2394ae6ce0 | |||
c274bbeb7c | |||
faec499b38 | |||
46fd5e17b2 | |||
17cba3387e | |||
766d9ec46b | |||
0e1b01b15f | |||
4c03561aa2 | |||
f4696e8964 | |||
b0ef737a74 | |||
6f7b6b65f3 | |||
923f0acf8f | |||
9b5c1da40f | |||
b4b053cecd | |||
0c5d74729c | |||
c4f941a5f5 | |||
d933609084 | |||
3d771aa21c | |||
52367dc9b2 | |||
979d4bb2ae | |||
fbc623f34e | |||
5350632e01 | |||
2938498b52 | |||
272128e61f | |||
50dccff7c1 | |||
634de97509 |
197 changed files with 3576 additions and 1359 deletions
6
.ci/Jenkinsfile
vendored
6
.ci/Jenkinsfile
vendored
|
@ -78,10 +78,4 @@ async {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
task('dco') {
|
|
||||||
container('git.frostfs.info/truecloudlab/commit-check:master') {
|
|
||||||
sh 'FROM=pull_request_target commit-check'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
158
.golangci.yml
158
.golangci.yml
|
@ -1,48 +1,63 @@
|
||||||
# This file contains all available configuration options
|
version: "2"
|
||||||
# with their default values.
|
|
||||||
|
|
||||||
# options for analysis running
|
|
||||||
run:
|
run:
|
||||||
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
|
||||||
timeout: 20m
|
|
||||||
|
|
||||||
# include test files or not, default is true
|
|
||||||
tests: false
|
tests: false
|
||||||
|
|
||||||
# output configuration options
|
|
||||||
output:
|
output:
|
||||||
# colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
|
|
||||||
formats:
|
formats:
|
||||||
- format: tab
|
tab:
|
||||||
|
path: stdout
|
||||||
# all available settings of specific linters
|
colors: false
|
||||||
linters-settings:
|
linters:
|
||||||
|
default: none
|
||||||
|
enable:
|
||||||
|
- bidichk
|
||||||
|
- containedctx
|
||||||
|
- contextcheck
|
||||||
|
- copyloopvar
|
||||||
|
- durationcheck
|
||||||
|
- errcheck
|
||||||
|
- exhaustive
|
||||||
|
- funlen
|
||||||
|
- gocognit
|
||||||
|
- gocritic
|
||||||
|
- godot
|
||||||
|
- importas
|
||||||
|
- ineffassign
|
||||||
|
- intrange
|
||||||
|
- misspell
|
||||||
|
- perfsprint
|
||||||
|
- predeclared
|
||||||
|
- protogetter
|
||||||
|
- reassign
|
||||||
|
- revive
|
||||||
|
- staticcheck
|
||||||
|
- testifylint
|
||||||
|
- truecloudlab-linters
|
||||||
|
- unconvert
|
||||||
|
- unparam
|
||||||
|
- unused
|
||||||
|
- usetesting
|
||||||
|
- whitespace
|
||||||
|
settings:
|
||||||
exhaustive:
|
exhaustive:
|
||||||
# indicates that switch statements are to be considered exhaustive if a
|
|
||||||
# 'default' case is present, even if all enum members aren't listed in the
|
|
||||||
# switch
|
|
||||||
default-signifies-exhaustive: true
|
default-signifies-exhaustive: true
|
||||||
gci:
|
|
||||||
sections:
|
|
||||||
- standard
|
|
||||||
- default
|
|
||||||
custom-order: true
|
|
||||||
govet:
|
|
||||||
# report about shadowed variables
|
|
||||||
check-shadowing: false
|
|
||||||
staticcheck:
|
|
||||||
checks: ["all", "-SA1019"] # TODO Enable SA1019 after deprecated warning are fixed.
|
|
||||||
funlen:
|
funlen:
|
||||||
lines: 80 # default 60
|
lines: 80
|
||||||
statements: 60 # default 40
|
statements: 60
|
||||||
gocognit:
|
gocognit:
|
||||||
min-complexity: 40 # default 30
|
min-complexity: 40
|
||||||
|
gocritic:
|
||||||
|
disabled-checks:
|
||||||
|
- ifElseChain
|
||||||
importas:
|
importas:
|
||||||
|
alias:
|
||||||
|
- pkg: git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object
|
||||||
|
alias: objectSDK
|
||||||
no-unaliased: true
|
no-unaliased: true
|
||||||
no-extra-aliases: false
|
no-extra-aliases: false
|
||||||
alias:
|
staticcheck:
|
||||||
pkg: git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object
|
checks:
|
||||||
alias: objectSDK
|
- all
|
||||||
|
- -QF1002
|
||||||
unused:
|
unused:
|
||||||
field-writes-are-uses: false
|
field-writes-are-uses: false
|
||||||
exported-fields-are-used: false
|
exported-fields-are-used: false
|
||||||
|
@ -53,49 +68,40 @@ linters-settings:
|
||||||
original-url: git.frostfs.info/TrueCloudLab/linters.git
|
original-url: git.frostfs.info/TrueCloudLab/linters.git
|
||||||
settings:
|
settings:
|
||||||
noliteral:
|
noliteral:
|
||||||
target-methods : ["reportFlushError", "reportError"]
|
constants-package: git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs
|
||||||
disable-packages: ["codes", "err", "res","exec"]
|
disable-packages:
|
||||||
constants-package: "git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
- codes
|
||||||
|
- err
|
||||||
linters:
|
- res
|
||||||
|
- exec
|
||||||
|
target-methods:
|
||||||
|
- reportFlushError
|
||||||
|
- reportError
|
||||||
|
exclusions:
|
||||||
|
generated: lax
|
||||||
|
presets:
|
||||||
|
- comments
|
||||||
|
- common-false-positives
|
||||||
|
- legacy
|
||||||
|
- std-error-handling
|
||||||
|
paths:
|
||||||
|
- third_party$
|
||||||
|
- builtin$
|
||||||
|
- examples$
|
||||||
|
formatters:
|
||||||
enable:
|
enable:
|
||||||
# mandatory linters
|
|
||||||
- govet
|
|
||||||
- revive
|
|
||||||
|
|
||||||
# some default golangci-lint linters
|
|
||||||
- errcheck
|
|
||||||
- gosimple
|
|
||||||
- godot
|
|
||||||
- ineffassign
|
|
||||||
- staticcheck
|
|
||||||
- typecheck
|
|
||||||
- unused
|
|
||||||
|
|
||||||
# extra linters
|
|
||||||
- bidichk
|
|
||||||
- durationcheck
|
|
||||||
- exhaustive
|
|
||||||
- copyloopvar
|
|
||||||
- gci
|
- gci
|
||||||
- gofmt
|
- gofmt
|
||||||
- goimports
|
- goimports
|
||||||
- misspell
|
settings:
|
||||||
- predeclared
|
gci:
|
||||||
- reassign
|
sections:
|
||||||
- whitespace
|
- standard
|
||||||
- containedctx
|
- default
|
||||||
- funlen
|
custom-order: true
|
||||||
- gocognit
|
exclusions:
|
||||||
- contextcheck
|
generated: lax
|
||||||
- importas
|
paths:
|
||||||
- truecloudlab-linters
|
- third_party$
|
||||||
- perfsprint
|
- builtin$
|
||||||
- testifylint
|
- examples$
|
||||||
- protogetter
|
|
||||||
- intrange
|
|
||||||
- tenv
|
|
||||||
- unconvert
|
|
||||||
- unparam
|
|
||||||
disable-all: true
|
|
||||||
fast: false
|
|
||||||
|
|
6
Makefile
6
Makefile
|
@ -9,8 +9,8 @@ HUB_IMAGE ?= git.frostfs.info/truecloudlab/frostfs
|
||||||
HUB_TAG ?= "$(shell echo ${VERSION} | sed 's/^v//')"
|
HUB_TAG ?= "$(shell echo ${VERSION} | sed 's/^v//')"
|
||||||
|
|
||||||
GO_VERSION ?= 1.23
|
GO_VERSION ?= 1.23
|
||||||
LINT_VERSION ?= 1.62.2
|
LINT_VERSION ?= 2.0.2
|
||||||
TRUECLOUDLAB_LINT_VERSION ?= 0.0.8
|
TRUECLOUDLAB_LINT_VERSION ?= 0.0.10
|
||||||
PROTOC_VERSION ?= 25.0
|
PROTOC_VERSION ?= 25.0
|
||||||
PROTOGEN_FROSTFS_VERSION ?= $(shell go list -f '{{.Version}}' -m git.frostfs.info/TrueCloudLab/frostfs-sdk-go)
|
PROTOGEN_FROSTFS_VERSION ?= $(shell go list -f '{{.Version}}' -m git.frostfs.info/TrueCloudLab/frostfs-sdk-go)
|
||||||
PROTOC_OS_VERSION=osx-x86_64
|
PROTOC_OS_VERSION=osx-x86_64
|
||||||
|
@ -224,7 +224,7 @@ lint-install: $(BIN)
|
||||||
@@make -C $(TMP_DIR)/linters lib CGO_ENABLED=1 OUT_DIR=$(OUTPUT_LINT_DIR)
|
@@make -C $(TMP_DIR)/linters lib CGO_ENABLED=1 OUT_DIR=$(OUTPUT_LINT_DIR)
|
||||||
@rm -rf $(TMP_DIR)/linters
|
@rm -rf $(TMP_DIR)/linters
|
||||||
@rmdir $(TMP_DIR) 2>/dev/null || true
|
@rmdir $(TMP_DIR) 2>/dev/null || true
|
||||||
@CGO_ENABLED=1 GOBIN=$(LINT_DIR) go install -trimpath github.com/golangci/golangci-lint/cmd/golangci-lint@v$(LINT_VERSION)
|
@CGO_ENABLED=1 GOBIN=$(LINT_DIR) go install -trimpath github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v$(LINT_VERSION)
|
||||||
|
|
||||||
# Run linters
|
# Run linters
|
||||||
lint:
|
lint:
|
||||||
|
|
15
cmd/frostfs-adm/internal/modules/maintenance/root.go
Normal file
15
cmd/frostfs-adm/internal/modules/maintenance/root.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package maintenance
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/maintenance/zombie"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var RootCmd = &cobra.Command{
|
||||||
|
Use: "maintenance",
|
||||||
|
Short: "Section for maintenance commands",
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
RootCmd.AddCommand(zombie.Cmd)
|
||||||
|
}
|
70
cmd/frostfs-adm/internal/modules/maintenance/zombie/key.go
Normal file
70
cmd/frostfs-adm/internal/modules/maintenance/zombie/key.go
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
package zombie
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config"
|
||||||
|
nodeconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/node"
|
||||||
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
|
"github.com/nspcc-dev/neo-go/cli/flags"
|
||||||
|
"github.com/nspcc-dev/neo-go/cli/input"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getPrivateKey(cmd *cobra.Command, appCfg *config.Config) *ecdsa.PrivateKey {
|
||||||
|
keyDesc := viper.GetString(walletFlag)
|
||||||
|
if keyDesc == "" {
|
||||||
|
return &nodeconfig.Key(appCfg).PrivateKey
|
||||||
|
}
|
||||||
|
data, err := os.ReadFile(keyDesc)
|
||||||
|
commonCmd.ExitOnErr(cmd, "open wallet file: %w", err)
|
||||||
|
|
||||||
|
priv, err := keys.NewPrivateKeyFromBytes(data)
|
||||||
|
if err != nil {
|
||||||
|
w, err := wallet.NewWalletFromFile(keyDesc)
|
||||||
|
commonCmd.ExitOnErr(cmd, "provided key is incorrect, only wallet or binary key supported: %w", err)
|
||||||
|
return fromWallet(cmd, w, viper.GetString(addressFlag))
|
||||||
|
}
|
||||||
|
return &priv.PrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func fromWallet(cmd *cobra.Command, w *wallet.Wallet, addrStr string) *ecdsa.PrivateKey {
|
||||||
|
var (
|
||||||
|
addr util.Uint160
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
if addrStr == "" {
|
||||||
|
addr = w.GetChangeAddress()
|
||||||
|
} else {
|
||||||
|
addr, err = flags.ParseAddress(addrStr)
|
||||||
|
commonCmd.ExitOnErr(cmd, "--address option must be specified and valid: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
acc := w.GetAccount(addr)
|
||||||
|
if acc == nil {
|
||||||
|
commonCmd.ExitOnErr(cmd, "--address option must be specified and valid: %w", fmt.Errorf("can't find wallet account for %s", addrStr))
|
||||||
|
}
|
||||||
|
|
||||||
|
pass, err := getPassword()
|
||||||
|
commonCmd.ExitOnErr(cmd, "invalid password for the encrypted key: %w", err)
|
||||||
|
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't decrypt account: %w", acc.Decrypt(pass, keys.NEP2ScryptParams()))
|
||||||
|
|
||||||
|
return &acc.PrivateKey().PrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPassword() (string, error) {
|
||||||
|
// this check allows empty passwords
|
||||||
|
if viper.IsSet("password") {
|
||||||
|
return viper.GetString("password"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return input.ReadPassword("Enter password > ")
|
||||||
|
}
|
31
cmd/frostfs-adm/internal/modules/maintenance/zombie/list.go
Normal file
31
cmd/frostfs-adm/internal/modules/maintenance/zombie/list.go
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package zombie
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config"
|
||||||
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func list(cmd *cobra.Command, _ []string) {
|
||||||
|
configFile, _ := cmd.Flags().GetString(commonflags.ConfigFlag)
|
||||||
|
configDir, _ := cmd.Flags().GetString(commonflags.ConfigDirFlag)
|
||||||
|
appCfg := config.New(configFile, configDir, config.EnvPrefix)
|
||||||
|
storageEngine := newEngine(cmd, appCfg)
|
||||||
|
q := createQuarantine(cmd, storageEngine.DumpInfo())
|
||||||
|
var containerID *cid.ID
|
||||||
|
if cidStr, _ := cmd.Flags().GetString(cidFlag); cidStr != "" {
|
||||||
|
containerID = &cid.ID{}
|
||||||
|
commonCmd.ExitOnErr(cmd, "decode container ID string: %w", containerID.DecodeString(cidStr))
|
||||||
|
}
|
||||||
|
|
||||||
|
commonCmd.ExitOnErr(cmd, "iterate over quarantine: %w", q.Iterate(cmd.Context(), func(a oid.Address) error {
|
||||||
|
if containerID != nil && a.Container() != *containerID {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
cmd.Println(a.EncodeToString())
|
||||||
|
return nil
|
||||||
|
}))
|
||||||
|
}
|
46
cmd/frostfs-adm/internal/modules/maintenance/zombie/morph.go
Normal file
46
cmd/frostfs-adm/internal/modules/maintenance/zombie/morph.go
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
package zombie
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config"
|
||||||
|
morphconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/morph"
|
||||||
|
nodeconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/node"
|
||||||
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
|
||||||
|
cntClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/container"
|
||||||
|
netmapClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func createMorphClient(cmd *cobra.Command, appCfg *config.Config) *client.Client {
|
||||||
|
addresses := morphconfig.RPCEndpoint(appCfg)
|
||||||
|
if len(addresses) == 0 {
|
||||||
|
commonCmd.ExitOnErr(cmd, "create morph client: %w", errors.New("no morph endpoints found"))
|
||||||
|
}
|
||||||
|
key := nodeconfig.Key(appCfg)
|
||||||
|
cli, err := client.New(cmd.Context(),
|
||||||
|
key,
|
||||||
|
client.WithDialTimeout(morphconfig.DialTimeout(appCfg)),
|
||||||
|
client.WithEndpoints(addresses...),
|
||||||
|
client.WithSwitchInterval(morphconfig.SwitchInterval(appCfg)),
|
||||||
|
)
|
||||||
|
commonCmd.ExitOnErr(cmd, "create morph client: %w", err)
|
||||||
|
return cli
|
||||||
|
}
|
||||||
|
|
||||||
|
func createContainerClient(cmd *cobra.Command, morph *client.Client) *cntClient.Client {
|
||||||
|
hs, err := morph.NNSContractAddress(client.NNSContainerContractName)
|
||||||
|
commonCmd.ExitOnErr(cmd, "resolve container contract hash: %w", err)
|
||||||
|
cc, err := cntClient.NewFromMorph(morph, hs, 0)
|
||||||
|
commonCmd.ExitOnErr(cmd, "create morph container client: %w", err)
|
||||||
|
return cc
|
||||||
|
}
|
||||||
|
|
||||||
|
func createNetmapClient(cmd *cobra.Command, morph *client.Client) *netmapClient.Client {
|
||||||
|
hs, err := morph.NNSContractAddress(client.NNSNetmapContractName)
|
||||||
|
commonCmd.ExitOnErr(cmd, "resolve netmap contract hash: %w", err)
|
||||||
|
cli, err := netmapClient.NewFromMorph(morph, hs, 0)
|
||||||
|
commonCmd.ExitOnErr(cmd, "create morph netmap client: %w", err)
|
||||||
|
return cli
|
||||||
|
}
|
|
@ -0,0 +1,154 @@
|
||||||
|
package zombie
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
|
objectcore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/fstree"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/engine"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode"
|
||||||
|
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
|
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||||
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
type quarantine struct {
|
||||||
|
// mtx protects current field.
|
||||||
|
mtx sync.Mutex
|
||||||
|
current int
|
||||||
|
trees []*fstree.FSTree
|
||||||
|
}
|
||||||
|
|
||||||
|
func createQuarantine(cmd *cobra.Command, engineInfo engine.Info) *quarantine {
|
||||||
|
var paths []string
|
||||||
|
for _, sh := range engineInfo.Shards {
|
||||||
|
var storagePaths []string
|
||||||
|
for _, st := range sh.BlobStorInfo.SubStorages {
|
||||||
|
storagePaths = append(storagePaths, st.Path)
|
||||||
|
}
|
||||||
|
if len(storagePaths) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
paths = append(paths, filepath.Join(commonPath(storagePaths), "quarantine"))
|
||||||
|
}
|
||||||
|
q, err := newQuarantine(paths)
|
||||||
|
commonCmd.ExitOnErr(cmd, "create quarantine: %w", err)
|
||||||
|
return q
|
||||||
|
}
|
||||||
|
|
||||||
|
func commonPath(paths []string) string {
|
||||||
|
if len(paths) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if len(paths) == 1 {
|
||||||
|
return paths[0]
|
||||||
|
}
|
||||||
|
minLen := math.MaxInt
|
||||||
|
for _, p := range paths {
|
||||||
|
if len(p) < minLen {
|
||||||
|
minLen = len(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var sb strings.Builder
|
||||||
|
for i := range minLen {
|
||||||
|
for _, path := range paths[1:] {
|
||||||
|
if paths[0][i] != path[i] {
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.WriteByte(paths[0][i])
|
||||||
|
}
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func newQuarantine(paths []string) (*quarantine, error) {
|
||||||
|
var q quarantine
|
||||||
|
for i := range paths {
|
||||||
|
f := fstree.New(
|
||||||
|
fstree.WithDepth(1),
|
||||||
|
fstree.WithDirNameLen(1),
|
||||||
|
fstree.WithPath(paths[i]),
|
||||||
|
fstree.WithPerm(os.ModePerm),
|
||||||
|
)
|
||||||
|
if err := f.Open(mode.ComponentReadWrite); err != nil {
|
||||||
|
return nil, fmt.Errorf("open fstree %s: %w", paths[i], err)
|
||||||
|
}
|
||||||
|
if err := f.Init(); err != nil {
|
||||||
|
return nil, fmt.Errorf("init fstree %s: %w", paths[i], err)
|
||||||
|
}
|
||||||
|
q.trees = append(q.trees, f)
|
||||||
|
}
|
||||||
|
return &q, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *quarantine) Get(ctx context.Context, a oid.Address) (*objectSDK.Object, error) {
|
||||||
|
for i := range q.trees {
|
||||||
|
res, err := q.trees[i].Get(ctx, common.GetPrm{Address: a})
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return res.Object, nil
|
||||||
|
}
|
||||||
|
return nil, &apistatus.ObjectNotFound{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *quarantine) Delete(ctx context.Context, a oid.Address) error {
|
||||||
|
for i := range q.trees {
|
||||||
|
_, err := q.trees[i].Delete(ctx, common.DeletePrm{Address: a})
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &apistatus.ObjectNotFound{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *quarantine) Put(ctx context.Context, obj *objectSDK.Object) error {
|
||||||
|
data, err := obj.Marshal()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var prm common.PutPrm
|
||||||
|
prm.Address = objectcore.AddressOf(obj)
|
||||||
|
prm.Object = obj
|
||||||
|
prm.RawData = data
|
||||||
|
|
||||||
|
q.mtx.Lock()
|
||||||
|
current := q.current
|
||||||
|
q.current = (q.current + 1) % len(q.trees)
|
||||||
|
q.mtx.Unlock()
|
||||||
|
|
||||||
|
_, err = q.trees[current].Put(ctx, prm)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *quarantine) Iterate(ctx context.Context, f func(oid.Address) error) error {
|
||||||
|
var prm common.IteratePrm
|
||||||
|
prm.Handler = func(elem common.IterationElement) error {
|
||||||
|
return f(elem.Address)
|
||||||
|
}
|
||||||
|
for i := range q.trees {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := q.trees[i].Iterate(ctx, prm)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
package zombie
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config"
|
||||||
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
|
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func remove(cmd *cobra.Command, _ []string) {
|
||||||
|
configFile, _ := cmd.Flags().GetString(commonflags.ConfigFlag)
|
||||||
|
configDir, _ := cmd.Flags().GetString(commonflags.ConfigDirFlag)
|
||||||
|
appCfg := config.New(configFile, configDir, config.EnvPrefix)
|
||||||
|
storageEngine := newEngine(cmd, appCfg)
|
||||||
|
q := createQuarantine(cmd, storageEngine.DumpInfo())
|
||||||
|
|
||||||
|
var containerID cid.ID
|
||||||
|
cidStr, _ := cmd.Flags().GetString(cidFlag)
|
||||||
|
commonCmd.ExitOnErr(cmd, "decode container ID string: %w", containerID.DecodeString(cidStr))
|
||||||
|
|
||||||
|
var objectID *oid.ID
|
||||||
|
oidStr, _ := cmd.Flags().GetString(oidFlag)
|
||||||
|
if oidStr != "" {
|
||||||
|
objectID = &oid.ID{}
|
||||||
|
commonCmd.ExitOnErr(cmd, "decode object ID string: %w", objectID.DecodeString(oidStr))
|
||||||
|
}
|
||||||
|
|
||||||
|
if objectID != nil {
|
||||||
|
var addr oid.Address
|
||||||
|
addr.SetContainer(containerID)
|
||||||
|
addr.SetObject(*objectID)
|
||||||
|
removeObject(cmd, q, addr)
|
||||||
|
} else {
|
||||||
|
commonCmd.ExitOnErr(cmd, "iterate over quarantine: %w", q.Iterate(cmd.Context(), func(addr oid.Address) error {
|
||||||
|
if addr.Container() != containerID {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
removeObject(cmd, q, addr)
|
||||||
|
return nil
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeObject(cmd *cobra.Command, q *quarantine, addr oid.Address) {
|
||||||
|
err := q.Delete(cmd.Context(), addr)
|
||||||
|
if errors.Is(err, new(apistatus.ObjectNotFound)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
commonCmd.ExitOnErr(cmd, "remove object from quarantine: %w", err)
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
package zombie
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config"
|
||||||
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
|
containerCore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/engine"
|
||||||
|
cntClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/container"
|
||||||
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func restore(cmd *cobra.Command, _ []string) {
|
||||||
|
configFile, _ := cmd.Flags().GetString(commonflags.ConfigFlag)
|
||||||
|
configDir, _ := cmd.Flags().GetString(commonflags.ConfigDirFlag)
|
||||||
|
appCfg := config.New(configFile, configDir, config.EnvPrefix)
|
||||||
|
storageEngine := newEngine(cmd, appCfg)
|
||||||
|
q := createQuarantine(cmd, storageEngine.DumpInfo())
|
||||||
|
morphClient := createMorphClient(cmd, appCfg)
|
||||||
|
cnrCli := createContainerClient(cmd, morphClient)
|
||||||
|
|
||||||
|
var containerID cid.ID
|
||||||
|
cidStr, _ := cmd.Flags().GetString(cidFlag)
|
||||||
|
commonCmd.ExitOnErr(cmd, "decode container ID string: %w", containerID.DecodeString(cidStr))
|
||||||
|
|
||||||
|
var objectID *oid.ID
|
||||||
|
oidStr, _ := cmd.Flags().GetString(oidFlag)
|
||||||
|
if oidStr != "" {
|
||||||
|
objectID = &oid.ID{}
|
||||||
|
commonCmd.ExitOnErr(cmd, "decode object ID string: %w", objectID.DecodeString(oidStr))
|
||||||
|
}
|
||||||
|
|
||||||
|
if objectID != nil {
|
||||||
|
var addr oid.Address
|
||||||
|
addr.SetContainer(containerID)
|
||||||
|
addr.SetObject(*objectID)
|
||||||
|
restoreObject(cmd, storageEngine, q, addr, cnrCli)
|
||||||
|
} else {
|
||||||
|
commonCmd.ExitOnErr(cmd, "iterate over quarantine: %w", q.Iterate(cmd.Context(), func(addr oid.Address) error {
|
||||||
|
if addr.Container() != containerID {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
restoreObject(cmd, storageEngine, q, addr, cnrCli)
|
||||||
|
return nil
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func restoreObject(cmd *cobra.Command, storageEngine *engine.StorageEngine, q *quarantine, addr oid.Address, cnrCli *cntClient.Client) {
|
||||||
|
obj, err := q.Get(cmd.Context(), addr)
|
||||||
|
commonCmd.ExitOnErr(cmd, "get object from quarantine: %w", err)
|
||||||
|
rawCID := make([]byte, sha256.Size)
|
||||||
|
|
||||||
|
cid := addr.Container()
|
||||||
|
cid.Encode(rawCID)
|
||||||
|
cnr, err := cnrCli.Get(cmd.Context(), rawCID)
|
||||||
|
commonCmd.ExitOnErr(cmd, "get container: %w", err)
|
||||||
|
|
||||||
|
putPrm := engine.PutPrm{
|
||||||
|
Object: obj,
|
||||||
|
IsIndexedContainer: containerCore.IsIndexedContainer(cnr.Value),
|
||||||
|
}
|
||||||
|
commonCmd.ExitOnErr(cmd, "put object to storage engine: %w", storageEngine.Put(cmd.Context(), putPrm))
|
||||||
|
commonCmd.ExitOnErr(cmd, "remove object from quarantine: %w", q.Delete(cmd.Context(), addr))
|
||||||
|
}
|
123
cmd/frostfs-adm/internal/modules/maintenance/zombie/root.go
Normal file
123
cmd/frostfs-adm/internal/modules/maintenance/zombie/root.go
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
package zombie
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
flagBatchSize = "batch-size"
|
||||||
|
flagBatchSizeUsage = "Objects iteration batch size"
|
||||||
|
cidFlag = "cid"
|
||||||
|
cidFlagUsage = "Container ID"
|
||||||
|
oidFlag = "oid"
|
||||||
|
oidFlagUsage = "Object ID"
|
||||||
|
walletFlag = "wallet"
|
||||||
|
walletFlagShorthand = "w"
|
||||||
|
walletFlagUsage = "Path to the wallet or binary key"
|
||||||
|
addressFlag = "address"
|
||||||
|
addressFlagUsage = "Address of wallet account"
|
||||||
|
moveFlag = "move"
|
||||||
|
moveFlagUsage = "Move objects from storage engine to quarantine"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
Cmd = &cobra.Command{
|
||||||
|
Use: "zombie",
|
||||||
|
Short: "Zombie objects related commands",
|
||||||
|
}
|
||||||
|
scanCmd = &cobra.Command{
|
||||||
|
Use: "scan",
|
||||||
|
Short: "Scan storage engine for zombie objects and move them to quarantine",
|
||||||
|
Long: "",
|
||||||
|
PreRun: func(cmd *cobra.Command, _ []string) {
|
||||||
|
_ = viper.BindPFlag(commonflags.ConfigFlag, cmd.Flags().Lookup(commonflags.ConfigFlag))
|
||||||
|
_ = viper.BindPFlag(commonflags.ConfigDirFlag, cmd.Flags().Lookup(commonflags.ConfigDirFlag))
|
||||||
|
_ = viper.BindPFlag(walletFlag, cmd.Flags().Lookup(walletFlag))
|
||||||
|
_ = viper.BindPFlag(addressFlag, cmd.Flags().Lookup(addressFlag))
|
||||||
|
_ = viper.BindPFlag(flagBatchSize, cmd.Flags().Lookup(flagBatchSize))
|
||||||
|
_ = viper.BindPFlag(moveFlag, cmd.Flags().Lookup(moveFlag))
|
||||||
|
},
|
||||||
|
Run: scan,
|
||||||
|
}
|
||||||
|
listCmd = &cobra.Command{
|
||||||
|
Use: "list",
|
||||||
|
Short: "List zombie objects from quarantine",
|
||||||
|
Long: "",
|
||||||
|
PreRun: func(cmd *cobra.Command, _ []string) {
|
||||||
|
_ = viper.BindPFlag(commonflags.ConfigFlag, cmd.Flags().Lookup(commonflags.ConfigFlag))
|
||||||
|
_ = viper.BindPFlag(commonflags.ConfigDirFlag, cmd.Flags().Lookup(commonflags.ConfigDirFlag))
|
||||||
|
_ = viper.BindPFlag(cidFlag, cmd.Flags().Lookup(cidFlag))
|
||||||
|
},
|
||||||
|
Run: list,
|
||||||
|
}
|
||||||
|
restoreCmd = &cobra.Command{
|
||||||
|
Use: "restore",
|
||||||
|
Short: "Restore zombie objects from quarantine",
|
||||||
|
Long: "",
|
||||||
|
PreRun: func(cmd *cobra.Command, _ []string) {
|
||||||
|
_ = viper.BindPFlag(commonflags.ConfigFlag, cmd.Flags().Lookup(commonflags.ConfigFlag))
|
||||||
|
_ = viper.BindPFlag(commonflags.ConfigDirFlag, cmd.Flags().Lookup(commonflags.ConfigDirFlag))
|
||||||
|
_ = viper.BindPFlag(cidFlag, cmd.Flags().Lookup(cidFlag))
|
||||||
|
_ = viper.BindPFlag(oidFlag, cmd.Flags().Lookup(oidFlag))
|
||||||
|
},
|
||||||
|
Run: restore,
|
||||||
|
}
|
||||||
|
removeCmd = &cobra.Command{
|
||||||
|
Use: "remove",
|
||||||
|
Short: "Remove zombie objects from quarantine",
|
||||||
|
Long: "",
|
||||||
|
PreRun: func(cmd *cobra.Command, _ []string) {
|
||||||
|
_ = viper.BindPFlag(commonflags.ConfigFlag, cmd.Flags().Lookup(commonflags.ConfigFlag))
|
||||||
|
_ = viper.BindPFlag(commonflags.ConfigDirFlag, cmd.Flags().Lookup(commonflags.ConfigDirFlag))
|
||||||
|
_ = viper.BindPFlag(cidFlag, cmd.Flags().Lookup(cidFlag))
|
||||||
|
_ = viper.BindPFlag(oidFlag, cmd.Flags().Lookup(oidFlag))
|
||||||
|
},
|
||||||
|
Run: remove,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
initScanCmd()
|
||||||
|
initListCmd()
|
||||||
|
initRestoreCmd()
|
||||||
|
initRemoveCmd()
|
||||||
|
}
|
||||||
|
|
||||||
|
func initScanCmd() {
|
||||||
|
Cmd.AddCommand(scanCmd)
|
||||||
|
|
||||||
|
scanCmd.Flags().StringP(commonflags.ConfigFlag, commonflags.ConfigFlagShorthand, "", commonflags.ConfigFlagUsage)
|
||||||
|
scanCmd.Flags().String(commonflags.ConfigDirFlag, "", commonflags.ConfigDirFlagUsage)
|
||||||
|
scanCmd.Flags().Uint32(flagBatchSize, 1000, flagBatchSizeUsage)
|
||||||
|
scanCmd.Flags().StringP(walletFlag, walletFlagShorthand, "", walletFlagUsage)
|
||||||
|
scanCmd.Flags().String(addressFlag, "", addressFlagUsage)
|
||||||
|
scanCmd.Flags().Bool(moveFlag, false, moveFlagUsage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func initListCmd() {
|
||||||
|
Cmd.AddCommand(listCmd)
|
||||||
|
|
||||||
|
listCmd.Flags().StringP(commonflags.ConfigFlag, commonflags.ConfigFlagShorthand, "", commonflags.ConfigFlagUsage)
|
||||||
|
listCmd.Flags().String(commonflags.ConfigDirFlag, "", commonflags.ConfigDirFlagUsage)
|
||||||
|
listCmd.Flags().String(cidFlag, "", cidFlagUsage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func initRestoreCmd() {
|
||||||
|
Cmd.AddCommand(restoreCmd)
|
||||||
|
|
||||||
|
restoreCmd.Flags().StringP(commonflags.ConfigFlag, commonflags.ConfigFlagShorthand, "", commonflags.ConfigFlagUsage)
|
||||||
|
restoreCmd.Flags().String(commonflags.ConfigDirFlag, "", commonflags.ConfigDirFlagUsage)
|
||||||
|
restoreCmd.Flags().String(cidFlag, "", cidFlagUsage)
|
||||||
|
restoreCmd.Flags().String(oidFlag, "", oidFlagUsage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func initRemoveCmd() {
|
||||||
|
Cmd.AddCommand(removeCmd)
|
||||||
|
|
||||||
|
removeCmd.Flags().StringP(commonflags.ConfigFlag, commonflags.ConfigFlagShorthand, "", commonflags.ConfigFlagUsage)
|
||||||
|
removeCmd.Flags().String(commonflags.ConfigDirFlag, "", commonflags.ConfigDirFlagUsage)
|
||||||
|
removeCmd.Flags().String(cidFlag, "", cidFlagUsage)
|
||||||
|
removeCmd.Flags().String(oidFlag, "", oidFlagUsage)
|
||||||
|
}
|
281
cmd/frostfs-adm/internal/modules/maintenance/zombie/scan.go
Normal file
281
cmd/frostfs-adm/internal/modules/maintenance/zombie/scan.go
Normal file
|
@ -0,0 +1,281 @@
|
||||||
|
package zombie
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/sha256"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config"
|
||||||
|
apiclientconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/apiclient"
|
||||||
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
|
clientCore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/client"
|
||||||
|
netmapCore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/netmap"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/engine"
|
||||||
|
cntClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/container"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/network/cache"
|
||||||
|
clientSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
||||||
|
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
|
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||||
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
|
)
|
||||||
|
|
||||||
|
func scan(cmd *cobra.Command, _ []string) {
|
||||||
|
configFile, _ := cmd.Flags().GetString(commonflags.ConfigFlag)
|
||||||
|
configDir, _ := cmd.Flags().GetString(commonflags.ConfigDirFlag)
|
||||||
|
appCfg := config.New(configFile, configDir, config.EnvPrefix)
|
||||||
|
batchSize, _ := cmd.Flags().GetUint32(flagBatchSize)
|
||||||
|
if batchSize == 0 {
|
||||||
|
commonCmd.ExitOnErr(cmd, "invalid batch size: %w", errors.New("batch size must be positive value"))
|
||||||
|
}
|
||||||
|
move, _ := cmd.Flags().GetBool(moveFlag)
|
||||||
|
|
||||||
|
storageEngine := newEngine(cmd, appCfg)
|
||||||
|
morphClient := createMorphClient(cmd, appCfg)
|
||||||
|
cnrCli := createContainerClient(cmd, morphClient)
|
||||||
|
nmCli := createNetmapClient(cmd, morphClient)
|
||||||
|
q := createQuarantine(cmd, storageEngine.DumpInfo())
|
||||||
|
pk := getPrivateKey(cmd, appCfg)
|
||||||
|
|
||||||
|
epoch, err := nmCli.Epoch(cmd.Context())
|
||||||
|
commonCmd.ExitOnErr(cmd, "read epoch from morph: %w", err)
|
||||||
|
|
||||||
|
nm, err := nmCli.GetNetMapByEpoch(cmd.Context(), epoch)
|
||||||
|
commonCmd.ExitOnErr(cmd, "read netmap from morph: %w", err)
|
||||||
|
|
||||||
|
cmd.Printf("Epoch: %d\n", nm.Epoch())
|
||||||
|
cmd.Printf("Nodes in the netmap: %d\n", len(nm.Nodes()))
|
||||||
|
|
||||||
|
ps := &processStatus{
|
||||||
|
statusCount: make(map[status]uint64),
|
||||||
|
}
|
||||||
|
|
||||||
|
stopCh := make(chan struct{})
|
||||||
|
start := time.Now()
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(2)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
tick := time.NewTicker(time.Second)
|
||||||
|
defer tick.Stop()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-cmd.Context().Done():
|
||||||
|
return
|
||||||
|
case <-stopCh:
|
||||||
|
return
|
||||||
|
case <-tick.C:
|
||||||
|
fmt.Printf("Objects processed: %d; Time elapsed: %s\n", ps.total(), time.Since(start))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
err = scanStorageEngine(cmd, batchSize, storageEngine, ps, appCfg, cnrCli, nmCli, q, pk, move)
|
||||||
|
close(stopCh)
|
||||||
|
}()
|
||||||
|
wg.Wait()
|
||||||
|
commonCmd.ExitOnErr(cmd, "scan storage engine for zombie objects: %w", err)
|
||||||
|
|
||||||
|
cmd.Println()
|
||||||
|
cmd.Println("Status description:")
|
||||||
|
cmd.Println("undefined -- nothing is clear")
|
||||||
|
cmd.Println("found -- object is found in cluster")
|
||||||
|
cmd.Println("quarantine -- object is not found in cluster")
|
||||||
|
cmd.Println()
|
||||||
|
for status, count := range ps.statusCount {
|
||||||
|
cmd.Printf("Status: %s, Count: %d\n", status, count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type status string
|
||||||
|
|
||||||
|
const (
|
||||||
|
statusUndefined status = "undefined"
|
||||||
|
statusFound status = "found"
|
||||||
|
statusQuarantine status = "quarantine"
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkAddr(ctx context.Context, cnrCli *cntClient.Client, nmCli *netmap.Client, cc *cache.ClientCache, obj object.Info) (status, error) {
|
||||||
|
rawCID := make([]byte, sha256.Size)
|
||||||
|
cid := obj.Address.Container()
|
||||||
|
cid.Encode(rawCID)
|
||||||
|
|
||||||
|
cnr, err := cnrCli.Get(ctx, rawCID)
|
||||||
|
if err != nil {
|
||||||
|
var errContainerNotFound *apistatus.ContainerNotFound
|
||||||
|
if errors.As(err, &errContainerNotFound) {
|
||||||
|
// Policer will deal with this object.
|
||||||
|
return statusFound, nil
|
||||||
|
}
|
||||||
|
return statusUndefined, fmt.Errorf("read container %s from morph: %w", cid, err)
|
||||||
|
}
|
||||||
|
nm, err := nmCli.NetMap(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return statusUndefined, fmt.Errorf("read netmap from morph: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes, err := nm.ContainerNodes(cnr.Value.PlacementPolicy(), rawCID)
|
||||||
|
if err != nil {
|
||||||
|
// Not enough nodes, check all netmap nodes.
|
||||||
|
nodes = append([][]netmap.NodeInfo{}, nm.Nodes())
|
||||||
|
}
|
||||||
|
|
||||||
|
objID := obj.Address.Object()
|
||||||
|
cnrID := obj.Address.Container()
|
||||||
|
local := true
|
||||||
|
raw := false
|
||||||
|
if obj.ECInfo != nil {
|
||||||
|
objID = obj.ECInfo.ParentID
|
||||||
|
local = false
|
||||||
|
raw = true
|
||||||
|
}
|
||||||
|
prm := clientSDK.PrmObjectHead{
|
||||||
|
ObjectID: &objID,
|
||||||
|
ContainerID: &cnrID,
|
||||||
|
Local: local,
|
||||||
|
Raw: raw,
|
||||||
|
}
|
||||||
|
|
||||||
|
var ni clientCore.NodeInfo
|
||||||
|
for i := range nodes {
|
||||||
|
for j := range nodes[i] {
|
||||||
|
if err := clientCore.NodeInfoFromRawNetmapElement(&ni, netmapCore.Node(nodes[i][j])); err != nil {
|
||||||
|
return statusUndefined, fmt.Errorf("parse node info: %w", err)
|
||||||
|
}
|
||||||
|
c, err := cc.Get(ni)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
res, err := c.ObjectHead(ctx, prm)
|
||||||
|
if err != nil {
|
||||||
|
var errECInfo *objectSDK.ECInfoError
|
||||||
|
if raw && errors.As(err, &errECInfo) {
|
||||||
|
return statusFound, nil
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := apistatus.ErrFromStatus(res.Status()); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return statusFound, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cnr.Value.PlacementPolicy().NumberOfReplicas() == 1 && cnr.Value.PlacementPolicy().ReplicaDescriptor(0).NumberOfObjects() == 1 {
|
||||||
|
return statusFound, nil
|
||||||
|
}
|
||||||
|
return statusQuarantine, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func scanStorageEngine(cmd *cobra.Command, batchSize uint32, storageEngine *engine.StorageEngine, ps *processStatus,
|
||||||
|
appCfg *config.Config, cnrCli *cntClient.Client, nmCli *netmap.Client, q *quarantine, pk *ecdsa.PrivateKey, move bool,
|
||||||
|
) error {
|
||||||
|
cc := cache.NewSDKClientCache(cache.ClientCacheOpts{
|
||||||
|
DialTimeout: apiclientconfig.DialTimeout(appCfg),
|
||||||
|
StreamTimeout: apiclientconfig.StreamTimeout(appCfg),
|
||||||
|
ReconnectTimeout: apiclientconfig.ReconnectTimeout(appCfg),
|
||||||
|
Key: pk,
|
||||||
|
AllowExternal: apiclientconfig.AllowExternal(appCfg),
|
||||||
|
})
|
||||||
|
ctx := cmd.Context()
|
||||||
|
|
||||||
|
var cursor *engine.Cursor
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
var prm engine.ListWithCursorPrm
|
||||||
|
prm.WithCursor(cursor)
|
||||||
|
prm.WithCount(batchSize)
|
||||||
|
|
||||||
|
res, err := storageEngine.ListWithCursor(ctx, prm)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, engine.ErrEndOfListing) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("list with cursor: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor = res.Cursor()
|
||||||
|
addrList := res.AddressList()
|
||||||
|
eg, egCtx := errgroup.WithContext(ctx)
|
||||||
|
eg.SetLimit(int(batchSize))
|
||||||
|
|
||||||
|
for i := range addrList {
|
||||||
|
addr := addrList[i]
|
||||||
|
eg.Go(func() error {
|
||||||
|
result, err := checkAddr(egCtx, cnrCli, nmCli, cc, addr)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("check object %s status: %w", addr.Address, err)
|
||||||
|
}
|
||||||
|
ps.add(result)
|
||||||
|
|
||||||
|
if !move && result == statusQuarantine {
|
||||||
|
cmd.Println(addr)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if result == statusQuarantine {
|
||||||
|
return moveToQuarantine(egCtx, storageEngine, q, addr.Address)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if err := eg.Wait(); err != nil {
|
||||||
|
return fmt.Errorf("process objects batch: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func moveToQuarantine(ctx context.Context, storageEngine *engine.StorageEngine, q *quarantine, addr oid.Address) error {
|
||||||
|
var getPrm engine.GetPrm
|
||||||
|
getPrm.WithAddress(addr)
|
||||||
|
res, err := storageEngine.Get(ctx, getPrm)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("get object %s from storage engine: %w", addr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := q.Put(ctx, res.Object()); err != nil {
|
||||||
|
return fmt.Errorf("put object %s to quarantine: %w", addr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var delPrm engine.DeletePrm
|
||||||
|
delPrm.WithForceRemoval()
|
||||||
|
delPrm.WithAddress(addr)
|
||||||
|
|
||||||
|
if err = storageEngine.Delete(ctx, delPrm); err != nil {
|
||||||
|
return fmt.Errorf("delete object %s from storage engine: %w", addr, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type processStatus struct {
|
||||||
|
guard sync.RWMutex
|
||||||
|
statusCount map[status]uint64
|
||||||
|
count uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *processStatus) add(st status) {
|
||||||
|
s.guard.Lock()
|
||||||
|
defer s.guard.Unlock()
|
||||||
|
s.statusCount[st]++
|
||||||
|
s.count++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *processStatus) total() uint64 {
|
||||||
|
s.guard.RLock()
|
||||||
|
defer s.guard.RUnlock()
|
||||||
|
return s.count
|
||||||
|
}
|
|
@ -0,0 +1,201 @@
|
||||||
|
package zombie
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config"
|
||||||
|
engineconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/engine"
|
||||||
|
shardconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/engine/shard"
|
||||||
|
blobovniczaconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/engine/shard/blobstor/blobovnicza"
|
||||||
|
fstreeconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/engine/shard/blobstor/fstree"
|
||||||
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/qos"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/blobovniczatree"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/fstree"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/engine"
|
||||||
|
meta "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/metabase"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/pilorama"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/writecache"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
|
||||||
|
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||||
|
"github.com/panjf2000/ants/v2"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"go.etcd.io/bbolt"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newEngine(cmd *cobra.Command, c *config.Config) *engine.StorageEngine {
|
||||||
|
ngOpts := storageEngineOptions(c)
|
||||||
|
shardOpts := shardOptions(cmd, c)
|
||||||
|
e := engine.New(ngOpts...)
|
||||||
|
for _, opts := range shardOpts {
|
||||||
|
_, err := e.AddShard(cmd.Context(), opts...)
|
||||||
|
commonCmd.ExitOnErr(cmd, "iterate shards from config: %w", err)
|
||||||
|
}
|
||||||
|
commonCmd.ExitOnErr(cmd, "open storage engine: %w", e.Open(cmd.Context()))
|
||||||
|
commonCmd.ExitOnErr(cmd, "init storage engine: %w", e.Init(cmd.Context()))
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func storageEngineOptions(c *config.Config) []engine.Option {
|
||||||
|
return []engine.Option{
|
||||||
|
engine.WithErrorThreshold(engineconfig.ShardErrorThreshold(c)),
|
||||||
|
engine.WithLogger(logger.NewLoggerWrapper(zap.NewNop())),
|
||||||
|
engine.WithLowMemoryConsumption(engineconfig.EngineLowMemoryConsumption(c)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func shardOptions(cmd *cobra.Command, c *config.Config) [][]shard.Option {
|
||||||
|
var result [][]shard.Option
|
||||||
|
err := engineconfig.IterateShards(c, false, func(sh *shardconfig.Config) error {
|
||||||
|
result = append(result, getShardOpts(cmd, c, sh))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
commonCmd.ExitOnErr(cmd, "iterate shards from config: %w", err)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func getShardOpts(cmd *cobra.Command, c *config.Config, sh *shardconfig.Config) []shard.Option {
|
||||||
|
wc, wcEnabled := getWriteCacheOpts(sh)
|
||||||
|
return []shard.Option{
|
||||||
|
shard.WithLogger(logger.NewLoggerWrapper(zap.NewNop())),
|
||||||
|
shard.WithRefillMetabase(sh.RefillMetabase()),
|
||||||
|
shard.WithRefillMetabaseWorkersCount(sh.RefillMetabaseWorkersCount()),
|
||||||
|
shard.WithMode(sh.Mode()),
|
||||||
|
shard.WithBlobStorOptions(getBlobstorOpts(cmd.Context(), sh)...),
|
||||||
|
shard.WithMetaBaseOptions(getMetabaseOpts(sh)...),
|
||||||
|
shard.WithPiloramaOptions(getPiloramaOpts(c, sh)...),
|
||||||
|
shard.WithWriteCache(wcEnabled),
|
||||||
|
shard.WithWriteCacheOptions(wc),
|
||||||
|
shard.WithRemoverBatchSize(sh.GC().RemoverBatchSize()),
|
||||||
|
shard.WithGCRemoverSleepInterval(sh.GC().RemoverSleepInterval()),
|
||||||
|
shard.WithExpiredCollectorBatchSize(sh.GC().ExpiredCollectorBatchSize()),
|
||||||
|
shard.WithExpiredCollectorWorkerCount(sh.GC().ExpiredCollectorWorkerCount()),
|
||||||
|
shard.WithGCWorkerPoolInitializer(func(sz int) util.WorkerPool {
|
||||||
|
pool, err := ants.NewPool(sz)
|
||||||
|
commonCmd.ExitOnErr(cmd, "init GC pool: %w", err)
|
||||||
|
return pool
|
||||||
|
}),
|
||||||
|
shard.WithLimiter(qos.NewNoopLimiter()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getWriteCacheOpts(sh *shardconfig.Config) ([]writecache.Option, bool) {
|
||||||
|
if wc := sh.WriteCache(); wc != nil && wc.Enabled() {
|
||||||
|
var result []writecache.Option
|
||||||
|
result = append(result,
|
||||||
|
writecache.WithPath(wc.Path()),
|
||||||
|
writecache.WithFlushSizeLimit(wc.MaxFlushingObjectsSize()),
|
||||||
|
writecache.WithMaxObjectSize(wc.MaxObjectSize()),
|
||||||
|
writecache.WithFlushWorkersCount(wc.WorkerCount()),
|
||||||
|
writecache.WithMaxCacheSize(wc.SizeLimit()),
|
||||||
|
writecache.WithMaxCacheCount(wc.CountLimit()),
|
||||||
|
writecache.WithNoSync(wc.NoSync()),
|
||||||
|
writecache.WithLogger(logger.NewLoggerWrapper(zap.NewNop())),
|
||||||
|
writecache.WithQoSLimiter(qos.NewNoopLimiter()),
|
||||||
|
)
|
||||||
|
return result, true
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPiloramaOpts(c *config.Config, sh *shardconfig.Config) []pilorama.Option {
|
||||||
|
var piloramaOpts []pilorama.Option
|
||||||
|
if config.BoolSafe(c.Sub("tree"), "enabled") {
|
||||||
|
pr := sh.Pilorama()
|
||||||
|
piloramaOpts = append(piloramaOpts,
|
||||||
|
pilorama.WithPath(pr.Path()),
|
||||||
|
pilorama.WithPerm(pr.Perm()),
|
||||||
|
pilorama.WithNoSync(pr.NoSync()),
|
||||||
|
pilorama.WithMaxBatchSize(pr.MaxBatchSize()),
|
||||||
|
pilorama.WithMaxBatchDelay(pr.MaxBatchDelay()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return piloramaOpts
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMetabaseOpts(sh *shardconfig.Config) []meta.Option {
|
||||||
|
return []meta.Option{
|
||||||
|
meta.WithPath(sh.Metabase().Path()),
|
||||||
|
meta.WithPermissions(sh.Metabase().BoltDB().Perm()),
|
||||||
|
meta.WithMaxBatchSize(sh.Metabase().BoltDB().MaxBatchSize()),
|
||||||
|
meta.WithMaxBatchDelay(sh.Metabase().BoltDB().MaxBatchDelay()),
|
||||||
|
meta.WithBoltDBOptions(&bbolt.Options{
|
||||||
|
Timeout: 100 * time.Millisecond,
|
||||||
|
}),
|
||||||
|
meta.WithLogger(logger.NewLoggerWrapper(zap.NewNop())),
|
||||||
|
meta.WithEpochState(&epochState{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBlobstorOpts(ctx context.Context, sh *shardconfig.Config) []blobstor.Option {
|
||||||
|
result := []blobstor.Option{
|
||||||
|
blobstor.WithCompression(sh.Compression()),
|
||||||
|
blobstor.WithStorages(getSubStorages(ctx, sh)),
|
||||||
|
blobstor.WithLogger(logger.NewLoggerWrapper(zap.NewNop())),
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSubStorages(ctx context.Context, sh *shardconfig.Config) []blobstor.SubStorage {
|
||||||
|
var ss []blobstor.SubStorage
|
||||||
|
for _, storage := range sh.BlobStor().Storages() {
|
||||||
|
switch storage.Type() {
|
||||||
|
case blobovniczatree.Type:
|
||||||
|
sub := blobovniczaconfig.From((*config.Config)(storage))
|
||||||
|
blobTreeOpts := []blobovniczatree.Option{
|
||||||
|
blobovniczatree.WithRootPath(storage.Path()),
|
||||||
|
blobovniczatree.WithPermissions(storage.Perm()),
|
||||||
|
blobovniczatree.WithBlobovniczaSize(sub.Size()),
|
||||||
|
blobovniczatree.WithBlobovniczaShallowDepth(sub.ShallowDepth()),
|
||||||
|
blobovniczatree.WithBlobovniczaShallowWidth(sub.ShallowWidth()),
|
||||||
|
blobovniczatree.WithOpenedCacheSize(sub.OpenedCacheSize()),
|
||||||
|
blobovniczatree.WithOpenedCacheTTL(sub.OpenedCacheTTL()),
|
||||||
|
blobovniczatree.WithOpenedCacheExpInterval(sub.OpenedCacheExpInterval()),
|
||||||
|
blobovniczatree.WithInitWorkerCount(sub.InitWorkerCount()),
|
||||||
|
blobovniczatree.WithWaitBeforeDropDB(sub.RebuildDropTimeout()),
|
||||||
|
blobovniczatree.WithBlobovniczaLogger(logger.NewLoggerWrapper(zap.NewNop())),
|
||||||
|
blobovniczatree.WithBlobovniczaTreeLogger(logger.NewLoggerWrapper(zap.NewNop())),
|
||||||
|
blobovniczatree.WithObjectSizeLimit(sh.SmallSizeLimit()),
|
||||||
|
}
|
||||||
|
|
||||||
|
ss = append(ss, blobstor.SubStorage{
|
||||||
|
Storage: blobovniczatree.NewBlobovniczaTree(ctx, blobTreeOpts...),
|
||||||
|
Policy: func(_ *objectSDK.Object, data []byte) bool {
|
||||||
|
return uint64(len(data)) < sh.SmallSizeLimit()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
case fstree.Type:
|
||||||
|
sub := fstreeconfig.From((*config.Config)(storage))
|
||||||
|
fstreeOpts := []fstree.Option{
|
||||||
|
fstree.WithPath(storage.Path()),
|
||||||
|
fstree.WithPerm(storage.Perm()),
|
||||||
|
fstree.WithDepth(sub.Depth()),
|
||||||
|
fstree.WithNoSync(sub.NoSync()),
|
||||||
|
fstree.WithLogger(logger.NewLoggerWrapper(zap.NewNop())),
|
||||||
|
}
|
||||||
|
|
||||||
|
ss = append(ss, blobstor.SubStorage{
|
||||||
|
Storage: fstree.New(fstreeOpts...),
|
||||||
|
Policy: func(_ *objectSDK.Object, _ []byte) bool {
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
// should never happen, that has already
|
||||||
|
// been handled: when the config was read
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ss
|
||||||
|
}
|
||||||
|
|
||||||
|
type epochState struct{}
|
||||||
|
|
||||||
|
func (epochState) CurrentEpoch() uint64 {
|
||||||
|
return 0
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/nns"
|
"git.frostfs.info/TrueCloudLab/frostfs-contract/nns"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/assert"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
|
@ -161,9 +162,7 @@ func printAlphabetContractBalances(cmd *cobra.Command, c helper.Client, inv *inv
|
||||||
helper.GetAlphabetNNSDomain(i),
|
helper.GetAlphabetNNSDomain(i),
|
||||||
int64(nns.TXT))
|
int64(nns.TXT))
|
||||||
}
|
}
|
||||||
if w.Err != nil {
|
assert.NoError(w.Err)
|
||||||
panic(w.Err)
|
|
||||||
}
|
|
||||||
|
|
||||||
alphaRes, err := c.InvokeScript(w.Bytes(), nil)
|
alphaRes, err := c.InvokeScript(w.Bytes(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -226,9 +225,7 @@ func fetchBalances(c *invoker.Invoker, gasHash util.Uint160, accounts []accBalan
|
||||||
for i := range accounts {
|
for i := range accounts {
|
||||||
emit.AppCall(w.BinWriter, gasHash, "balanceOf", callflag.ReadStates, accounts[i].scriptHash)
|
emit.AppCall(w.BinWriter, gasHash, "balanceOf", callflag.ReadStates, accounts[i].scriptHash)
|
||||||
}
|
}
|
||||||
if w.Err != nil {
|
assert.NoError(w.Err)
|
||||||
panic(w.Err)
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := c.Run(w.Bytes())
|
res, err := c.Run(w.Bytes())
|
||||||
if err != nil || res.State != vmstate.Halt.String() || len(res.Stack) != len(accounts) {
|
if err != nil || res.State != vmstate.Halt.String() || len(res.Stack) != len(accounts) {
|
||||||
|
|
|
@ -63,7 +63,7 @@ func dumpNetworkConfig(cmd *cobra.Command, _ []string) error {
|
||||||
netmap.MaxObjectSizeConfig, netmap.WithdrawFeeConfig,
|
netmap.MaxObjectSizeConfig, netmap.WithdrawFeeConfig,
|
||||||
netmap.MaxECDataCountConfig, netmap.MaxECParityCountConfig:
|
netmap.MaxECDataCountConfig, netmap.MaxECParityCountConfig:
|
||||||
nbuf := make([]byte, 8)
|
nbuf := make([]byte, 8)
|
||||||
copy(nbuf[:], v)
|
copy(nbuf, v)
|
||||||
n := binary.LittleEndian.Uint64(nbuf)
|
n := binary.LittleEndian.Uint64(nbuf)
|
||||||
_, _ = tw.Write(fmt.Appendf(nil, "%s:\t%d (int)\n", k, n))
|
_, _ = tw.Write(fmt.Appendf(nil, "%s:\t%d (int)\n", k, n))
|
||||||
case netmap.HomomorphicHashingDisabledKey, netmap.MaintenanceModeAllowedConfig:
|
case netmap.HomomorphicHashingDisabledKey, netmap.MaintenanceModeAllowedConfig:
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/assert"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
|
@ -235,9 +236,7 @@ func restoreOrPutContainers(containers []Container, isOK func([]byte) bool, cmd
|
||||||
|
|
||||||
putContainer(bw, ch, cnt)
|
putContainer(bw, ch, cnt)
|
||||||
|
|
||||||
if bw.Err != nil {
|
assert.NoError(bw.Err)
|
||||||
panic(bw.Err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := wCtx.SendConsensusTx(bw.Bytes()); err != nil {
|
if err := wCtx.SendConsensusTx(bw.Bytes()); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/assert"
|
||||||
"github.com/nspcc-dev/neo-go/cli/cmdargs"
|
"github.com/nspcc-dev/neo-go/cli/cmdargs"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
|
@ -120,9 +121,7 @@ func deployContractCmd(cmd *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if writer.Err != nil {
|
assert.NoError(writer.Err, "can't create deployment script")
|
||||||
panic(fmt.Errorf("BUG: can't create deployment script: %w", writer.Err))
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.SendCommitteeTx(writer.Bytes(), false); err != nil {
|
if err := c.SendCommitteeTx(writer.Bytes(), false); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -173,9 +172,8 @@ func registerNNS(nnsCs *state.Contract, c *helper.InitializeContext, zone string
|
||||||
domain, int64(nns.TXT), address.Uint160ToString(cs.Hash))
|
domain, int64(nns.TXT), address.Uint160ToString(cs.Hash))
|
||||||
}
|
}
|
||||||
|
|
||||||
if bw.Err != nil {
|
assert.NoError(bw.Err, "can't create deployment script")
|
||||||
panic(fmt.Errorf("BUG: can't create deployment script: %w", writer.Err))
|
if bw.Len() != start {
|
||||||
} else if bw.Len() != start {
|
|
||||||
writer.WriteBytes(bw.Bytes())
|
writer.WriteBytes(bw.Bytes())
|
||||||
emit.Opcodes(writer.BinWriter, opcode.LDSFLD0, opcode.PUSH1, opcode.PACK)
|
emit.Opcodes(writer.BinWriter, opcode.LDSFLD0, opcode.PUSH1, opcode.PACK)
|
||||||
emit.AppCallNoArgs(writer.BinWriter, nnsCs.Hash, "setPrice", callflag.All)
|
emit.AppCallNoArgs(writer.BinWriter, nnsCs.Hash, "setPrice", callflag.All)
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/assert"
|
||||||
morphClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
|
morphClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
||||||
|
@ -236,21 +237,17 @@ func fillContractVersion(cmd *cobra.Command, c helper.Client, infos []contractDu
|
||||||
} else {
|
} else {
|
||||||
sub.Reset()
|
sub.Reset()
|
||||||
emit.AppCall(sub.BinWriter, infos[i].hash, "version", callflag.NoneFlag)
|
emit.AppCall(sub.BinWriter, infos[i].hash, "version", callflag.NoneFlag)
|
||||||
if sub.Err != nil {
|
assert.NoError(sub.Err, "can't create version script")
|
||||||
panic(fmt.Errorf("BUG: can't create version script: %w", bw.Err))
|
|
||||||
}
|
|
||||||
|
|
||||||
script := sub.Bytes()
|
script := sub.Bytes()
|
||||||
emit.Instruction(bw.BinWriter, opcode.TRY, []byte{byte(3 + len(script) + 2), 0})
|
emit.Instruction(bw.BinWriter, opcode.TRY, []byte{byte(3 + len(script) + 2), 0})
|
||||||
bw.BinWriter.WriteBytes(script)
|
bw.WriteBytes(script)
|
||||||
emit.Instruction(bw.BinWriter, opcode.ENDTRY, []byte{2 + 1})
|
emit.Instruction(bw.BinWriter, opcode.ENDTRY, []byte{2 + 1})
|
||||||
emit.Opcodes(bw.BinWriter, opcode.PUSH0)
|
emit.Opcodes(bw.BinWriter, opcode.PUSH0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
emit.Opcodes(bw.BinWriter, opcode.NOP) // for the last ENDTRY target
|
emit.Opcodes(bw.BinWriter, opcode.NOP) // for the last ENDTRY target
|
||||||
if bw.Err != nil {
|
assert.NoError(bw.Err, "can't create version script")
|
||||||
panic(fmt.Errorf("BUG: can't create version script: %w", bw.Err))
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := c.InvokeScript(bw.Bytes(), nil)
|
res, err := c.InvokeScript(bw.Bytes(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/assert"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
|
@ -21,6 +22,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/context"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/context"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
|
@ -28,7 +30,6 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
@ -375,9 +376,7 @@ func (c *InitializeContext) sendMultiTx(script []byte, tryGroup bool, withConsen
|
||||||
}
|
}
|
||||||
act, err = actor.New(c.Client, signers)
|
act, err = actor.New(c.Client, signers)
|
||||||
} else {
|
} else {
|
||||||
if withConsensus {
|
assert.False(withConsensus, "BUG: should never happen")
|
||||||
panic("BUG: should never happen")
|
|
||||||
}
|
|
||||||
act, err = c.CommitteeAct, nil
|
act, err = c.CommitteeAct, nil
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -411,11 +410,9 @@ func (c *InitializeContext) MultiSignAndSend(tx *transaction.Transaction, accTyp
|
||||||
|
|
||||||
func (c *InitializeContext) MultiSign(tx *transaction.Transaction, accType string) error {
|
func (c *InitializeContext) MultiSign(tx *transaction.Transaction, accType string) error {
|
||||||
version, err := c.Client.GetVersion()
|
version, err := c.Client.GetVersion()
|
||||||
if err != nil {
|
|
||||||
// error appears only if client
|
// error appears only if client
|
||||||
// has not been initialized
|
// has not been initialized
|
||||||
panic(err)
|
assert.NoError(err)
|
||||||
}
|
|
||||||
network := version.Protocol.Network
|
network := version.Protocol.Network
|
||||||
|
|
||||||
// Use parameter context to avoid dealing with signature order.
|
// Use parameter context to avoid dealing with signature order.
|
||||||
|
@ -447,12 +444,12 @@ func (c *InitializeContext) MultiSign(tx *transaction.Transaction, accType strin
|
||||||
|
|
||||||
for i := range tx.Signers {
|
for i := range tx.Signers {
|
||||||
if tx.Signers[i].Account == h {
|
if tx.Signers[i].Account == h {
|
||||||
|
assert.True(i <= len(tx.Scripts), "BUG: invalid signing order")
|
||||||
if i < len(tx.Scripts) {
|
if i < len(tx.Scripts) {
|
||||||
tx.Scripts[i] = *w
|
tx.Scripts[i] = *w
|
||||||
} else if i == len(tx.Scripts) {
|
}
|
||||||
|
if i == len(tx.Scripts) {
|
||||||
tx.Scripts = append(tx.Scripts, *w)
|
tx.Scripts = append(tx.Scripts, *w)
|
||||||
} else {
|
|
||||||
panic("BUG: invalid signing order")
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -510,9 +507,7 @@ func (c *InitializeContext) NNSRegisterDomainScript(nnsHash, expectedHash util.U
|
||||||
int64(constants.DefaultExpirationTime), constants.NNSTtlDefVal)
|
int64(constants.DefaultExpirationTime), constants.NNSTtlDefVal)
|
||||||
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
||||||
|
|
||||||
if bw.Err != nil {
|
assert.NoError(bw.Err)
|
||||||
panic(bw.Err)
|
|
||||||
}
|
|
||||||
return bw.Bytes(), false, nil
|
return bw.Bytes(), false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -524,12 +519,8 @@ func (c *InitializeContext) NNSRegisterDomainScript(nnsHash, expectedHash util.U
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *InitializeContext) NNSRootRegistered(nnsHash util.Uint160, zone string) (bool, error) {
|
func (c *InitializeContext) NNSRootRegistered(nnsHash util.Uint160, zone string) (bool, error) {
|
||||||
res, err := c.CommitteeAct.Call(nnsHash, "isAvailable", "name."+zone)
|
avail, err := unwrap.Bool(c.CommitteeAct.Call(nnsHash, "isAvailable", zone))
|
||||||
if err != nil {
|
return !avail, err
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.State == vmstate.Halt.String(), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *InitializeContext) IsUpdated(ctrHash util.Uint160, cs *ContractState) bool {
|
func (c *InitializeContext) IsUpdated(ctrHash util.Uint160, cs *ContractState) bool {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/assert"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core"
|
"github.com/nspcc-dev/neo-go/pkg/core"
|
||||||
|
@ -316,9 +317,7 @@ func (l *LocalClient) SendRawTransaction(tx *transaction.Transaction) (util.Uint
|
||||||
func (l *LocalClient) putTransactions() error {
|
func (l *LocalClient) putTransactions() error {
|
||||||
// 1. Prepare new block.
|
// 1. Prepare new block.
|
||||||
lastBlock, err := l.bc.GetBlock(l.bc.CurrentBlockHash())
|
lastBlock, err := l.bc.GetBlock(l.bc.CurrentBlockHash())
|
||||||
if err != nil {
|
assert.NoError(err)
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer func() { l.transactions = l.transactions[:0] }()
|
defer func() { l.transactions = l.transactions[:0] }()
|
||||||
|
|
||||||
b := &block.Block{
|
b := &block.Block{
|
||||||
|
@ -359,9 +358,7 @@ func InvokeFunction(c Client, h util.Uint160, method string, parameters []any, s
|
||||||
w := io.NewBufBinWriter()
|
w := io.NewBufBinWriter()
|
||||||
emit.Array(w.BinWriter, parameters...)
|
emit.Array(w.BinWriter, parameters...)
|
||||||
emit.AppCallNoArgs(w.BinWriter, h, method, callflag.All)
|
emit.AppCallNoArgs(w.BinWriter, h, method, callflag.All)
|
||||||
if w.Err != nil {
|
assert.True(w.Err == nil, fmt.Sprintf("BUG: invalid parameters for '%s': %v", method, w.Err))
|
||||||
panic(fmt.Sprintf("BUG: invalid parameters for '%s': %v", method, w.Err))
|
|
||||||
}
|
|
||||||
return c.InvokeScript(w.Bytes(), signers)
|
return c.InvokeScript(w.Bytes(), signers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,8 +40,6 @@ type ClientContext struct {
|
||||||
CommitteeAct *actor.Actor // committee actor with the Global witness scope
|
CommitteeAct *actor.Actor // committee actor with the Global witness scope
|
||||||
ReadOnlyInvoker *invoker.Invoker // R/O contract invoker, does not contain any signer
|
ReadOnlyInvoker *invoker.Invoker // R/O contract invoker, does not contain any signer
|
||||||
SentTxs []HashVUBPair
|
SentTxs []HashVUBPair
|
||||||
|
|
||||||
AwaitDisabled bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRemoteClient(v *viper.Viper) (Client, error) {
|
func NewRemoteClient(v *viper.Viper) (Client, error) {
|
||||||
|
@ -122,7 +120,7 @@ func (c *ClientContext) SendTx(tx *transaction.Transaction, cmd *cobra.Command,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ClientContext) AwaitTx(cmd *cobra.Command) error {
|
func (c *ClientContext) AwaitTx(cmd *cobra.Command) error {
|
||||||
if len(c.SentTxs) == 0 || c.AwaitDisabled {
|
if len(c.SentTxs) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,6 @@ func initializeSideChainCmd(cmd *cobra.Command, _ []string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
initCtx.AwaitDisabled = true
|
|
||||||
cmd.Println("Stage 4.1: Transfer GAS to proxy contract.")
|
cmd.Println("Stage 4.1: Transfer GAS to proxy contract.")
|
||||||
if err := transferGASToProxy(initCtx); err != nil {
|
if err := transferGASToProxy(initCtx); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -56,10 +55,5 @@ func initializeSideChainCmd(cmd *cobra.Command, _ []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.Println("Stage 7: set addresses in NNS.")
|
cmd.Println("Stage 7: set addresses in NNS.")
|
||||||
if err := setNNS(initCtx); err != nil {
|
return setNNS(initCtx)
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
initCtx.AwaitDisabled = false
|
|
||||||
return initCtx.AwaitTx()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/nns"
|
"git.frostfs.info/TrueCloudLab/frostfs-contract/nns"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/assert"
|
||||||
morphClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
|
morphClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
@ -111,9 +112,7 @@ func wrapRegisterScriptWithPrice(w *io.BufBinWriter, nnsHash util.Uint160, s []b
|
||||||
emit.Opcodes(w.BinWriter, opcode.LDSFLD0, opcode.PUSH1, opcode.PACK)
|
emit.Opcodes(w.BinWriter, opcode.LDSFLD0, opcode.PUSH1, opcode.PACK)
|
||||||
emit.AppCallNoArgs(w.BinWriter, nnsHash, "setPrice", callflag.All)
|
emit.AppCallNoArgs(w.BinWriter, nnsHash, "setPrice", callflag.All)
|
||||||
|
|
||||||
if w.Err != nil {
|
assert.NoError(w.Err, "can't wrap register script")
|
||||||
panic(fmt.Errorf("BUG: can't wrap register script: %w", w.Err))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func nnsRegisterDomain(c *helper.InitializeContext, nnsHash, expectedHash util.Uint160, domain string) error {
|
func nnsRegisterDomain(c *helper.InitializeContext, nnsHash, expectedHash util.Uint160, domain string) error {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/assert"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
|
@ -39,9 +40,7 @@ func registerCandidateRange(c *helper.InitializeContext, start, end int) error {
|
||||||
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
||||||
}
|
}
|
||||||
emit.AppCall(w.BinWriter, neo.Hash, "setRegisterPrice", callflag.States, regPrice)
|
emit.AppCall(w.BinWriter, neo.Hash, "setRegisterPrice", callflag.States, regPrice)
|
||||||
if w.Err != nil {
|
assert.NoError(w.Err)
|
||||||
panic(fmt.Sprintf("BUG: %v", w.Err))
|
|
||||||
}
|
|
||||||
|
|
||||||
signers := []actor.SignerAccount{{
|
signers := []actor.SignerAccount{{
|
||||||
Signer: c.GetSigner(false, c.CommitteeAcc),
|
Signer: c.GetSigner(false, c.CommitteeAcc),
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/maintenance"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/metabase"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/metabase"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/misc"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/misc"
|
||||||
|
@ -41,6 +42,7 @@ func init() {
|
||||||
rootCmd.AddCommand(config.RootCmd)
|
rootCmd.AddCommand(config.RootCmd)
|
||||||
rootCmd.AddCommand(morph.RootCmd)
|
rootCmd.AddCommand(morph.RootCmd)
|
||||||
rootCmd.AddCommand(metabase.RootCmd)
|
rootCmd.AddCommand(metabase.RootCmd)
|
||||||
|
rootCmd.AddCommand(maintenance.RootCmd)
|
||||||
|
|
||||||
rootCmd.AddCommand(autocomplete.Command("frostfs-adm"))
|
rootCmd.AddCommand(autocomplete.Command("frostfs-adm"))
|
||||||
rootCmd.AddCommand(gendoc.Command(rootCmd, gendoc.Options{}))
|
rootCmd.AddCommand(gendoc.Command(rootCmd, gendoc.Options{}))
|
||||||
|
|
|
@ -9,7 +9,7 @@ const (
|
||||||
TTL = "ttl"
|
TTL = "ttl"
|
||||||
TTLShorthand = ""
|
TTLShorthand = ""
|
||||||
TTLDefault = 2
|
TTLDefault = 2
|
||||||
TTLUsage = "TTL value in request meta header"
|
TTLUsage = "The maximum number of intermediate nodes in the request route"
|
||||||
|
|
||||||
XHeadersKey = "xhdr"
|
XHeadersKey = "xhdr"
|
||||||
XHeadersShorthand = "x"
|
XHeadersShorthand = "x"
|
||||||
|
|
|
@ -44,6 +44,7 @@ is set to current epoch + n.
|
||||||
|
|
||||||
_ = viper.BindPFlag(commonflags.WalletPath, ff.Lookup(commonflags.WalletPath))
|
_ = viper.BindPFlag(commonflags.WalletPath, ff.Lookup(commonflags.WalletPath))
|
||||||
_ = viper.BindPFlag(commonflags.Account, ff.Lookup(commonflags.Account))
|
_ = viper.BindPFlag(commonflags.Account, ff.Lookup(commonflags.Account))
|
||||||
|
_ = viper.BindPFlag(commonflags.RPC, ff.Lookup(commonflags.RPC))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +82,7 @@ func createToken(cmd *cobra.Command, _ []string) {
|
||||||
commonCmd.ExitOnErr(cmd, "can't parse --"+notValidBeforeFlag+" flag: %w", err)
|
commonCmd.ExitOnErr(cmd, "can't parse --"+notValidBeforeFlag+" flag: %w", err)
|
||||||
|
|
||||||
if iatRelative || expRelative || nvbRelative {
|
if iatRelative || expRelative || nvbRelative {
|
||||||
endpoint, _ := cmd.Flags().GetString(commonflags.RPC)
|
endpoint := viper.GetString(commonflags.RPC)
|
||||||
if len(endpoint) == 0 {
|
if len(endpoint) == 0 {
|
||||||
commonCmd.ExitOnErr(cmd, "can't fetch current epoch: %w", fmt.Errorf("'%s' flag value must be specified", commonflags.RPC))
|
commonCmd.ExitOnErr(cmd, "can't fetch current epoch: %w", fmt.Errorf("'%s' flag value must be specified", commonflags.RPC))
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,9 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"maps"
|
||||||
"os"
|
"os"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
||||||
|
@ -21,6 +23,7 @@ import (
|
||||||
type policyPlaygroundREPL struct {
|
type policyPlaygroundREPL struct {
|
||||||
cmd *cobra.Command
|
cmd *cobra.Command
|
||||||
nodes map[string]netmap.NodeInfo
|
nodes map[string]netmap.NodeInfo
|
||||||
|
console *readline.Instance
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPolicyPlaygroundREPL(cmd *cobra.Command) *policyPlaygroundREPL {
|
func newPolicyPlaygroundREPL(cmd *cobra.Command) *policyPlaygroundREPL {
|
||||||
|
@ -37,10 +40,10 @@ func (repl *policyPlaygroundREPL) handleLs(args []string) error {
|
||||||
i := 1
|
i := 1
|
||||||
for id, node := range repl.nodes {
|
for id, node := range repl.nodes {
|
||||||
var attrs []string
|
var attrs []string
|
||||||
node.IterateAttributes(func(k, v string) {
|
for k, v := range node.Attributes() {
|
||||||
attrs = append(attrs, fmt.Sprintf("%s:%q", k, v))
|
attrs = append(attrs, fmt.Sprintf("%s:%q", k, v))
|
||||||
})
|
}
|
||||||
fmt.Printf("\t%2d: id=%s attrs={%v}\n", i, id, strings.Join(attrs, " "))
|
fmt.Fprintf(repl.console, "\t%2d: id=%s attrs={%v}\n", i, id, strings.Join(attrs, " "))
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -147,12 +150,29 @@ func (repl *policyPlaygroundREPL) handleEval(args []string) error {
|
||||||
for _, node := range ns {
|
for _, node := range ns {
|
||||||
ids = append(ids, hex.EncodeToString(node.PublicKey()))
|
ids = append(ids, hex.EncodeToString(node.PublicKey()))
|
||||||
}
|
}
|
||||||
fmt.Printf("\t%2d: %v\n", i+1, ids)
|
fmt.Fprintf(repl.console, "\t%2d: %v\n", i+1, ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (repl *policyPlaygroundREPL) handleHelp(args []string) error {
|
||||||
|
if len(args) != 0 {
|
||||||
|
if _, ok := commands[args[0]]; !ok {
|
||||||
|
return fmt.Errorf("unknown command: %q", args[0])
|
||||||
|
}
|
||||||
|
fmt.Fprintln(repl.console, commands[args[0]].usage)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
commandList := slices.Collect(maps.Keys(commands))
|
||||||
|
slices.Sort(commandList)
|
||||||
|
for _, command := range commandList {
|
||||||
|
fmt.Fprintf(repl.console, "%s: %s\n", command, commands[command].descriprion)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (repl *policyPlaygroundREPL) netMap() netmap.NetMap {
|
func (repl *policyPlaygroundREPL) netMap() netmap.NetMap {
|
||||||
var nm netmap.NetMap
|
var nm netmap.NetMap
|
||||||
var nodes []netmap.NodeInfo
|
var nodes []netmap.NodeInfo
|
||||||
|
@ -163,15 +183,104 @@ func (repl *policyPlaygroundREPL) netMap() netmap.NetMap {
|
||||||
return nm
|
return nm
|
||||||
}
|
}
|
||||||
|
|
||||||
var policyPlaygroundCompleter = readline.NewPrefixCompleter(
|
type commandDescription struct {
|
||||||
readline.PcItem("list"),
|
descriprion string
|
||||||
readline.PcItem("ls"),
|
usage string
|
||||||
readline.PcItem("add"),
|
}
|
||||||
readline.PcItem("load"),
|
|
||||||
readline.PcItem("remove"),
|
var commands = map[string]commandDescription{
|
||||||
readline.PcItem("rm"),
|
"list": {
|
||||||
readline.PcItem("eval"),
|
descriprion: "Display all nodes in the netmap",
|
||||||
)
|
usage: `Display all nodes in the netmap
|
||||||
|
Example of usage:
|
||||||
|
list
|
||||||
|
1: id=03ff65b6ae79134a4dce9d0d39d3851e9bab4ee97abf86e81e1c5bbc50cd2826ae attrs={Continent:"Europe" Country:"Poland"}
|
||||||
|
2: id=02ac920cd7df0b61b289072e6b946e2da4e1a31b9ab1c621bb475e30fa4ab102c3 attrs={Continent:"Antarctica" Country:"Heard Island"}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
|
||||||
|
"ls": {
|
||||||
|
descriprion: "Display all nodes in the netmap",
|
||||||
|
usage: `Display all nodes in the netmap
|
||||||
|
Example of usage:
|
||||||
|
ls
|
||||||
|
1: id=03ff65b6ae79134a4dce9d0d39d3851e9bab4ee97abf86e81e1c5bbc50cd2826ae attrs={Continent:"Europe" Country:"Poland"}
|
||||||
|
2: id=02ac920cd7df0b61b289072e6b946e2da4e1a31b9ab1c621bb475e30fa4ab102c3 attrs={Continent:"Antarctica" Country:"Heard Island"}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
|
||||||
|
"add": {
|
||||||
|
descriprion: "Add a new node: add <node-hash> attr=value",
|
||||||
|
usage: `Add a new node
|
||||||
|
Example of usage:
|
||||||
|
add 03ff65b6ae79134a4dce9d0d39d3851e9bab4ee97abf86e81e1c5bbc50cd2826ae continent:Europe country:Poland`,
|
||||||
|
},
|
||||||
|
|
||||||
|
"load": {
|
||||||
|
descriprion: "Load netmap from file: load <path>",
|
||||||
|
usage: `Load netmap from file
|
||||||
|
Example of usage:
|
||||||
|
load "netmap.json"
|
||||||
|
File format (netmap.json):
|
||||||
|
{
|
||||||
|
"03ff65b6ae79134a4dce9d0d39d3851e9bab4ee97abf86e81e1c5bbc50cd2826ae": {
|
||||||
|
"continent": "Europe",
|
||||||
|
"country": "Poland"
|
||||||
|
},
|
||||||
|
"02ac920cd7df0b61b289072e6b946e2da4e1a31b9ab1c621bb475e30fa4ab102c3": {
|
||||||
|
"continent": "Antarctica",
|
||||||
|
"country": "Heard Island"
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
|
||||||
|
"remove": {
|
||||||
|
descriprion: "Remove a node: remove <node-hash>",
|
||||||
|
usage: `Remove a node
|
||||||
|
Example of usage:
|
||||||
|
remove 03ff65b6ae79134a4dce9d0d39d3851e9bab4ee97abf86e81e1c5bbc50cd2826ae`,
|
||||||
|
},
|
||||||
|
|
||||||
|
"rm": {
|
||||||
|
descriprion: "Remove a node: rm <node-hash>",
|
||||||
|
usage: `Remove a node
|
||||||
|
Example of usage:
|
||||||
|
rm 03ff65b6ae79134a4dce9d0d39d3851e9bab4ee97abf86e81e1c5bbc50cd2826ae`,
|
||||||
|
},
|
||||||
|
|
||||||
|
"eval": {
|
||||||
|
descriprion: "Evaluate a policy: eval <policy>",
|
||||||
|
usage: `Evaluate a policy
|
||||||
|
Example of usage:
|
||||||
|
eval REP 2`,
|
||||||
|
},
|
||||||
|
|
||||||
|
"help": {
|
||||||
|
descriprion: "Show available commands",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repl *policyPlaygroundREPL) handleCommand(args []string) error {
|
||||||
|
if len(args) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch args[0] {
|
||||||
|
case "list", "ls":
|
||||||
|
return repl.handleLs(args[1:])
|
||||||
|
case "add":
|
||||||
|
return repl.handleAdd(args[1:])
|
||||||
|
case "load":
|
||||||
|
return repl.handleLoad(args[1:])
|
||||||
|
case "remove", "rm":
|
||||||
|
return repl.handleRemove(args[1:])
|
||||||
|
case "eval":
|
||||||
|
return repl.handleEval(args[1:])
|
||||||
|
case "help":
|
||||||
|
return repl.handleHelp(args[1:])
|
||||||
|
}
|
||||||
|
return fmt.Errorf("unknown command %q. See 'help' for assistance", args[0])
|
||||||
|
}
|
||||||
|
|
||||||
func (repl *policyPlaygroundREPL) run() error {
|
func (repl *policyPlaygroundREPL) run() error {
|
||||||
if len(viper.GetString(commonflags.RPC)) > 0 {
|
if len(viper.GetString(commonflags.RPC)) > 0 {
|
||||||
|
@ -190,24 +299,32 @@ func (repl *policyPlaygroundREPL) run() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cmdHandlers := map[string]func([]string) error{
|
if len(viper.GetString(netmapConfigPath)) > 0 {
|
||||||
"list": repl.handleLs,
|
err := repl.handleLoad([]string{viper.GetString(netmapConfigPath)})
|
||||||
"ls": repl.handleLs,
|
commonCmd.ExitOnErr(repl.cmd, "load netmap config error: %w", err)
|
||||||
"add": repl.handleAdd,
|
|
||||||
"load": repl.handleLoad,
|
|
||||||
"remove": repl.handleRemove,
|
|
||||||
"rm": repl.handleRemove,
|
|
||||||
"eval": repl.handleEval,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var cfgCompleter []readline.PrefixCompleterInterface
|
||||||
|
var helpSubItems []readline.PrefixCompleterInterface
|
||||||
|
|
||||||
|
for name := range commands {
|
||||||
|
if name != "help" {
|
||||||
|
cfgCompleter = append(cfgCompleter, readline.PcItem(name))
|
||||||
|
helpSubItems = append(helpSubItems, readline.PcItem(name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cfgCompleter = append(cfgCompleter, readline.PcItem("help", helpSubItems...))
|
||||||
|
completer := readline.NewPrefixCompleter(cfgCompleter...)
|
||||||
rl, err := readline.NewEx(&readline.Config{
|
rl, err := readline.NewEx(&readline.Config{
|
||||||
Prompt: "> ",
|
Prompt: "> ",
|
||||||
InterruptPrompt: "^C",
|
InterruptPrompt: "^C",
|
||||||
AutoComplete: policyPlaygroundCompleter,
|
AutoComplete: completer,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error initializing readline: %w", err)
|
return fmt.Errorf("error initializing readline: %w", err)
|
||||||
}
|
}
|
||||||
|
repl.console = rl
|
||||||
defer rl.Close()
|
defer rl.Close()
|
||||||
|
|
||||||
var exit bool
|
var exit bool
|
||||||
|
@ -225,17 +342,8 @@ func (repl *policyPlaygroundREPL) run() error {
|
||||||
}
|
}
|
||||||
exit = false
|
exit = false
|
||||||
|
|
||||||
parts := strings.Fields(line)
|
if err := repl.handleCommand(strings.Fields(line)); err != nil {
|
||||||
if len(parts) == 0 {
|
fmt.Fprintf(repl.console, "error: %v\n", err)
|
||||||
continue
|
|
||||||
}
|
|
||||||
cmd := parts[0]
|
|
||||||
if handler, exists := cmdHandlers[cmd]; exists {
|
|
||||||
if err := handler(parts[1:]); err != nil {
|
|
||||||
fmt.Printf("error: %v\n", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fmt.Printf("error: unknown command %q\n", cmd)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -251,6 +359,14 @@ If a wallet and endpoint is provided, the initial netmap data will be loaded fro
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
netmapConfigPath = "netmap-config"
|
||||||
|
netmapConfigUsage = "Path to the netmap configuration file"
|
||||||
|
)
|
||||||
|
|
||||||
func initContainerPolicyPlaygroundCmd() {
|
func initContainerPolicyPlaygroundCmd() {
|
||||||
commonflags.Init(policyPlaygroundCmd)
|
commonflags.Init(policyPlaygroundCmd)
|
||||||
|
policyPlaygroundCmd.Flags().String(netmapConfigPath, "", netmapConfigUsage)
|
||||||
|
|
||||||
|
_ = viper.BindPFlag(netmapConfigPath, policyPlaygroundCmd.Flags().Lookup(netmapConfigPath))
|
||||||
}
|
}
|
||||||
|
|
|
@ -296,7 +296,7 @@ func appendEstimation(sb *strings.Builder, resp *control.GetShardEvacuationStatu
|
||||||
leftSeconds := avgObjEvacuationTimeSeconds * objectsLeft
|
leftSeconds := avgObjEvacuationTimeSeconds * objectsLeft
|
||||||
leftMinutes := int(leftSeconds / 60)
|
leftMinutes := int(leftSeconds / 60)
|
||||||
|
|
||||||
sb.WriteString(fmt.Sprintf(" Estimated time left: %d minutes.", leftMinutes))
|
fmt.Fprintf(sb, " Estimated time left: %d minutes.", leftMinutes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func appendDuration(sb *strings.Builder, resp *control.GetShardEvacuationStatusResponse) {
|
func appendDuration(sb *strings.Builder, resp *control.GetShardEvacuationStatusResponse) {
|
||||||
|
@ -305,20 +305,20 @@ func appendDuration(sb *strings.Builder, resp *control.GetShardEvacuationStatusR
|
||||||
hour := int(duration.Seconds() / 3600)
|
hour := int(duration.Seconds() / 3600)
|
||||||
minute := int(duration.Seconds()/60) % 60
|
minute := int(duration.Seconds()/60) % 60
|
||||||
second := int(duration.Seconds()) % 60
|
second := int(duration.Seconds()) % 60
|
||||||
sb.WriteString(fmt.Sprintf(" Duration: %02d:%02d:%02d.", hour, minute, second))
|
fmt.Fprintf(sb, " Duration: %02d:%02d:%02d.", hour, minute, second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func appendStartedAt(sb *strings.Builder, resp *control.GetShardEvacuationStatusResponse) {
|
func appendStartedAt(sb *strings.Builder, resp *control.GetShardEvacuationStatusResponse) {
|
||||||
if resp.GetBody().GetStartedAt() != nil {
|
if resp.GetBody().GetStartedAt() != nil {
|
||||||
startedAt := time.Unix(resp.GetBody().GetStartedAt().GetValue(), 0).UTC()
|
startedAt := time.Unix(resp.GetBody().GetStartedAt().GetValue(), 0).UTC()
|
||||||
sb.WriteString(fmt.Sprintf(" Started at: %s UTC.", startedAt.Format(time.RFC3339)))
|
fmt.Fprintf(sb, " Started at: %s UTC.", startedAt.Format(time.RFC3339))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func appendError(sb *strings.Builder, resp *control.GetShardEvacuationStatusResponse) {
|
func appendError(sb *strings.Builder, resp *control.GetShardEvacuationStatusResponse) {
|
||||||
if len(resp.GetBody().GetErrorMessage()) > 0 {
|
if len(resp.GetBody().GetErrorMessage()) > 0 {
|
||||||
sb.WriteString(fmt.Sprintf(" Error: %s.", resp.GetBody().GetErrorMessage()))
|
fmt.Fprintf(sb, " Error: %s.", resp.GetBody().GetErrorMessage())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,7 +332,7 @@ func appendStatus(sb *strings.Builder, resp *control.GetShardEvacuationStatusRes
|
||||||
default:
|
default:
|
||||||
status = "undefined"
|
status = "undefined"
|
||||||
}
|
}
|
||||||
sb.WriteString(fmt.Sprintf(" Status: %s.", status))
|
fmt.Fprintf(sb, " Status: %s.", status)
|
||||||
}
|
}
|
||||||
|
|
||||||
func appendShardIDs(sb *strings.Builder, resp *control.GetShardEvacuationStatusResponse) {
|
func appendShardIDs(sb *strings.Builder, resp *control.GetShardEvacuationStatusResponse) {
|
||||||
|
@ -350,14 +350,14 @@ func appendShardIDs(sb *strings.Builder, resp *control.GetShardEvacuationStatusR
|
||||||
}
|
}
|
||||||
|
|
||||||
func appendCounts(sb *strings.Builder, resp *control.GetShardEvacuationStatusResponse) {
|
func appendCounts(sb *strings.Builder, resp *control.GetShardEvacuationStatusResponse) {
|
||||||
sb.WriteString(fmt.Sprintf(" Evacuated %d objects out of %d, failed to evacuate: %d, skipped: %d; evacuated %d trees out of %d, failed to evacuate: %d.",
|
fmt.Fprintf(sb, " Evacuated %d objects out of %d, failed to evacuate: %d, skipped: %d; evacuated %d trees out of %d, failed to evacuate: %d.",
|
||||||
resp.GetBody().GetEvacuatedObjects(),
|
resp.GetBody().GetEvacuatedObjects(),
|
||||||
resp.GetBody().GetTotalObjects(),
|
resp.GetBody().GetTotalObjects(),
|
||||||
resp.GetBody().GetFailedObjects(),
|
resp.GetBody().GetFailedObjects(),
|
||||||
resp.GetBody().GetSkippedObjects(),
|
resp.GetBody().GetSkippedObjects(),
|
||||||
resp.GetBody().GetEvacuatedTrees(),
|
resp.GetBody().GetEvacuatedTrees(),
|
||||||
resp.GetBody().GetTotalTrees(),
|
resp.GetBody().GetTotalTrees(),
|
||||||
resp.GetBody().GetFailedTrees()))
|
resp.GetBody().GetFailedTrees())
|
||||||
}
|
}
|
||||||
|
|
||||||
func initControlEvacuationShardCmd() {
|
func initControlEvacuationShardCmd() {
|
||||||
|
|
|
@ -62,11 +62,11 @@ func prettyPrintNodeInfo(cmd *cobra.Command, i netmap.NodeInfo) {
|
||||||
|
|
||||||
cmd.Println("state:", stateWord)
|
cmd.Println("state:", stateWord)
|
||||||
|
|
||||||
netmap.IterateNetworkEndpoints(i, func(s string) {
|
for s := range i.NetworkEndpoints() {
|
||||||
cmd.Println("address:", s)
|
cmd.Println("address:", s)
|
||||||
})
|
}
|
||||||
|
|
||||||
i.IterateAttributes(func(key, value string) {
|
for key, value := range i.Attributes() {
|
||||||
cmd.Printf("attribute: %s=%s\n", key, value)
|
cmd.Printf("attribute: %s=%s\n", key, value)
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import (
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
// object lock command.
|
// object lock command.
|
||||||
|
@ -78,7 +79,7 @@ var objectLockCmd = &cobra.Command{
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
endpoint, _ := cmd.Flags().GetString(commonflags.RPC)
|
endpoint := viper.GetString(commonflags.RPC)
|
||||||
|
|
||||||
currEpoch, err := internalclient.GetCurrentEpoch(ctx, cmd, endpoint)
|
currEpoch, err := internalclient.GetCurrentEpoch(ctx, cmd, endpoint)
|
||||||
commonCmd.ExitOnErr(cmd, "Request current epoch: %w", err)
|
commonCmd.ExitOnErr(cmd, "Request current epoch: %w", err)
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"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"
|
||||||
|
@ -48,6 +49,12 @@ type ecHeader struct {
|
||||||
parent oid.ID
|
parent oid.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type objectCounter struct {
|
||||||
|
sync.Mutex
|
||||||
|
total uint32
|
||||||
|
isECcounted bool
|
||||||
|
}
|
||||||
|
|
||||||
type objectPlacement struct {
|
type objectPlacement struct {
|
||||||
requiredNodes []netmapSDK.NodeInfo
|
requiredNodes []netmapSDK.NodeInfo
|
||||||
confirmedNodes []netmapSDK.NodeInfo
|
confirmedNodes []netmapSDK.NodeInfo
|
||||||
|
@ -56,6 +63,7 @@ type objectPlacement struct {
|
||||||
type objectNodesResult struct {
|
type objectNodesResult struct {
|
||||||
errors []error
|
errors []error
|
||||||
placements map[oid.ID]objectPlacement
|
placements map[oid.ID]objectPlacement
|
||||||
|
total uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
type ObjNodesDataObject struct {
|
type ObjNodesDataObject struct {
|
||||||
|
@ -106,18 +114,18 @@ func objectNodes(cmd *cobra.Command, _ []string) {
|
||||||
pk := key.GetOrGenerate(cmd)
|
pk := key.GetOrGenerate(cmd)
|
||||||
cli := internalclient.GetSDKClientByFlag(cmd, pk, commonflags.RPC)
|
cli := internalclient.GetSDKClientByFlag(cmd, pk, commonflags.RPC)
|
||||||
|
|
||||||
objects := getPhyObjects(cmd, cnrID, objID, cli, pk)
|
objects, count := getPhyObjects(cmd, cnrID, objID, cli, pk)
|
||||||
|
|
||||||
placementPolicy, netmap := getPlacementPolicyAndNetmap(cmd, cnrID, cli)
|
placementPolicy, netmap := getPlacementPolicyAndNetmap(cmd, cnrID, cli)
|
||||||
|
|
||||||
result := getRequiredPlacement(cmd, objects, placementPolicy, netmap)
|
result := getRequiredPlacement(cmd, objects, placementPolicy, netmap)
|
||||||
|
|
||||||
getActualPlacement(cmd, netmap, pk, objects, result)
|
getActualPlacement(cmd, netmap, pk, objects, count, result)
|
||||||
|
|
||||||
printPlacement(cmd, objID, objects, result)
|
printPlacement(cmd, objID, objects, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPhyObjects(cmd *cobra.Command, cnrID cid.ID, objID oid.ID, cli *client.Client, pk *ecdsa.PrivateKey) []phyObject {
|
func getPhyObjects(cmd *cobra.Command, cnrID cid.ID, objID oid.ID, cli *client.Client, pk *ecdsa.PrivateKey) ([]phyObject, int) {
|
||||||
var addrObj oid.Address
|
var addrObj oid.Address
|
||||||
addrObj.SetContainer(cnrID)
|
addrObj.SetContainer(cnrID)
|
||||||
addrObj.SetObject(objID)
|
addrObj.SetObject(objID)
|
||||||
|
@ -145,7 +153,7 @@ func getPhyObjects(cmd *cobra.Command, cnrID cid.ID, objID oid.ID, cli *client.C
|
||||||
parent: res.Header().ECHeader().Parent(),
|
parent: res.Header().ECHeader().Parent(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return []phyObject{obj}
|
return []phyObject{obj}, 1
|
||||||
}
|
}
|
||||||
|
|
||||||
var errSplitInfo *objectSDK.SplitInfoError
|
var errSplitInfo *objectSDK.SplitInfoError
|
||||||
|
@ -155,29 +163,34 @@ func getPhyObjects(cmd *cobra.Command, cnrID cid.ID, objID oid.ID, cli *client.C
|
||||||
|
|
||||||
var ecInfoError *objectSDK.ECInfoError
|
var ecInfoError *objectSDK.ECInfoError
|
||||||
if errors.As(err, &ecInfoError) {
|
if errors.As(err, &ecInfoError) {
|
||||||
return getECObjectChunks(cmd, cnrID, objID, ecInfoError)
|
return getECObjectChunks(cmd, cnrID, objID, ecInfoError), 1
|
||||||
}
|
}
|
||||||
commonCmd.ExitOnErr(cmd, "failed to get object info: %w", err)
|
commonCmd.ExitOnErr(cmd, "failed to get object info: %w", err)
|
||||||
return nil
|
return nil, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func getComplexObjectParts(cmd *cobra.Command, cnrID cid.ID, objID oid.ID, cli *client.Client, prmHead internalclient.HeadObjectPrm, errSplitInfo *objectSDK.SplitInfoError) []phyObject {
|
func getComplexObjectParts(cmd *cobra.Command, cnrID cid.ID, objID oid.ID, cli *client.Client, prmHead internalclient.HeadObjectPrm, errSplitInfo *objectSDK.SplitInfoError) ([]phyObject, int) {
|
||||||
members := getCompexObjectMembers(cmd, cnrID, objID, cli, prmHead, errSplitInfo)
|
members, total := getCompexObjectMembers(cmd, cnrID, objID, cli, prmHead, errSplitInfo)
|
||||||
return flattenComplexMembersIfECContainer(cmd, cnrID, members, prmHead)
|
return flattenComplexMembersIfECContainer(cmd, cnrID, members, prmHead), total
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCompexObjectMembers(cmd *cobra.Command, cnrID cid.ID, objID oid.ID, cli *client.Client, prmHead internalclient.HeadObjectPrm, errSplitInfo *objectSDK.SplitInfoError) []oid.ID {
|
func getCompexObjectMembers(cmd *cobra.Command, cnrID cid.ID, objID oid.ID, cli *client.Client, prmHead internalclient.HeadObjectPrm, errSplitInfo *objectSDK.SplitInfoError) ([]oid.ID, int) {
|
||||||
|
var total int
|
||||||
splitInfo := errSplitInfo.SplitInfo()
|
splitInfo := errSplitInfo.SplitInfo()
|
||||||
|
|
||||||
if members, ok := tryGetSplitMembersByLinkingObject(cmd, splitInfo, prmHead, cnrID); ok {
|
if members, ok := tryGetSplitMembersByLinkingObject(cmd, splitInfo, prmHead, cnrID); ok {
|
||||||
return members
|
if total = len(members); total > 0 {
|
||||||
|
total-- // linking object is not data object
|
||||||
|
}
|
||||||
|
return members, total
|
||||||
}
|
}
|
||||||
|
|
||||||
if members, ok := tryGetSplitMembersBySplitID(cmd, splitInfo, cli, cnrID); ok {
|
if members, ok := tryGetSplitMembersBySplitID(cmd, splitInfo, cli, cnrID); ok {
|
||||||
return members
|
return members, len(members)
|
||||||
}
|
}
|
||||||
|
|
||||||
return tryRestoreChainInReverse(cmd, splitInfo, prmHead, cli, cnrID, objID)
|
members := tryRestoreChainInReverse(cmd, splitInfo, prmHead, cli, cnrID, objID)
|
||||||
|
return members, len(members)
|
||||||
}
|
}
|
||||||
|
|
||||||
func flattenComplexMembersIfECContainer(cmd *cobra.Command, cnrID cid.ID, members []oid.ID, prmHead internalclient.HeadObjectPrm) []phyObject {
|
func flattenComplexMembersIfECContainer(cmd *cobra.Command, cnrID cid.ID, members []oid.ID, prmHead internalclient.HeadObjectPrm) []phyObject {
|
||||||
|
@ -383,8 +396,11 @@ func getECRequiredPlacementInternal(cmd *cobra.Command, object phyObject, placem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getActualPlacement(cmd *cobra.Command, netmap *netmapSDK.NetMap, pk *ecdsa.PrivateKey, objects []phyObject, result *objectNodesResult) {
|
func getActualPlacement(cmd *cobra.Command, netmap *netmapSDK.NetMap, pk *ecdsa.PrivateKey, objects []phyObject, count int, result *objectNodesResult) {
|
||||||
resultMtx := &sync.Mutex{}
|
resultMtx := &sync.Mutex{}
|
||||||
|
counter := &objectCounter{
|
||||||
|
total: uint32(count),
|
||||||
|
}
|
||||||
|
|
||||||
candidates := getNodesToCheckObjectExistance(cmd, netmap, result)
|
candidates := getNodesToCheckObjectExistance(cmd, netmap, result)
|
||||||
|
|
||||||
|
@ -401,7 +417,7 @@ func getActualPlacement(cmd *cobra.Command, netmap *netmapSDK.NetMap, pk *ecdsa.
|
||||||
|
|
||||||
for _, object := range objects {
|
for _, object := range objects {
|
||||||
eg.Go(func() error {
|
eg.Go(func() error {
|
||||||
stored, err := isObjectStoredOnNode(egCtx, cmd, object.containerID, object.objectID, cli, pk)
|
stored, err := isObjectStoredOnNode(egCtx, cmd, object.containerID, object.objectID, cli, pk, counter)
|
||||||
resultMtx.Lock()
|
resultMtx.Lock()
|
||||||
defer resultMtx.Unlock()
|
defer resultMtx.Unlock()
|
||||||
if err == nil && stored {
|
if err == nil && stored {
|
||||||
|
@ -420,6 +436,7 @@ func getActualPlacement(cmd *cobra.Command, netmap *netmapSDK.NetMap, pk *ecdsa.
|
||||||
}
|
}
|
||||||
|
|
||||||
commonCmd.ExitOnErr(cmd, "failed to get actual placement: %w", eg.Wait())
|
commonCmd.ExitOnErr(cmd, "failed to get actual placement: %w", eg.Wait())
|
||||||
|
result.total = counter.total
|
||||||
}
|
}
|
||||||
|
|
||||||
func getNodesToCheckObjectExistance(cmd *cobra.Command, netmap *netmapSDK.NetMap, result *objectNodesResult) []netmapSDK.NodeInfo {
|
func getNodesToCheckObjectExistance(cmd *cobra.Command, netmap *netmapSDK.NetMap, result *objectNodesResult) []netmapSDK.NodeInfo {
|
||||||
|
@ -444,17 +461,11 @@ func createClient(ctx context.Context, cmd *cobra.Command, candidate netmapSDK.N
|
||||||
var cli *client.Client
|
var cli *client.Client
|
||||||
var addresses []string
|
var addresses []string
|
||||||
if preferInternal, _ := cmd.Flags().GetBool(preferInternalAddressesFlag); preferInternal {
|
if preferInternal, _ := cmd.Flags().GetBool(preferInternalAddressesFlag); preferInternal {
|
||||||
candidate.IterateNetworkEndpoints(func(s string) bool {
|
addresses = slices.AppendSeq(addresses, candidate.NetworkEndpoints())
|
||||||
addresses = append(addresses, s)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
addresses = append(addresses, candidate.ExternalAddresses()...)
|
addresses = append(addresses, candidate.ExternalAddresses()...)
|
||||||
} else {
|
} else {
|
||||||
addresses = append(addresses, candidate.ExternalAddresses()...)
|
addresses = append(addresses, candidate.ExternalAddresses()...)
|
||||||
candidate.IterateNetworkEndpoints(func(s string) bool {
|
addresses = slices.AppendSeq(addresses, candidate.NetworkEndpoints())
|
||||||
addresses = append(addresses, s)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var lastErr error
|
var lastErr error
|
||||||
|
@ -478,7 +489,7 @@ func createClient(ctx context.Context, cmd *cobra.Command, candidate netmapSDK.N
|
||||||
return cli, nil
|
return cli, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isObjectStoredOnNode(ctx context.Context, cmd *cobra.Command, cnrID cid.ID, objID oid.ID, cli *client.Client, pk *ecdsa.PrivateKey) (bool, error) {
|
func isObjectStoredOnNode(ctx context.Context, cmd *cobra.Command, cnrID cid.ID, objID oid.ID, cli *client.Client, pk *ecdsa.PrivateKey, counter *objectCounter) (bool, error) {
|
||||||
var addrObj oid.Address
|
var addrObj oid.Address
|
||||||
addrObj.SetContainer(cnrID)
|
addrObj.SetContainer(cnrID)
|
||||||
addrObj.SetObject(objID)
|
addrObj.SetObject(objID)
|
||||||
|
@ -493,6 +504,14 @@ func isObjectStoredOnNode(ctx context.Context, cmd *cobra.Command, cnrID cid.ID,
|
||||||
|
|
||||||
res, err := internalclient.HeadObject(ctx, prmHead)
|
res, err := internalclient.HeadObject(ctx, prmHead)
|
||||||
if err == nil && res != nil {
|
if err == nil && res != nil {
|
||||||
|
if res.Header().ECHeader() != nil {
|
||||||
|
counter.Lock()
|
||||||
|
defer counter.Unlock()
|
||||||
|
if !counter.isECcounted {
|
||||||
|
counter.total *= res.Header().ECHeader().Total()
|
||||||
|
}
|
||||||
|
counter.isECcounted = true
|
||||||
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
var notFound *apistatus.ObjectNotFound
|
var notFound *apistatus.ObjectNotFound
|
||||||
|
@ -512,7 +531,8 @@ func printPlacement(cmd *cobra.Command, objID oid.ID, objects []phyObject, resul
|
||||||
}
|
}
|
||||||
|
|
||||||
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(), result.total)
|
||||||
|
fmt.Fprintf(cmd.OutOrStdout(), "Found %d:\n", len(objects))
|
||||||
|
|
||||||
for _, object := range objects {
|
for _, object := range objects {
|
||||||
fmt.Fprintf(cmd.OutOrStdout(), "- %s\n", object.objectID)
|
fmt.Fprintf(cmd.OutOrStdout(), "- %s\n", object.objectID)
|
||||||
|
|
|
@ -154,7 +154,7 @@ func printECInfoErr(cmd *cobra.Command, err error) bool {
|
||||||
if ok {
|
if ok {
|
||||||
toJSON, _ := cmd.Flags().GetBool(commonflags.JSON)
|
toJSON, _ := cmd.Flags().GetBool(commonflags.JSON)
|
||||||
toProto, _ := cmd.Flags().GetBool("proto")
|
toProto, _ := cmd.Flags().GetBool("proto")
|
||||||
if !(toJSON || toProto) {
|
if !toJSON && !toProto {
|
||||||
cmd.PrintErrln("Object is erasure-encoded, ec information received.")
|
cmd.PrintErrln("Object is erasure-encoded, ec information received.")
|
||||||
}
|
}
|
||||||
printECInfo(cmd, errECInfo.ECInfo())
|
printECInfo(cmd, errECInfo.ECInfo())
|
||||||
|
|
|
@ -262,13 +262,8 @@ func OpenSessionViaClient(cmd *cobra.Command, dst SessionPrm, cli *client.Client
|
||||||
if _, ok := dst.(*internal.DeleteObjectPrm); ok {
|
if _, ok := dst.(*internal.DeleteObjectPrm); ok {
|
||||||
common.PrintVerbose(cmd, "Collecting relatives of the removal object...")
|
common.PrintVerbose(cmd, "Collecting relatives of the removal object...")
|
||||||
|
|
||||||
rels := collectObjectRelatives(cmd, cli, cnr, *obj)
|
objs = collectObjectRelatives(cmd, cli, cnr, *obj)
|
||||||
|
objs = append(objs, *obj)
|
||||||
if len(rels) == 0 {
|
|
||||||
objs = []oid.ID{*obj}
|
|
||||||
} else {
|
|
||||||
objs = append(rels, *obj)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,12 +4,14 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"strconv"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
configViper "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common/config"
|
configViper "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common/config"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
||||||
control "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control/ir"
|
control "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control/ir"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
|
||||||
|
"github.com/spf13/cast"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
@ -44,11 +46,30 @@ func reloadConfig() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Reload(logPrm)
|
err = logPrm.SetTags(loggerTags())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logger.UpdateLevelForTags(logPrm)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loggerTags() [][]string {
|
||||||
|
var res [][]string
|
||||||
|
for i := 0; ; i++ {
|
||||||
|
var item []string
|
||||||
|
index := strconv.FormatInt(int64(i), 10)
|
||||||
|
names := cast.ToString(cfg.Get("logger.tags." + index + ".names"))
|
||||||
|
if names == "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
item = append(item, names, cast.ToString(cfg.Get("logger.tags."+index+".level")))
|
||||||
|
res = append(res, item)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
func watchForSignal(ctx context.Context, cancel func()) {
|
func watchForSignal(ctx context.Context, cancel func()) {
|
||||||
ch := make(chan os.Signal, 1)
|
ch := make(chan os.Signal, 1)
|
||||||
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
|
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
|
@ -80,10 +80,14 @@ func main() {
|
||||||
exitErr(err)
|
exitErr(err)
|
||||||
logPrm.SamplingHook = metrics.LogMetrics().GetSamplingHook()
|
logPrm.SamplingHook = metrics.LogMetrics().GetSamplingHook()
|
||||||
logPrm.PrependTimestamp = cfg.GetBool("logger.timestamp")
|
logPrm.PrependTimestamp = cfg.GetBool("logger.timestamp")
|
||||||
|
err = logPrm.SetTags(loggerTags())
|
||||||
|
exitErr(err)
|
||||||
|
|
||||||
log, err = logger.NewLogger(logPrm)
|
log, err = logger.NewLogger(logPrm)
|
||||||
exitErr(err)
|
exitErr(err)
|
||||||
|
|
||||||
|
logger.UpdateLevelForTags(logPrm)
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
pprofCmp = newPprofComponent()
|
pprofCmp = newPprofComponent()
|
||||||
|
|
|
@ -3,6 +3,8 @@ package common
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
type FilterResult byte
|
type FilterResult byte
|
||||||
|
@ -71,11 +73,7 @@ func (fp FallbackParser) ToParser() Parser {
|
||||||
func (p Parser) ToFallbackParser() FallbackParser {
|
func (p Parser) ToFallbackParser() FallbackParser {
|
||||||
return func(key, value []byte) (SchemaEntry, Parser) {
|
return func(key, value []byte) (SchemaEntry, Parser) {
|
||||||
entry, next, err := p(key, value)
|
entry, next, err := p(key, value)
|
||||||
if err != nil {
|
assert.NoError(err, "couldn't use that parser as a fallback parser")
|
||||||
panic(fmt.Errorf(
|
|
||||||
"couldn't use that parser as a fallback parser, it returned an error: %w", err,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
return entry, next
|
return entry, next
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ func DefaultRecordParser(key, value []byte) (common.SchemaEntry, common.Parser,
|
||||||
r.addr.SetContainer(cnr)
|
r.addr.SetContainer(cnr)
|
||||||
r.addr.SetObject(obj)
|
r.addr.SetObject(obj)
|
||||||
|
|
||||||
r.data = value[:]
|
r.data = value
|
||||||
|
|
||||||
return &r, nil, nil
|
return &r, nil, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,17 +53,17 @@ func (f *InputFieldWithHistory) InputHandler() func(event *tcell.EventKey, setFo
|
||||||
f.historyPointer++
|
f.historyPointer++
|
||||||
// Stop iterating over history.
|
// Stop iterating over history.
|
||||||
if f.historyPointer == len(f.history) {
|
if f.historyPointer == len(f.history) {
|
||||||
f.InputField.SetText(f.currentContent)
|
f.SetText(f.currentContent)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
f.InputField.SetText(f.history[f.historyPointer])
|
f.SetText(f.history[f.historyPointer])
|
||||||
case tcell.KeyUp:
|
case tcell.KeyUp:
|
||||||
if len(f.history) == 0 {
|
if len(f.history) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Start iterating over history.
|
// Start iterating over history.
|
||||||
if f.historyPointer == len(f.history) {
|
if f.historyPointer == len(f.history) {
|
||||||
f.currentContent = f.InputField.GetText()
|
f.currentContent = f.GetText()
|
||||||
}
|
}
|
||||||
// End of history.
|
// End of history.
|
||||||
if f.historyPointer == 0 {
|
if f.historyPointer == 0 {
|
||||||
|
@ -71,7 +71,7 @@ func (f *InputFieldWithHistory) InputHandler() func(event *tcell.EventKey, setFo
|
||||||
}
|
}
|
||||||
// Iterate to least recent prompts.
|
// Iterate to least recent prompts.
|
||||||
f.historyPointer--
|
f.historyPointer--
|
||||||
f.InputField.SetText(f.history[f.historyPointer])
|
f.SetText(f.history[f.historyPointer])
|
||||||
default:
|
default:
|
||||||
f.InputField.InputHandler()(event, func(tview.Primitive) {})
|
f.InputField.InputHandler()(event, func(tview.Primitive) {})
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/internal/schema/common"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/internal/schema/common"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/assert"
|
||||||
"github.com/gdamore/tcell/v2"
|
"github.com/gdamore/tcell/v2"
|
||||||
"github.com/rivo/tview"
|
"github.com/rivo/tview"
|
||||||
)
|
)
|
||||||
|
@ -94,9 +95,7 @@ func (v *RecordsView) Mount(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *RecordsView) Unmount() {
|
func (v *RecordsView) Unmount() {
|
||||||
if v.onUnmount == nil {
|
assert.False(v.onUnmount == nil, "try to unmount not mounted component")
|
||||||
panic("try to unmount not mounted component")
|
|
||||||
}
|
|
||||||
v.onUnmount()
|
v.onUnmount()
|
||||||
v.onUnmount = nil
|
v.onUnmount = nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -460,11 +460,11 @@ func (ui *UI) handleInputOnSearching(event *tcell.EventKey) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ui.mountedPage.(type) {
|
switch v := ui.mountedPage.(type) {
|
||||||
case *BucketsView:
|
case *BucketsView:
|
||||||
ui.moveNextPage(NewBucketsView(ui, res))
|
ui.moveNextPage(NewBucketsView(ui, res))
|
||||||
case *RecordsView:
|
case *RecordsView:
|
||||||
bucket := ui.mountedPage.(*RecordsView).bucket
|
bucket := v.bucket
|
||||||
ui.moveNextPage(NewRecordsView(ui, bucket, res))
|
ui.moveNextPage(NewRecordsView(ui, bucket, res))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -482,7 +482,7 @@ func (ui *UI) handleInputOnSearching(event *tcell.EventKey) {
|
||||||
ui.searchBar.InputHandler()(event, func(tview.Primitive) {})
|
ui.searchBar.InputHandler()(event, func(tview.Primitive) {})
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.Box.MouseHandler()
|
ui.MouseHandler()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ui *UI) WithPrompt(prompt string) error {
|
func (ui *UI) WithPrompt(prompt string) error {
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
func initAPEManagerService(c *cfg) {
|
func initAPEManagerService(c *cfg) {
|
||||||
contractStorage := ape_contract.NewProxyVerificationContractStorage(
|
contractStorage := ape_contract.NewProxyVerificationContractStorage(
|
||||||
morph.NewSwitchRPCGuardedActor(c.cfgMorph.client),
|
morph.NewSwitchRPCGuardedActor(c.cfgMorph.client),
|
||||||
c.shared.key,
|
c.key,
|
||||||
c.cfgMorph.proxyScriptHash,
|
c.cfgMorph.proxyScriptHash,
|
||||||
c.cfgObject.cfgAccessPolicyEngine.policyContractHash)
|
c.cfgObject.cfgAccessPolicyEngine.policyContractHash)
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,27 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"cmp"
|
||||||
"context"
|
"context"
|
||||||
|
"slices"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/metrics"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/metrics"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/netmap"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/netmap"
|
||||||
objectwriter "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object/common/writer"
|
objectwriter "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object/common/writer"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
|
||||||
utilSync "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/sync"
|
utilSync "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/sync"
|
||||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
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"
|
||||||
netmapSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
netmapSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||||
lru "github.com/hashicorp/golang-lru/v2"
|
|
||||||
"github.com/hashicorp/golang-lru/v2/expirable"
|
"github.com/hashicorp/golang-lru/v2/expirable"
|
||||||
|
"github.com/hashicorp/golang-lru/v2/simplelru"
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
type netValueReader[K any, V any] func(ctx context.Context, cid K) (V, error)
|
type netValueReader[K any, V any] func(ctx context.Context, cid K) (V, error)
|
||||||
|
@ -110,55 +117,6 @@ func (c *ttlNetCache[K, V]) remove(key K) {
|
||||||
hit = c.cache.Remove(key)
|
hit = c.cache.Remove(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// entity that provides LRU cache interface.
|
|
||||||
type lruNetCache struct {
|
|
||||||
cache *lru.Cache[uint64, *netmapSDK.NetMap]
|
|
||||||
|
|
||||||
netRdr netValueReader[uint64, *netmapSDK.NetMap]
|
|
||||||
|
|
||||||
metrics cacheMetrics
|
|
||||||
}
|
|
||||||
|
|
||||||
// newNetworkLRUCache returns wrapper over netValueReader with LRU cache.
|
|
||||||
func newNetworkLRUCache(sz int, netRdr netValueReader[uint64, *netmapSDK.NetMap], metrics cacheMetrics) *lruNetCache {
|
|
||||||
cache, err := lru.New[uint64, *netmapSDK.NetMap](sz)
|
|
||||||
fatalOnErr(err)
|
|
||||||
|
|
||||||
return &lruNetCache{
|
|
||||||
cache: cache,
|
|
||||||
netRdr: netRdr,
|
|
||||||
metrics: metrics,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// reads value by the key.
|
|
||||||
//
|
|
||||||
// updates the value from the network on cache miss.
|
|
||||||
//
|
|
||||||
// returned value should not be modified.
|
|
||||||
func (c *lruNetCache) get(ctx context.Context, key uint64) (*netmapSDK.NetMap, error) {
|
|
||||||
hit := false
|
|
||||||
startedAt := time.Now()
|
|
||||||
defer func() {
|
|
||||||
c.metrics.AddMethodDuration("Get", time.Since(startedAt), hit)
|
|
||||||
}()
|
|
||||||
|
|
||||||
val, ok := c.cache.Get(key)
|
|
||||||
if ok {
|
|
||||||
hit = true
|
|
||||||
return val, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
val, err := c.netRdr(ctx, key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
c.cache.Add(key, val)
|
|
||||||
|
|
||||||
return val, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// wrapper over TTL cache of values read from the network
|
// wrapper over TTL cache of values read from the network
|
||||||
// that implements container storage.
|
// that implements container storage.
|
||||||
type ttlContainerStorage struct {
|
type ttlContainerStorage struct {
|
||||||
|
@ -200,20 +158,222 @@ func (s ttlContainerStorage) DeletionInfo(ctx context.Context, cnr cid.ID) (*con
|
||||||
type lruNetmapSource struct {
|
type lruNetmapSource struct {
|
||||||
netState netmap.State
|
netState netmap.State
|
||||||
|
|
||||||
cache *lruNetCache
|
client rawSource
|
||||||
|
cache *simplelru.LRU[uint64, *atomic.Pointer[netmapSDK.NetMap]]
|
||||||
|
mtx sync.RWMutex
|
||||||
|
metrics cacheMetrics
|
||||||
|
log *logger.Logger
|
||||||
|
candidates atomic.Pointer[[]netmapSDK.NodeInfo]
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCachedNetmapStorage(s netmap.State, v netmap.Source) netmap.Source {
|
type rawSource interface {
|
||||||
|
GetCandidates(ctx context.Context) ([]netmapSDK.NodeInfo, error)
|
||||||
|
GetNetMapByEpoch(ctx context.Context, epoch uint64) (*netmapSDK.NetMap, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCachedNetmapStorage(ctx context.Context, log *logger.Logger,
|
||||||
|
netState netmap.State, client rawSource, wg *sync.WaitGroup, d time.Duration,
|
||||||
|
) netmap.Source {
|
||||||
const netmapCacheSize = 10
|
const netmapCacheSize = 10
|
||||||
|
|
||||||
lruNetmapCache := newNetworkLRUCache(netmapCacheSize, func(ctx context.Context, key uint64) (*netmapSDK.NetMap, error) {
|
cache, err := simplelru.NewLRU[uint64, *atomic.Pointer[netmapSDK.NetMap]](netmapCacheSize, nil)
|
||||||
return v.GetNetMapByEpoch(ctx, key)
|
fatalOnErr(err)
|
||||||
}, metrics.NewCacheMetrics("netmap"))
|
|
||||||
|
|
||||||
return &lruNetmapSource{
|
src := &lruNetmapSource{
|
||||||
netState: s,
|
netState: netState,
|
||||||
cache: lruNetmapCache,
|
client: client,
|
||||||
|
cache: cache,
|
||||||
|
log: log,
|
||||||
|
metrics: metrics.NewCacheMetrics("netmap"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
src.updateCandidates(ctx, d)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return src
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateCandidates routine to merge netmap in cache with candidates list.
|
||||||
|
func (s *lruNetmapSource) updateCandidates(ctx context.Context, d time.Duration) {
|
||||||
|
timer := time.NewTimer(d)
|
||||||
|
defer timer.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case <-timer.C:
|
||||||
|
newCandidates, err := s.client.GetCandidates(ctx)
|
||||||
|
if err != nil {
|
||||||
|
s.log.Debug(ctx, logs.FailedToUpdateNetmapCandidates, zap.Error(err))
|
||||||
|
timer.Reset(d)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if len(newCandidates) == 0 {
|
||||||
|
s.candidates.Store(&newCandidates)
|
||||||
|
timer.Reset(d)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
slices.SortFunc(newCandidates, func(n1 netmapSDK.NodeInfo, n2 netmapSDK.NodeInfo) int {
|
||||||
|
return cmp.Compare(n1.Hash(), n2.Hash())
|
||||||
|
})
|
||||||
|
|
||||||
|
// Check once state changed
|
||||||
|
v := s.candidates.Load()
|
||||||
|
if v == nil {
|
||||||
|
s.candidates.Store(&newCandidates)
|
||||||
|
s.mergeCacheWithCandidates(newCandidates)
|
||||||
|
timer.Reset(d)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
ret := slices.CompareFunc(*v, newCandidates, func(n1 netmapSDK.NodeInfo, n2 netmapSDK.NodeInfo) int {
|
||||||
|
if !bytes.Equal(n1.PublicKey(), n2.PublicKey()) ||
|
||||||
|
uint32(n1.Status()) != uint32(n2.Status()) ||
|
||||||
|
slices.Compare(n1.ExternalAddresses(), n2.ExternalAddresses()) != 0 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
ne1 := slices.Collect(n1.NetworkEndpoints())
|
||||||
|
ne2 := slices.Collect(n2.NetworkEndpoints())
|
||||||
|
return slices.Compare(ne1, ne2)
|
||||||
|
})
|
||||||
|
if ret != 0 {
|
||||||
|
s.candidates.Store(&newCandidates)
|
||||||
|
s.mergeCacheWithCandidates(newCandidates)
|
||||||
|
}
|
||||||
|
timer.Reset(d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *lruNetmapSource) mergeCacheWithCandidates(candidates []netmapSDK.NodeInfo) {
|
||||||
|
s.mtx.Lock()
|
||||||
|
tmp := s.cache.Values()
|
||||||
|
s.mtx.Unlock()
|
||||||
|
for _, pointer := range tmp {
|
||||||
|
nm := pointer.Load()
|
||||||
|
updates := getNetMapNodesToUpdate(nm, candidates)
|
||||||
|
if len(updates) > 0 {
|
||||||
|
nm = nm.Clone()
|
||||||
|
mergeNetmapWithCandidates(updates, nm)
|
||||||
|
pointer.Store(nm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reads value by the key.
|
||||||
|
//
|
||||||
|
// updates the value from the network on cache miss.
|
||||||
|
//
|
||||||
|
// returned value should not be modified.
|
||||||
|
func (s *lruNetmapSource) get(ctx context.Context, key uint64) (*netmapSDK.NetMap, error) {
|
||||||
|
hit := false
|
||||||
|
startedAt := time.Now()
|
||||||
|
defer func() {
|
||||||
|
s.metrics.AddMethodDuration("Get", time.Since(startedAt), hit)
|
||||||
|
}()
|
||||||
|
|
||||||
|
s.mtx.RLock()
|
||||||
|
val, ok := s.cache.Get(key)
|
||||||
|
s.mtx.RUnlock()
|
||||||
|
if ok {
|
||||||
|
hit = true
|
||||||
|
return val.Load(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
s.mtx.Lock()
|
||||||
|
defer s.mtx.Unlock()
|
||||||
|
|
||||||
|
val, ok = s.cache.Get(key)
|
||||||
|
if ok {
|
||||||
|
hit = true
|
||||||
|
return val.Load(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
nm, err := s.client.GetNetMapByEpoch(ctx, key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
v := s.candidates.Load()
|
||||||
|
if v != nil {
|
||||||
|
updates := getNetMapNodesToUpdate(nm, *v)
|
||||||
|
if len(updates) > 0 {
|
||||||
|
mergeNetmapWithCandidates(updates, nm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p := atomic.Pointer[netmapSDK.NetMap]{}
|
||||||
|
p.Store(nm)
|
||||||
|
s.cache.Add(key, &p)
|
||||||
|
|
||||||
|
return nm, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// mergeNetmapWithCandidates updates nodes state in the provided netmap with state in the list of candidates.
|
||||||
|
func mergeNetmapWithCandidates(updates []nodeToUpdate, nm *netmapSDK.NetMap) {
|
||||||
|
for _, v := range updates {
|
||||||
|
if v.status != netmapSDK.UnspecifiedState {
|
||||||
|
nm.Nodes()[v.netmapIndex].SetStatus(v.status)
|
||||||
|
}
|
||||||
|
if v.externalAddresses != nil {
|
||||||
|
nm.Nodes()[v.netmapIndex].SetExternalAddresses(v.externalAddresses...)
|
||||||
|
}
|
||||||
|
if v.endpoints != nil {
|
||||||
|
nm.Nodes()[v.netmapIndex].SetNetworkEndpoints(v.endpoints...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type nodeToUpdate struct {
|
||||||
|
netmapIndex int
|
||||||
|
status netmapSDK.NodeState
|
||||||
|
externalAddresses []string
|
||||||
|
endpoints []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// getNetMapNodesToUpdate checks for the changes between provided netmap and the list of candidates.
|
||||||
|
func getNetMapNodesToUpdate(nm *netmapSDK.NetMap, candidates []netmapSDK.NodeInfo) []nodeToUpdate {
|
||||||
|
var res []nodeToUpdate
|
||||||
|
for i := range nm.Nodes() {
|
||||||
|
for _, cnd := range candidates {
|
||||||
|
if bytes.Equal(nm.Nodes()[i].PublicKey(), cnd.PublicKey()) {
|
||||||
|
var tmp nodeToUpdate
|
||||||
|
var update bool
|
||||||
|
|
||||||
|
if cnd.Status() != nm.Nodes()[i].Status() &&
|
||||||
|
(cnd.Status() == netmapSDK.Online || cnd.Status() == netmapSDK.Maintenance) {
|
||||||
|
update = true
|
||||||
|
tmp.status = cnd.Status()
|
||||||
|
}
|
||||||
|
|
||||||
|
externalAddresses := cnd.ExternalAddresses()
|
||||||
|
if externalAddresses != nil &&
|
||||||
|
slices.Compare(externalAddresses, nm.Nodes()[i].ExternalAddresses()) != 0 {
|
||||||
|
update = true
|
||||||
|
tmp.externalAddresses = externalAddresses
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeEndpoints := make([]string, 0, nm.Nodes()[i].NumberOfNetworkEndpoints())
|
||||||
|
nodeEndpoints = slices.AppendSeq(nodeEndpoints, nm.Nodes()[i].NetworkEndpoints())
|
||||||
|
candidateEndpoints := make([]string, 0, cnd.NumberOfNetworkEndpoints())
|
||||||
|
candidateEndpoints = slices.AppendSeq(candidateEndpoints, cnd.NetworkEndpoints())
|
||||||
|
if slices.Compare(nodeEndpoints, candidateEndpoints) != 0 {
|
||||||
|
update = true
|
||||||
|
tmp.endpoints = candidateEndpoints
|
||||||
|
}
|
||||||
|
|
||||||
|
if update {
|
||||||
|
tmp.netmapIndex = i
|
||||||
|
res = append(res, tmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *lruNetmapSource) GetNetMap(ctx context.Context, diff uint64) (*netmapSDK.NetMap, error) {
|
func (s *lruNetmapSource) GetNetMap(ctx context.Context, diff uint64) (*netmapSDK.NetMap, error) {
|
||||||
|
@ -225,7 +385,7 @@ func (s *lruNetmapSource) GetNetMapByEpoch(ctx context.Context, epoch uint64) (*
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *lruNetmapSource) getNetMapByEpoch(ctx context.Context, epoch uint64) (*netmapSDK.NetMap, error) {
|
func (s *lruNetmapSource) getNetMapByEpoch(ctx context.Context, epoch uint64) (*netmapSDK.NetMap, error) {
|
||||||
val, err := s.cache.get(ctx, epoch)
|
val, err := s.get(ctx, epoch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,11 @@ package main
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
netmapSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -59,3 +61,75 @@ func testNetValueReader(_ context.Context, key string) (time.Time, error) {
|
||||||
type noopCacheMetricts struct{}
|
type noopCacheMetricts struct{}
|
||||||
|
|
||||||
func (m *noopCacheMetricts) AddMethodDuration(method string, d time.Duration, hit bool) {}
|
func (m *noopCacheMetricts) AddMethodDuration(method string, d time.Duration, hit bool) {}
|
||||||
|
|
||||||
|
type rawSrc struct{}
|
||||||
|
|
||||||
|
func (r *rawSrc) GetCandidates(_ context.Context) ([]netmapSDK.NodeInfo, error) {
|
||||||
|
node0 := netmapSDK.NodeInfo{}
|
||||||
|
node0.SetPublicKey([]byte{byte(1)})
|
||||||
|
node0.SetStatus(netmapSDK.Online)
|
||||||
|
node0.SetExternalAddresses("1", "0")
|
||||||
|
node0.SetNetworkEndpoints("1", "0")
|
||||||
|
|
||||||
|
node1 := netmapSDK.NodeInfo{}
|
||||||
|
node1.SetPublicKey([]byte{byte(1)})
|
||||||
|
node1.SetStatus(netmapSDK.Online)
|
||||||
|
node1.SetExternalAddresses("1", "0")
|
||||||
|
node1.SetNetworkEndpoints("1", "0")
|
||||||
|
|
||||||
|
return []netmapSDK.NodeInfo{node0, node1}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rawSrc) GetNetMapByEpoch(ctx context.Context, epoch uint64) (*netmapSDK.NetMap, error) {
|
||||||
|
nm := netmapSDK.NetMap{}
|
||||||
|
nm.SetEpoch(1)
|
||||||
|
|
||||||
|
node0 := netmapSDK.NodeInfo{}
|
||||||
|
node0.SetPublicKey([]byte{byte(1)})
|
||||||
|
node0.SetStatus(netmapSDK.Maintenance)
|
||||||
|
node0.SetExternalAddresses("0")
|
||||||
|
node0.SetNetworkEndpoints("0")
|
||||||
|
|
||||||
|
node1 := netmapSDK.NodeInfo{}
|
||||||
|
node1.SetPublicKey([]byte{byte(1)})
|
||||||
|
node1.SetStatus(netmapSDK.Maintenance)
|
||||||
|
node1.SetExternalAddresses("0")
|
||||||
|
node1.SetNetworkEndpoints("0")
|
||||||
|
|
||||||
|
nm.SetNodes([]netmapSDK.NodeInfo{node0, node1})
|
||||||
|
|
||||||
|
return &nm, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type st struct{}
|
||||||
|
|
||||||
|
func (s *st) CurrentEpoch() uint64 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNetmapStorage(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
cache := newCachedNetmapStorage(ctx, nil, &st{}, &rawSrc{}, &wg, time.Millisecond*50)
|
||||||
|
|
||||||
|
nm, err := cache.GetNetMapByEpoch(ctx, 1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, nm.Nodes()[0].Status() == netmapSDK.Maintenance)
|
||||||
|
require.True(t, len(nm.Nodes()[0].ExternalAddresses()) == 1)
|
||||||
|
require.True(t, nm.Nodes()[0].NumberOfNetworkEndpoints() == 1)
|
||||||
|
|
||||||
|
require.Eventually(t, func() bool {
|
||||||
|
nm, err := cache.GetNetMapByEpoch(ctx, 1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
for _, node := range nm.Nodes() {
|
||||||
|
if !(node.Status() == netmapSDK.Online && len(node.ExternalAddresses()) == 2 &&
|
||||||
|
node.NumberOfNetworkEndpoints() == 2) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}, time.Second*5, time.Millisecond*10)
|
||||||
|
|
||||||
|
cancel()
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ import (
|
||||||
objectconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/object"
|
objectconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/object"
|
||||||
replicatorconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/replicator"
|
replicatorconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/replicator"
|
||||||
tracingconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/tracing"
|
tracingconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/tracing"
|
||||||
|
treeconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/tree"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/metrics"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/metrics"
|
||||||
internalNet "git.frostfs.info/TrueCloudLab/frostfs-node/internal/net"
|
internalNet "git.frostfs.info/TrueCloudLab/frostfs-node/internal/net"
|
||||||
|
@ -40,6 +41,7 @@ import (
|
||||||
netmapCore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/netmap"
|
netmapCore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/netmap"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/blobovniczatree"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/blobovniczatree"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/compression"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/fstree"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/fstree"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/engine"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/engine"
|
||||||
meta "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/metabase"
|
meta "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/metabase"
|
||||||
|
@ -109,6 +111,7 @@ type applicationConfiguration struct {
|
||||||
destination string
|
destination string
|
||||||
timestamp bool
|
timestamp bool
|
||||||
options []zap.Option
|
options []zap.Option
|
||||||
|
tags [][]string
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectCfg struct {
|
ObjectCfg struct {
|
||||||
|
@ -127,12 +130,9 @@ type applicationConfiguration struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type shardCfg struct {
|
type shardCfg struct {
|
||||||
compress bool
|
compression compression.Config
|
||||||
estimateCompressibility bool
|
|
||||||
estimateCompressibilityThreshold float64
|
|
||||||
|
|
||||||
smallSizeObjectLimit uint64
|
smallSizeObjectLimit uint64
|
||||||
uncompressableContentType []string
|
|
||||||
refillMetabase bool
|
refillMetabase bool
|
||||||
refillMetabaseWorkersCount int
|
refillMetabaseWorkersCount int
|
||||||
mode shardmode.Mode
|
mode shardmode.Mode
|
||||||
|
@ -241,19 +241,21 @@ func (a *applicationConfiguration) readConfig(c *config.Config) error {
|
||||||
})}
|
})}
|
||||||
}
|
}
|
||||||
a.LoggerCfg.options = opts
|
a.LoggerCfg.options = opts
|
||||||
|
a.LoggerCfg.tags = loggerconfig.Tags(c)
|
||||||
|
|
||||||
// Object
|
// Object
|
||||||
|
|
||||||
a.ObjectCfg.tombstoneLifetime = objectconfig.TombstoneLifetime(c)
|
a.ObjectCfg.tombstoneLifetime = objectconfig.TombstoneLifetime(c)
|
||||||
var pm []placement.Metric
|
locodeDBPath := nodeconfig.LocodeDBPath(c)
|
||||||
for _, raw := range objectconfig.Get(c).Priority() {
|
parser, err := placement.NewMetricsParser(locodeDBPath)
|
||||||
m, err := placement.ParseMetric(raw)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("metrics parser creation: %w", err)
|
||||||
}
|
}
|
||||||
pm = append(pm, m)
|
m, err := parser.ParseMetrics(objectconfig.Get(c).Priority())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("parse metrics: %w", err)
|
||||||
}
|
}
|
||||||
a.ObjectCfg.priorityMetrics = pm
|
a.ObjectCfg.priorityMetrics = m
|
||||||
|
|
||||||
// Storage Engine
|
// Storage Engine
|
||||||
|
|
||||||
|
@ -269,10 +271,7 @@ func (a *applicationConfiguration) updateShardConfig(c *config.Config, source *s
|
||||||
target.refillMetabase = source.RefillMetabase()
|
target.refillMetabase = source.RefillMetabase()
|
||||||
target.refillMetabaseWorkersCount = source.RefillMetabaseWorkersCount()
|
target.refillMetabaseWorkersCount = source.RefillMetabaseWorkersCount()
|
||||||
target.mode = source.Mode()
|
target.mode = source.Mode()
|
||||||
target.compress = source.Compress()
|
target.compression = source.Compression()
|
||||||
target.estimateCompressibility = source.EstimateCompressibility()
|
|
||||||
target.estimateCompressibilityThreshold = source.EstimateCompressibilityThreshold()
|
|
||||||
target.uncompressableContentType = source.UncompressableContentTypes()
|
|
||||||
target.smallSizeObjectLimit = source.SmallSizeLimit()
|
target.smallSizeObjectLimit = source.SmallSizeLimit()
|
||||||
|
|
||||||
a.setShardWriteCacheConfig(&target, source)
|
a.setShardWriteCacheConfig(&target, source)
|
||||||
|
@ -383,14 +382,11 @@ func (a *applicationConfiguration) setGCConfig(target *shardCfg, source *shardco
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *applicationConfiguration) setLimiter(target *shardCfg, source *shardconfig.Config) error {
|
func (a *applicationConfiguration) setLimiter(target *shardCfg, source *shardconfig.Config) error {
|
||||||
limitsConfig := source.Limits()
|
limitsConfig := source.Limits().ToConfig()
|
||||||
limiter, err := qos.NewLimiter(limitsConfig)
|
limiter, err := qos.NewLimiter(limitsConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if target.limiter != nil {
|
|
||||||
target.limiter.Close()
|
|
||||||
}
|
|
||||||
target.limiter = limiter
|
target.limiter = limiter
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -727,6 +723,7 @@ func initCfg(appCfg *config.Config) *cfg {
|
||||||
logPrm.SamplingHook = c.metricsCollector.LogMetrics().GetSamplingHook()
|
logPrm.SamplingHook = c.metricsCollector.LogMetrics().GetSamplingHook()
|
||||||
log, err := logger.NewLogger(logPrm)
|
log, err := logger.NewLogger(logPrm)
|
||||||
fatalOnErr(err)
|
fatalOnErr(err)
|
||||||
|
logger.UpdateLevelForTags(logPrm)
|
||||||
|
|
||||||
c.internals = initInternals(appCfg, log)
|
c.internals = initInternals(appCfg, log)
|
||||||
|
|
||||||
|
@ -895,7 +892,7 @@ func (c *cfg) engineOpts() []engine.Option {
|
||||||
|
|
||||||
opts = append(opts,
|
opts = append(opts,
|
||||||
engine.WithErrorThreshold(c.EngineCfg.errorThreshold),
|
engine.WithErrorThreshold(c.EngineCfg.errorThreshold),
|
||||||
engine.WithLogger(c.log),
|
engine.WithLogger(c.log.WithTag(logger.TagEngine)),
|
||||||
engine.WithLowMemoryConsumption(c.EngineCfg.lowMem),
|
engine.WithLowMemoryConsumption(c.EngineCfg.lowMem),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -932,7 +929,7 @@ func (c *cfg) getWriteCacheOpts(shCfg shardCfg) []writecache.Option {
|
||||||
writecache.WithMaxCacheSize(wcRead.sizeLimit),
|
writecache.WithMaxCacheSize(wcRead.sizeLimit),
|
||||||
writecache.WithMaxCacheCount(wcRead.countLimit),
|
writecache.WithMaxCacheCount(wcRead.countLimit),
|
||||||
writecache.WithNoSync(wcRead.noSync),
|
writecache.WithNoSync(wcRead.noSync),
|
||||||
writecache.WithLogger(c.log),
|
writecache.WithLogger(c.log.WithTag(logger.TagWriteCache)),
|
||||||
writecache.WithQoSLimiter(shCfg.limiter),
|
writecache.WithQoSLimiter(shCfg.limiter),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -972,7 +969,8 @@ func (c *cfg) getSubstorageOpts(ctx context.Context, shCfg shardCfg) []blobstor.
|
||||||
blobovniczatree.WithOpenedCacheExpInterval(sRead.openedCacheExpInterval),
|
blobovniczatree.WithOpenedCacheExpInterval(sRead.openedCacheExpInterval),
|
||||||
blobovniczatree.WithInitWorkerCount(sRead.initWorkerCount),
|
blobovniczatree.WithInitWorkerCount(sRead.initWorkerCount),
|
||||||
blobovniczatree.WithWaitBeforeDropDB(sRead.rebuildDropTimeout),
|
blobovniczatree.WithWaitBeforeDropDB(sRead.rebuildDropTimeout),
|
||||||
blobovniczatree.WithLogger(c.log),
|
blobovniczatree.WithBlobovniczaLogger(c.log.WithTag(logger.TagBlobovnicza)),
|
||||||
|
blobovniczatree.WithBlobovniczaTreeLogger(c.log.WithTag(logger.TagBlobovniczaTree)),
|
||||||
blobovniczatree.WithObjectSizeLimit(shCfg.smallSizeObjectLimit),
|
blobovniczatree.WithObjectSizeLimit(shCfg.smallSizeObjectLimit),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -995,7 +993,7 @@ func (c *cfg) getSubstorageOpts(ctx context.Context, shCfg shardCfg) []blobstor.
|
||||||
fstree.WithPerm(sRead.perm),
|
fstree.WithPerm(sRead.perm),
|
||||||
fstree.WithDepth(sRead.depth),
|
fstree.WithDepth(sRead.depth),
|
||||||
fstree.WithNoSync(sRead.noSync),
|
fstree.WithNoSync(sRead.noSync),
|
||||||
fstree.WithLogger(c.log),
|
fstree.WithLogger(c.log.WithTag(logger.TagFSTree)),
|
||||||
}
|
}
|
||||||
if c.metricsCollector != nil {
|
if c.metricsCollector != nil {
|
||||||
fstreeOpts = append(fstreeOpts,
|
fstreeOpts = append(fstreeOpts,
|
||||||
|
@ -1025,12 +1023,9 @@ func (c *cfg) getShardOpts(ctx context.Context, shCfg shardCfg) shardOptsWithID
|
||||||
ss := c.getSubstorageOpts(ctx, shCfg)
|
ss := c.getSubstorageOpts(ctx, shCfg)
|
||||||
|
|
||||||
blobstoreOpts := []blobstor.Option{
|
blobstoreOpts := []blobstor.Option{
|
||||||
blobstor.WithCompressObjects(shCfg.compress),
|
blobstor.WithCompression(shCfg.compression),
|
||||||
blobstor.WithUncompressableContentTypes(shCfg.uncompressableContentType),
|
|
||||||
blobstor.WithCompressibilityEstimate(shCfg.estimateCompressibility),
|
|
||||||
blobstor.WithCompressibilityEstimateThreshold(shCfg.estimateCompressibilityThreshold),
|
|
||||||
blobstor.WithStorages(ss),
|
blobstor.WithStorages(ss),
|
||||||
blobstor.WithLogger(c.log),
|
blobstor.WithLogger(c.log.WithTag(logger.TagBlobstor)),
|
||||||
}
|
}
|
||||||
if c.metricsCollector != nil {
|
if c.metricsCollector != nil {
|
||||||
blobstoreOpts = append(blobstoreOpts, blobstor.WithMetrics(lsmetrics.NewBlobstoreMetrics(c.metricsCollector.Blobstore())))
|
blobstoreOpts = append(blobstoreOpts, blobstor.WithMetrics(lsmetrics.NewBlobstoreMetrics(c.metricsCollector.Blobstore())))
|
||||||
|
@ -1055,7 +1050,7 @@ func (c *cfg) getShardOpts(ctx context.Context, shCfg shardCfg) shardOptsWithID
|
||||||
var sh shardOptsWithID
|
var sh shardOptsWithID
|
||||||
sh.configID = shCfg.id()
|
sh.configID = shCfg.id()
|
||||||
sh.shOpts = []shard.Option{
|
sh.shOpts = []shard.Option{
|
||||||
shard.WithLogger(c.log),
|
shard.WithLogger(c.log.WithTag(logger.TagShard)),
|
||||||
shard.WithRefillMetabase(shCfg.refillMetabase),
|
shard.WithRefillMetabase(shCfg.refillMetabase),
|
||||||
shard.WithRefillMetabaseWorkersCount(shCfg.refillMetabaseWorkersCount),
|
shard.WithRefillMetabaseWorkersCount(shCfg.refillMetabaseWorkersCount),
|
||||||
shard.WithMode(shCfg.mode),
|
shard.WithMode(shCfg.mode),
|
||||||
|
@ -1094,6 +1089,11 @@ func (c *cfg) loggerPrm() (logger.Prm, error) {
|
||||||
}
|
}
|
||||||
prm.PrependTimestamp = c.LoggerCfg.timestamp
|
prm.PrependTimestamp = c.LoggerCfg.timestamp
|
||||||
prm.Options = c.LoggerCfg.options
|
prm.Options = c.LoggerCfg.options
|
||||||
|
err = prm.SetTags(c.LoggerCfg.tags)
|
||||||
|
if err != nil {
|
||||||
|
// not expected since validation should be performed before
|
||||||
|
return logger.Prm{}, errors.New("incorrect allowed tags format: " + c.LoggerCfg.destination)
|
||||||
|
}
|
||||||
|
|
||||||
return prm, nil
|
return prm, nil
|
||||||
}
|
}
|
||||||
|
@ -1381,7 +1381,7 @@ func (c *cfg) getComponents(ctx context.Context) []dCmp {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.log.Reload(prm)
|
logger.UpdateLevelForTags(prm)
|
||||||
return nil
|
return nil
|
||||||
}})
|
}})
|
||||||
components = append(components, dCmp{"runtime", func() error {
|
components = append(components, dCmp{"runtime", func() error {
|
||||||
|
@ -1404,6 +1404,12 @@ func (c *cfg) getComponents(ctx context.Context) []dCmp {
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}})
|
}})
|
||||||
|
if c.treeService != nil {
|
||||||
|
components = append(components, dCmp{"tree", func() error {
|
||||||
|
c.treeService.ReloadAuthorizedKeys(treeconfig.Tree(c.appCfg).AuthorizedKeys())
|
||||||
|
return nil
|
||||||
|
}})
|
||||||
|
}
|
||||||
if cmp, updated := metricsComponent(c); updated {
|
if cmp, updated := metricsComponent(c); updated {
|
||||||
if cmp.enabled {
|
if cmp.enabled {
|
||||||
cmp.preReload = enableMetricsSvc
|
cmp.preReload = enableMetricsSvc
|
||||||
|
|
|
@ -11,10 +11,11 @@ import (
|
||||||
blobovniczaconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/engine/shard/blobstor/blobovnicza"
|
blobovniczaconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/engine/shard/blobstor/blobovnicza"
|
||||||
fstreeconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/engine/shard/blobstor/fstree"
|
fstreeconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/engine/shard/blobstor/fstree"
|
||||||
gcconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/engine/shard/gc"
|
gcconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/engine/shard/gc"
|
||||||
limitsconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/engine/shard/limits"
|
|
||||||
piloramaconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/engine/shard/pilorama"
|
piloramaconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/engine/shard/pilorama"
|
||||||
writecacheconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/engine/shard/writecache"
|
writecacheconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/engine/shard/writecache"
|
||||||
configtest "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/test"
|
configtest "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/test"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/qos"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/compression"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -100,10 +101,11 @@ func TestEngineSection(t *testing.T) {
|
||||||
require.Equal(t, 100, meta.BoltDB().MaxBatchSize())
|
require.Equal(t, 100, meta.BoltDB().MaxBatchSize())
|
||||||
require.Equal(t, 10*time.Millisecond, meta.BoltDB().MaxBatchDelay())
|
require.Equal(t, 10*time.Millisecond, meta.BoltDB().MaxBatchDelay())
|
||||||
|
|
||||||
require.Equal(t, true, sc.Compress())
|
require.Equal(t, true, sc.Compression().Enabled)
|
||||||
require.Equal(t, []string{"audio/*", "video/*"}, sc.UncompressableContentTypes())
|
require.Equal(t, compression.LevelFastest, sc.Compression().Level)
|
||||||
require.Equal(t, true, sc.EstimateCompressibility())
|
require.Equal(t, []string{"audio/*", "video/*"}, sc.Compression().UncompressableContentTypes)
|
||||||
require.Equal(t, float64(0.7), sc.EstimateCompressibilityThreshold())
|
require.Equal(t, true, sc.Compression().EstimateCompressibility)
|
||||||
|
require.Equal(t, float64(0.7), sc.Compression().EstimateCompressibilityThreshold)
|
||||||
require.EqualValues(t, 102400, sc.SmallSizeLimit())
|
require.EqualValues(t, 102400, sc.SmallSizeLimit())
|
||||||
|
|
||||||
require.Equal(t, 2, len(ss))
|
require.Equal(t, 2, len(ss))
|
||||||
|
@ -135,8 +137,8 @@ func TestEngineSection(t *testing.T) {
|
||||||
require.Equal(t, mode.ReadOnly, sc.Mode())
|
require.Equal(t, mode.ReadOnly, sc.Mode())
|
||||||
require.Equal(t, 100, sc.RefillMetabaseWorkersCount())
|
require.Equal(t, 100, sc.RefillMetabaseWorkersCount())
|
||||||
|
|
||||||
readLimits := limits.Read()
|
readLimits := limits.ToConfig().Read
|
||||||
writeLimits := limits.Write()
|
writeLimits := limits.ToConfig().Write
|
||||||
require.Equal(t, 30*time.Second, readLimits.IdleTimeout)
|
require.Equal(t, 30*time.Second, readLimits.IdleTimeout)
|
||||||
require.Equal(t, int64(10_000), readLimits.MaxRunningOps)
|
require.Equal(t, int64(10_000), readLimits.MaxRunningOps)
|
||||||
require.Equal(t, int64(1_000), readLimits.MaxWaitingOps)
|
require.Equal(t, int64(1_000), readLimits.MaxWaitingOps)
|
||||||
|
@ -144,7 +146,7 @@ func TestEngineSection(t *testing.T) {
|
||||||
require.Equal(t, int64(1_000), writeLimits.MaxRunningOps)
|
require.Equal(t, int64(1_000), writeLimits.MaxRunningOps)
|
||||||
require.Equal(t, int64(100), writeLimits.MaxWaitingOps)
|
require.Equal(t, int64(100), writeLimits.MaxWaitingOps)
|
||||||
require.ElementsMatch(t, readLimits.Tags,
|
require.ElementsMatch(t, readLimits.Tags,
|
||||||
[]limitsconfig.IOTagConfig{
|
[]qos.IOTagConfig{
|
||||||
{
|
{
|
||||||
Tag: "internal",
|
Tag: "internal",
|
||||||
Weight: toPtr(20),
|
Weight: toPtr(20),
|
||||||
|
@ -173,9 +175,14 @@ func TestEngineSection(t *testing.T) {
|
||||||
LimitOps: toPtr(25000),
|
LimitOps: toPtr(25000),
|
||||||
Prohibited: true,
|
Prohibited: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Tag: "treesync",
|
||||||
|
Weight: toPtr(5),
|
||||||
|
LimitOps: toPtr(25),
|
||||||
|
},
|
||||||
})
|
})
|
||||||
require.ElementsMatch(t, writeLimits.Tags,
|
require.ElementsMatch(t, writeLimits.Tags,
|
||||||
[]limitsconfig.IOTagConfig{
|
[]qos.IOTagConfig{
|
||||||
{
|
{
|
||||||
Tag: "internal",
|
Tag: "internal",
|
||||||
Weight: toPtr(200),
|
Weight: toPtr(200),
|
||||||
|
@ -203,6 +210,11 @@ func TestEngineSection(t *testing.T) {
|
||||||
Weight: toPtr(50),
|
Weight: toPtr(50),
|
||||||
LimitOps: toPtr(2500),
|
LimitOps: toPtr(2500),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Tag: "treesync",
|
||||||
|
Weight: toPtr(50),
|
||||||
|
LimitOps: toPtr(100),
|
||||||
|
},
|
||||||
})
|
})
|
||||||
case 1:
|
case 1:
|
||||||
require.Equal(t, "tmp/1/blob/pilorama.db", pl.Path())
|
require.Equal(t, "tmp/1/blob/pilorama.db", pl.Path())
|
||||||
|
@ -226,8 +238,9 @@ func TestEngineSection(t *testing.T) {
|
||||||
require.Equal(t, 200, meta.BoltDB().MaxBatchSize())
|
require.Equal(t, 200, meta.BoltDB().MaxBatchSize())
|
||||||
require.Equal(t, 20*time.Millisecond, meta.BoltDB().MaxBatchDelay())
|
require.Equal(t, 20*time.Millisecond, meta.BoltDB().MaxBatchDelay())
|
||||||
|
|
||||||
require.Equal(t, false, sc.Compress())
|
require.Equal(t, false, sc.Compression().Enabled)
|
||||||
require.Equal(t, []string(nil), sc.UncompressableContentTypes())
|
require.Equal(t, compression.LevelDefault, sc.Compression().Level)
|
||||||
|
require.Equal(t, []string(nil), sc.Compression().UncompressableContentTypes)
|
||||||
require.EqualValues(t, 102400, sc.SmallSizeLimit())
|
require.EqualValues(t, 102400, sc.SmallSizeLimit())
|
||||||
|
|
||||||
require.Equal(t, 2, len(ss))
|
require.Equal(t, 2, len(ss))
|
||||||
|
@ -259,14 +272,14 @@ func TestEngineSection(t *testing.T) {
|
||||||
require.Equal(t, mode.ReadWrite, sc.Mode())
|
require.Equal(t, mode.ReadWrite, sc.Mode())
|
||||||
require.Equal(t, shardconfig.RefillMetabaseWorkersCountDefault, sc.RefillMetabaseWorkersCount())
|
require.Equal(t, shardconfig.RefillMetabaseWorkersCountDefault, sc.RefillMetabaseWorkersCount())
|
||||||
|
|
||||||
readLimits := limits.Read()
|
readLimits := limits.ToConfig().Read
|
||||||
writeLimits := limits.Write()
|
writeLimits := limits.ToConfig().Write
|
||||||
require.Equal(t, limitsconfig.DefaultIdleTimeout, readLimits.IdleTimeout)
|
require.Equal(t, qos.DefaultIdleTimeout, readLimits.IdleTimeout)
|
||||||
require.Equal(t, limitsconfig.NoLimit, readLimits.MaxRunningOps)
|
require.Equal(t, qos.NoLimit, readLimits.MaxRunningOps)
|
||||||
require.Equal(t, limitsconfig.NoLimit, readLimits.MaxWaitingOps)
|
require.Equal(t, qos.NoLimit, readLimits.MaxWaitingOps)
|
||||||
require.Equal(t, limitsconfig.DefaultIdleTimeout, writeLimits.IdleTimeout)
|
require.Equal(t, qos.DefaultIdleTimeout, writeLimits.IdleTimeout)
|
||||||
require.Equal(t, limitsconfig.NoLimit, writeLimits.MaxRunningOps)
|
require.Equal(t, qos.NoLimit, writeLimits.MaxRunningOps)
|
||||||
require.Equal(t, limitsconfig.NoLimit, writeLimits.MaxWaitingOps)
|
require.Equal(t, qos.NoLimit, writeLimits.MaxWaitingOps)
|
||||||
require.Equal(t, 0, len(readLimits.Tags))
|
require.Equal(t, 0, len(readLimits.Tags))
|
||||||
require.Equal(t, 0, len(writeLimits.Tags))
|
require.Equal(t, 0, len(writeLimits.Tags))
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
metabaseconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/engine/shard/metabase"
|
metabaseconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/engine/shard/metabase"
|
||||||
piloramaconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/engine/shard/pilorama"
|
piloramaconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/engine/shard/pilorama"
|
||||||
writecacheconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/engine/shard/writecache"
|
writecacheconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/engine/shard/writecache"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/compression"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -27,42 +28,27 @@ func From(c *config.Config) *Config {
|
||||||
return (*Config)(c)
|
return (*Config)(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compress returns the value of "compress" config parameter.
|
func (x *Config) Compression() compression.Config {
|
||||||
//
|
cc := (*config.Config)(x).Sub("compression")
|
||||||
// Returns false if the value is not a valid bool.
|
if cc == nil {
|
||||||
func (x *Config) Compress() bool {
|
return compression.Config{}
|
||||||
return config.BoolSafe(
|
|
||||||
(*config.Config)(x),
|
|
||||||
"compress",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
return compression.Config{
|
||||||
// UncompressableContentTypes returns the value of "compress_skip_content_types" config parameter.
|
Enabled: config.BoolSafe(cc, "enabled"),
|
||||||
//
|
UncompressableContentTypes: config.StringSliceSafe(cc, "exclude_content_types"),
|
||||||
// Returns nil if a the value is missing or is invalid.
|
Level: compression.Level(config.StringSafe(cc, "level")),
|
||||||
func (x *Config) UncompressableContentTypes() []string {
|
EstimateCompressibility: config.BoolSafe(cc, "estimate_compressibility"),
|
||||||
return config.StringSliceSafe(
|
EstimateCompressibilityThreshold: estimateCompressibilityThreshold(cc),
|
||||||
(*config.Config)(x),
|
|
||||||
"compression_exclude_content_types")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EstimateCompressibility returns the value of "estimate_compressibility" config parameter.
|
|
||||||
//
|
|
||||||
// Returns false if the value is not a valid bool.
|
|
||||||
func (x *Config) EstimateCompressibility() bool {
|
|
||||||
return config.BoolSafe(
|
|
||||||
(*config.Config)(x),
|
|
||||||
"compression_estimate_compressibility",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EstimateCompressibilityThreshold returns the value of "estimate_compressibility_threshold" config parameter.
|
// EstimateCompressibilityThreshold returns the value of "estimate_compressibility_threshold" config parameter.
|
||||||
//
|
//
|
||||||
// Returns EstimateCompressibilityThresholdDefault if the value is not defined, not valid float or not in range [0.0; 1.0].
|
// Returns EstimateCompressibilityThresholdDefault if the value is not defined, not valid float or not in range [0.0; 1.0].
|
||||||
func (x *Config) EstimateCompressibilityThreshold() float64 {
|
func estimateCompressibilityThreshold(c *config.Config) float64 {
|
||||||
v := config.FloatOrDefault(
|
v := config.FloatOrDefault(
|
||||||
(*config.Config)(x),
|
c,
|
||||||
"compression_estimate_compressibility_threshold",
|
"estimate_compressibility_threshold",
|
||||||
EstimateCompressibilityThresholdDefault)
|
EstimateCompressibilityThresholdDefault)
|
||||||
if v < 0.0 || v > 1.0 {
|
if v < 0.0 || v > 1.0 {
|
||||||
return EstimateCompressibilityThresholdDefault
|
return EstimateCompressibilityThresholdDefault
|
||||||
|
|
|
@ -1,19 +1,13 @@
|
||||||
package limits
|
package limits
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/qos"
|
||||||
"github.com/spf13/cast"
|
"github.com/spf13/cast"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
NoLimit int64 = math.MaxInt64
|
|
||||||
DefaultIdleTimeout = 5 * time.Minute
|
|
||||||
)
|
|
||||||
|
|
||||||
// From wraps config section into Config.
|
// From wraps config section into Config.
|
||||||
func From(c *config.Config) *Config {
|
func From(c *config.Config) *Config {
|
||||||
return (*Config)(c)
|
return (*Config)(c)
|
||||||
|
@ -23,36 +17,43 @@ func From(c *config.Config) *Config {
|
||||||
// which provides access to Shard's limits configurations.
|
// which provides access to Shard's limits configurations.
|
||||||
type Config config.Config
|
type Config config.Config
|
||||||
|
|
||||||
// Read returns the value of "read" limits config section.
|
func (x *Config) ToConfig() qos.LimiterConfig {
|
||||||
func (x *Config) Read() OpConfig {
|
result := qos.LimiterConfig{
|
||||||
|
Read: x.read(),
|
||||||
|
Write: x.write(),
|
||||||
|
}
|
||||||
|
panicOnErr(result.Validate())
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) read() qos.OpConfig {
|
||||||
return x.parse("read")
|
return x.parse("read")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write returns the value of "write" limits config section.
|
func (x *Config) write() qos.OpConfig {
|
||||||
func (x *Config) Write() OpConfig {
|
|
||||||
return x.parse("write")
|
return x.parse("write")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) parse(sub string) OpConfig {
|
func (x *Config) parse(sub string) qos.OpConfig {
|
||||||
c := (*config.Config)(x).Sub(sub)
|
c := (*config.Config)(x).Sub(sub)
|
||||||
var result OpConfig
|
var result qos.OpConfig
|
||||||
|
|
||||||
if s := config.Int(c, "max_waiting_ops"); s > 0 {
|
if s := config.Int(c, "max_waiting_ops"); s > 0 {
|
||||||
result.MaxWaitingOps = s
|
result.MaxWaitingOps = s
|
||||||
} else {
|
} else {
|
||||||
result.MaxWaitingOps = NoLimit
|
result.MaxWaitingOps = qos.NoLimit
|
||||||
}
|
}
|
||||||
|
|
||||||
if s := config.Int(c, "max_running_ops"); s > 0 {
|
if s := config.Int(c, "max_running_ops"); s > 0 {
|
||||||
result.MaxRunningOps = s
|
result.MaxRunningOps = s
|
||||||
} else {
|
} else {
|
||||||
result.MaxRunningOps = NoLimit
|
result.MaxRunningOps = qos.NoLimit
|
||||||
}
|
}
|
||||||
|
|
||||||
if s := config.DurationSafe(c, "idle_timeout"); s > 0 {
|
if s := config.DurationSafe(c, "idle_timeout"); s > 0 {
|
||||||
result.IdleTimeout = s
|
result.IdleTimeout = s
|
||||||
} else {
|
} else {
|
||||||
result.IdleTimeout = DefaultIdleTimeout
|
result.IdleTimeout = qos.DefaultIdleTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
result.Tags = tags(c)
|
result.Tags = tags(c)
|
||||||
|
@ -60,43 +61,16 @@ func (x *Config) parse(sub string) OpConfig {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
type OpConfig struct {
|
func tags(c *config.Config) []qos.IOTagConfig {
|
||||||
// MaxWaitingOps returns the value of "max_waiting_ops" config parameter.
|
|
||||||
//
|
|
||||||
// Equals NoLimit if the value is not a positive number.
|
|
||||||
MaxWaitingOps int64
|
|
||||||
// MaxRunningOps returns the value of "max_running_ops" config parameter.
|
|
||||||
//
|
|
||||||
// Equals NoLimit if the value is not a positive number.
|
|
||||||
MaxRunningOps int64
|
|
||||||
// IdleTimeout returns the value of "idle_timeout" config parameter.
|
|
||||||
//
|
|
||||||
// Equals DefaultIdleTimeout if the value is not a valid duration.
|
|
||||||
IdleTimeout time.Duration
|
|
||||||
// Tags returns the value of "tags" config parameter.
|
|
||||||
//
|
|
||||||
// Equals nil if the value is not a valid tags config slice.
|
|
||||||
Tags []IOTagConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
type IOTagConfig struct {
|
|
||||||
Tag string
|
|
||||||
Weight *float64
|
|
||||||
LimitOps *float64
|
|
||||||
ReservedOps *float64
|
|
||||||
Prohibited bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func tags(c *config.Config) []IOTagConfig {
|
|
||||||
c = c.Sub("tags")
|
c = c.Sub("tags")
|
||||||
var result []IOTagConfig
|
var result []qos.IOTagConfig
|
||||||
for i := 0; ; i++ {
|
for i := 0; ; i++ {
|
||||||
tag := config.String(c, strconv.Itoa(i)+".tag")
|
tag := config.String(c, strconv.Itoa(i)+".tag")
|
||||||
if tag == "" {
|
if tag == "" {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
var tagConfig IOTagConfig
|
var tagConfig qos.IOTagConfig
|
||||||
tagConfig.Tag = tag
|
tagConfig.Tag = tag
|
||||||
|
|
||||||
v := c.Value(strconv.Itoa(i) + ".weight")
|
v := c.Value(strconv.Itoa(i) + ".weight")
|
||||||
|
|
|
@ -2,6 +2,7 @@ package loggerconfig
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config"
|
||||||
|
@ -60,6 +61,21 @@ func Timestamp(c *config.Config) bool {
|
||||||
return config.BoolSafe(c.Sub(subsection), "timestamp")
|
return config.BoolSafe(c.Sub(subsection), "timestamp")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tags returns the value of "tags" config parameter from "logger" section.
|
||||||
|
func Tags(c *config.Config) [][]string {
|
||||||
|
var res [][]string
|
||||||
|
sub := c.Sub(subsection).Sub("tags")
|
||||||
|
for i := 0; ; i++ {
|
||||||
|
s := sub.Sub(strconv.FormatInt(int64(i), 10))
|
||||||
|
names := config.StringSafe(s, "names")
|
||||||
|
if names == "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
res = append(res, []string{names, config.StringSafe(s, "level")})
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
// ToLokiConfig extracts loki config.
|
// ToLokiConfig extracts loki config.
|
||||||
func ToLokiConfig(c *config.Config) loki.Config {
|
func ToLokiConfig(c *config.Config) loki.Config {
|
||||||
hostname, _ := os.Hostname()
|
hostname, _ := os.Hostname()
|
||||||
|
|
|
@ -22,6 +22,9 @@ func TestLoggerSection_Level(t *testing.T) {
|
||||||
require.Equal(t, "debug", loggerconfig.Level(c))
|
require.Equal(t, "debug", loggerconfig.Level(c))
|
||||||
require.Equal(t, "journald", loggerconfig.Destination(c))
|
require.Equal(t, "journald", loggerconfig.Destination(c))
|
||||||
require.Equal(t, true, loggerconfig.Timestamp(c))
|
require.Equal(t, true, loggerconfig.Timestamp(c))
|
||||||
|
tags := loggerconfig.Tags(c)
|
||||||
|
require.Equal(t, "main, morph", tags[0][0])
|
||||||
|
require.Equal(t, "debug", tags[0][1])
|
||||||
}
|
}
|
||||||
|
|
||||||
configtest.ForEachFileType(path, fileConfigTest)
|
configtest.ForEachFileType(path, fileConfigTest)
|
||||||
|
|
|
@ -33,6 +33,9 @@ const (
|
||||||
|
|
||||||
// ContainerCacheSizeDefault represents the default size for the container cache.
|
// ContainerCacheSizeDefault represents the default size for the container cache.
|
||||||
ContainerCacheSizeDefault = 100
|
ContainerCacheSizeDefault = 100
|
||||||
|
|
||||||
|
// PollCandidatesTimeoutDefault is a default poll timeout for netmap candidates.
|
||||||
|
PollCandidatesTimeoutDefault = 20 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
var errNoMorphEndpoints = errors.New("no morph chain RPC endpoints, see `morph.rpc_endpoint` section")
|
var errNoMorphEndpoints = errors.New("no morph chain RPC endpoints, see `morph.rpc_endpoint` section")
|
||||||
|
@ -154,3 +157,17 @@ func FrostfsIDCacheSize(c *config.Config) uint32 {
|
||||||
}
|
}
|
||||||
return config.Uint32Safe(c.Sub(subsection), "frostfsid_cache_size")
|
return config.Uint32Safe(c.Sub(subsection), "frostfsid_cache_size")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NetmapCandidatesPollInterval returns the value of "netmap.candidates.poll_interval" config parameter
|
||||||
|
// from "morph" section.
|
||||||
|
//
|
||||||
|
// Returns PollCandidatesTimeoutDefault if the value is not positive duration.
|
||||||
|
func NetmapCandidatesPollInterval(c *config.Config) time.Duration {
|
||||||
|
v := config.DurationSafe(c.Sub(subsection).
|
||||||
|
Sub("netmap").Sub("candidates"), "poll_interval")
|
||||||
|
if v > 0 {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
return PollCandidatesTimeoutDefault
|
||||||
|
}
|
||||||
|
|
|
@ -3,7 +3,9 @@ package nodeconfig
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
"iter"
|
||||||
"os"
|
"os"
|
||||||
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config"
|
||||||
|
@ -88,12 +90,8 @@ func Wallet(c *config.Config) *keys.PrivateKey {
|
||||||
|
|
||||||
type stringAddressGroup []string
|
type stringAddressGroup []string
|
||||||
|
|
||||||
func (x stringAddressGroup) IterateAddresses(f func(string) bool) {
|
func (x stringAddressGroup) Addresses() iter.Seq[string] {
|
||||||
for i := range x {
|
return slices.Values(x)
|
||||||
if f(x[i]) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x stringAddressGroup) NumberOfAddresses() int {
|
func (x stringAddressGroup) NumberOfAddresses() int {
|
||||||
|
@ -217,3 +215,8 @@ func (l PersistentPolicyRulesConfig) NoSync() bool {
|
||||||
func CompatibilityMode(c *config.Config) bool {
|
func CompatibilityMode(c *config.Config) bool {
|
||||||
return config.BoolSafe(c.Sub(subsection), "kludge_compatibility_mode")
|
return config.BoolSafe(c.Sub(subsection), "kludge_compatibility_mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LocodeDBPath returns path to LOCODE database.
|
||||||
|
func LocodeDBPath(c *config.Config) string {
|
||||||
|
return config.String(c.Sub(subsection), "locode_db_path")
|
||||||
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ func initContainerService(_ context.Context, c *cfg) {
|
||||||
wrap, err := cntClient.NewFromMorph(c.cfgMorph.client, c.cfgContainer.scriptHash, 0)
|
wrap, err := cntClient.NewFromMorph(c.cfgMorph.client, c.cfgContainer.scriptHash, 0)
|
||||||
fatalOnErr(err)
|
fatalOnErr(err)
|
||||||
|
|
||||||
c.shared.cnrClient = wrap
|
c.cnrClient = wrap
|
||||||
|
|
||||||
cnrSrc := cntClient.AsContainerSource(wrap)
|
cnrSrc := cntClient.AsContainerSource(wrap)
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ func initContainerService(_ context.Context, c *cfg) {
|
||||||
frostfsIDSubjectProvider = newMorphFrostfsIDCache(frostfsIDSubjectProvider, int(cacheSize), c.cfgMorph.cacheTTL, metrics.NewCacheMetrics("frostfs_id"))
|
frostfsIDSubjectProvider = newMorphFrostfsIDCache(frostfsIDSubjectProvider, int(cacheSize), c.cfgMorph.cacheTTL, metrics.NewCacheMetrics("frostfs_id"))
|
||||||
}
|
}
|
||||||
|
|
||||||
c.shared.frostfsidClient = frostfsIDSubjectProvider
|
c.frostfsidClient = frostfsIDSubjectProvider
|
||||||
c.cfgContainer.containerBatchSize = containerconfig.ContainerBatchSize(c.appCfg)
|
c.cfgContainer.containerBatchSize = containerconfig.ContainerBatchSize(c.appCfg)
|
||||||
|
|
||||||
defaultChainRouter := engine.NewDefaultChainRouterWithLocalOverrides(
|
defaultChainRouter := engine.NewDefaultChainRouterWithLocalOverrides(
|
||||||
|
@ -57,7 +57,7 @@ func initContainerService(_ context.Context, c *cfg) {
|
||||||
service := containerService.NewSignService(
|
service := containerService.NewSignService(
|
||||||
&c.key.PrivateKey,
|
&c.key.PrivateKey,
|
||||||
containerService.NewAPEServer(defaultChainRouter, cnrRdr,
|
containerService.NewAPEServer(defaultChainRouter, cnrRdr,
|
||||||
newCachedIRFetcher(createInnerRingFetcher(c)), c.netMapSource, c.shared.frostfsidClient,
|
newCachedIRFetcher(createInnerRingFetcher(c)), c.netMapSource, c.frostfsidClient,
|
||||||
containerService.NewSplitterService(
|
containerService.NewSplitterService(
|
||||||
c.cfgContainer.containerBatchSize, c.respSvc,
|
c.cfgContainer.containerBatchSize, c.respSvc,
|
||||||
containerService.NewExecutionService(containerMorph.NewExecutor(cnrRdr, cnrWrt), c.respSvc)),
|
containerService.NewExecutionService(containerMorph.NewExecutor(cnrRdr, cnrWrt), c.respSvc)),
|
||||||
|
|
|
@ -8,38 +8,38 @@ import (
|
||||||
func metricsComponent(c *cfg) (*httpComponent, bool) {
|
func metricsComponent(c *cfg) (*httpComponent, bool) {
|
||||||
var updated bool
|
var updated bool
|
||||||
// check if it has been inited before
|
// check if it has been inited before
|
||||||
if c.dynamicConfiguration.metrics == nil {
|
if c.metrics == nil {
|
||||||
c.dynamicConfiguration.metrics = new(httpComponent)
|
c.metrics = new(httpComponent)
|
||||||
c.dynamicConfiguration.metrics.cfg = c
|
c.metrics.cfg = c
|
||||||
c.dynamicConfiguration.metrics.name = "metrics"
|
c.metrics.name = "metrics"
|
||||||
c.dynamicConfiguration.metrics.handler = metrics.Handler()
|
c.metrics.handler = metrics.Handler()
|
||||||
updated = true
|
updated = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// (re)init read configuration
|
// (re)init read configuration
|
||||||
enabled := metricsconfig.Enabled(c.appCfg)
|
enabled := metricsconfig.Enabled(c.appCfg)
|
||||||
if enabled != c.dynamicConfiguration.metrics.enabled {
|
if enabled != c.metrics.enabled {
|
||||||
c.dynamicConfiguration.metrics.enabled = enabled
|
c.metrics.enabled = enabled
|
||||||
updated = true
|
updated = true
|
||||||
}
|
}
|
||||||
address := metricsconfig.Address(c.appCfg)
|
address := metricsconfig.Address(c.appCfg)
|
||||||
if address != c.dynamicConfiguration.metrics.address {
|
if address != c.metrics.address {
|
||||||
c.dynamicConfiguration.metrics.address = address
|
c.metrics.address = address
|
||||||
updated = true
|
updated = true
|
||||||
}
|
}
|
||||||
dur := metricsconfig.ShutdownTimeout(c.appCfg)
|
dur := metricsconfig.ShutdownTimeout(c.appCfg)
|
||||||
if dur != c.dynamicConfiguration.metrics.shutdownDur {
|
if dur != c.metrics.shutdownDur {
|
||||||
c.dynamicConfiguration.metrics.shutdownDur = dur
|
c.metrics.shutdownDur = dur
|
||||||
updated = true
|
updated = true
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.dynamicConfiguration.metrics, updated
|
return c.metrics, updated
|
||||||
}
|
}
|
||||||
|
|
||||||
func enableMetricsSvc(c *cfg) {
|
func enableMetricsSvc(c *cfg) {
|
||||||
c.shared.metricsSvc.Enable()
|
c.metricsSvc.Enable()
|
||||||
}
|
}
|
||||||
|
|
||||||
func disableMetricsSvc(c *cfg) {
|
func disableMetricsSvc(c *cfg) {
|
||||||
c.shared.metricsSvc.Disable()
|
c.metricsSvc.Disable()
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event"
|
||||||
netmapEvent "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/netmap"
|
netmapEvent "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/netmap"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/subscriber"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/subscriber"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/rand"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/rand"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
|
@ -60,10 +61,11 @@ func (c *cfg) initMorphComponents(ctx context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.cfgMorph.cacheTTL < 0 {
|
if c.cfgMorph.cacheTTL < 0 {
|
||||||
netmapSource = wrap
|
netmapSource = newRawNetmapStorage(wrap)
|
||||||
} else {
|
} else {
|
||||||
// use RPC node as source of netmap (with caching)
|
// use RPC node as source of netmap (with caching)
|
||||||
netmapSource = newCachedNetmapStorage(c.cfgNetmap.state, wrap)
|
netmapSource = newCachedNetmapStorage(ctx, c.log, c.cfgNetmap.state, wrap, &c.wg,
|
||||||
|
morphconfig.NetmapCandidatesPollInterval(c.appCfg))
|
||||||
}
|
}
|
||||||
|
|
||||||
c.netMapSource = netmapSource
|
c.netMapSource = netmapSource
|
||||||
|
@ -83,7 +85,7 @@ func initMorphClient(ctx context.Context, c *cfg) {
|
||||||
cli, err := client.New(ctx,
|
cli, err := client.New(ctx,
|
||||||
c.key,
|
c.key,
|
||||||
client.WithDialTimeout(morphconfig.DialTimeout(c.appCfg)),
|
client.WithDialTimeout(morphconfig.DialTimeout(c.appCfg)),
|
||||||
client.WithLogger(c.log),
|
client.WithLogger(c.log.WithTag(logger.TagMorph)),
|
||||||
client.WithMetrics(c.metricsCollector.MorphClientMetrics()),
|
client.WithMetrics(c.metricsCollector.MorphClientMetrics()),
|
||||||
client.WithEndpoints(addresses...),
|
client.WithEndpoints(addresses...),
|
||||||
client.WithConnLostCallback(func() {
|
client.WithConnLostCallback(func() {
|
||||||
|
@ -164,6 +166,7 @@ func listenMorphNotifications(ctx context.Context, c *cfg) {
|
||||||
err error
|
err error
|
||||||
subs subscriber.Subscriber
|
subs subscriber.Subscriber
|
||||||
)
|
)
|
||||||
|
log := c.log.WithTag(logger.TagMorph)
|
||||||
|
|
||||||
fromSideChainBlock, err := c.persistate.UInt32(persistateSideChainLastBlockKey)
|
fromSideChainBlock, err := c.persistate.UInt32(persistateSideChainLastBlockKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -172,14 +175,14 @@ func listenMorphNotifications(ctx context.Context, c *cfg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
subs, err = subscriber.New(ctx, &subscriber.Params{
|
subs, err = subscriber.New(ctx, &subscriber.Params{
|
||||||
Log: c.log,
|
Log: log,
|
||||||
StartFromBlock: fromSideChainBlock,
|
StartFromBlock: fromSideChainBlock,
|
||||||
Client: c.cfgMorph.client,
|
Client: c.cfgMorph.client,
|
||||||
})
|
})
|
||||||
fatalOnErr(err)
|
fatalOnErr(err)
|
||||||
|
|
||||||
lis, err := event.NewListener(event.ListenerParams{
|
lis, err := event.NewListener(event.ListenerParams{
|
||||||
Logger: c.log,
|
Logger: log,
|
||||||
Subscriber: subs,
|
Subscriber: subs,
|
||||||
})
|
})
|
||||||
fatalOnErr(err)
|
fatalOnErr(err)
|
||||||
|
@ -197,7 +200,7 @@ func listenMorphNotifications(ctx context.Context, c *cfg) {
|
||||||
setNetmapNotificationParser(c, newEpochNotification, func(src *state.ContainedNotificationEvent) (event.Event, error) {
|
setNetmapNotificationParser(c, newEpochNotification, func(src *state.ContainedNotificationEvent) (event.Event, error) {
|
||||||
res, err := netmapEvent.ParseNewEpoch(src)
|
res, err := netmapEvent.ParseNewEpoch(src)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
c.log.Info(ctx, logs.FrostFSNodeNewEpochEventFromSidechain,
|
log.Info(ctx, logs.FrostFSNodeNewEpochEventFromSidechain,
|
||||||
zap.Uint64("number", res.(netmapEvent.NewEpoch).EpochNumber()),
|
zap.Uint64("number", res.(netmapEvent.NewEpoch).EpochNumber()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -208,11 +211,11 @@ func listenMorphNotifications(ctx context.Context, c *cfg) {
|
||||||
registerNotificationHandlers(c.cfgContainer.scriptHash, lis, c.cfgContainer.parsers, c.cfgContainer.subscribers)
|
registerNotificationHandlers(c.cfgContainer.scriptHash, lis, c.cfgContainer.parsers, c.cfgContainer.subscribers)
|
||||||
|
|
||||||
registerBlockHandler(lis, func(ctx context.Context, block *block.Block) {
|
registerBlockHandler(lis, func(ctx context.Context, block *block.Block) {
|
||||||
c.log.Debug(ctx, logs.FrostFSNodeNewBlock, zap.Uint32("index", block.Index))
|
log.Debug(ctx, logs.FrostFSNodeNewBlock, zap.Uint32("index", block.Index))
|
||||||
|
|
||||||
err = c.persistate.SetUInt32(persistateSideChainLastBlockKey, block.Index)
|
err = c.persistate.SetUInt32(persistateSideChainLastBlockKey, block.Index)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.log.Warn(ctx, logs.FrostFSNodeCantUpdatePersistentState,
|
log.Warn(ctx, logs.FrostFSNodeCantUpdatePersistentState,
|
||||||
zap.String("chain", "side"),
|
zap.String("chain", "side"),
|
||||||
zap.Uint32("block_index", block.Index))
|
zap.Uint32("block_index", block.Index))
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/assert"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/metrics"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/metrics"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/netmap"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/netmap"
|
||||||
|
@ -104,9 +105,7 @@ func (s *networkState) getNodeInfo() (res netmapSDK.NodeInfo, ok bool) {
|
||||||
v := s.nodeInfo.Load()
|
v := s.nodeInfo.Load()
|
||||||
if v != nil {
|
if v != nil {
|
||||||
res, ok = v.(netmapSDK.NodeInfo)
|
res, ok = v.(netmapSDK.NodeInfo)
|
||||||
if !ok {
|
assert.True(ok, fmt.Sprintf("unexpected value in atomic node info state: %T", v))
|
||||||
panic(fmt.Sprintf("unexpected value in atomic node info state: %T", v))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -124,7 +123,11 @@ func nodeKeyFromNetmap(c *cfg) []byte {
|
||||||
func (c *cfg) iterateNetworkAddresses(f func(string) bool) {
|
func (c *cfg) iterateNetworkAddresses(f func(string) bool) {
|
||||||
ni, ok := c.cfgNetmap.state.getNodeInfo()
|
ni, ok := c.cfgNetmap.state.getNodeInfo()
|
||||||
if ok {
|
if ok {
|
||||||
ni.IterateNetworkEndpoints(f)
|
for s := range ni.NetworkEndpoints() {
|
||||||
|
if f(s) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
55
cmd/frostfs-node/netmap_source.go
Normal file
55
cmd/frostfs-node/netmap_source.go
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/netmap"
|
||||||
|
netmapClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
|
||||||
|
netmapSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type rawNetmapSource struct {
|
||||||
|
client *netmapClient.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRawNetmapStorage(client *netmapClient.Client) netmap.Source {
|
||||||
|
return &rawNetmapSource{
|
||||||
|
client: client,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *rawNetmapSource) GetNetMap(ctx context.Context, diff uint64) (*netmapSDK.NetMap, error) {
|
||||||
|
nm, err := s.client.GetNetMap(ctx, diff)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
candidates, err := s.client.GetCandidates(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
updates := getNetMapNodesToUpdate(nm, candidates)
|
||||||
|
if len(updates) > 0 {
|
||||||
|
mergeNetmapWithCandidates(updates, nm)
|
||||||
|
}
|
||||||
|
return nm, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *rawNetmapSource) GetNetMapByEpoch(ctx context.Context, epoch uint64) (*netmapSDK.NetMap, error) {
|
||||||
|
nm, err := s.client.GetNetMapByEpoch(ctx, epoch)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
candidates, err := s.client.GetCandidates(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
updates := getNetMapNodesToUpdate(nm, candidates)
|
||||||
|
if len(updates) > 0 {
|
||||||
|
mergeNetmapWithCandidates(updates, nm)
|
||||||
|
}
|
||||||
|
return nm, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *rawNetmapSource) Epoch(ctx context.Context) (uint64, error) {
|
||||||
|
return s.client.Epoch(ctx)
|
||||||
|
}
|
|
@ -31,6 +31,7 @@ import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object_manager/placement"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object_manager/placement"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/policer"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/policer"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/replicator"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/replicator"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/object"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/object"
|
||||||
objectGRPC "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/object/grpc"
|
objectGRPC "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/object/grpc"
|
||||||
netmapSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
netmapSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||||
|
@ -186,9 +187,9 @@ func initObjectService(c *cfg) {
|
||||||
respSvc,
|
respSvc,
|
||||||
)
|
)
|
||||||
|
|
||||||
c.shared.metricsSvc = objectService.NewMetricCollector(
|
c.metricsSvc = objectService.NewMetricCollector(
|
||||||
signSvc, c.metricsCollector.ObjectService(), metricsconfig.Enabled(c.appCfg))
|
signSvc, c.metricsCollector.ObjectService(), metricsconfig.Enabled(c.appCfg))
|
||||||
qosService := objectService.NewQoSObjectService(c.shared.metricsSvc, &c.cfgQoSService)
|
qosService := objectService.NewQoSObjectService(c.metricsSvc, &c.cfgQoSService)
|
||||||
auditSvc := objectService.NewAuditService(qosService, c.log, c.audit)
|
auditSvc := objectService.NewAuditService(qosService, c.log, c.audit)
|
||||||
server := objectTransportGRPC.New(auditSvc)
|
server := objectTransportGRPC.New(auditSvc)
|
||||||
|
|
||||||
|
@ -217,9 +218,8 @@ func addPolicer(c *cfg, keyStorage *util.KeyStorage, clientConstructor *cache.Cl
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteReader := objectService.NewRemoteReader(keyStorage, clientConstructor)
|
remoteReader := objectService.NewRemoteReader(keyStorage, clientConstructor)
|
||||||
|
|
||||||
pol := policer.New(
|
pol := policer.New(
|
||||||
policer.WithLogger(c.log),
|
policer.WithLogger(c.log.WithTag(logger.TagPolicer)),
|
||||||
policer.WithKeySpaceIterator(&keySpaceIterator{ng: ls}),
|
policer.WithKeySpaceIterator(&keySpaceIterator{ng: ls}),
|
||||||
policer.WithBuryFunc(buryFn),
|
policer.WithBuryFunc(buryFn),
|
||||||
policer.WithContainerSource(c.cfgObject.cnrSource),
|
policer.WithContainerSource(c.cfgObject.cnrSource),
|
||||||
|
@ -291,7 +291,7 @@ func createReplicator(c *cfg, keyStorage *util.KeyStorage, cache *cache.ClientCa
|
||||||
ls := c.cfgObject.cfgLocalStorage.localStorage
|
ls := c.cfgObject.cfgLocalStorage.localStorage
|
||||||
|
|
||||||
return replicator.New(
|
return replicator.New(
|
||||||
replicator.WithLogger(c.log),
|
replicator.WithLogger(c.log.WithTag(logger.TagReplicator)),
|
||||||
replicator.WithPutTimeout(
|
replicator.WithPutTimeout(
|
||||||
replicatorconfig.PutTimeout(c.appCfg),
|
replicatorconfig.PutTimeout(c.appCfg),
|
||||||
),
|
),
|
||||||
|
@ -348,7 +348,7 @@ func createSearchSvc(c *cfg, keyStorage *util.KeyStorage, traverseGen *util.Trav
|
||||||
c.netMapSource,
|
c.netMapSource,
|
||||||
keyStorage,
|
keyStorage,
|
||||||
containerSource,
|
containerSource,
|
||||||
searchsvc.WithLogger(c.log),
|
searchsvc.WithLogger(c.log.WithTag(logger.TagSearchSvc)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -374,7 +374,7 @@ func createGetService(c *cfg, keyStorage *util.KeyStorage, traverseGen *util.Tra
|
||||||
),
|
),
|
||||||
coreConstructor,
|
coreConstructor,
|
||||||
containerSource,
|
containerSource,
|
||||||
getsvc.WithLogger(c.log))
|
getsvc.WithLogger(c.log.WithTag(logger.TagGetSvc)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func createGetServiceV2(c *cfg, sGet *getsvc.Service, keyStorage *util.KeyStorage) *getsvcV2.Service {
|
func createGetServiceV2(c *cfg, sGet *getsvc.Service, keyStorage *util.KeyStorage) *getsvcV2.Service {
|
||||||
|
@ -385,7 +385,7 @@ func createGetServiceV2(c *cfg, sGet *getsvc.Service, keyStorage *util.KeyStorag
|
||||||
c.netMapSource,
|
c.netMapSource,
|
||||||
c,
|
c,
|
||||||
c.cfgObject.cnrSource,
|
c.cfgObject.cnrSource,
|
||||||
getsvcV2.WithLogger(c.log),
|
getsvcV2.WithLogger(c.log.WithTag(logger.TagGetSvc)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -402,7 +402,7 @@ func createDeleteService(c *cfg, keyStorage *util.KeyStorage, sGet *getsvc.Servi
|
||||||
cfg: c,
|
cfg: c,
|
||||||
},
|
},
|
||||||
keyStorage,
|
keyStorage,
|
||||||
deletesvc.WithLogger(c.log),
|
deletesvc.WithLogger(c.log.WithTag(logger.TagDeleteSvc)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -432,7 +432,7 @@ func createAPEService(c *cfg, irFetcher *cachedIRFetcher, splitSvc *objectServic
|
||||||
c.cfgObject.cfgAccessPolicyEngine.accessPolicyEngine.LocalStorage(),
|
c.cfgObject.cfgAccessPolicyEngine.accessPolicyEngine.LocalStorage(),
|
||||||
c.cfgObject.cfgAccessPolicyEngine.accessPolicyEngine.MorphRuleChainStorage(),
|
c.cfgObject.cfgAccessPolicyEngine.accessPolicyEngine.MorphRuleChainStorage(),
|
||||||
objectAPE.NewStorageEngineHeaderProvider(c.cfgObject.cfgLocalStorage.localStorage, c.cfgObject.getSvc),
|
objectAPE.NewStorageEngineHeaderProvider(c.cfgObject.cfgLocalStorage.localStorage, c.cfgObject.getSvc),
|
||||||
c.shared.frostfsidClient,
|
c.frostfsidClient,
|
||||||
c.netMapSource,
|
c.netMapSource,
|
||||||
c.cfgNetmap.state,
|
c.cfgNetmap.state,
|
||||||
c.cfgObject.cnrSource,
|
c.cfgObject.cnrSource,
|
||||||
|
|
|
@ -18,33 +18,33 @@ func initProfilerService(ctx context.Context, c *cfg) {
|
||||||
func pprofComponent(c *cfg) (*httpComponent, bool) {
|
func pprofComponent(c *cfg) (*httpComponent, bool) {
|
||||||
var updated bool
|
var updated bool
|
||||||
// check if it has been inited before
|
// check if it has been inited before
|
||||||
if c.dynamicConfiguration.pprof == nil {
|
if c.pprof == nil {
|
||||||
c.dynamicConfiguration.pprof = new(httpComponent)
|
c.pprof = new(httpComponent)
|
||||||
c.dynamicConfiguration.pprof.cfg = c
|
c.pprof.cfg = c
|
||||||
c.dynamicConfiguration.pprof.name = "pprof"
|
c.pprof.name = "pprof"
|
||||||
c.dynamicConfiguration.pprof.handler = httputil.Handler()
|
c.pprof.handler = httputil.Handler()
|
||||||
c.dynamicConfiguration.pprof.preReload = tuneProfilers
|
c.pprof.preReload = tuneProfilers
|
||||||
updated = true
|
updated = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// (re)init read configuration
|
// (re)init read configuration
|
||||||
enabled := profilerconfig.Enabled(c.appCfg)
|
enabled := profilerconfig.Enabled(c.appCfg)
|
||||||
if enabled != c.dynamicConfiguration.pprof.enabled {
|
if enabled != c.pprof.enabled {
|
||||||
c.dynamicConfiguration.pprof.enabled = enabled
|
c.pprof.enabled = enabled
|
||||||
updated = true
|
updated = true
|
||||||
}
|
}
|
||||||
address := profilerconfig.Address(c.appCfg)
|
address := profilerconfig.Address(c.appCfg)
|
||||||
if address != c.dynamicConfiguration.pprof.address {
|
if address != c.pprof.address {
|
||||||
c.dynamicConfiguration.pprof.address = address
|
c.pprof.address = address
|
||||||
updated = true
|
updated = true
|
||||||
}
|
}
|
||||||
dur := profilerconfig.ShutdownTimeout(c.appCfg)
|
dur := profilerconfig.ShutdownTimeout(c.appCfg)
|
||||||
if dur != c.dynamicConfiguration.pprof.shutdownDur {
|
if dur != c.pprof.shutdownDur {
|
||||||
c.dynamicConfiguration.pprof.shutdownDur = dur
|
c.pprof.shutdownDur = dur
|
||||||
updated = true
|
updated = true
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.dynamicConfiguration.pprof, updated
|
return c.pprof, updated
|
||||||
}
|
}
|
||||||
|
|
||||||
func tuneProfilers(c *cfg) {
|
func tuneProfilers(c *cfg) {
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/session/storage"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/session/storage"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/session/storage/persistent"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/session/storage/persistent"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/session/storage/temporary"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/session/storage/temporary"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/session"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/session"
|
||||||
sessionGRPC "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/session/grpc"
|
sessionGRPC "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/session/grpc"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||||
|
@ -55,7 +56,7 @@ func initSessionService(c *cfg) {
|
||||||
server := sessionTransportGRPC.New(
|
server := sessionTransportGRPC.New(
|
||||||
sessionSvc.NewSignService(
|
sessionSvc.NewSignService(
|
||||||
&c.key.PrivateKey,
|
&c.key.PrivateKey,
|
||||||
sessionSvc.NewExecutionService(c.privateTokenStore, c.respSvc, c.log),
|
sessionSvc.NewExecutionService(c.privateTokenStore, c.respSvc, c.log.WithTag(logger.TagSessionSvc)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event"
|
||||||
containerEvent "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/container"
|
containerEvent "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/container"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/tree"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/tree"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
@ -51,12 +52,12 @@ func initTreeService(c *cfg) {
|
||||||
c.treeService = tree.New(
|
c.treeService = tree.New(
|
||||||
tree.WithContainerSource(cnrSource{
|
tree.WithContainerSource(cnrSource{
|
||||||
src: c.cfgObject.cnrSource,
|
src: c.cfgObject.cnrSource,
|
||||||
cli: c.shared.cnrClient,
|
cli: c.cnrClient,
|
||||||
}),
|
}),
|
||||||
tree.WithFrostfsidSubjectProvider(c.shared.frostfsidClient),
|
tree.WithFrostfsidSubjectProvider(c.frostfsidClient),
|
||||||
tree.WithNetmapSource(c.netMapSource),
|
tree.WithNetmapSource(c.netMapSource),
|
||||||
tree.WithPrivateKey(&c.key.PrivateKey),
|
tree.WithPrivateKey(&c.key.PrivateKey),
|
||||||
tree.WithLogger(c.log),
|
tree.WithLogger(c.log.WithTag(logger.TagTreeSvc)),
|
||||||
tree.WithStorage(c.cfgObject.cfgLocalStorage.localStorage),
|
tree.WithStorage(c.cfgObject.cfgLocalStorage.localStorage),
|
||||||
tree.WithContainerCacheSize(treeConfig.CacheSize()),
|
tree.WithContainerCacheSize(treeConfig.CacheSize()),
|
||||||
tree.WithReplicationTimeout(treeConfig.ReplicationTimeout()),
|
tree.WithReplicationTimeout(treeConfig.ReplicationTimeout()),
|
||||||
|
|
|
@ -30,6 +30,11 @@ func validateConfig(c *config.Config) error {
|
||||||
return fmt.Errorf("invalid logger destination: %w", err)
|
return fmt.Errorf("invalid logger destination: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = loggerPrm.SetTags(loggerconfig.Tags(c))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid list of allowed tags: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
// shard configuration validation
|
// shard configuration validation
|
||||||
|
|
||||||
shardNum := 0
|
shardNum := 0
|
||||||
|
|
|
@ -51,8 +51,13 @@ func ExitOnErr(cmd *cobra.Command, errFmt string, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.PrintErrln(err)
|
cmd.PrintErrln(err)
|
||||||
if cmd.PersistentPostRun != nil {
|
for p := cmd; p != nil; p = p.Parent() {
|
||||||
cmd.PersistentPostRun(cmd, nil)
|
if p.PersistentPostRun != nil {
|
||||||
|
p.PersistentPostRun(cmd, nil)
|
||||||
|
if !cobra.EnableTraverseRunHooks {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
os.Exit(code)
|
os.Exit(code)
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,15 +27,15 @@ func PrettyPrintNodeInfo(cmd *cobra.Command, node netmap.NodeInfo,
|
||||||
|
|
||||||
cmd.Printf("%sNode %d: %s %s ", indent, index+1, hex.EncodeToString(node.PublicKey()), strState)
|
cmd.Printf("%sNode %d: %s %s ", indent, index+1, hex.EncodeToString(node.PublicKey()), strState)
|
||||||
|
|
||||||
netmap.IterateNetworkEndpoints(node, func(endpoint string) {
|
for endpoint := range node.NetworkEndpoints() {
|
||||||
cmd.Printf("%s ", endpoint)
|
cmd.Printf("%s ", endpoint)
|
||||||
})
|
}
|
||||||
cmd.Println()
|
cmd.Println()
|
||||||
|
|
||||||
if !short {
|
if !short {
|
||||||
node.IterateAttributes(func(key, value string) {
|
for key, value := range node.Attributes() {
|
||||||
cmd.Printf("%s\t%s: %s\n", indent, key, value)
|
cmd.Printf("%s\t%s: %s\n", indent, key, value)
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
FROSTFS_IR_LOGGER_LEVEL=info
|
FROSTFS_IR_LOGGER_LEVEL=info
|
||||||
FROSTFS_IR_LOGGER_TIMESTAMP=true
|
FROSTFS_IR_LOGGER_TIMESTAMP=true
|
||||||
|
FROSTFS_IR_LOGGER_TAGS_0_NAMES="main, morph"
|
||||||
|
FROSTFS_IR_LOGGER_TAGS_0_LEVEL="debug"
|
||||||
|
|
||||||
FROSTFS_IR_WALLET_PATH=/path/to/wallet.json
|
FROSTFS_IR_WALLET_PATH=/path/to/wallet.json
|
||||||
FROSTFS_IR_WALLET_ADDRESS=NUHtW3eM6a4mmFCgyyr4rj4wygsTKB88XX
|
FROSTFS_IR_WALLET_ADDRESS=NUHtW3eM6a4mmFCgyyr4rj4wygsTKB88XX
|
||||||
|
|
|
@ -3,6 +3,9 @@
|
||||||
logger:
|
logger:
|
||||||
level: info # Logger level: one of "debug", "info" (default), "warn", "error", "dpanic", "panic", "fatal"
|
level: info # Logger level: one of "debug", "info" (default), "warn", "error", "dpanic", "panic", "fatal"
|
||||||
timestamp: true
|
timestamp: true
|
||||||
|
tags:
|
||||||
|
- names: "main, morph" # Possible values: `main`, `morph`, `grpcsvc`, `ir`, `processor`.
|
||||||
|
level: debug
|
||||||
|
|
||||||
wallet:
|
wallet:
|
||||||
path: /path/to/wallet.json # Path to NEP-6 NEO wallet file
|
path: /path/to/wallet.json # Path to NEP-6 NEO wallet file
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
FROSTFS_LOGGER_LEVEL=debug
|
FROSTFS_LOGGER_LEVEL=debug
|
||||||
FROSTFS_LOGGER_DESTINATION=journald
|
FROSTFS_LOGGER_DESTINATION=journald
|
||||||
FROSTFS_LOGGER_TIMESTAMP=true
|
FROSTFS_LOGGER_TIMESTAMP=true
|
||||||
|
FROSTFS_LOGGER_TAGS_0_NAMES="main, morph"
|
||||||
|
FROSTFS_LOGGER_TAGS_0_LEVEL="debug"
|
||||||
|
|
||||||
FROSTFS_PPROF_ENABLED=true
|
FROSTFS_PPROF_ENABLED=true
|
||||||
FROSTFS_PPROF_ADDRESS=localhost:6060
|
FROSTFS_PPROF_ADDRESS=localhost:6060
|
||||||
|
@ -23,6 +25,7 @@ FROSTFS_NODE_ATTRIBUTE_1="UN-LOCODE:RU MSK"
|
||||||
FROSTFS_NODE_RELAY=true
|
FROSTFS_NODE_RELAY=true
|
||||||
FROSTFS_NODE_PERSISTENT_SESSIONS_PATH=/sessions
|
FROSTFS_NODE_PERSISTENT_SESSIONS_PATH=/sessions
|
||||||
FROSTFS_NODE_PERSISTENT_STATE_PATH=/state
|
FROSTFS_NODE_PERSISTENT_STATE_PATH=/state
|
||||||
|
FROSTFS_NODE_LOCODE_DB_PATH=/path/to/locode/db
|
||||||
|
|
||||||
# Tree service section
|
# Tree service section
|
||||||
FROSTFS_TREE_ENABLED=true
|
FROSTFS_TREE_ENABLED=true
|
||||||
|
@ -121,7 +124,8 @@ FROSTFS_STORAGE_SHARD_0_METABASE_PERM=0644
|
||||||
FROSTFS_STORAGE_SHARD_0_METABASE_MAX_BATCH_SIZE=100
|
FROSTFS_STORAGE_SHARD_0_METABASE_MAX_BATCH_SIZE=100
|
||||||
FROSTFS_STORAGE_SHARD_0_METABASE_MAX_BATCH_DELAY=10ms
|
FROSTFS_STORAGE_SHARD_0_METABASE_MAX_BATCH_DELAY=10ms
|
||||||
### Blobstor config
|
### Blobstor config
|
||||||
FROSTFS_STORAGE_SHARD_0_COMPRESS=true
|
FROSTFS_STORAGE_SHARD_0_COMPRESSION_ENABLED=true
|
||||||
|
FROSTFS_STORAGE_SHARD_0_COMPRESSION_LEVEL=fastest
|
||||||
FROSTFS_STORAGE_SHARD_0_COMPRESSION_EXCLUDE_CONTENT_TYPES="audio/* video/*"
|
FROSTFS_STORAGE_SHARD_0_COMPRESSION_EXCLUDE_CONTENT_TYPES="audio/* video/*"
|
||||||
FROSTFS_STORAGE_SHARD_0_COMPRESSION_ESTIMATE_COMPRESSIBILITY=true
|
FROSTFS_STORAGE_SHARD_0_COMPRESSION_ESTIMATE_COMPRESSIBILITY=true
|
||||||
FROSTFS_STORAGE_SHARD_0_COMPRESSION_ESTIMATE_COMPRESSIBILITY_THRESHOLD=0.7
|
FROSTFS_STORAGE_SHARD_0_COMPRESSION_ESTIMATE_COMPRESSIBILITY_THRESHOLD=0.7
|
||||||
|
@ -181,6 +185,9 @@ FROSTFS_STORAGE_SHARD_0_LIMITS_READ_TAGS_4_TAG=policer
|
||||||
FROSTFS_STORAGE_SHARD_0_LIMITS_READ_TAGS_4_WEIGHT=5
|
FROSTFS_STORAGE_SHARD_0_LIMITS_READ_TAGS_4_WEIGHT=5
|
||||||
FROSTFS_STORAGE_SHARD_0_LIMITS_READ_TAGS_4_LIMIT_OPS=25000
|
FROSTFS_STORAGE_SHARD_0_LIMITS_READ_TAGS_4_LIMIT_OPS=25000
|
||||||
FROSTFS_STORAGE_SHARD_0_LIMITS_READ_TAGS_4_PROHIBITED=true
|
FROSTFS_STORAGE_SHARD_0_LIMITS_READ_TAGS_4_PROHIBITED=true
|
||||||
|
FROSTFS_STORAGE_SHARD_0_LIMITS_READ_TAGS_5_TAG=treesync
|
||||||
|
FROSTFS_STORAGE_SHARD_0_LIMITS_READ_TAGS_5_WEIGHT=5
|
||||||
|
FROSTFS_STORAGE_SHARD_0_LIMITS_READ_TAGS_5_LIMIT_OPS=25
|
||||||
FROSTFS_STORAGE_SHARD_0_LIMITS_WRITE_TAGS_0_TAG=internal
|
FROSTFS_STORAGE_SHARD_0_LIMITS_WRITE_TAGS_0_TAG=internal
|
||||||
FROSTFS_STORAGE_SHARD_0_LIMITS_WRITE_TAGS_0_WEIGHT=200
|
FROSTFS_STORAGE_SHARD_0_LIMITS_WRITE_TAGS_0_WEIGHT=200
|
||||||
FROSTFS_STORAGE_SHARD_0_LIMITS_WRITE_TAGS_0_LIMIT_OPS=0
|
FROSTFS_STORAGE_SHARD_0_LIMITS_WRITE_TAGS_0_LIMIT_OPS=0
|
||||||
|
@ -198,6 +205,9 @@ FROSTFS_STORAGE_SHARD_0_LIMITS_WRITE_TAGS_3_LIMIT_OPS=2500
|
||||||
FROSTFS_STORAGE_SHARD_0_LIMITS_WRITE_TAGS_4_TAG=policer
|
FROSTFS_STORAGE_SHARD_0_LIMITS_WRITE_TAGS_4_TAG=policer
|
||||||
FROSTFS_STORAGE_SHARD_0_LIMITS_WRITE_TAGS_4_WEIGHT=50
|
FROSTFS_STORAGE_SHARD_0_LIMITS_WRITE_TAGS_4_WEIGHT=50
|
||||||
FROSTFS_STORAGE_SHARD_0_LIMITS_WRITE_TAGS_4_LIMIT_OPS=2500
|
FROSTFS_STORAGE_SHARD_0_LIMITS_WRITE_TAGS_4_LIMIT_OPS=2500
|
||||||
|
FROSTFS_STORAGE_SHARD_0_LIMITS_WRITE_TAGS_5_TAG=treesync
|
||||||
|
FROSTFS_STORAGE_SHARD_0_LIMITS_WRITE_TAGS_5_WEIGHT=50
|
||||||
|
FROSTFS_STORAGE_SHARD_0_LIMITS_WRITE_TAGS_5_LIMIT_OPS=100
|
||||||
|
|
||||||
## 1 shard
|
## 1 shard
|
||||||
### Flag to refill Metabase from BlobStor
|
### Flag to refill Metabase from BlobStor
|
||||||
|
|
|
@ -2,7 +2,13 @@
|
||||||
"logger": {
|
"logger": {
|
||||||
"level": "debug",
|
"level": "debug",
|
||||||
"destination": "journald",
|
"destination": "journald",
|
||||||
"timestamp": true
|
"timestamp": true,
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"names": "main, morph",
|
||||||
|
"level": "debug"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"pprof": {
|
"pprof": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
@ -37,7 +43,8 @@
|
||||||
},
|
},
|
||||||
"persistent_state": {
|
"persistent_state": {
|
||||||
"path": "/state"
|
"path": "/state"
|
||||||
}
|
},
|
||||||
|
"locode_db_path": "/path/to/locode/db"
|
||||||
},
|
},
|
||||||
"grpc": {
|
"grpc": {
|
||||||
"0": {
|
"0": {
|
||||||
|
@ -182,12 +189,15 @@
|
||||||
"max_batch_size": 100,
|
"max_batch_size": 100,
|
||||||
"max_batch_delay": "10ms"
|
"max_batch_delay": "10ms"
|
||||||
},
|
},
|
||||||
"compress": true,
|
"compression": {
|
||||||
"compression_exclude_content_types": [
|
"enabled": true,
|
||||||
|
"level": "fastest",
|
||||||
|
"exclude_content_types": [
|
||||||
"audio/*", "video/*"
|
"audio/*", "video/*"
|
||||||
],
|
],
|
||||||
"compression_estimate_compressibility": true,
|
"estimate_compressibility": true,
|
||||||
"compression_estimate_compressibility_threshold": 0.7,
|
"estimate_compressibility_threshold": 0.7
|
||||||
|
},
|
||||||
"small_object_size": 102400,
|
"small_object_size": 102400,
|
||||||
"blobstor": [
|
"blobstor": [
|
||||||
{
|
{
|
||||||
|
@ -254,6 +264,11 @@
|
||||||
"weight": 5,
|
"weight": 5,
|
||||||
"limit_ops": 25000,
|
"limit_ops": 25000,
|
||||||
"prohibited": true
|
"prohibited": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tag": "treesync",
|
||||||
|
"weight": 5,
|
||||||
|
"limit_ops": 25
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -288,6 +303,11 @@
|
||||||
"tag": "policer",
|
"tag": "policer",
|
||||||
"weight": 50,
|
"weight": 50,
|
||||||
"limit_ops": 2500
|
"limit_ops": 2500
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tag": "treesync",
|
||||||
|
"weight": 50,
|
||||||
|
"limit_ops": 100
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -311,7 +331,9 @@
|
||||||
"max_batch_size": 200,
|
"max_batch_size": 200,
|
||||||
"max_batch_delay": "20ms"
|
"max_batch_delay": "20ms"
|
||||||
},
|
},
|
||||||
"compress": false,
|
"compression": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
"small_object_size": 102400,
|
"small_object_size": 102400,
|
||||||
"blobstor": [
|
"blobstor": [
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,6 +2,9 @@ logger:
|
||||||
level: debug # logger level: one of "debug", "info" (default), "warn", "error", "dpanic", "panic", "fatal"
|
level: debug # logger level: one of "debug", "info" (default), "warn", "error", "dpanic", "panic", "fatal"
|
||||||
destination: journald # logger destination: one of "stdout" (default), "journald"
|
destination: journald # logger destination: one of "stdout" (default), "journald"
|
||||||
timestamp: true
|
timestamp: true
|
||||||
|
tags:
|
||||||
|
- names: "main, morph"
|
||||||
|
level: debug
|
||||||
|
|
||||||
systemdnotify:
|
systemdnotify:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
@ -36,6 +39,7 @@ node:
|
||||||
path: /sessions # path to persistent session tokens file of Storage node (default: in-memory sessions)
|
path: /sessions # path to persistent session tokens file of Storage node (default: in-memory sessions)
|
||||||
persistent_state:
|
persistent_state:
|
||||||
path: /state # path to persistent state file of Storage node
|
path: /state # path to persistent state file of Storage node
|
||||||
|
"locode_db_path": "/path/to/locode/db"
|
||||||
|
|
||||||
grpc:
|
grpc:
|
||||||
- endpoint: s01.frostfs.devenv:8080 # endpoint for gRPC server
|
- endpoint: s01.frostfs.devenv:8080 # endpoint for gRPC server
|
||||||
|
@ -95,6 +99,9 @@ morph:
|
||||||
- address: wss://rpc2.morph.frostfs.info:40341/ws
|
- address: wss://rpc2.morph.frostfs.info:40341/ws
|
||||||
priority: 2
|
priority: 2
|
||||||
ape_chain_cache_size: 100000
|
ape_chain_cache_size: 100000
|
||||||
|
netmap:
|
||||||
|
candidates:
|
||||||
|
poll_interval: 20s
|
||||||
|
|
||||||
apiclient:
|
apiclient:
|
||||||
dial_timeout: 15s # timeout for FrostFS API client connection
|
dial_timeout: 15s # timeout for FrostFS API client connection
|
||||||
|
@ -156,7 +163,8 @@ storage:
|
||||||
max_batch_delay: 5ms # maximum delay for a batch of operations to be executed
|
max_batch_delay: 5ms # maximum delay for a batch of operations to be executed
|
||||||
max_batch_size: 100 # maximum amount of operations in a single batch
|
max_batch_size: 100 # maximum amount of operations in a single batch
|
||||||
|
|
||||||
compress: false # turn on/off zstd(level 3) compression of stored objects
|
compression:
|
||||||
|
enabled: false # turn on/off zstd compression of stored objects
|
||||||
small_object_size: 100 kb # size threshold for "small" objects which are cached in key-value DB, not in FS, bytes
|
small_object_size: 100 kb # size threshold for "small" objects which are cached in key-value DB, not in FS, bytes
|
||||||
|
|
||||||
blobstor:
|
blobstor:
|
||||||
|
@ -198,12 +206,14 @@ storage:
|
||||||
max_batch_size: 100
|
max_batch_size: 100
|
||||||
max_batch_delay: 10ms
|
max_batch_delay: 10ms
|
||||||
|
|
||||||
compress: true # turn on/off zstd(level 3) compression of stored objects
|
compression:
|
||||||
compression_exclude_content_types:
|
enabled: true # turn on/off zstd compression of stored objects
|
||||||
|
level: fastest
|
||||||
|
exclude_content_types:
|
||||||
- audio/*
|
- audio/*
|
||||||
- video/*
|
- video/*
|
||||||
compression_estimate_compressibility: true
|
estimate_compressibility: true
|
||||||
compression_estimate_compressibility_threshold: 0.7
|
estimate_compressibility_threshold: 0.7
|
||||||
|
|
||||||
blobstor:
|
blobstor:
|
||||||
- type: blobovnicza
|
- type: blobovnicza
|
||||||
|
@ -250,6 +260,9 @@ storage:
|
||||||
weight: 5
|
weight: 5
|
||||||
limit_ops: 25000
|
limit_ops: 25000
|
||||||
prohibited: true
|
prohibited: true
|
||||||
|
- tag: treesync
|
||||||
|
weight: 5
|
||||||
|
limit_ops: 25
|
||||||
write:
|
write:
|
||||||
max_running_ops: 1000
|
max_running_ops: 1000
|
||||||
max_waiting_ops: 100
|
max_waiting_ops: 100
|
||||||
|
@ -272,6 +285,9 @@ storage:
|
||||||
- tag: policer
|
- tag: policer
|
||||||
weight: 50
|
weight: 50
|
||||||
limit_ops: 2500
|
limit_ops: 2500
|
||||||
|
- tag: treesync
|
||||||
|
weight: 50
|
||||||
|
limit_ops: 100
|
||||||
|
|
||||||
1:
|
1:
|
||||||
writecache:
|
writecache:
|
||||||
|
|
|
@ -13,7 +13,8 @@ There are some custom types used for brevity:
|
||||||
# Structure
|
# Structure
|
||||||
|
|
||||||
| Section | Description |
|
| Section | Description |
|
||||||
|------------------------|---------------------------------------------------------------------|
|
|--------------|---------------------------------------------------------|
|
||||||
|
| `node` | [Node parameters](#node-section) |
|
||||||
| `logger` | [Logging parameters](#logger-section) |
|
| `logger` | [Logging parameters](#logger-section) |
|
||||||
| `pprof` | [PProf configuration](#pprof-section) |
|
| `pprof` | [PProf configuration](#pprof-section) |
|
||||||
| `prometheus` | [Prometheus metrics configuration](#prometheus-section) |
|
| `prometheus` | [Prometheus metrics configuration](#prometheus-section) |
|
||||||
|
@ -111,11 +112,21 @@ Contains logger parameters.
|
||||||
```yaml
|
```yaml
|
||||||
logger:
|
logger:
|
||||||
level: info
|
level: info
|
||||||
|
tags:
|
||||||
|
- names: "main, morph"
|
||||||
|
level: debug
|
||||||
```
|
```
|
||||||
|
|
||||||
| Parameter | Type | Default value | Description |
|
| Parameter | Type | Default value | Description |
|
||||||
|-----------|----------|---------------|---------------------------------------------------------------------------------------------------|
|
|-----------|-----------------------------------------------|---------------|---------------------------------------------------------------------------------------------------|
|
||||||
| `level` | `string` | `info` | Logging level.<br/>Possible values: `debug`, `info`, `warn`, `error`, `dpanic`, `panic`, `fatal` |
|
| `level` | `string` | `info` | Logging level.<br/>Possible values: `debug`, `info`, `warn`, `error`, `dpanic`, `panic`, `fatal` |
|
||||||
|
| `tags` | list of [tags descriptions](#tags-subsection) | | Array of tags description. |
|
||||||
|
|
||||||
|
## `tags` subsection
|
||||||
|
| Parameter | Type | Default value | Description |
|
||||||
|
|-----------|----------|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| `names` | `string` | | List of components divided by `,`.<br/>Possible values: `main`, `engine`, `blobovnicza`, `blobovniczatree`, `blobstor`, `fstree`, `gc`, `shard`, `writecache`, `deletesvc`, `getsvc`, `searchsvc`, `sessionsvc`, `treesvc`, `policer`, `replicator`. |
|
||||||
|
| `level` | `string` | | Logging level for the components from `names`, overrides default logging level. |
|
||||||
|
|
||||||
# `contracts` section
|
# `contracts` section
|
||||||
Contains override values for FrostFS side-chain contract hashes. Most of the time contract
|
Contains override values for FrostFS side-chain contract hashes. Most of the time contract
|
||||||
|
@ -148,15 +159,19 @@ morph:
|
||||||
- address: wss://rpc2.morph.frostfs.info:40341/ws
|
- address: wss://rpc2.morph.frostfs.info:40341/ws
|
||||||
priority: 2
|
priority: 2
|
||||||
switch_interval: 2m
|
switch_interval: 2m
|
||||||
|
netmap:
|
||||||
|
candidates:
|
||||||
|
poll_interval: 20s
|
||||||
```
|
```
|
||||||
|
|
||||||
| Parameter | Type | Default value | Description |
|
| Parameter | Type | Default value | Description |
|
||||||
| ---------------------- | --------------------------------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|-----------------------------------|-----------------------------------------------------------|------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `dial_timeout` | `duration` | `5s` | Timeout for dialing connections to N3 RPCs. |
|
| `dial_timeout` | `duration` | `5s` | Timeout for dialing connections to N3 RPCs. |
|
||||||
| `cache_ttl` | `duration` | Morph block time | Sidechain cache TTL value (min interval between similar calls).<br/>Negative value disables caching.<br/>Cached entities: containers, container lists, eACL tables. |
|
| `cache_ttl` | `duration` | Morph block time | Sidechain cache TTL value (min interval between similar calls).<br/>Negative value disables caching.<br/>Cached entities: containers, container lists, eACL tables. |
|
||||||
| `rpc_endpoint` | list of [endpoint descriptions](#rpc_endpoint-subsection) | | Array of endpoint descriptions. |
|
| `rpc_endpoint` | list of [endpoint descriptions](#rpc_endpoint-subsection) | | Array of endpoint descriptions. |
|
||||||
| `switch_interval` | `duration` | `2m` | Time interval between the attempts to connect to the highest priority RPC node if the connection is not established yet. |
|
| `switch_interval` | `duration` | `2m` | Time interval between the attempts to connect to the highest priority RPC node if the connection is not established yet. |
|
||||||
| `ape_chain_cache_size` | `int` | `10000` | Size of the morph cache for APE chains. |
|
| `ape_chain_cache_size` | `int` | `10000` | Size of the morph cache for APE chains. |
|
||||||
|
| `netmap.candidates.poll_interval` | `duration` | `20s` | Timeout to set up frequency of merge candidates to netmap with netmap in local cache. |
|
||||||
|
|
||||||
## `rpc_endpoint` subsection
|
## `rpc_endpoint` subsection
|
||||||
| Parameter | Type | Default value | Description |
|
| Parameter | Type | Default value | Description |
|
||||||
|
@ -181,11 +196,8 @@ Contains configuration for each shard. Keys must be consecutive numbers starting
|
||||||
The following table describes configuration for each shard.
|
The following table describes configuration for each shard.
|
||||||
|
|
||||||
| Parameter | Type | Default value | Description |
|
| Parameter | Type | Default value | Description |
|
||||||
| ------------------------------------------------ | ------------------------------------------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ------------------------------ | --------------------------------------------- | ------------- | --------------------------------------------------------------------------------------------------------- |
|
||||||
| `compress` | `bool` | `false` | Flag to enable compression. |
|
| `compression` | [Compression config](#compression-subsection) | | Compression config. |
|
||||||
| `compression_exclude_content_types` | `[]string` | | List of content-types to disable compression for. Content-type is taken from `Content-Type` object attribute. Each element can contain a star `*` as a first (last) character, which matches any prefix (suffix). |
|
|
||||||
| `compression_estimate_compressibility` | `bool` | `false` | If `true`, then noramalized compressibility estimation is used to decide compress data or not. |
|
|
||||||
| `compression_estimate_compressibility_threshold` | `float` | `0.1` | Normilized compressibility estimate threshold: data will compress if estimation if greater than this value. |
|
|
||||||
| `mode` | `string` | `read-write` | Shard Mode.<br/>Possible values: `read-write`, `read-only`, `degraded`, `degraded-read-only`, `disabled` |
|
| `mode` | `string` | `read-write` | Shard Mode.<br/>Possible values: `read-write`, `read-only`, `degraded`, `degraded-read-only`, `disabled` |
|
||||||
| `resync_metabase` | `bool` | `false` | Flag to enable metabase resync on start. |
|
| `resync_metabase` | `bool` | `false` | Flag to enable metabase resync on start. |
|
||||||
| `resync_metabase_worker_count` | `int` | `1000` | Count of concurrent workers to resync metabase. |
|
| `resync_metabase_worker_count` | `int` | `1000` | Count of concurrent workers to resync metabase. |
|
||||||
|
@ -196,6 +208,29 @@ The following table describes configuration for each shard.
|
||||||
| `gc` | [GC config](#gc-subsection) | | GC configuration. |
|
| `gc` | [GC config](#gc-subsection) | | GC configuration. |
|
||||||
| `limits` | [Shard limits config](#limits-subsection) | | Shard limits configuration. |
|
| `limits` | [Shard limits config](#limits-subsection) | | Shard limits configuration. |
|
||||||
|
|
||||||
|
### `compression` subsection
|
||||||
|
|
||||||
|
Contains compression config.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
compression:
|
||||||
|
enabled: true
|
||||||
|
level: smallest_size
|
||||||
|
exclude_content_types:
|
||||||
|
- audio/*
|
||||||
|
- video/*
|
||||||
|
estimate_compressibility: true
|
||||||
|
estimate_compressibility_threshold: 0.7
|
||||||
|
```
|
||||||
|
|
||||||
|
| Parameter | Type | Default value | Description |
|
||||||
|
| ------------------------------------ | ---------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| `enabled` | `bool` | `false` | Flag to enable compression. |
|
||||||
|
| `level` | `string` | `optimal` | Compression level. Available values are `optimal`, `fastest`, `smallest_size`. |
|
||||||
|
| `exclude_content_types` | `[]string` | | List of content-types to disable compression for. Content-type is taken from `Content-Type` object attribute. Each element can contain a star `*` as a first (last) character, which matches any prefix (suffix). |
|
||||||
|
| `estimate_compressibility` | `bool` | `false` | If `true`, then noramalized compressibility estimation is used to decide compress data or not. |
|
||||||
|
| `estimate_compressibility_threshold` | `float` | `0.1` | Normilized compressibility estimate threshold: data will compress if estimation if greater than this value. |
|
||||||
|
|
||||||
### `blobstor` subsection
|
### `blobstor` subsection
|
||||||
|
|
||||||
Contains a list of substorages each with it's own type.
|
Contains a list of substorages each with it's own type.
|
||||||
|
@ -380,10 +415,11 @@ node:
|
||||||
path: /sessions
|
path: /sessions
|
||||||
persistent_state:
|
persistent_state:
|
||||||
path: /state
|
path: /state
|
||||||
|
locode_db_path: "/path/to/locode/db"
|
||||||
```
|
```
|
||||||
|
|
||||||
| Parameter | Type | Default value | Description |
|
| Parameter | Type | Default value | Description |
|
||||||
|-----------------------|---------------------------------------------------------------|---------------|-------------------------------------------------------------------------|
|
|-----------------------|---------------------------------------------------------------|---------------|-----------------------------------------------------------------------------------------------------|
|
||||||
| `key` | `string` | | Path to the binary-encoded private key. |
|
| `key` | `string` | | Path to the binary-encoded private key. |
|
||||||
| `wallet` | [Wallet config](#wallet-subsection) | | Wallet configuration. Has no effect if `key` is provided. |
|
| `wallet` | [Wallet config](#wallet-subsection) | | Wallet configuration. Has no effect if `key` is provided. |
|
||||||
| `addresses` | `[]string` | | Addresses advertised in the netmap. |
|
| `addresses` | `[]string` | | Addresses advertised in the netmap. |
|
||||||
|
@ -391,6 +427,7 @@ node:
|
||||||
| `relay` | `bool` | | Enable relay mode. |
|
| `relay` | `bool` | | Enable relay mode. |
|
||||||
| `persistent_sessions` | [Persistent sessions config](#persistent_sessions-subsection) | | Persistent session token store configuration. |
|
| `persistent_sessions` | [Persistent sessions config](#persistent_sessions-subsection) | | Persistent session token store configuration. |
|
||||||
| `persistent_state` | [Persistent state config](#persistent_state-subsection) | | Persistent state configuration. |
|
| `persistent_state` | [Persistent state config](#persistent_state-subsection) | | Persistent state configuration. |
|
||||||
|
| `locode_db_path` | `string` | empty | Path to UN/LOCODE [database](https://git.frostfs.info/TrueCloudLab/frostfs-locode-db/) for FrostFS. |
|
||||||
|
|
||||||
## `wallet` subsection
|
## `wallet` subsection
|
||||||
N3 wallet configuration.
|
N3 wallet configuration.
|
||||||
|
|
4
go.mod
4
go.mod
|
@ -9,10 +9,10 @@ require (
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-locode-db v0.5.2
|
git.frostfs.info/TrueCloudLab/frostfs-locode-db v0.5.2
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20250321063246-93b681a20248
|
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20250321063246-93b681a20248
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-qos v0.0.0-20250331080422-b5ed0b6eff47
|
git.frostfs.info/TrueCloudLab/frostfs-qos v0.0.0-20250331080422-b5ed0b6eff47
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20250326101739-4d36a49d3945
|
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20250404152210-6458c11e833d
|
||||||
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-20240822104152-a3bc3099bd5b
|
git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20250402100642-acd94d200f88
|
||||||
git.frostfs.info/TrueCloudLab/tzhash v1.8.0
|
git.frostfs.info/TrueCloudLab/tzhash v1.8.0
|
||||||
git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20240124114243-cb2e66427d02
|
git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20240124114243-cb2e66427d02
|
||||||
github.com/VictoriaMetrics/easyproto v0.1.4
|
github.com/VictoriaMetrics/easyproto v0.1.4
|
||||||
|
|
8
go.sum
8
go.sum
|
@ -10,16 +10,16 @@ git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20250321063246-93b681
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20250321063246-93b681a20248/go.mod h1:kbwB4v2o6RyOfCo9kEFeUDZIX3LKhmS0yXPrtvzkQ1g=
|
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20250321063246-93b681a20248/go.mod h1:kbwB4v2o6RyOfCo9kEFeUDZIX3LKhmS0yXPrtvzkQ1g=
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-qos v0.0.0-20250331080422-b5ed0b6eff47 h1:O2c3VOlaGZ862hf2ZPLBMdTG6vGJzhIgDvFEFGfntzU=
|
git.frostfs.info/TrueCloudLab/frostfs-qos v0.0.0-20250331080422-b5ed0b6eff47 h1:O2c3VOlaGZ862hf2ZPLBMdTG6vGJzhIgDvFEFGfntzU=
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-qos v0.0.0-20250331080422-b5ed0b6eff47/go.mod h1:PCijYq4oa8vKtIEcUX6jRiszI6XAW+nBwU+T1kB4d1U=
|
git.frostfs.info/TrueCloudLab/frostfs-qos v0.0.0-20250331080422-b5ed0b6eff47/go.mod h1:PCijYq4oa8vKtIEcUX6jRiszI6XAW+nBwU+T1kB4d1U=
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20250326101739-4d36a49d3945 h1:zM2l316J55h9p30snl6vHBI/h0xmnuqZjnxIjRDtJZw=
|
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20250404152210-6458c11e833d h1:ZLKDupw362Ciing7kdIZhDYGMyo2QZyJ6sS/8X9QWJ0=
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20250326101739-4d36a49d3945/go.mod h1:aQpPWfG8oyfJ2X+FenPTJpSRWZjwcP5/RAtkW+/VEX8=
|
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20250404152210-6458c11e833d/go.mod h1:2PWt5GwJTnhjHp+mankcfCeAJBMn7puxPm+RS+lliVk=
|
||||||
git.frostfs.info/TrueCloudLab/hrw v1.2.1 h1:ccBRK21rFvY5R1WotI6LNoPlizk7qSvdfD8lNIRudVc=
|
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/hrw v1.2.1/go.mod h1:C1Ygde2n843yTZEQ0FP69jYiuaYV0kriLvP4zm8JuvM=
|
||||||
git.frostfs.info/TrueCloudLab/multinet v0.0.0-20241015075604-6cb0d80e0972 h1:/960fWeyn2AFHwQUwDsWB3sbP6lTEnFnMzLMM6tx6N8=
|
git.frostfs.info/TrueCloudLab/multinet v0.0.0-20241015075604-6cb0d80e0972 h1:/960fWeyn2AFHwQUwDsWB3sbP6lTEnFnMzLMM6tx6N8=
|
||||||
git.frostfs.info/TrueCloudLab/multinet v0.0.0-20241015075604-6cb0d80e0972/go.mod h1:2hM42MBrlhvN6XToaW6OWNk5ZLcu1FhaukGgxtfpDDI=
|
git.frostfs.info/TrueCloudLab/multinet v0.0.0-20241015075604-6cb0d80e0972/go.mod h1:2hM42MBrlhvN6XToaW6OWNk5ZLcu1FhaukGgxtfpDDI=
|
||||||
git.frostfs.info/TrueCloudLab/neoneo-go v0.106.1-0.20241015133823-8aee80dbdc07 h1:gPaqGsk6gSWQyNVjaStydfUz6Z/loHc9XyvGrJ5qSPY=
|
git.frostfs.info/TrueCloudLab/neoneo-go v0.106.1-0.20241015133823-8aee80dbdc07 h1:gPaqGsk6gSWQyNVjaStydfUz6Z/loHc9XyvGrJ5qSPY=
|
||||||
git.frostfs.info/TrueCloudLab/neoneo-go v0.106.1-0.20241015133823-8aee80dbdc07/go.mod h1:bZyJexBlrja4ngxiBgo8by5pVHuAbhg9l09/8yVGDyg=
|
git.frostfs.info/TrueCloudLab/neoneo-go v0.106.1-0.20241015133823-8aee80dbdc07/go.mod h1:bZyJexBlrja4ngxiBgo8by5pVHuAbhg9l09/8yVGDyg=
|
||||||
git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240822104152-a3bc3099bd5b h1:M50kdfrf/h8c3cz0bJ2AEUcbXvAlPFVC1Wp1WkfZ/8E=
|
git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20250402100642-acd94d200f88 h1:V0a7ia84ZpSM2YxpJq1SKLQfeYmsqFWqcxwweBHJIzc=
|
||||||
git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240822104152-a3bc3099bd5b/go.mod h1:GZTk55RI4dKzsK6BCn5h2xxE28UHNfgoq/NJxW/LQ6A=
|
git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20250402100642-acd94d200f88/go.mod h1:GZTk55RI4dKzsK6BCn5h2xxE28UHNfgoq/NJxW/LQ6A=
|
||||||
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 h1:M2KR3iBj7WpY3hP10IevfIB9MURr4O9mwVfJ+SjT3HA=
|
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 h1:M2KR3iBj7WpY3hP10IevfIB9MURr4O9mwVfJ+SjT3HA=
|
||||||
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0/go.mod h1:okpbKfVYf/BpejtfFTfhZqFP+sZ8rsHrP8Rr/jYPNRc=
|
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0/go.mod h1:okpbKfVYf/BpejtfFTfhZqFP+sZ8rsHrP8Rr/jYPNRc=
|
||||||
git.frostfs.info/TrueCloudLab/tzhash v1.8.0 h1:UFMnUIk0Zh17m8rjGHJMqku2hCgaXDqjqZzS4gsb4UA=
|
git.frostfs.info/TrueCloudLab/tzhash v1.8.0 h1:UFMnUIk0Zh17m8rjGHJMqku2hCgaXDqjqZzS4gsb4UA=
|
||||||
|
|
|
@ -1,9 +1,29 @@
|
||||||
package assert
|
package assert
|
||||||
|
|
||||||
import "strings"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
func True(cond bool, details ...string) {
|
func True(cond bool, details ...string) {
|
||||||
if !cond {
|
if !cond {
|
||||||
panic(strings.Join(details, " "))
|
panic(strings.Join(details, " "))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func False(cond bool, details ...string) {
|
||||||
|
if cond {
|
||||||
|
panic(strings.Join(details, " "))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NoError(err error, details ...string) {
|
||||||
|
if err != nil {
|
||||||
|
content := fmt.Sprintf("BUG: %v: %s", err, strings.Join(details, " "))
|
||||||
|
panic(content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Fail(details ...string) {
|
||||||
|
panic(strings.Join(details, " "))
|
||||||
|
}
|
||||||
|
|
|
@ -515,4 +515,6 @@ const (
|
||||||
FailedToGetNetmapToAdjustIOTag = "failed to get netmap to adjust IO tag"
|
FailedToGetNetmapToAdjustIOTag = "failed to get netmap to adjust IO tag"
|
||||||
FailedToValidateIncomingIOTag = "failed to validate incoming IO tag, replaced with `client`"
|
FailedToValidateIncomingIOTag = "failed to validate incoming IO tag, replaced with `client`"
|
||||||
WriteCacheFailedToAcquireRPSQuota = "writecache failed to acquire RPS quota to flush object"
|
WriteCacheFailedToAcquireRPSQuota = "writecache failed to acquire RPS quota to flush object"
|
||||||
|
FailedToUpdateNetmapCandidates = "update netmap candidates failed"
|
||||||
|
UnknownCompressionLevelDefaultWillBeUsed = "unknown compression level, 'optimal' will be used"
|
||||||
)
|
)
|
||||||
|
|
31
internal/qos/config.go
Normal file
31
internal/qos/config.go
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package qos
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
NoLimit int64 = math.MaxInt64
|
||||||
|
DefaultIdleTimeout = 5 * time.Minute
|
||||||
|
)
|
||||||
|
|
||||||
|
type LimiterConfig struct {
|
||||||
|
Read OpConfig
|
||||||
|
Write OpConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
type OpConfig struct {
|
||||||
|
MaxWaitingOps int64
|
||||||
|
MaxRunningOps int64
|
||||||
|
IdleTimeout time.Duration
|
||||||
|
Tags []IOTagConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
type IOTagConfig struct {
|
||||||
|
Tag string
|
||||||
|
Weight *float64
|
||||||
|
LimitOps *float64
|
||||||
|
ReservedOps *float64
|
||||||
|
Prohibited bool
|
||||||
|
}
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/engine/shard/limits"
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-qos/scheduling"
|
"git.frostfs.info/TrueCloudLab/frostfs-qos/scheduling"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-qos/tagging"
|
"git.frostfs.info/TrueCloudLab/frostfs-qos/tagging"
|
||||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
|
@ -37,15 +36,15 @@ type scheduler interface {
|
||||||
Close()
|
Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLimiter(c *limits.Config) (Limiter, error) {
|
func NewLimiter(c LimiterConfig) (Limiter, error) {
|
||||||
if err := validateConfig(c); err != nil {
|
if err := c.Validate(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
readScheduler, err := createScheduler(c.Read())
|
readScheduler, err := createScheduler(c.Read)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("create read scheduler: %w", err)
|
return nil, fmt.Errorf("create read scheduler: %w", err)
|
||||||
}
|
}
|
||||||
writeScheduler, err := createScheduler(c.Write())
|
writeScheduler, err := createScheduler(c.Write)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("create write scheduler: %w", err)
|
return nil, fmt.Errorf("create write scheduler: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -63,8 +62,8 @@ func NewLimiter(c *limits.Config) (Limiter, error) {
|
||||||
return l, nil
|
return l, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createScheduler(config limits.OpConfig) (scheduler, error) {
|
func createScheduler(config OpConfig) (scheduler, error) {
|
||||||
if len(config.Tags) == 0 && config.MaxWaitingOps == limits.NoLimit {
|
if len(config.Tags) == 0 && config.MaxWaitingOps == NoLimit {
|
||||||
return newSemaphoreScheduler(config.MaxRunningOps), nil
|
return newSemaphoreScheduler(config.MaxRunningOps), nil
|
||||||
}
|
}
|
||||||
return scheduling.NewMClock(
|
return scheduling.NewMClock(
|
||||||
|
@ -72,7 +71,7 @@ func createScheduler(config limits.OpConfig) (scheduler, error) {
|
||||||
converToSchedulingTags(config.Tags), config.IdleTimeout)
|
converToSchedulingTags(config.Tags), config.IdleTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
func converToSchedulingTags(limits []limits.IOTagConfig) map[string]scheduling.TagInfo {
|
func converToSchedulingTags(limits []IOTagConfig) map[string]scheduling.TagInfo {
|
||||||
result := make(map[string]scheduling.TagInfo)
|
result := make(map[string]scheduling.TagInfo)
|
||||||
for _, tag := range []IOTag{IOTagBackground, IOTagClient, IOTagInternal, IOTagPolicer, IOTagTreeSync, IOTagWritecache} {
|
for _, tag := range []IOTag{IOTagBackground, IOTagClient, IOTagInternal, IOTagPolicer, IOTagTreeSync, IOTagWritecache} {
|
||||||
result[tag.String()] = scheduling.TagInfo{
|
result[tag.String()] = scheduling.TagInfo{
|
||||||
|
|
|
@ -4,8 +4,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/engine/shard/limits"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var errWeightsMustBeSpecified = errors.New("invalid weights: weights must be specified for all tags or not specified for any")
|
var errWeightsMustBeSpecified = errors.New("invalid weights: weights must be specified for all tags or not specified for any")
|
||||||
|
@ -14,17 +12,17 @@ type tagConfig struct {
|
||||||
Shares, Limit, Reserved *float64
|
Shares, Limit, Reserved *float64
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateConfig(c *limits.Config) error {
|
func (c *LimiterConfig) Validate() error {
|
||||||
if err := validateOpConfig(c.Read()); err != nil {
|
if err := validateOpConfig(c.Read); err != nil {
|
||||||
return fmt.Errorf("limits 'read' section validation error: %w", err)
|
return fmt.Errorf("limits 'read' section validation error: %w", err)
|
||||||
}
|
}
|
||||||
if err := validateOpConfig(c.Write()); err != nil {
|
if err := validateOpConfig(c.Write); err != nil {
|
||||||
return fmt.Errorf("limits 'write' section validation error: %w", err)
|
return fmt.Errorf("limits 'write' section validation error: %w", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateOpConfig(c limits.OpConfig) error {
|
func validateOpConfig(c OpConfig) error {
|
||||||
if c.MaxRunningOps <= 0 {
|
if c.MaxRunningOps <= 0 {
|
||||||
return fmt.Errorf("invalid 'max_running_ops = %d': must be greater than zero", c.MaxRunningOps)
|
return fmt.Errorf("invalid 'max_running_ops = %d': must be greater than zero", c.MaxRunningOps)
|
||||||
}
|
}
|
||||||
|
@ -40,7 +38,7 @@ func validateOpConfig(c limits.OpConfig) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateTags(configTags []limits.IOTagConfig) error {
|
func validateTags(configTags []IOTagConfig) error {
|
||||||
tags := map[IOTag]tagConfig{
|
tags := map[IOTag]tagConfig{
|
||||||
IOTagBackground: {},
|
IOTagBackground: {},
|
||||||
IOTagClient: {},
|
IOTagClient: {},
|
||||||
|
|
|
@ -3,6 +3,7 @@ package client
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"iter"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/network"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/network"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
||||||
|
@ -19,7 +20,7 @@ func nodeInfoFromKeyAddr(dst *NodeInfo, k []byte, a, external network.AddressGro
|
||||||
// Args must not be nil.
|
// Args must not be nil.
|
||||||
func NodeInfoFromRawNetmapElement(dst *NodeInfo, info interface {
|
func NodeInfoFromRawNetmapElement(dst *NodeInfo, info interface {
|
||||||
PublicKey() []byte
|
PublicKey() []byte
|
||||||
IterateAddresses(func(string) bool)
|
Addresses() iter.Seq[string]
|
||||||
NumberOfAddresses() int
|
NumberOfAddresses() int
|
||||||
ExternalAddresses() []string
|
ExternalAddresses() []string
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
package netmap
|
package netmap
|
||||||
|
|
||||||
import "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
import (
|
||||||
|
"iter"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||||
|
)
|
||||||
|
|
||||||
// Node is a named type of netmap.NodeInfo which provides interface needed
|
// Node is a named type of netmap.NodeInfo which provides interface needed
|
||||||
// in the current repository. Node is expected to be used everywhere instead
|
// in the current repository. Node is expected to be used everywhere instead
|
||||||
|
@ -14,10 +18,20 @@ func (x Node) PublicKey() []byte {
|
||||||
return (netmap.NodeInfo)(x).PublicKey()
|
return (netmap.NodeInfo)(x).PublicKey()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Addresses returns an iterator over all announced network addresses.
|
||||||
|
func (x Node) Addresses() iter.Seq[string] {
|
||||||
|
return (netmap.NodeInfo)(x).NetworkEndpoints()
|
||||||
|
}
|
||||||
|
|
||||||
// IterateAddresses iterates over all announced network addresses
|
// IterateAddresses iterates over all announced network addresses
|
||||||
// and passes them into f. Handler MUST NOT be nil.
|
// and passes them into f. Handler MUST NOT be nil.
|
||||||
|
// Deprecated: use [Node.Addresses] instead.
|
||||||
func (x Node) IterateAddresses(f func(string) bool) {
|
func (x Node) IterateAddresses(f func(string) bool) {
|
||||||
(netmap.NodeInfo)(x).IterateNetworkEndpoints(f)
|
for s := range (netmap.NodeInfo)(x).NetworkEndpoints() {
|
||||||
|
if f(s) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NumberOfAddresses returns number of announced network addresses.
|
// NumberOfAddresses returns number of announced network addresses.
|
||||||
|
|
|
@ -13,6 +13,13 @@ type ECInfo struct {
|
||||||
Total uint32
|
Total uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *ECInfo) String() string {
|
||||||
|
if v == nil {
|
||||||
|
return "<nil>"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("parent ID: %s, index: %d, total %d", v.ParentID, v.Index, v.Total)
|
||||||
|
}
|
||||||
|
|
||||||
// Info groups object address with its FrostFS
|
// Info groups object address with its FrostFS
|
||||||
// object info.
|
// object info.
|
||||||
type Info struct {
|
type Info struct {
|
||||||
|
@ -23,5 +30,5 @@ type Info struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v Info) String() string {
|
func (v Info) String() string {
|
||||||
return fmt.Sprintf("address: %s, type: %s, is linking: %t", v.Address, v.Type, v.IsLinkingObject)
|
return fmt.Sprintf("address: %s, type: %s, is linking: %t, EC header: %s", v.Address, v.Type, v.IsLinkingObject, v.ECInfo)
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ func (s *Server) initNetmapProcessor(ctx context.Context, cfg *viper.Viper,
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
s.netmapProcessor, err = netmap.New(&netmap.Params{
|
s.netmapProcessor, err = netmap.New(&netmap.Params{
|
||||||
Log: s.log,
|
Log: s.log.WithTag(logger.TagProcessor),
|
||||||
Metrics: s.irMetrics,
|
Metrics: s.irMetrics,
|
||||||
PoolSize: poolSize,
|
PoolSize: poolSize,
|
||||||
NetmapClient: netmap.NewNetmapClient(s.netmapClient),
|
NetmapClient: netmap.NewNetmapClient(s.netmapClient),
|
||||||
|
@ -159,7 +159,7 @@ func (s *Server) createAlphaSync(cfg *viper.Viper, frostfsCli *frostfsClient.Cli
|
||||||
} else {
|
} else {
|
||||||
// create governance processor
|
// create governance processor
|
||||||
governanceProcessor, err := governance.New(&governance.Params{
|
governanceProcessor, err := governance.New(&governance.Params{
|
||||||
Log: s.log,
|
Log: s.log.WithTag(logger.TagProcessor),
|
||||||
Metrics: s.irMetrics,
|
Metrics: s.irMetrics,
|
||||||
FrostFSClient: frostfsCli,
|
FrostFSClient: frostfsCli,
|
||||||
AlphabetState: s,
|
AlphabetState: s,
|
||||||
|
@ -225,7 +225,7 @@ func (s *Server) initAlphabetProcessor(ctx context.Context, cfg *viper.Viper) er
|
||||||
// create alphabet processor
|
// create alphabet processor
|
||||||
s.alphabetProcessor, err = alphabet.New(&alphabet.Params{
|
s.alphabetProcessor, err = alphabet.New(&alphabet.Params{
|
||||||
ParsedWallets: parsedWallets,
|
ParsedWallets: parsedWallets,
|
||||||
Log: s.log,
|
Log: s.log.WithTag(logger.TagProcessor),
|
||||||
Metrics: s.irMetrics,
|
Metrics: s.irMetrics,
|
||||||
PoolSize: poolSize,
|
PoolSize: poolSize,
|
||||||
AlphabetContracts: s.contracts.alphabet,
|
AlphabetContracts: s.contracts.alphabet,
|
||||||
|
@ -247,7 +247,7 @@ func (s *Server) initContainerProcessor(ctx context.Context, cfg *viper.Viper, c
|
||||||
s.log.Debug(ctx, logs.ContainerContainerWorkerPool, zap.Int("size", poolSize))
|
s.log.Debug(ctx, logs.ContainerContainerWorkerPool, zap.Int("size", poolSize))
|
||||||
// container processor
|
// container processor
|
||||||
containerProcessor, err := cont.New(&cont.Params{
|
containerProcessor, err := cont.New(&cont.Params{
|
||||||
Log: s.log,
|
Log: s.log.WithTag(logger.TagProcessor),
|
||||||
Metrics: s.irMetrics,
|
Metrics: s.irMetrics,
|
||||||
PoolSize: poolSize,
|
PoolSize: poolSize,
|
||||||
AlphabetState: s,
|
AlphabetState: s,
|
||||||
|
@ -268,7 +268,7 @@ func (s *Server) initBalanceProcessor(ctx context.Context, cfg *viper.Viper, fro
|
||||||
s.log.Debug(ctx, logs.BalanceBalanceWorkerPool, zap.Int("size", poolSize))
|
s.log.Debug(ctx, logs.BalanceBalanceWorkerPool, zap.Int("size", poolSize))
|
||||||
// create balance processor
|
// create balance processor
|
||||||
balanceProcessor, err := balance.New(&balance.Params{
|
balanceProcessor, err := balance.New(&balance.Params{
|
||||||
Log: s.log,
|
Log: s.log.WithTag(logger.TagProcessor),
|
||||||
Metrics: s.irMetrics,
|
Metrics: s.irMetrics,
|
||||||
PoolSize: poolSize,
|
PoolSize: poolSize,
|
||||||
FrostFSClient: frostfsCli,
|
FrostFSClient: frostfsCli,
|
||||||
|
@ -291,7 +291,7 @@ func (s *Server) initFrostFSMainnetProcessor(ctx context.Context, cfg *viper.Vip
|
||||||
s.log.Debug(ctx, logs.FrostFSFrostfsWorkerPool, zap.Int("size", poolSize))
|
s.log.Debug(ctx, logs.FrostFSFrostfsWorkerPool, zap.Int("size", poolSize))
|
||||||
|
|
||||||
frostfsProcessor, err := frostfs.New(&frostfs.Params{
|
frostfsProcessor, err := frostfs.New(&frostfs.Params{
|
||||||
Log: s.log,
|
Log: s.log.WithTag(logger.TagProcessor),
|
||||||
Metrics: s.irMetrics,
|
Metrics: s.irMetrics,
|
||||||
PoolSize: poolSize,
|
PoolSize: poolSize,
|
||||||
FrostFSContract: s.contracts.frostfs,
|
FrostFSContract: s.contracts.frostfs,
|
||||||
|
@ -342,7 +342,7 @@ func (s *Server) initGRPCServer(ctx context.Context, cfg *viper.Viper, log *logg
|
||||||
|
|
||||||
controlSvc := controlsrv.NewAuditService(controlsrv.New(p, s.netmapClient, s.containerClient,
|
controlSvc := controlsrv.NewAuditService(controlsrv.New(p, s.netmapClient, s.containerClient,
|
||||||
controlsrv.WithAllowedKeys(authKeys),
|
controlsrv.WithAllowedKeys(authKeys),
|
||||||
), log, audit)
|
), log.WithTag(logger.TagGrpcSvc), audit)
|
||||||
|
|
||||||
grpcControlSrv := grpc.NewServer()
|
grpcControlSrv := grpc.NewServer()
|
||||||
control.RegisterControlServiceServer(grpcControlSrv, controlSvc)
|
control.RegisterControlServiceServer(grpcControlSrv, controlSvc)
|
||||||
|
@ -458,7 +458,7 @@ func (s *Server) initMorph(ctx context.Context, cfg *viper.Viper, errChan chan<-
|
||||||
}
|
}
|
||||||
|
|
||||||
morphChain := &chainParams{
|
morphChain := &chainParams{
|
||||||
log: s.log,
|
log: s.log.WithTag(logger.TagMorph),
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
key: s.key,
|
key: s.key,
|
||||||
name: morphPrefix,
|
name: morphPrefix,
|
||||||
|
|
|
@ -339,7 +339,7 @@ func New(ctx context.Context, log *logger.Logger, cfg *viper.Viper, errChan chan
|
||||||
) (*Server, error) {
|
) (*Server, error) {
|
||||||
var err error
|
var err error
|
||||||
server := &Server{
|
server := &Server{
|
||||||
log: log,
|
log: log.WithTag(logger.TagIr),
|
||||||
irMetrics: metrics,
|
irMetrics: metrics,
|
||||||
cmode: cmode,
|
cmode: cmode,
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,7 +110,7 @@ func WithFullSizeLimit(lim uint64) Option {
|
||||||
// WithLogger returns an option to specify Blobovnicza's logger.
|
// WithLogger returns an option to specify Blobovnicza's logger.
|
||||||
func WithLogger(l *logger.Logger) Option {
|
func WithLogger(l *logger.Logger) Option {
|
||||||
return func(c *cfg) {
|
return func(c *cfg) {
|
||||||
c.log = l.With(zap.String("component", "Blobovnicza"))
|
c.log = l
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -158,11 +158,11 @@ func (b *Blobovniczas) Path() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetCompressor implements common.Storage.
|
// SetCompressor implements common.Storage.
|
||||||
func (b *Blobovniczas) SetCompressor(cc *compression.Config) {
|
func (b *Blobovniczas) SetCompressor(cc *compression.Compressor) {
|
||||||
b.compression = cc
|
b.compression = cc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Blobovniczas) Compressor() *compression.Config {
|
func (b *Blobovniczas) Compressor() *compression.Compressor {
|
||||||
return b.compression
|
return b.compression
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,8 @@ func TestBlobovniczaTree_Concurrency(t *testing.T) {
|
||||||
|
|
||||||
st := NewBlobovniczaTree(
|
st := NewBlobovniczaTree(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
WithLogger(test.NewLogger(t)),
|
WithBlobovniczaLogger(test.NewLogger(t)),
|
||||||
|
WithBlobovniczaTreeLogger(test.NewLogger(t)),
|
||||||
WithObjectSizeLimit(1024),
|
WithObjectSizeLimit(1024),
|
||||||
WithBlobovniczaShallowWidth(10),
|
WithBlobovniczaShallowWidth(10),
|
||||||
WithBlobovniczaShallowDepth(1),
|
WithBlobovniczaShallowDepth(1),
|
||||||
|
|
|
@ -19,7 +19,8 @@ func TestExistsInvalidStorageID(t *testing.T) {
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
b := NewBlobovniczaTree(
|
b := NewBlobovniczaTree(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
WithLogger(test.NewLogger(t)),
|
WithBlobovniczaLogger(test.NewLogger(t)),
|
||||||
|
WithBlobovniczaTreeLogger(test.NewLogger(t)),
|
||||||
WithObjectSizeLimit(1024),
|
WithObjectSizeLimit(1024),
|
||||||
WithBlobovniczaShallowWidth(2),
|
WithBlobovniczaShallowWidth(2),
|
||||||
WithBlobovniczaShallowDepth(2),
|
WithBlobovniczaShallowDepth(2),
|
||||||
|
|
|
@ -15,7 +15,8 @@ func TestGeneric(t *testing.T) {
|
||||||
helper := func(t *testing.T, dir string) common.Storage {
|
helper := func(t *testing.T, dir string) common.Storage {
|
||||||
return NewBlobovniczaTree(
|
return NewBlobovniczaTree(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
WithLogger(test.NewLogger(t)),
|
WithBlobovniczaLogger(test.NewLogger(t)),
|
||||||
|
WithBlobovniczaTreeLogger(test.NewLogger(t)),
|
||||||
WithObjectSizeLimit(maxObjectSize),
|
WithObjectSizeLimit(maxObjectSize),
|
||||||
WithBlobovniczaShallowWidth(2),
|
WithBlobovniczaShallowWidth(2),
|
||||||
WithBlobovniczaShallowDepth(2),
|
WithBlobovniczaShallowDepth(2),
|
||||||
|
@ -43,7 +44,8 @@ func TestControl(t *testing.T) {
|
||||||
newTree := func(t *testing.T) common.Storage {
|
newTree := func(t *testing.T) common.Storage {
|
||||||
return NewBlobovniczaTree(
|
return NewBlobovniczaTree(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
WithLogger(test.NewLogger(t)),
|
WithBlobovniczaLogger(test.NewLogger(t)),
|
||||||
|
WithBlobovniczaTreeLogger(test.NewLogger(t)),
|
||||||
WithObjectSizeLimit(maxObjectSize),
|
WithObjectSizeLimit(maxObjectSize),
|
||||||
WithBlobovniczaShallowWidth(2),
|
WithBlobovniczaShallowWidth(2),
|
||||||
WithBlobovniczaShallowDepth(2),
|
WithBlobovniczaShallowDepth(2),
|
||||||
|
|
|
@ -141,8 +141,8 @@ func (b *sharedDB) SystemPath() string {
|
||||||
return b.path
|
return b.path
|
||||||
}
|
}
|
||||||
|
|
||||||
// levelDbManager stores pointers of the sharedDB's for the leaf directory of the blobovnicza tree.
|
// levelDBManager stores pointers of the sharedDB's for the leaf directory of the blobovnicza tree.
|
||||||
type levelDbManager struct {
|
type levelDBManager struct {
|
||||||
dbMtx *sync.RWMutex
|
dbMtx *sync.RWMutex
|
||||||
databases map[uint64]*sharedDB
|
databases map[uint64]*sharedDB
|
||||||
|
|
||||||
|
@ -157,8 +157,8 @@ type levelDbManager struct {
|
||||||
|
|
||||||
func newLevelDBManager(options []blobovnicza.Option, rootPath string, lvlPath string,
|
func newLevelDBManager(options []blobovnicza.Option, rootPath string, lvlPath string,
|
||||||
readOnly bool, metrics blobovnicza.Metrics, openDBCounter *openDBCounter, closedFlag *atomic.Bool, log *logger.Logger,
|
readOnly bool, metrics blobovnicza.Metrics, openDBCounter *openDBCounter, closedFlag *atomic.Bool, log *logger.Logger,
|
||||||
) *levelDbManager {
|
) *levelDBManager {
|
||||||
result := &levelDbManager{
|
result := &levelDBManager{
|
||||||
databases: make(map[uint64]*sharedDB),
|
databases: make(map[uint64]*sharedDB),
|
||||||
dbMtx: &sync.RWMutex{},
|
dbMtx: &sync.RWMutex{},
|
||||||
|
|
||||||
|
@ -173,7 +173,7 @@ func newLevelDBManager(options []blobovnicza.Option, rootPath string, lvlPath st
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *levelDbManager) GetByIndex(idx uint64) *sharedDB {
|
func (m *levelDBManager) GetByIndex(idx uint64) *sharedDB {
|
||||||
res := m.getDBIfExists(idx)
|
res := m.getDBIfExists(idx)
|
||||||
if res != nil {
|
if res != nil {
|
||||||
return res
|
return res
|
||||||
|
@ -181,14 +181,14 @@ func (m *levelDbManager) GetByIndex(idx uint64) *sharedDB {
|
||||||
return m.getOrCreateDB(idx)
|
return m.getOrCreateDB(idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *levelDbManager) getDBIfExists(idx uint64) *sharedDB {
|
func (m *levelDBManager) getDBIfExists(idx uint64) *sharedDB {
|
||||||
m.dbMtx.RLock()
|
m.dbMtx.RLock()
|
||||||
defer m.dbMtx.RUnlock()
|
defer m.dbMtx.RUnlock()
|
||||||
|
|
||||||
return m.databases[idx]
|
return m.databases[idx]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *levelDbManager) getOrCreateDB(idx uint64) *sharedDB {
|
func (m *levelDBManager) getOrCreateDB(idx uint64) *sharedDB {
|
||||||
m.dbMtx.Lock()
|
m.dbMtx.Lock()
|
||||||
defer m.dbMtx.Unlock()
|
defer m.dbMtx.Unlock()
|
||||||
|
|
||||||
|
@ -202,7 +202,7 @@ func (m *levelDbManager) getOrCreateDB(idx uint64) *sharedDB {
|
||||||
return db
|
return db
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *levelDbManager) hasAnyDB() bool {
|
func (m *levelDBManager) hasAnyDB() bool {
|
||||||
m.dbMtx.RLock()
|
m.dbMtx.RLock()
|
||||||
defer m.dbMtx.RUnlock()
|
defer m.dbMtx.RUnlock()
|
||||||
|
|
||||||
|
@ -213,7 +213,7 @@ func (m *levelDbManager) hasAnyDB() bool {
|
||||||
//
|
//
|
||||||
// The blobovnicza opens at the first request, closes after the last request.
|
// The blobovnicza opens at the first request, closes after the last request.
|
||||||
type dbManager struct {
|
type dbManager struct {
|
||||||
levelToManager map[string]*levelDbManager
|
levelToManager map[string]*levelDBManager
|
||||||
levelToManagerGuard *sync.RWMutex
|
levelToManagerGuard *sync.RWMutex
|
||||||
closedFlag *atomic.Bool
|
closedFlag *atomic.Bool
|
||||||
dbCounter *openDBCounter
|
dbCounter *openDBCounter
|
||||||
|
@ -231,7 +231,7 @@ func newDBManager(rootPath string, options []blobovnicza.Option, readOnly bool,
|
||||||
options: options,
|
options: options,
|
||||||
readOnly: readOnly,
|
readOnly: readOnly,
|
||||||
metrics: metrics,
|
metrics: metrics,
|
||||||
levelToManager: make(map[string]*levelDbManager),
|
levelToManager: make(map[string]*levelDBManager),
|
||||||
levelToManagerGuard: &sync.RWMutex{},
|
levelToManagerGuard: &sync.RWMutex{},
|
||||||
log: log,
|
log: log,
|
||||||
closedFlag: &atomic.Bool{},
|
closedFlag: &atomic.Bool{},
|
||||||
|
@ -266,7 +266,7 @@ func (m *dbManager) Close() {
|
||||||
m.dbCounter.WaitUntilAllClosed()
|
m.dbCounter.WaitUntilAllClosed()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *dbManager) getLevelManager(lvlPath string) *levelDbManager {
|
func (m *dbManager) getLevelManager(lvlPath string) *levelDBManager {
|
||||||
result := m.getLevelManagerIfExists(lvlPath)
|
result := m.getLevelManagerIfExists(lvlPath)
|
||||||
if result != nil {
|
if result != nil {
|
||||||
return result
|
return result
|
||||||
|
@ -274,14 +274,14 @@ func (m *dbManager) getLevelManager(lvlPath string) *levelDbManager {
|
||||||
return m.getOrCreateLevelManager(lvlPath)
|
return m.getOrCreateLevelManager(lvlPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *dbManager) getLevelManagerIfExists(lvlPath string) *levelDbManager {
|
func (m *dbManager) getLevelManagerIfExists(lvlPath string) *levelDBManager {
|
||||||
m.levelToManagerGuard.RLock()
|
m.levelToManagerGuard.RLock()
|
||||||
defer m.levelToManagerGuard.RUnlock()
|
defer m.levelToManagerGuard.RUnlock()
|
||||||
|
|
||||||
return m.levelToManager[lvlPath]
|
return m.levelToManager[lvlPath]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *dbManager) getOrCreateLevelManager(lvlPath string) *levelDbManager {
|
func (m *dbManager) getOrCreateLevelManager(lvlPath string) *levelDBManager {
|
||||||
m.levelToManagerGuard.Lock()
|
m.levelToManagerGuard.Lock()
|
||||||
defer m.levelToManagerGuard.Unlock()
|
defer m.levelToManagerGuard.Unlock()
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ type cfg struct {
|
||||||
openedCacheSize int
|
openedCacheSize int
|
||||||
blzShallowDepth uint64
|
blzShallowDepth uint64
|
||||||
blzShallowWidth uint64
|
blzShallowWidth uint64
|
||||||
compression *compression.Config
|
compression *compression.Compressor
|
||||||
blzOpts []blobovnicza.Option
|
blzOpts []blobovnicza.Option
|
||||||
reportError func(context.Context, string, error) // reportError is the function called when encountering disk errors.
|
reportError func(context.Context, string, error) // reportError is the function called when encountering disk errors.
|
||||||
metrics Metrics
|
metrics Metrics
|
||||||
|
@ -63,10 +63,15 @@ func initConfig(c *cfg) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithLogger(l *logger.Logger) Option {
|
func WithBlobovniczaTreeLogger(log *logger.Logger) Option {
|
||||||
return func(c *cfg) {
|
return func(c *cfg) {
|
||||||
c.log = l
|
c.log = log
|
||||||
c.blzOpts = append(c.blzOpts, blobovnicza.WithLogger(l))
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithBlobovniczaLogger(log *logger.Logger) Option {
|
||||||
|
return func(c *cfg) {
|
||||||
|
c.blzOpts = append(c.blzOpts, blobovnicza.WithLogger(log))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -226,7 +226,7 @@ func (b *Blobovniczas) rebuildDB(ctx context.Context, path string, meta common.M
|
||||||
|
|
||||||
func (b *Blobovniczas) addRebuildTempFile(ctx context.Context, path string) (func(), error) {
|
func (b *Blobovniczas) addRebuildTempFile(ctx context.Context, path string) (func(), error) {
|
||||||
sysPath := filepath.Join(b.rootPath, path)
|
sysPath := filepath.Join(b.rootPath, path)
|
||||||
sysPath = sysPath + rebuildSuffix
|
sysPath += rebuildSuffix
|
||||||
_, err := os.OpenFile(sysPath, os.O_RDWR|os.O_CREATE|os.O_EXCL|os.O_SYNC, b.perm)
|
_, err := os.OpenFile(sysPath, os.O_RDWR|os.O_CREATE|os.O_EXCL|os.O_SYNC, b.perm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -328,7 +328,7 @@ func (b *Blobovniczas) moveObject(ctx context.Context, source *blobovnicza.Blobo
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Blobovniczas) dropDB(ctx context.Context, path string, shDb *sharedDB) (bool, error) {
|
func (b *Blobovniczas) dropDB(ctx context.Context, path string, shDB *sharedDB) (bool, error) {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return false, ctx.Err()
|
return false, ctx.Err()
|
||||||
|
@ -341,7 +341,7 @@ func (b *Blobovniczas) dropDB(ctx context.Context, path string, shDb *sharedDB)
|
||||||
b.dbFilesGuard.Lock()
|
b.dbFilesGuard.Lock()
|
||||||
defer b.dbFilesGuard.Unlock()
|
defer b.dbFilesGuard.Unlock()
|
||||||
|
|
||||||
if err := shDb.CloseAndRemoveFile(ctx); err != nil {
|
if err := shDB.CloseAndRemoveFile(ctx); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
b.commondbManager.CleanResources(path)
|
b.commondbManager.CleanResources(path)
|
||||||
|
|
|
@ -140,7 +140,8 @@ func testRebuildFailoverObjectDeletedFromSource(t *testing.T) {
|
||||||
func testRebuildFailoverValidate(t *testing.T, dir string, obj *objectSDK.Object, mustUpdateStorageID bool) {
|
func testRebuildFailoverValidate(t *testing.T, dir string, obj *objectSDK.Object, mustUpdateStorageID bool) {
|
||||||
b := NewBlobovniczaTree(
|
b := NewBlobovniczaTree(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
WithLogger(test.NewLogger(t)),
|
WithBlobovniczaLogger(test.NewLogger(t)),
|
||||||
|
WithBlobovniczaTreeLogger(test.NewLogger(t)),
|
||||||
WithObjectSizeLimit(2048),
|
WithObjectSizeLimit(2048),
|
||||||
WithBlobovniczaShallowWidth(2),
|
WithBlobovniczaShallowWidth(2),
|
||||||
WithBlobovniczaShallowDepth(2),
|
WithBlobovniczaShallowDepth(2),
|
||||||
|
|
|
@ -50,7 +50,8 @@ func TestBlobovniczaTreeFillPercentRebuild(t *testing.T) {
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
b := NewBlobovniczaTree(
|
b := NewBlobovniczaTree(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
WithLogger(test.NewLogger(t)),
|
WithBlobovniczaLogger(test.NewLogger(t)),
|
||||||
|
WithBlobovniczaTreeLogger(test.NewLogger(t)),
|
||||||
WithObjectSizeLimit(64*1024),
|
WithObjectSizeLimit(64*1024),
|
||||||
WithBlobovniczaShallowWidth(1), // single directory
|
WithBlobovniczaShallowWidth(1), // single directory
|
||||||
WithBlobovniczaShallowDepth(1),
|
WithBlobovniczaShallowDepth(1),
|
||||||
|
@ -106,7 +107,8 @@ func TestBlobovniczaTreeFillPercentRebuild(t *testing.T) {
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
b := NewBlobovniczaTree(
|
b := NewBlobovniczaTree(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
WithLogger(test.NewLogger(t)),
|
WithBlobovniczaLogger(test.NewLogger(t)),
|
||||||
|
WithBlobovniczaTreeLogger(test.NewLogger(t)),
|
||||||
WithObjectSizeLimit(64*1024),
|
WithObjectSizeLimit(64*1024),
|
||||||
WithBlobovniczaShallowWidth(1), // single directory
|
WithBlobovniczaShallowWidth(1), // single directory
|
||||||
WithBlobovniczaShallowDepth(1),
|
WithBlobovniczaShallowDepth(1),
|
||||||
|
@ -160,7 +162,8 @@ func TestBlobovniczaTreeFillPercentRebuild(t *testing.T) {
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
b := NewBlobovniczaTree(
|
b := NewBlobovniczaTree(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
WithLogger(test.NewLogger(t)),
|
WithBlobovniczaLogger(test.NewLogger(t)),
|
||||||
|
WithBlobovniczaTreeLogger(test.NewLogger(t)),
|
||||||
WithObjectSizeLimit(64*1024),
|
WithObjectSizeLimit(64*1024),
|
||||||
WithBlobovniczaShallowWidth(1), // single directory
|
WithBlobovniczaShallowWidth(1), // single directory
|
||||||
WithBlobovniczaShallowDepth(1),
|
WithBlobovniczaShallowDepth(1),
|
||||||
|
@ -231,7 +234,8 @@ func TestBlobovniczaTreeFillPercentRebuild(t *testing.T) {
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
b := NewBlobovniczaTree(
|
b := NewBlobovniczaTree(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
WithLogger(test.NewLogger(t)),
|
WithBlobovniczaLogger(test.NewLogger(t)),
|
||||||
|
WithBlobovniczaTreeLogger(test.NewLogger(t)),
|
||||||
WithObjectSizeLimit(64*1024),
|
WithObjectSizeLimit(64*1024),
|
||||||
WithBlobovniczaShallowWidth(1), // single directory
|
WithBlobovniczaShallowWidth(1), // single directory
|
||||||
WithBlobovniczaShallowDepth(1),
|
WithBlobovniczaShallowDepth(1),
|
||||||
|
@ -262,7 +266,8 @@ func TestBlobovniczaTreeFillPercentRebuild(t *testing.T) {
|
||||||
require.NoError(t, b.Close(context.Background()))
|
require.NoError(t, b.Close(context.Background()))
|
||||||
b = NewBlobovniczaTree(
|
b = NewBlobovniczaTree(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
WithLogger(test.NewLogger(t)),
|
WithBlobovniczaLogger(test.NewLogger(t)),
|
||||||
|
WithBlobovniczaTreeLogger(test.NewLogger(t)),
|
||||||
WithObjectSizeLimit(64*1024),
|
WithObjectSizeLimit(64*1024),
|
||||||
WithBlobovniczaShallowWidth(1),
|
WithBlobovniczaShallowWidth(1),
|
||||||
WithBlobovniczaShallowDepth(1),
|
WithBlobovniczaShallowDepth(1),
|
||||||
|
@ -304,7 +309,8 @@ func TestBlobovniczaTreeRebuildLargeObject(t *testing.T) {
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
b := NewBlobovniczaTree(
|
b := NewBlobovniczaTree(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
WithLogger(test.NewLogger(t)),
|
WithBlobovniczaLogger(test.NewLogger(t)),
|
||||||
|
WithBlobovniczaTreeLogger(test.NewLogger(t)),
|
||||||
WithObjectSizeLimit(64*1024), // 64KB object size limit
|
WithObjectSizeLimit(64*1024), // 64KB object size limit
|
||||||
WithBlobovniczaShallowWidth(5),
|
WithBlobovniczaShallowWidth(5),
|
||||||
WithBlobovniczaShallowDepth(2), // depth = 2
|
WithBlobovniczaShallowDepth(2), // depth = 2
|
||||||
|
@ -332,7 +338,8 @@ func TestBlobovniczaTreeRebuildLargeObject(t *testing.T) {
|
||||||
|
|
||||||
b = NewBlobovniczaTree(
|
b = NewBlobovniczaTree(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
WithLogger(test.NewLogger(t)),
|
WithBlobovniczaLogger(test.NewLogger(t)),
|
||||||
|
WithBlobovniczaTreeLogger(test.NewLogger(t)),
|
||||||
WithObjectSizeLimit(32*1024), // 32KB object size limit
|
WithObjectSizeLimit(32*1024), // 32KB object size limit
|
||||||
WithBlobovniczaShallowWidth(5),
|
WithBlobovniczaShallowWidth(5),
|
||||||
WithBlobovniczaShallowDepth(3), // depth = 3
|
WithBlobovniczaShallowDepth(3), // depth = 3
|
||||||
|
@ -374,7 +381,8 @@ func testBlobovniczaTreeRebuildHelper(t *testing.T, sourceDepth, sourceWidth, ta
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
b := NewBlobovniczaTree(
|
b := NewBlobovniczaTree(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
WithLogger(test.NewLogger(t)),
|
WithBlobovniczaLogger(test.NewLogger(t)),
|
||||||
|
WithBlobovniczaTreeLogger(test.NewLogger(t)),
|
||||||
WithObjectSizeLimit(2048),
|
WithObjectSizeLimit(2048),
|
||||||
WithBlobovniczaShallowWidth(sourceWidth),
|
WithBlobovniczaShallowWidth(sourceWidth),
|
||||||
WithBlobovniczaShallowDepth(sourceDepth),
|
WithBlobovniczaShallowDepth(sourceDepth),
|
||||||
|
@ -415,7 +423,8 @@ func testBlobovniczaTreeRebuildHelper(t *testing.T, sourceDepth, sourceWidth, ta
|
||||||
|
|
||||||
b = NewBlobovniczaTree(
|
b = NewBlobovniczaTree(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
WithLogger(test.NewLogger(t)),
|
WithBlobovniczaLogger(test.NewLogger(t)),
|
||||||
|
WithBlobovniczaTreeLogger(test.NewLogger(t)),
|
||||||
WithObjectSizeLimit(2048),
|
WithObjectSizeLimit(2048),
|
||||||
WithBlobovniczaShallowWidth(targetWidth),
|
WithBlobovniczaShallowWidth(targetWidth),
|
||||||
WithBlobovniczaShallowDepth(targetDepth),
|
WithBlobovniczaShallowDepth(targetDepth),
|
||||||
|
|
|
@ -41,7 +41,7 @@ type SubStorageInfo struct {
|
||||||
type Option func(*cfg)
|
type Option func(*cfg)
|
||||||
|
|
||||||
type cfg struct {
|
type cfg struct {
|
||||||
compression compression.Config
|
compression compression.Compressor
|
||||||
log *logger.Logger
|
log *logger.Logger
|
||||||
storage []SubStorage
|
storage []SubStorage
|
||||||
metrics Metrics
|
metrics Metrics
|
||||||
|
@ -91,50 +91,13 @@ func WithStorages(st []SubStorage) Option {
|
||||||
// WithLogger returns option to specify BlobStor's logger.
|
// WithLogger returns option to specify BlobStor's logger.
|
||||||
func WithLogger(l *logger.Logger) Option {
|
func WithLogger(l *logger.Logger) Option {
|
||||||
return func(c *cfg) {
|
return func(c *cfg) {
|
||||||
c.log = l.With(zap.String("component", "BlobStor"))
|
c.log = l
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithCompressObjects returns option to toggle
|
func WithCompression(comp compression.Config) Option {
|
||||||
// compression of the stored objects.
|
|
||||||
//
|
|
||||||
// If true, Zstandard algorithm is used for data compression.
|
|
||||||
//
|
|
||||||
// If compressor (decompressor) creation failed,
|
|
||||||
// the uncompressed option will be used, and the error
|
|
||||||
// is recorded in the provided log.
|
|
||||||
func WithCompressObjects(comp bool) Option {
|
|
||||||
return func(c *cfg) {
|
return func(c *cfg) {
|
||||||
c.compression.Enabled = comp
|
c.compression.Config = comp
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithCompressibilityEstimate returns an option to use
|
|
||||||
// normilized compressibility estimate to decide compress
|
|
||||||
// data or not.
|
|
||||||
//
|
|
||||||
// See https://github.com/klauspost/compress/blob/v1.17.2/compressible.go#L5
|
|
||||||
func WithCompressibilityEstimate(v bool) Option {
|
|
||||||
return func(c *cfg) {
|
|
||||||
c.compression.UseCompressEstimation = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithCompressibilityEstimateThreshold returns an option to set
|
|
||||||
// normilized compressibility estimate threshold.
|
|
||||||
//
|
|
||||||
// See https://github.com/klauspost/compress/blob/v1.17.2/compressible.go#L5
|
|
||||||
func WithCompressibilityEstimateThreshold(threshold float64) Option {
|
|
||||||
return func(c *cfg) {
|
|
||||||
c.compression.CompressEstimationThreshold = threshold
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithUncompressableContentTypes returns option to disable decompression
|
|
||||||
// for specific content types as seen by object.AttributeContentType attribute.
|
|
||||||
func WithUncompressableContentTypes(values []string) Option {
|
|
||||||
return func(c *cfg) {
|
|
||||||
c.compression.UncompressableContentTypes = values
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,6 +115,6 @@ func WithMetrics(m Metrics) Option {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BlobStor) Compressor() *compression.Config {
|
func (b *BlobStor) Compressor() *compression.Compressor {
|
||||||
return &b.cfg.compression
|
return &b.compression
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ 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/blobstor/blobovniczatree"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/blobovniczatree"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/compression"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/fstree"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/fstree"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/teststore"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/teststore"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode"
|
||||||
|
@ -51,7 +52,9 @@ func TestCompression(t *testing.T) {
|
||||||
|
|
||||||
newBlobStor := func(t *testing.T, compress bool) *BlobStor {
|
newBlobStor := func(t *testing.T, compress bool) *BlobStor {
|
||||||
bs := New(
|
bs := New(
|
||||||
WithCompressObjects(compress),
|
WithCompression(compression.Config{
|
||||||
|
Enabled: compress,
|
||||||
|
}),
|
||||||
WithStorages(defaultStorages(dir, smallSizeLimit)))
|
WithStorages(defaultStorages(dir, smallSizeLimit)))
|
||||||
require.NoError(t, bs.Open(context.Background(), mode.ReadWrite))
|
require.NoError(t, bs.Open(context.Background(), mode.ReadWrite))
|
||||||
require.NoError(t, bs.Init(context.Background()))
|
require.NoError(t, bs.Init(context.Background()))
|
||||||
|
@ -113,8 +116,10 @@ func TestBlobstor_needsCompression(t *testing.T) {
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
|
|
||||||
bs := New(
|
bs := New(
|
||||||
WithCompressObjects(compress),
|
WithCompression(compression.Config{
|
||||||
WithUncompressableContentTypes(ct),
|
Enabled: compress,
|
||||||
|
UncompressableContentTypes: ct,
|
||||||
|
}),
|
||||||
WithStorages([]SubStorage{
|
WithStorages([]SubStorage{
|
||||||
{
|
{
|
||||||
Storage: blobovniczatree.NewBlobovniczaTree(
|
Storage: blobovniczatree.NewBlobovniczaTree(
|
||||||
|
|
|
@ -18,8 +18,8 @@ type Storage interface {
|
||||||
Path() string
|
Path() string
|
||||||
ObjectsCount(ctx context.Context) (uint64, error)
|
ObjectsCount(ctx context.Context) (uint64, error)
|
||||||
|
|
||||||
SetCompressor(cc *compression.Config)
|
SetCompressor(cc *compression.Compressor)
|
||||||
Compressor() *compression.Config
|
Compressor() *compression.Compressor
|
||||||
|
|
||||||
// SetReportErrorFunc allows to provide a function to be called on disk errors.
|
// SetReportErrorFunc allows to provide a function to be called on disk errors.
|
||||||
// This function MUST be called before Open.
|
// This function MUST be called before Open.
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func BenchmarkCompression(b *testing.B) {
|
func BenchmarkCompression(b *testing.B) {
|
||||||
c := Config{Enabled: true}
|
c := Compressor{Config: Config{Enabled: true}}
|
||||||
require.NoError(b, c.Init())
|
require.NoError(b, c.Init())
|
||||||
|
|
||||||
for _, size := range []int{128, 1024, 32 * 1024, 32 * 1024 * 1024} {
|
for _, size := range []int{128, 1024, 32 * 1024, 32 * 1024 * 1024} {
|
||||||
|
@ -33,7 +33,7 @@ func BenchmarkCompression(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func benchWith(b *testing.B, c Config, data []byte) {
|
func benchWith(b *testing.B, c Compressor, data []byte) {
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for range b.N {
|
for range b.N {
|
||||||
|
@ -56,8 +56,10 @@ func BenchmarkCompressionRealVSEstimate(b *testing.B) {
|
||||||
b.Run("estimate", func(b *testing.B) {
|
b.Run("estimate", func(b *testing.B) {
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
||||||
c := &Config{
|
c := &Compressor{
|
||||||
|
Config: Config{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
require.NoError(b, c.Init())
|
require.NoError(b, c.Init())
|
||||||
|
|
||||||
|
@ -76,8 +78,10 @@ func BenchmarkCompressionRealVSEstimate(b *testing.B) {
|
||||||
b.Run("compress", func(b *testing.B) {
|
b.Run("compress", func(b *testing.B) {
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
||||||
c := &Config{
|
c := &Compressor{
|
||||||
|
Config: Config{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
require.NoError(b, c.Init())
|
require.NoError(b, c.Init())
|
||||||
|
|
||||||
|
|
|
@ -4,21 +4,36 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/assert"
|
||||||
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||||
"github.com/klauspost/compress"
|
"github.com/klauspost/compress"
|
||||||
"github.com/klauspost/compress/zstd"
|
"github.com/klauspost/compress/zstd"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Level string
|
||||||
|
|
||||||
|
const (
|
||||||
|
LevelDefault Level = ""
|
||||||
|
LevelOptimal Level = "optimal"
|
||||||
|
LevelFastest Level = "fastest"
|
||||||
|
LevelSmallestSize Level = "smallest_size"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Compressor struct {
|
||||||
|
Config
|
||||||
|
|
||||||
|
encoder *zstd.Encoder
|
||||||
|
decoder *zstd.Decoder
|
||||||
|
}
|
||||||
|
|
||||||
// Config represents common compression-related configuration.
|
// Config represents common compression-related configuration.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Enabled bool
|
Enabled bool
|
||||||
UncompressableContentTypes []string
|
UncompressableContentTypes []string
|
||||||
|
Level Level
|
||||||
|
|
||||||
UseCompressEstimation bool
|
EstimateCompressibility bool
|
||||||
CompressEstimationThreshold float64
|
EstimateCompressibilityThreshold float64
|
||||||
|
|
||||||
encoder *zstd.Encoder
|
|
||||||
decoder *zstd.Decoder
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// zstdFrameMagic contains first 4 bytes of any compressed object
|
// zstdFrameMagic contains first 4 bytes of any compressed object
|
||||||
|
@ -26,11 +41,11 @@ type Config struct {
|
||||||
var zstdFrameMagic = []byte{0x28, 0xb5, 0x2f, 0xfd}
|
var zstdFrameMagic = []byte{0x28, 0xb5, 0x2f, 0xfd}
|
||||||
|
|
||||||
// Init initializes compression routines.
|
// Init initializes compression routines.
|
||||||
func (c *Config) Init() error {
|
func (c *Compressor) Init() error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
if c.Enabled {
|
if c.Enabled {
|
||||||
c.encoder, err = zstd.NewWriter(nil)
|
c.encoder, err = zstd.NewWriter(nil, zstd.WithEncoderLevel(c.compressionLevel()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -73,7 +88,7 @@ func (c *Config) NeedsCompression(obj *objectSDK.Object) bool {
|
||||||
|
|
||||||
// Decompress decompresses data if it starts with the magic
|
// Decompress decompresses data if it starts with the magic
|
||||||
// and returns data untouched otherwise.
|
// and returns data untouched otherwise.
|
||||||
func (c *Config) Decompress(data []byte) ([]byte, error) {
|
func (c *Compressor) Decompress(data []byte) ([]byte, error) {
|
||||||
if len(data) < 4 || !bytes.Equal(data[:4], zstdFrameMagic) {
|
if len(data) < 4 || !bytes.Equal(data[:4], zstdFrameMagic) {
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
@ -82,13 +97,13 @@ func (c *Config) Decompress(data []byte) ([]byte, error) {
|
||||||
|
|
||||||
// Compress compresses data if compression is enabled
|
// Compress compresses data if compression is enabled
|
||||||
// and returns data untouched otherwise.
|
// and returns data untouched otherwise.
|
||||||
func (c *Config) Compress(data []byte) []byte {
|
func (c *Compressor) Compress(data []byte) []byte {
|
||||||
if c == nil || !c.Enabled {
|
if c == nil || !c.Enabled {
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
if c.UseCompressEstimation {
|
if c.EstimateCompressibility {
|
||||||
estimated := compress.Estimate(data)
|
estimated := compress.Estimate(data)
|
||||||
if estimated >= c.CompressEstimationThreshold {
|
if estimated >= c.EstimateCompressibilityThreshold {
|
||||||
return c.compress(data)
|
return c.compress(data)
|
||||||
}
|
}
|
||||||
return data
|
return data
|
||||||
|
@ -96,7 +111,7 @@ func (c *Config) Compress(data []byte) []byte {
|
||||||
return c.compress(data)
|
return c.compress(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) compress(data []byte) []byte {
|
func (c *Compressor) compress(data []byte) []byte {
|
||||||
maxSize := c.encoder.MaxEncodedSize(len(data))
|
maxSize := c.encoder.MaxEncodedSize(len(data))
|
||||||
compressed := c.encoder.EncodeAll(data, make([]byte, 0, maxSize))
|
compressed := c.encoder.EncodeAll(data, make([]byte, 0, maxSize))
|
||||||
if len(data) < len(compressed) {
|
if len(data) < len(compressed) {
|
||||||
|
@ -106,7 +121,7 @@ func (c *Config) compress(data []byte) []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes encoder and decoder, returns any error occurred.
|
// Close closes encoder and decoder, returns any error occurred.
|
||||||
func (c *Config) Close() error {
|
func (c *Compressor) Close() error {
|
||||||
var err error
|
var err error
|
||||||
if c.encoder != nil {
|
if c.encoder != nil {
|
||||||
err = c.encoder.Close()
|
err = c.encoder.Close()
|
||||||
|
@ -116,3 +131,24 @@ func (c *Config) Close() error {
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Config) HasValidCompressionLevel() bool {
|
||||||
|
return c.Level == LevelDefault ||
|
||||||
|
c.Level == LevelOptimal ||
|
||||||
|
c.Level == LevelFastest ||
|
||||||
|
c.Level == LevelSmallestSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compressor) compressionLevel() zstd.EncoderLevel {
|
||||||
|
switch c.Level {
|
||||||
|
case LevelDefault, LevelOptimal:
|
||||||
|
return zstd.SpeedDefault
|
||||||
|
case LevelFastest:
|
||||||
|
return zstd.SpeedFastest
|
||||||
|
case LevelSmallestSize:
|
||||||
|
return zstd.SpeedBestCompression
|
||||||
|
default:
|
||||||
|
assert.Fail("unknown compression level", string(c.Level))
|
||||||
|
return zstd.SpeedDefault
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/compression"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
@ -53,6 +54,10 @@ var ErrInitBlobovniczas = errors.New("failure on blobovnicza initialization stag
|
||||||
func (b *BlobStor) Init(ctx context.Context) error {
|
func (b *BlobStor) Init(ctx context.Context) error {
|
||||||
b.log.Debug(ctx, logs.BlobstorInitializing)
|
b.log.Debug(ctx, logs.BlobstorInitializing)
|
||||||
|
|
||||||
|
if !b.compression.HasValidCompressionLevel() {
|
||||||
|
b.log.Warn(ctx, logs.UnknownCompressionLevelDefaultWillBeUsed, zap.String("level", string(b.compression.Level)))
|
||||||
|
b.compression.Level = compression.LevelDefault
|
||||||
|
}
|
||||||
if err := b.compression.Init(); err != nil {
|
if err := b.compression.Init(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ package fstree
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FileCounter used to count files in FSTree. The implementation must be thread-safe.
|
// FileCounter used to count files in FSTree. The implementation must be thread-safe.
|
||||||
|
@ -52,16 +54,11 @@ func (c *SimpleCounter) Dec(size uint64) {
|
||||||
c.mtx.Lock()
|
c.mtx.Lock()
|
||||||
defer c.mtx.Unlock()
|
defer c.mtx.Unlock()
|
||||||
|
|
||||||
if c.count > 0 {
|
assert.True(c.count > 0, "fstree.SimpleCounter: invalid count")
|
||||||
c.count--
|
c.count--
|
||||||
} else {
|
|
||||||
panic("fstree.SimpleCounter: invalid count")
|
assert.True(c.size >= size, "fstree.SimpleCounter: invalid size")
|
||||||
}
|
|
||||||
if c.size >= size {
|
|
||||||
c.size -= size
|
c.size -= size
|
||||||
} else {
|
|
||||||
panic("fstree.SimpleCounter: invalid size")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *SimpleCounter) CountSize() (uint64, uint64) {
|
func (c *SimpleCounter) CountSize() (uint64, uint64) {
|
||||||
|
|
|
@ -45,7 +45,7 @@ type FSTree struct {
|
||||||
|
|
||||||
log *logger.Logger
|
log *logger.Logger
|
||||||
|
|
||||||
*compression.Config
|
compressor *compression.Compressor
|
||||||
Depth uint64
|
Depth uint64
|
||||||
DirNameLen int
|
DirNameLen int
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ func New(opts ...Option) *FSTree {
|
||||||
Permissions: 0o700,
|
Permissions: 0o700,
|
||||||
RootPath: "./",
|
RootPath: "./",
|
||||||
},
|
},
|
||||||
Config: nil,
|
compressor: nil,
|
||||||
Depth: 4,
|
Depth: 4,
|
||||||
DirNameLen: DirNameLen,
|
DirNameLen: DirNameLen,
|
||||||
metrics: &noopMetrics{},
|
metrics: &noopMetrics{},
|
||||||
|
@ -196,7 +196,7 @@ func (t *FSTree) iterate(ctx context.Context, depth uint64, curPath []string, pr
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
data, err = t.Decompress(data)
|
data, err = t.compressor.Decompress(data)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if prm.IgnoreErrors {
|
if prm.IgnoreErrors {
|
||||||
|
@ -405,7 +405,7 @@ func (t *FSTree) Put(ctx context.Context, prm common.PutPrm) (common.PutRes, err
|
||||||
return common.PutRes{}, err
|
return common.PutRes{}, err
|
||||||
}
|
}
|
||||||
if !prm.DontCompress {
|
if !prm.DontCompress {
|
||||||
prm.RawData = t.Compress(prm.RawData)
|
prm.RawData = t.compressor.Compress(prm.RawData)
|
||||||
}
|
}
|
||||||
|
|
||||||
size = len(prm.RawData)
|
size = len(prm.RawData)
|
||||||
|
@ -448,7 +448,7 @@ func (t *FSTree) Get(ctx context.Context, prm common.GetPrm) (common.GetRes, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err = t.Decompress(data)
|
data, err = t.compressor.Decompress(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.GetRes{}, err
|
return common.GetRes{}, err
|
||||||
}
|
}
|
||||||
|
@ -597,12 +597,12 @@ func (t *FSTree) Path() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetCompressor implements common.Storage.
|
// SetCompressor implements common.Storage.
|
||||||
func (t *FSTree) SetCompressor(cc *compression.Config) {
|
func (t *FSTree) SetCompressor(cc *compression.Compressor) {
|
||||||
t.Config = cc
|
t.compressor = cc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *FSTree) Compressor() *compression.Config {
|
func (t *FSTree) Compressor() *compression.Compressor {
|
||||||
return t.Config
|
return t.compressor
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetReportErrorFunc implements common.Storage.
|
// SetReportErrorFunc implements common.Storage.
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue