diff --git a/cmd/frostfs-node/config.go b/cmd/frostfs-node/config.go index 5606ed24c..d713cf2fb 100644 --- a/cmd/frostfs-node/config.go +++ b/cmd/frostfs-node/config.go @@ -32,6 +32,7 @@ import ( "git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/ape/chainbase" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container" + frostfsidcore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/frostfsid" 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/blobovniczatree" @@ -46,7 +47,6 @@ import ( "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/metrics" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client" containerClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/container" - frostfsidClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/frostfsid" nmClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event" netmap2 "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/netmap" @@ -421,7 +421,7 @@ type shared struct { cnrClient *containerClient.Client - frostfsidClient *frostfsidClient.Client + frostfsidClient frostfsidcore.SubjectProvider respSvc *response.Service diff --git a/cmd/frostfs-node/config/morph/config.go b/cmd/frostfs-node/config/morph/config.go index c32940625..a240c5d49 100644 --- a/cmd/frostfs-node/config/morph/config.go +++ b/cmd/frostfs-node/config/morph/config.go @@ -28,6 +28,9 @@ const ( // APEChainCacheSizeDefault is a default value of APE chain cache. APEChainCacheSizeDefault = 10_000 + + // FrostfsIDCacheSizeDefault is a default value of APE chain cache. + FrostfsIDCacheSizeDefault = 10_000 ) var errNoMorphEndpoints = errors.New("no morph chain RPC endpoints, see `morph.rpc_endpoint` section") @@ -114,3 +117,15 @@ func APEChainCacheSize(c *config.Config) uint32 { } return config.Uint32Safe(c.Sub(subsection), "ape_chain_cache_size") } + +// FrostfsIDCacheSize returns the value of "frostfsid_cache_size" config parameter +// from "morph" section. +// +// Returns 0 if the value is not positive integer. +// Returns FrostfsIDCacheSizeDefault if the value is missing. +func FrostfsIDCacheSize(c *config.Config) uint32 { + if c.Sub(subsection).Value("frostfsid_cache_size") == nil { + return FrostfsIDCacheSizeDefault + } + return config.Uint32Safe(c.Sub(subsection), "frostfsid_cache_size") +} diff --git a/cmd/frostfs-node/container.go b/cmd/frostfs-node/container.go index e006648ed..61600376b 100644 --- a/cmd/frostfs-node/container.go +++ b/cmd/frostfs-node/container.go @@ -6,8 +6,10 @@ import ( "net" containerGRPC "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container/grpc" + morphconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/morph" "git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs" containerCore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container" + frostfsidcore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/frostfsid" cntClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/container" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/frostfsid" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event" @@ -33,9 +35,16 @@ func initContainerService(_ context.Context, c *cfg) { cnrRdr, cnrWrt := configureEACLAndContainerSources(c, wrap, cnrSrc) - frostFSIDClient, err := frostfsid.NewFromMorph(c.cfgMorph.client, c.cfgFrostfsID.scriptHash, 0) + var frostfsIDSubjectProvider frostfsidcore.SubjectProvider + frostfsIDSubjectProvider, err = frostfsid.NewFromMorph(c.cfgMorph.client, c.cfgFrostfsID.scriptHash, 0) fatalOnErr(err) - c.shared.frostfsidClient = frostFSIDClient + + cacheSize := morphconfig.FrostfsIDCacheSize(c.appCfg) + if cacheSize > 0 { + frostfsIDSubjectProvider = newMorphFrostfsIDCache(frostfsIDSubjectProvider, int(cacheSize), c.cfgMorph.cacheTTL) + } + + c.shared.frostfsidClient = frostfsIDSubjectProvider server := containerTransportGRPC.New( containerService.NewSignService( diff --git a/cmd/frostfs-node/frostfsid.go b/cmd/frostfs-node/frostfsid.go new file mode 100644 index 000000000..68cc1d040 --- /dev/null +++ b/cmd/frostfs-node/frostfsid.go @@ -0,0 +1,71 @@ +package main + +import ( + "time" + + "git.frostfs.info/TrueCloudLab/frostfs-contract/frostfsid/client" + frostfsidcore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/frostfsid" + "github.com/hashicorp/golang-lru/v2/expirable" + "github.com/nspcc-dev/neo-go/pkg/util" +) + +type morphFrostfsIDCache struct { + subjProvider frostfsidcore.SubjectProvider + + subjCache *expirable.LRU[util.Uint160, *client.Subject] + + subjExtCache *expirable.LRU[util.Uint160, *client.SubjectExtended] +} + +func newMorphFrostfsIDCache(subjProvider frostfsidcore.SubjectProvider, size int, ttl time.Duration) frostfsidcore.SubjectProvider { + return &morphFrostfsIDCache{ + subjProvider: subjProvider, + + subjCache: expirable.NewLRU(size, func(util.Uint160, *client.Subject) {}, ttl), + + subjExtCache: expirable.NewLRU(size, func(util.Uint160, *client.SubjectExtended) {}, ttl), + } +} + +func (m *morphFrostfsIDCache) GetSubject(addr util.Uint160) (*client.Subject, error) { + result, found := m.subjCache.Get(addr) + if found { + return result, nil + } + + result, err := m.subjProvider.GetSubject(addr) + if err != nil { + return nil, err + } + + m.subjCache.Add(addr, result) + return result, nil +} + +func (m *morphFrostfsIDCache) GetSubjectExtended(addr util.Uint160) (*client.SubjectExtended, error) { + subjExt, found := m.subjExtCache.Get(addr) + if found { + return subjExt, nil + } + + var err error + subjExt, err = m.subjProvider.GetSubjectExtended(addr) + if err != nil { + return nil, err + } + + m.subjExtCache.Add(addr, subjExt) + m.subjCache.Add(addr, subjectFromSubjectExtended(subjExt)) + + return subjExt, nil +} + +func subjectFromSubjectExtended(subjExt *client.SubjectExtended) *client.Subject { + return &client.Subject{ + PrimaryKey: subjExt.PrimaryKey, + AdditionalKeys: subjExt.AdditionalKeys, + Namespace: subjExt.Name, + Name: subjExt.Name, + KV: subjExt.KV, + } +}