github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/ais/gconfig.go (about)

     1  // Package ais provides core functionality for the AIStore object storage.
     2  /*
     3   * Copyright (c) 2021-2023, NVIDIA CORPORATION. All rights reserved.
     4   */
     5  package ais
     6  
     7  import (
     8  	"net/http"
     9  	"net/url"
    10  	"os"
    11  	"path/filepath"
    12  	"sync"
    13  	"time"
    14  
    15  	"github.com/NVIDIA/aistore/api/apc"
    16  	"github.com/NVIDIA/aistore/cmn"
    17  	"github.com/NVIDIA/aistore/cmn/cos"
    18  	"github.com/NVIDIA/aistore/cmn/debug"
    19  	"github.com/NVIDIA/aistore/cmn/fname"
    20  	"github.com/NVIDIA/aistore/cmn/jsp"
    21  	"github.com/NVIDIA/aistore/cmn/nlog"
    22  	"github.com/NVIDIA/aistore/memsys"
    23  )
    24  
    25  type (
    26  	globalConfig struct {
    27  		_sgl *memsys.SGL
    28  		cmn.ClusterConfig
    29  	}
    30  	configOwner struct {
    31  		globalFpath string
    32  		immSize     int64
    33  		sync.Mutex
    34  	}
    35  
    36  	configModifier struct {
    37  		pre   func(ctx *configModifier, clone *globalConfig) (updated bool, err error)
    38  		final func(ctx *configModifier, clone *globalConfig)
    39  
    40  		oldConfig *cmn.Config
    41  		toUpdate  *cmn.ConfigToSet
    42  		msg       *apc.ActMsg
    43  		query     url.Values
    44  		hdr       http.Header
    45  		wait      bool
    46  	}
    47  )
    48  
    49  // interface guard
    50  var _ revs = (*globalConfig)(nil)
    51  
    52  // as revs
    53  func (*globalConfig) tag() string           { return revsConfTag }
    54  func (config *globalConfig) version() int64 { return config.Version }
    55  func (*globalConfig) jit(p *proxy) revs     { g, _ := p.owner.config.get(); return g }
    56  
    57  func (config *globalConfig) sgl() *memsys.SGL {
    58  	if config._sgl.IsNil() {
    59  		return nil
    60  	}
    61  	return config._sgl
    62  }
    63  
    64  func (config *globalConfig) marshal() []byte {
    65  	config._sgl = config._encode(0)
    66  	return config._sgl.Bytes()
    67  }
    68  
    69  func (config *globalConfig) _encode(immSize int64) (sgl *memsys.SGL) {
    70  	sgl = memsys.PageMM().NewSGL(immSize)
    71  	err := jsp.Encode(sgl, config, config.JspOpts())
    72  	debug.AssertNoErr(err)
    73  	return
    74  }
    75  
    76  /////////////////
    77  // configOwner //
    78  /////////////////
    79  
    80  func newConfigOwner(config *cmn.Config) (co *configOwner) {
    81  	co = &configOwner{}
    82  	co.globalFpath = filepath.Join(config.ConfigDir, fname.GlobalConfig)
    83  	return
    84  }
    85  
    86  // NOTE:
    87  // iff user ever executed transient config updates this loaded
    88  // from disk version will differ from in-memory cmn.GCO.Get()
    89  // (with respect to those in-memory only updated values)
    90  // See also:
    91  // - api.SetClusterConfig
    92  // - apc.ActTransient
    93  func (co *configOwner) get() (clone *globalConfig, err error) {
    94  	clone = &globalConfig{}
    95  	if _, err = jsp.LoadMeta(co.globalFpath, clone); err == nil {
    96  		return clone, nil
    97  	}
    98  	if os.IsNotExist(err) {
    99  		err = nil
   100  	} else {
   101  		nlog.Errorf("failed to load global config from %s: %v", co.globalFpath, err)
   102  	}
   103  	return nil, err
   104  }
   105  
   106  func (*configOwner) version() int64 { return cmn.GCO.Get().Version }
   107  
   108  // is called under co.lock
   109  func (co *configOwner) _runPre(ctx *configModifier) (clone *globalConfig, err error) {
   110  	clone, err = co.get()
   111  	if err != nil {
   112  		return
   113  	}
   114  	if clone == nil {
   115  		// missing config - try to load initial plain-text
   116  		clone = &globalConfig{}
   117  		_, err = jsp.Load(cmn.GCO.GetInitialGconfPath(), clone, jsp.Plain()) // must exist
   118  		if err != nil {
   119  			return clone, err
   120  		}
   121  	}
   122  
   123  	var updated bool
   124  	if updated, err = ctx.pre(ctx, clone); err != nil || !updated {
   125  		return nil, err
   126  	}
   127  
   128  	ctx.oldConfig = cmn.GCO.Get()
   129  	if err = cmn.GCO.Update(&clone.ClusterConfig); err != nil {
   130  		return nil, err
   131  	}
   132  
   133  	clone.Version++
   134  	clone.LastUpdated = time.Now().String()
   135  	clone._sgl = clone._encode(co.immSize)
   136  	co.immSize = max(co.immSize, clone._sgl.Len())
   137  	if err = co.persist(clone, nil); err != nil {
   138  		clone._sgl.Free()
   139  		clone._sgl = nil
   140  		return nil, cmn.NewErrFailedTo(nil, "persist", clone, err)
   141  	}
   142  	return clone, nil
   143  }
   144  
   145  // Update the global config on primary proxy.
   146  func (co *configOwner) modify(ctx *configModifier) (config *globalConfig, err error) {
   147  	co.Lock()
   148  	config, err = co._runPre(ctx)
   149  	co.Unlock()
   150  	if err != nil || config == nil {
   151  		return config, err
   152  	}
   153  	if ctx.final != nil {
   154  		ctx.final(ctx, config)
   155  	}
   156  	return config, nil
   157  }
   158  
   159  func (co *configOwner) persist(clone *globalConfig, payload msPayload) error {
   160  	if co.persistBytes(payload, co.globalFpath) {
   161  		return nil
   162  	}
   163  
   164  	sgl := clone._sgl
   165  	if sgl == nil {
   166  		sgl = clone._encode(co.immSize)
   167  		co.immSize = max(co.immSize, sgl.Len())
   168  		defer sgl.Free()
   169  	}
   170  	return jsp.SaveMeta(co.globalFpath, clone, sgl)
   171  }
   172  
   173  func (*configOwner) persistBytes(payload msPayload, globalFpath string) (done bool) {
   174  	if payload == nil {
   175  		return
   176  	}
   177  	confValue := payload[revsConfTag]
   178  	if confValue == nil {
   179  		return
   180  	}
   181  	var (
   182  		config globalConfig
   183  		wto    = cos.NewBuffer(confValue)
   184  	)
   185  	err := jsp.SaveMeta(globalFpath, &config, wto)
   186  	done = err == nil
   187  	return
   188  }
   189  
   190  // NOTE: must be called under config-owner lock
   191  func setConfig(toUpdate *cmn.ConfigToSet, transient bool) (err error) {
   192  	clone := cmn.GCO.Clone()
   193  	err = setConfigInMem(toUpdate, clone, apc.Daemon)
   194  	if err != nil {
   195  		return err
   196  	}
   197  	override := cmn.GCO.GetOverride()
   198  	if override == nil {
   199  		override = toUpdate
   200  	} else {
   201  		override.Merge(toUpdate)
   202  	}
   203  	if !transient {
   204  		if err = cmn.SaveOverrideConfig(clone.ConfigDir, override); err != nil {
   205  			return err
   206  		}
   207  	}
   208  
   209  	cmn.GCO.Put(clone)
   210  	cmn.GCO.PutOverride(override)
   211  	return nil
   212  }
   213  
   214  func setConfigInMem(toUpdate *cmn.ConfigToSet, config *cmn.Config, asType string) (err error) {
   215  	err = config.UpdateClusterConfig(toUpdate, asType)
   216  	return
   217  }
   218  
   219  func (co *configOwner) resetDaemonConfig() (err error) {
   220  	co.Lock()
   221  	oldConfig := cmn.GCO.Get()
   222  	config, err := co.get()
   223  	if err != nil || config == nil {
   224  		co.Unlock()
   225  		nlog.Infof("Warning: reset config %s: %v", oldConfig, err)
   226  		return err
   227  	}
   228  	cmn.GCO.PutOverride(nil)
   229  	err = cos.RemoveFile(filepath.Join(oldConfig.ConfigDir, fname.OverrideConfig))
   230  	if err != nil {
   231  		co.Unlock()
   232  		return
   233  	}
   234  	cmn.GCO.Update(&config.ClusterConfig)
   235  	co.Unlock()
   236  	return
   237  }