[#104] app: Reload copies numbers on SIGHUP

Signed-off-by: Artem Tataurov <a.tataurov@yadro.com>
This commit is contained in:
Artem Tataurov 2023-05-18 16:20:00 +03:00
parent 11f30a037b
commit 9f186d9aba
7 changed files with 105 additions and 50 deletions

View file

@ -11,6 +11,7 @@ This document outlines major changes between releases.
- Don't create unnecessary delete-markers (#83) - Don't create unnecessary delete-markers (#83)
### Added ### Added
- Reload default and custom copies numbers on SIGHUP (#104)
- Add `copies_numbers` section to `placement_policy` in config file and support vectors of copies numbers (#70) - Add `copies_numbers` section to `placement_policy` in config file and support vectors of copies numbers (#70)
- Return `X-Owner-Id` in `head-bucket` response (#79) - Return `X-Owner-Id` in `head-bucket` response (#79)
- Return container name in `head-bucket` response (TrueCloudLab#18) - Return container name in `head-bucket` response (TrueCloudLab#18)

View file

@ -34,16 +34,16 @@ type (
XMLDecoder XMLDecoderProvider XMLDecoder XMLDecoderProvider
DefaultMaxAge int DefaultMaxAge int
NotificatorEnabled bool NotificatorEnabled bool
DefaultCopiesNumbers []uint32
CopiesNumbers map[string][]uint32
ResolveZoneList []string ResolveZoneList []string
IsResolveListAllow bool // True if ResolveZoneList contains allowed zones IsResolveListAllow bool // True if ResolveZoneList contains allowed zones
CompleteMultipartKeepalive time.Duration CompleteMultipartKeepalive time.Duration
} }
PlacementPolicy interface { PlacementPolicy interface {
Default() netmap.PlacementPolicy DefaultPlacementPolicy() netmap.PlacementPolicy
Get(string) (netmap.PlacementPolicy, bool) PlacementPolicy(string) (netmap.PlacementPolicy, bool)
CopiesNumbers(string) ([]uint32, bool)
DefaultCopiesNumbers() []uint32
} }
XMLDecoderProvider interface { XMLDecoderProvider interface {
@ -97,12 +97,12 @@ func (h *handler) pickCopiesNumbers(metadata map[string]string, locationConstrai
return result, nil return result, nil
} }
copiesNumbers, ok := h.cfg.CopiesNumbers[locationConstraint] copiesNumbers, ok := h.cfg.Policy.CopiesNumbers(locationConstraint)
if ok { if ok {
return copiesNumbers, nil return copiesNumbers, nil
} }
return h.cfg.DefaultCopiesNumbers, nil return h.cfg.Policy.DefaultCopiesNumbers(), nil
} }
func parseCopiesNumbers(copiesNumbersStr string) ([]uint32, error) { func parseCopiesNumbers(copiesNumbersStr string) ([]uint32, error) {

View file

@ -7,14 +7,16 @@ import (
) )
func TestCopiesNumberPicker(t *testing.T) { func TestCopiesNumberPicker(t *testing.T) {
var locConstraints = map[string][]uint32{} var locationConstraints = map[string][]uint32{}
locationConstraint1 := "one" locationConstraint1 := "one"
locationConstraint2 := "two" locationConstraint2 := "two"
locConstraints[locationConstraint1] = []uint32{2, 3, 4} locationConstraints[locationConstraint1] = []uint32{2, 3, 4}
config := &Config{ config := &Config{
DefaultCopiesNumbers: []uint32{1}, Policy: &placementPolicyMock{
CopiesNumbers: locConstraints, copiesNumbers: locationConstraints,
defaultCopiesNumbers: []uint32{1},
},
} }
h := handler{ h := handler{
cfg: config, cfg: config,

View file

@ -55,16 +55,27 @@ func (hc *handlerContext) Context() context.Context {
type placementPolicyMock struct { type placementPolicyMock struct {
defaultPolicy netmap.PlacementPolicy defaultPolicy netmap.PlacementPolicy
copiesNumbers map[string][]uint32
defaultCopiesNumbers []uint32
} }
func (p *placementPolicyMock) Default() netmap.PlacementPolicy { func (p *placementPolicyMock) DefaultPlacementPolicy() netmap.PlacementPolicy {
return p.defaultPolicy return p.defaultPolicy
} }
func (p *placementPolicyMock) Get(string) (netmap.PlacementPolicy, bool) { func (p *placementPolicyMock) PlacementPolicy(string) (netmap.PlacementPolicy, bool) {
return netmap.PlacementPolicy{}, false return netmap.PlacementPolicy{}, false
} }
func (p *placementPolicyMock) CopiesNumbers(locationConstraint string) ([]uint32, bool) {
result, ok := p.copiesNumbers[locationConstraint]
return result, ok
}
func (p *placementPolicyMock) DefaultCopiesNumbers() []uint32 {
return p.defaultCopiesNumbers
}
type xmlDecoderProviderMock struct{} type xmlDecoderProviderMock struct{}
func (p *xmlDecoderProviderMock) NewCompleteMultipartDecoder(r io.Reader) *xml.Decoder { func (p *xmlDecoderProviderMock) NewCompleteMultipartDecoder(r io.Reader) *xml.Decoder {

View file

@ -737,7 +737,7 @@ func (h *handler) CreateBucketHandler(w http.ResponseWriter, r *http.Request) {
} }
func (h handler) setPolicy(prm *layer.CreateBucketParams, locationConstraint string, userPolicies []*accessbox.ContainerPolicy) error { func (h handler) setPolicy(prm *layer.CreateBucketParams, locationConstraint string, userPolicies []*accessbox.ContainerPolicy) error {
prm.Policy = h.cfg.Policy.Default() prm.Policy = h.cfg.Policy.DefaultPlacementPolicy()
prm.LocationConstraint = locationConstraint prm.LocationConstraint = locationConstraint
if locationConstraint == "" { if locationConstraint == "" {
@ -751,7 +751,7 @@ func (h handler) setPolicy(prm *layer.CreateBucketParams, locationConstraint str
} }
} }
if policy, ok := h.cfg.Policy.Get(locationConstraint); ok { if policy, ok := h.cfg.Policy.PlacementPolicy(locationConstraint); ok {
prm.Policy = policy prm.Policy = policy
return nil return nil
} }

View file

@ -76,6 +76,8 @@ type (
mu sync.RWMutex mu sync.RWMutex
defaultPolicy netmap.PlacementPolicy defaultPolicy netmap.PlacementPolicy
regionMap map[string]netmap.PlacementPolicy regionMap map[string]netmap.PlacementPolicy
copiesNumbers map[string][]uint32
defaultCopiesNumbers []uint32
} }
) )
@ -154,7 +156,7 @@ func (a *App) initLayer(ctx context.Context) {
} }
func newAppSettings(log *Logger, v *viper.Viper) *appSettings { func newAppSettings(log *Logger, v *viper.Viper) *appSettings {
policies, err := newPlacementPolicy(getDefaultPolicyValue(v), v.GetString(cfgPolicyRegionMapFile)) policies, err := newPlacementPolicy(log.logger, v)
if err != nil { if err != nil {
log.logger.Fatal("failed to create new policy mapping", zap.Error(err)) log.logger.Fatal("failed to create new policy mapping", zap.Error(err))
} }
@ -285,21 +287,25 @@ func getPool(ctx context.Context, logger *zap.Logger, cfg *viper.Viper) (*pool.P
return p, key return p, key
} }
func newPlacementPolicy(defaultPolicy string, regionPolicyFilepath string) (*placementPolicy, error) { func newPlacementPolicy(l *zap.Logger, v *viper.Viper) (*placementPolicy, error) {
policies := &placementPolicy{ policies := &placementPolicy{
regionMap: make(map[string]netmap.PlacementPolicy), regionMap: make(map[string]netmap.PlacementPolicy),
defaultCopiesNumbers: []uint32{handler.DefaultCopiesNumber},
} }
return policies, policies.update(defaultPolicy, regionPolicyFilepath) policies.updateCopiesNumbers(l, v)
policies.updateDefaultCopiesNumbers(l, v)
return policies, policies.updatePolicy(getDefaultPolicyValue(v), v.GetString(cfgPolicyRegionMapFile))
} }
func (p *placementPolicy) Default() netmap.PlacementPolicy { func (p *placementPolicy) DefaultPlacementPolicy() netmap.PlacementPolicy {
p.mu.RLock() p.mu.RLock()
defer p.mu.RUnlock() defer p.mu.RUnlock()
return p.defaultPolicy return p.defaultPolicy
} }
func (p *placementPolicy) Get(name string) (netmap.PlacementPolicy, bool) { func (p *placementPolicy) PlacementPolicy(name string) (netmap.PlacementPolicy, bool) {
p.mu.RLock() p.mu.RLock()
policy, ok := p.regionMap[name] policy, ok := p.regionMap[name]
p.mu.RUnlock() p.mu.RUnlock()
@ -307,7 +313,30 @@ func (p *placementPolicy) Get(name string) (netmap.PlacementPolicy, bool) {
return policy, ok return policy, ok
} }
func (p *placementPolicy) update(defaultPolicy string, regionPolicyFilepath string) error { func (p *placementPolicy) CopiesNumbers(locationConstraint string) ([]uint32, bool) {
p.mu.RLock()
copiesNumbers, ok := p.copiesNumbers[locationConstraint]
p.mu.RUnlock()
return copiesNumbers, ok
}
func (p *placementPolicy) DefaultCopiesNumbers() []uint32 {
p.mu.RLock()
defer p.mu.RUnlock()
return p.defaultCopiesNumbers
}
func (p *placementPolicy) update(l *zap.Logger, v *viper.Viper) {
if err := p.updatePolicy(getDefaultPolicyValue(v), v.GetString(cfgPolicyRegionMapFile)); err != nil {
l.Warn("policies won't be updated", zap.Error(err))
}
p.updateCopiesNumbers(l, v)
p.updateDefaultCopiesNumbers(l, v)
}
func (p *placementPolicy) updatePolicy(defaultPolicy string, regionPolicyFilepath string) error {
var defaultPlacementPolicy netmap.PlacementPolicy var defaultPlacementPolicy netmap.PlacementPolicy
if err := defaultPlacementPolicy.DecodeString(defaultPolicy); err != nil { if err := defaultPlacementPolicy.DecodeString(defaultPolicy); err != nil {
return fmt.Errorf("parse default policy '%s': %w", defaultPolicy, err) return fmt.Errorf("parse default policy '%s': %w", defaultPolicy, err)
@ -342,6 +371,31 @@ func (p *placementPolicy) update(defaultPolicy string, regionPolicyFilepath stri
return nil return nil
} }
func (p *placementPolicy) updateCopiesNumbers(l *zap.Logger, v *viper.Viper) {
if newCopiesNumbers, err := fetchCopiesNumbers(l, v); err != nil {
l.Warn("copies numbers won't be updated", zap.Error(err))
} else {
p.mu.Lock()
p.copiesNumbers = newCopiesNumbers
p.mu.Unlock()
}
}
func (p *placementPolicy) updateDefaultCopiesNumbers(l *zap.Logger, v *viper.Viper) {
configuredValues, err := fetchDefaultCopiesNumbers(v)
if err == nil {
p.mu.Lock()
p.defaultCopiesNumbers = configuredValues
p.mu.Unlock()
l.Info("default copies numbers", zap.Uint32s("vector", p.defaultCopiesNumbers))
return
}
l.Error("cannot parse default copies numbers", zap.Error(err))
l.Warn("default copies numbers won't be updated", zap.Uint32s("current value", p.DefaultCopiesNumbers()))
}
func remove(list []string, element string) []string { func remove(list []string, element string) []string {
for i, item := range list { for i, item := range list {
if item == element { if item == element {
@ -465,9 +519,7 @@ func (a *App) updateSettings() {
a.settings.logLevel.SetLevel(lvl) a.settings.logLevel.SetLevel(lvl)
} }
if err := a.settings.policies.update(getDefaultPolicyValue(a.cfg), a.cfg.GetString(cfgPolicyRegionMapFile)); err != nil { a.settings.policies.update(a.log, a.cfg)
a.log.Warn("policies won't be updated", zap.Error(err))
}
a.settings.xmlDecoder.UseDefaultNamespaceForCompleteMultipart(a.cfg.GetBool(cfgKludgeUseDefaultXMLNSForCompleteMultipartUpload)) a.settings.xmlDecoder.UseDefaultNamespaceForCompleteMultipart(a.cfg.GetBool(cfgKludgeUseDefaultXMLNSForCompleteMultipartUpload))
} }
@ -637,7 +689,6 @@ func (a *App) initHandler() {
Policy: a.settings.policies, Policy: a.settings.policies,
DefaultMaxAge: handler.DefaultMaxAge, DefaultMaxAge: handler.DefaultMaxAge,
NotificatorEnabled: a.cfg.GetBool(cfgEnableNATS), NotificatorEnabled: a.cfg.GetBool(cfgEnableNATS),
DefaultCopiesNumbers: []uint32{handler.DefaultCopiesNumber},
XMLDecoder: a.settings.xmlDecoder, XMLDecoder: a.settings.xmlDecoder,
} }
@ -652,13 +703,6 @@ func (a *App) initHandler() {
cfg.DefaultMaxAge = defaultMaxAge cfg.DefaultMaxAge = defaultMaxAge
} }
cfg.CopiesNumbers = fetchCopiesNumbers(a.log, a.cfg)
if val := fetchDefaultCopiesNumbers(a.log, a.cfg); len(val) > 0 {
cfg.DefaultCopiesNumbers = val
}
a.log.Info("setting default copies numbers", zap.Uint32s("vector", cfg.DefaultCopiesNumbers))
cfg.ResolveZoneList = a.cfg.GetStringSlice(cfgResolveBucketAllow) cfg.ResolveZoneList = a.cfg.GetStringSlice(cfgResolveBucketAllow)
cfg.IsResolveListAllow = len(cfg.ResolveZoneList) > 0 cfg.IsResolveListAllow = len(cfg.ResolveZoneList) > 0
if !cfg.IsResolveListAllow { if !cfg.IsResolveListAllow {

View file

@ -152,25 +152,23 @@ var ignore = map[string]struct{}{
cmdVersion: {}, cmdVersion: {},
} }
func fetchDefaultCopiesNumbers(l *zap.Logger, v *viper.Viper) []uint32 { func fetchDefaultCopiesNumbers(v *viper.Viper) ([]uint32, error) {
unparsed := v.GetStringSlice(cfgSetCopiesNumber) unparsed := v.GetStringSlice(cfgSetCopiesNumber)
var result []uint32 var result []uint32
for i := range unparsed { for i := range unparsed {
parsedValue, err := strconv.ParseUint(unparsed[i], 10, 32) parsedValue, err := strconv.ParseUint(unparsed[i], 10, 32)
if err != nil { if err != nil {
l.Error("cannot parse default copies numbers", zap.Error(err)) return nil, err
return make([]uint32, 0)
} }
result = append(result, uint32(parsedValue)) result = append(result, uint32(parsedValue))
} }
return result return result, nil
} }
func fetchCopiesNumbers(l *zap.Logger, v *viper.Viper) map[string][]uint32 { func fetchCopiesNumbers(l *zap.Logger, v *viper.Viper) (map[string][]uint32, error) {
var copiesNums = make(map[string][]uint32) var copiesNums = make(map[string][]uint32)
LOOP:
for i := 0; ; i++ { for i := 0; ; i++ {
key := cfgCopiesNumbers + "." + strconv.Itoa(i) + "." key := cfgCopiesNumbers + "." + strconv.Itoa(i) + "."
constraint := v.GetString(key + "location_constraint") constraint := v.GetString(key + "location_constraint")
@ -184,16 +182,15 @@ LOOP:
for j := range vector { for j := range vector {
parsedValue, err := strconv.ParseUint(vector[j], 10, 32) parsedValue, err := strconv.ParseUint(vector[j], 10, 32)
if err != nil { if err != nil {
l.Error("cannot parse copies numbers", zap.Error(err)) return nil, err
break LOOP
} }
vector32[j] = uint32(parsedValue) vector32[j] = uint32(parsedValue)
} }
copiesNums[constraint] = vector32 copiesNums[constraint] = vector32
l.Debug("added constraint", zap.String("location", constraint), zap.Strings("copies numbers", vector)) l.Info("constraint added", zap.String("location", constraint), zap.Strings("copies numbers", vector))
} }
return copiesNums return copiesNums, nil
} }
func fetchPeers(l *zap.Logger, v *viper.Viper) []pool.NodeParam { func fetchPeers(l *zap.Logger, v *viper.Viper) []pool.NodeParam {