package main import ( "strings" "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 subjectWithError struct { subject *client.Subject err error } type subjectExtWithError struct { subject *client.SubjectExtended err error } type morphFrostfsIDCache struct { subjProvider frostfsidcore.SubjectProvider subjCache *expirable.LRU[util.Uint160, subjectWithError] subjExtCache *expirable.LRU[util.Uint160, subjectExtWithError] metrics cacheMetrics } func newMorphFrostfsIDCache(subjProvider frostfsidcore.SubjectProvider, size int, ttl time.Duration, metrics cacheMetrics) frostfsidcore.SubjectProvider { return &morphFrostfsIDCache{ subjProvider: subjProvider, subjCache: expirable.NewLRU(size, func(util.Uint160, subjectWithError) {}, ttl), subjExtCache: expirable.NewLRU(size, func(util.Uint160, subjectExtWithError) {}, ttl), metrics: metrics, } } func (m *morphFrostfsIDCache) GetSubject(addr util.Uint160) (*client.Subject, error) { hit := false startedAt := time.Now() defer func() { m.metrics.AddMethodDuration("GetSubject", time.Since(startedAt), hit) }() result, found := m.subjCache.Get(addr) if found { hit = true return result.subject, result.err } subj, err := m.subjProvider.GetSubject(addr) if err != nil { if m.isCacheableError(err) { m.subjCache.Add(addr, subjectWithError{ err: err, }) } return nil, err } m.subjCache.Add(addr, subjectWithError{subject: subj}) return subj, nil } func (m *morphFrostfsIDCache) GetSubjectExtended(addr util.Uint160) (*client.SubjectExtended, error) { hit := false startedAt := time.Now() defer func() { m.metrics.AddMethodDuration("GetSubjectExtended", time.Since(startedAt), hit) }() result, found := m.subjExtCache.Get(addr) if found { hit = true return result.subject, result.err } subjExt, err := m.subjProvider.GetSubjectExtended(addr) if err != nil { if m.isCacheableError(err) { m.subjExtCache.Add(addr, subjectExtWithError{ err: err, }) m.subjCache.Add(addr, subjectWithError{ err: err, }) } return nil, err } m.subjExtCache.Add(addr, subjectExtWithError{subject: subjExt}) m.subjCache.Add(addr, subjectWithError{subject: subjectFromSubjectExtended(subjExt)}) return subjExt, nil } func (m *morphFrostfsIDCache) isCacheableError(err error) bool { return strings.Contains(err.Error(), frostfsidcore.SubjectNotFoundErrorMessage) } func subjectFromSubjectExtended(subjExt *client.SubjectExtended) *client.Subject { return &client.Subject{ PrimaryKey: subjExt.PrimaryKey, AdditionalKeys: subjExt.AdditionalKeys, Namespace: subjExt.Namespace, Name: subjExt.Name, KV: subjExt.KV, } }