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 }