github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/core/meta/bmd.go (about)

     1  // Package meta: cluster-level metadata
     2  /*
     3   * Copyright (c) 2018-2023, NVIDIA CORPORATION. All rights reserved.
     4   */
     5  package meta
     6  
     7  import (
     8  	"fmt"
     9  	"sort"
    10  	"strconv"
    11  
    12  	"github.com/NVIDIA/aistore/api/apc"
    13  	"github.com/NVIDIA/aistore/cmn"
    14  	"github.com/NVIDIA/aistore/cmn/debug"
    15  )
    16  
    17  type (
    18  	// interface to Get current (versioned, immutable) BMD instance
    19  	// (for implementation, see ais/bucketmeta.go)
    20  	Bowner interface {
    21  		Get() (bmd *BMD)
    22  	}
    23  
    24  	Buckets    map[string]*cmn.Bprops
    25  	Namespaces map[string]Buckets
    26  	Providers  map[string]Namespaces
    27  
    28  	// - BMD is the root of the (providers, namespaces, buckets) hierarchy
    29  	// - BMD (instance) can be obtained via Bowner.Get()
    30  	// - BMD is immutable and versioned
    31  	// - BMD versioning is monotonic and incremental
    32  	BMD struct {
    33  		Ext       any       `json:"ext,omitempty"`  // within meta-version extensions
    34  		Providers Providers `json:"providers"`      // (provider, namespace, bucket) hierarchy
    35  		UUID      string    `json:"uuid"`           // unique & immutable
    36  		Version   int64     `json:"version,string"` // gets incremented on every update
    37  	}
    38  )
    39  
    40  func (m *BMD) String() string {
    41  	if m == nil {
    42  		return "BMD <nil>"
    43  	}
    44  	return "BMD v" + strconv.FormatInt(m.Version, 10)
    45  }
    46  
    47  func (m *BMD) StringEx() string {
    48  	if m == nil {
    49  		return "BMD <nil>"
    50  	}
    51  	na, nar, nc, no := m.numBuckets(false /*check empty*/)
    52  	if na+nar+nc+no == 0 {
    53  		return fmt.Sprintf("BMD v%d[%s (no buckets)]", m.Version, m.UUID)
    54  	}
    55  	if nar == 0 && no == 0 {
    56  		return fmt.Sprintf("BMD v%d[%s, buckets: ais(%d), cloud(%d)]", m.Version, m.UUID, na, nc)
    57  	}
    58  	return fmt.Sprintf("BMD v%d[%s, buckets: ais(%d), cloud(%d), remote-ais(%d), remote-other(%d)]",
    59  		m.Version, m.UUID, na, nc, nar, no)
    60  }
    61  
    62  func (m *BMD) Get(bck *Bck) (p *cmn.Bprops, present bool) {
    63  	buckets := m.getBuckets(bck)
    64  	if buckets != nil {
    65  		p, present = buckets[bck.Name]
    66  	}
    67  	return
    68  }
    69  
    70  func (m *BMD) Del(bck *Bck) (deleted bool) {
    71  	buckets := m.getBuckets(bck)
    72  	if buckets == nil {
    73  		return
    74  	}
    75  	if _, present := buckets[bck.Name]; !present {
    76  		return
    77  	}
    78  	delete(buckets, bck.Name)
    79  	return true
    80  }
    81  
    82  func (m *BMD) Set(bck *Bck, p *cmn.Bprops) {
    83  	buckets := m.getBuckets(bck)
    84  	buckets[bck.Name] = p
    85  }
    86  
    87  func (m *BMD) Exists(bck *Bck, bckID uint64) (exists bool) {
    88  	p, present := m.Get(bck)
    89  	if present {
    90  		bck.Props = p
    91  		if bck.IsAIS() {
    92  			exists = bckID != 0 && p.BID == bckID
    93  		} else {
    94  			exists = p.BID == bckID
    95  		}
    96  	}
    97  	return
    98  }
    99  
   100  func (m *BMD) Add(bck *Bck) {
   101  	var (
   102  		namespaces Namespaces
   103  		buckets    Buckets
   104  		ok         bool
   105  	)
   106  	if namespaces, ok = m.Providers[bck.Provider]; !ok {
   107  		namespaces = make(Namespaces, 1)
   108  		m.Providers[bck.Provider] = namespaces
   109  	}
   110  	nsUname := bck.Ns.Uname()
   111  	if buckets, ok = namespaces[nsUname]; !ok {
   112  		buckets = make(Buckets)
   113  		namespaces[nsUname] = buckets
   114  	}
   115  	buckets[bck.Name] = bck.Props
   116  }
   117  
   118  func (m *BMD) IsEmpty() bool {
   119  	na, nar, nc, no := m.numBuckets(true)
   120  	return na+nar+nc+no == 0
   121  }
   122  
   123  func (m *BMD) IsECUsed() (yes bool) {
   124  	m.Range(nil, nil, func(bck *Bck) (stop bool) {
   125  		if bck.Props.EC.Enabled {
   126  			yes, stop = true, true
   127  		}
   128  		return
   129  	})
   130  	return
   131  }
   132  
   133  // providerQuery == nil: all providers; nsQuery == nil: all namespaces
   134  func (m *BMD) Range(providerQuery *string, nsQuery *cmn.Ns, callback func(*Bck) bool) {
   135  	var qname string
   136  	if nsQuery != nil {
   137  		qname = nsQuery.Uname()
   138  	}
   139  	for provider, namespaces := range m.Providers {
   140  		if providerQuery != nil && provider != *providerQuery {
   141  			continue
   142  		}
   143  		for nsUname, buckets := range namespaces {
   144  			if nsQuery != nil && nsUname != qname {
   145  				continue
   146  			}
   147  			for name, props := range buckets {
   148  				ns := cmn.ParseNsUname(nsUname)
   149  				bck := NewBck(name, provider, ns, props)
   150  				if callback(bck) { // break?
   151  					return
   152  				}
   153  			}
   154  		}
   155  	}
   156  }
   157  
   158  func (m *BMD) Select(qbck *cmn.QueryBcks) cmn.Bcks {
   159  	var (
   160  		cp   *string
   161  		bcks = cmn.Bcks{} // (json representation: nil slice != empty slice)
   162  	)
   163  	if qbck.Provider != "" {
   164  		cp = &qbck.Provider
   165  	}
   166  	m.Range(cp, nil, func(bck *Bck) bool {
   167  		b := bck.Bucket()
   168  		if qbck.Equal(b) || qbck.Contains(b) {
   169  			if len(bcks) == 0 {
   170  				bcks = make(cmn.Bcks, 0, 8)
   171  			}
   172  			bcks = append(bcks, bck.Clone())
   173  		}
   174  		return false
   175  	})
   176  	sort.Sort(bcks)
   177  	return bcks
   178  }
   179  
   180  //
   181  // private methods
   182  //
   183  
   184  func (m *BMD) getBuckets(bck *Bck) (buckets Buckets) {
   185  	if namespaces, ok := m.Providers[bck.Provider]; ok {
   186  		buckets = namespaces[bck.Ns.Uname()]
   187  	}
   188  	return
   189  }
   190  
   191  func (m *BMD) numBuckets(checkEmpty bool) (na, nar, nc, no int) {
   192  	for provider, namespaces := range m.Providers {
   193  		for nsUname, buckets := range namespaces {
   194  			ns := cmn.ParseNsUname(nsUname)
   195  			if provider == apc.AIS {
   196  				if ns.IsRemote() {
   197  					nar += len(buckets)
   198  				} else {
   199  					na += len(buckets)
   200  				}
   201  			} else if apc.IsCloudProvider(provider) {
   202  				nc += len(buckets)
   203  			} else {
   204  				no += len(buckets)
   205  			}
   206  			if checkEmpty && (na+nar+nc+no > 0) {
   207  				return
   208  			}
   209  		}
   210  	}
   211  	return
   212  }
   213  
   214  func (m *BMD) initBckGlobalNs(bck *Bck) bool {
   215  	namespaces, ok := m.Providers[bck.Provider]
   216  	if !ok {
   217  		return false
   218  	}
   219  	buckets, ok := namespaces[cmn.NsGlobal.Uname()]
   220  	if !ok {
   221  		return false
   222  	}
   223  	p, present := buckets[bck.Name]
   224  	if present {
   225  		debug.Assert(bck.Ns.IsGlobal())
   226  		bck.Props = p
   227  	}
   228  	return present
   229  }
   230  
   231  func (m *BMD) initBck(bck *Bck) {
   232  	namespaces, ok := m.Providers[bck.Provider]
   233  	if !ok {
   234  		return
   235  	}
   236  	for nsUname, buckets := range namespaces {
   237  		if p, present := buckets[bck.Name]; present {
   238  			bck.Props = p
   239  			bck.Ns = cmn.ParseNsUname(nsUname)
   240  			return
   241  		}
   242  	}
   243  }
   244  
   245  func (m *BMD) getAllByName(bckName string) (all []Bck) {
   246  	for provider, namespace := range m.Providers {
   247  		for nsUname, buckets := range namespace {
   248  			if props, present := buckets[bckName]; present {
   249  				bck := Bck{Name: bckName, Provider: provider, Props: props}
   250  				bck.Ns = cmn.ParseNsUname(nsUname)
   251  				all = append(all, bck)
   252  			}
   253  		}
   254  	}
   255  	return
   256  }