github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/data-usage-cache.go (about) 1 // Copyright (c) 2015-2023 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package cmd 19 20 import ( 21 "bytes" 22 "context" 23 "errors" 24 "fmt" 25 "io" 26 "math/rand" 27 "net/http" 28 "path" 29 "path/filepath" 30 "sort" 31 "strings" 32 "time" 33 34 "github.com/cespare/xxhash/v2" 35 "github.com/dustin/go-humanize" 36 "github.com/klauspost/compress/zstd" 37 "github.com/minio/madmin-go/v3" 38 "github.com/minio/minio/internal/bucket/lifecycle" 39 "github.com/minio/minio/internal/hash" 40 "github.com/minio/minio/internal/logger" 41 "github.com/tinylib/msgp/msgp" 42 "github.com/valyala/bytebufferpool" 43 ) 44 45 //go:generate msgp -file $GOFILE -unexported 46 47 // dataUsageHash is the hash type used. 48 type dataUsageHash string 49 50 // sizeHistogramV1 is size histogram V1, which has fewer intervals esp. between 51 // 1024B and 1MiB. 52 type sizeHistogramV1 [dataUsageBucketLenV1]uint64 53 54 // sizeHistogram is a size histogram. 55 type sizeHistogram [dataUsageBucketLen]uint64 56 57 // versionsHistogram is a histogram of number of versions in an object. 58 type versionsHistogram [dataUsageVersionLen]uint64 59 60 type dataUsageEntry struct { 61 Children dataUsageHashMap `msg:"ch"` 62 // These fields do no include any children. 63 Size int64 `msg:"sz"` 64 Objects uint64 `msg:"os"` 65 Versions uint64 `msg:"vs"` // Versions that are not delete markers. 66 DeleteMarkers uint64 `msg:"dms"` 67 ObjSizes sizeHistogram `msg:"szs"` 68 ObjVersions versionsHistogram `msg:"vh"` 69 ReplicationStats *replicationAllStats `msg:"rs,omitempty"` 70 AllTierStats *allTierStats `msg:"ats,omitempty"` 71 Compacted bool `msg:"c"` 72 } 73 74 // allTierStats is a collection of per-tier stats across all configured remote 75 // tiers. 76 type allTierStats struct { 77 Tiers map[string]tierStats `msg:"ts"` 78 } 79 80 func newAllTierStats() *allTierStats { 81 return &allTierStats{ 82 Tiers: make(map[string]tierStats), 83 } 84 } 85 86 func (ats *allTierStats) addSizes(tiers map[string]tierStats) { 87 for tier, st := range tiers { 88 ats.Tiers[tier] = ats.Tiers[tier].add(st) 89 } 90 } 91 92 func (ats *allTierStats) merge(other *allTierStats) { 93 for tier, st := range other.Tiers { 94 ats.Tiers[tier] = ats.Tiers[tier].add(st) 95 } 96 } 97 98 func (ats *allTierStats) clone() *allTierStats { 99 if ats == nil { 100 return nil 101 } 102 dst := *ats 103 dst.Tiers = make(map[string]tierStats, len(ats.Tiers)) 104 for tier, st := range ats.Tiers { 105 dst.Tiers[tier] = st 106 } 107 return &dst 108 } 109 110 func (ats *allTierStats) populateStats(stats map[string]madmin.TierStats) { 111 if ats == nil { 112 return 113 } 114 115 // Update stats for tiers as they become available. 116 for tier, st := range ats.Tiers { 117 stats[tier] = madmin.TierStats{ 118 TotalSize: st.TotalSize, 119 NumVersions: st.NumVersions, 120 NumObjects: st.NumObjects, 121 } 122 } 123 return 124 } 125 126 // tierStats holds per-tier stats of a remote tier. 127 type tierStats struct { 128 TotalSize uint64 `msg:"ts"` 129 NumVersions int `msg:"nv"` 130 NumObjects int `msg:"no"` 131 } 132 133 func (ts tierStats) add(u tierStats) tierStats { 134 return tierStats{ 135 TotalSize: ts.TotalSize + u.TotalSize, 136 NumVersions: ts.NumVersions + u.NumVersions, 137 NumObjects: ts.NumObjects + u.NumObjects, 138 } 139 } 140 141 //msgp:tuple replicationStatsV1 142 type replicationStatsV1 struct { 143 PendingSize uint64 144 ReplicatedSize uint64 145 FailedSize uint64 146 ReplicaSize uint64 147 FailedCount uint64 148 PendingCount uint64 149 MissedThresholdSize uint64 150 AfterThresholdSize uint64 151 MissedThresholdCount uint64 152 AfterThresholdCount uint64 153 } 154 155 func (rsv1 replicationStatsV1) Empty() bool { 156 return rsv1.ReplicatedSize == 0 && 157 rsv1.FailedSize == 0 && 158 rsv1.FailedCount == 0 159 } 160 161 //msgp:tuple replicationStats 162 type replicationStats struct { 163 PendingSize uint64 164 ReplicatedSize uint64 165 FailedSize uint64 166 FailedCount uint64 167 PendingCount uint64 168 MissedThresholdSize uint64 169 AfterThresholdSize uint64 170 MissedThresholdCount uint64 171 AfterThresholdCount uint64 172 ReplicatedCount uint64 173 } 174 175 func (rs replicationStats) Empty() bool { 176 return rs.ReplicatedSize == 0 && 177 rs.FailedSize == 0 && 178 rs.FailedCount == 0 179 } 180 181 type replicationAllStats struct { 182 Targets map[string]replicationStats `msg:"t,omitempty"` 183 ReplicaSize uint64 `msg:"r,omitempty"` 184 ReplicaCount uint64 `msg:"rc,omitempty"` 185 } 186 187 //msgp:tuple replicationAllStatsV1 188 type replicationAllStatsV1 struct { 189 Targets map[string]replicationStats 190 ReplicaSize uint64 `msg:"ReplicaSize,omitempty"` 191 ReplicaCount uint64 `msg:"ReplicaCount,omitempty"` 192 } 193 194 // empty returns true if the replicationAllStats is empty (contains no entries). 195 func (r *replicationAllStats) empty() bool { 196 if r == nil { 197 return true 198 } 199 if r.ReplicaSize != 0 || r.ReplicaCount != 0 { 200 return false 201 } 202 for _, v := range r.Targets { 203 if !v.Empty() { 204 return false 205 } 206 } 207 return true 208 } 209 210 // clone creates a deep-copy clone. 211 func (r *replicationAllStats) clone() *replicationAllStats { 212 if r == nil { 213 return nil 214 } 215 216 // Shallow copy 217 dst := *r 218 219 // Copy individual targets. 220 dst.Targets = make(map[string]replicationStats, len(r.Targets)) 221 for k, v := range r.Targets { 222 dst.Targets[k] = v 223 } 224 225 return &dst 226 } 227 228 //msgp:encode ignore dataUsageEntryV2 dataUsageEntryV3 dataUsageEntryV4 dataUsageEntryV5 dataUsageEntryV6 dataUsageEntryV7 229 //msgp:marshal ignore dataUsageEntryV2 dataUsageEntryV3 dataUsageEntryV4 dataUsageEntryV5 dataUsageEntryV6 dataUsageEntryV7 230 231 //msgp:tuple dataUsageEntryV2 232 type dataUsageEntryV2 struct { 233 // These fields do no include any children. 234 Size int64 235 Objects uint64 236 ObjSizes sizeHistogram 237 Children dataUsageHashMap 238 } 239 240 //msgp:tuple dataUsageEntryV3 241 type dataUsageEntryV3 struct { 242 // These fields do no include any children. 243 Size int64 244 ReplicatedSize uint64 245 ReplicationPendingSize uint64 246 ReplicationFailedSize uint64 247 ReplicaSize uint64 248 Objects uint64 249 ObjSizes sizeHistogram 250 Children dataUsageHashMap 251 } 252 253 //msgp:tuple dataUsageEntryV4 254 type dataUsageEntryV4 struct { 255 Children dataUsageHashMap 256 // These fields do no include any children. 257 Size int64 258 Objects uint64 259 ObjSizes sizeHistogram 260 ReplicationStats replicationStatsV1 261 } 262 263 //msgp:tuple dataUsageEntryV5 264 type dataUsageEntryV5 struct { 265 Children dataUsageHashMap 266 // These fields do no include any children. 267 Size int64 268 Objects uint64 269 Versions uint64 // Versions that are not delete markers. 270 ObjSizes sizeHistogram 271 ReplicationStats *replicationStatsV1 272 Compacted bool 273 } 274 275 //msgp:tuple dataUsageEntryV6 276 type dataUsageEntryV6 struct { 277 Children dataUsageHashMap 278 // These fields do no include any children. 279 Size int64 280 Objects uint64 281 Versions uint64 // Versions that are not delete markers. 282 ObjSizes sizeHistogram 283 ReplicationStats *replicationAllStatsV1 284 Compacted bool 285 } 286 287 type dataUsageEntryV7 struct { 288 Children dataUsageHashMap `msg:"ch"` 289 // These fields do no include any children. 290 Size int64 `msg:"sz"` 291 Objects uint64 `msg:"os"` 292 Versions uint64 `msg:"vs"` // Versions that are not delete markers. 293 DeleteMarkers uint64 `msg:"dms"` 294 ObjSizes sizeHistogramV1 `msg:"szs"` 295 ObjVersions versionsHistogram `msg:"vh"` 296 ReplicationStats *replicationAllStats `msg:"rs,omitempty"` 297 AllTierStats *allTierStats `msg:"ats,omitempty"` 298 Compacted bool `msg:"c"` 299 } 300 301 // dataUsageCache contains a cache of data usage entries latest version. 302 type dataUsageCache struct { 303 Info dataUsageCacheInfo 304 Cache map[string]dataUsageEntry 305 } 306 307 //msgp:encode ignore dataUsageCacheV2 dataUsageCacheV3 dataUsageCacheV4 dataUsageCacheV5 dataUsageCacheV6 dataUsageCacheV7 308 //msgp:marshal ignore dataUsageCacheV2 dataUsageCacheV3 dataUsageCacheV4 dataUsageCacheV5 dataUsageCacheV6 dataUsageCacheV7 309 310 // dataUsageCacheV2 contains a cache of data usage entries version 2. 311 type dataUsageCacheV2 struct { 312 Info dataUsageCacheInfo 313 Cache map[string]dataUsageEntryV2 314 } 315 316 // dataUsageCacheV3 contains a cache of data usage entries version 3. 317 type dataUsageCacheV3 struct { 318 Info dataUsageCacheInfo 319 Cache map[string]dataUsageEntryV3 320 } 321 322 // dataUsageCacheV4 contains a cache of data usage entries version 4. 323 type dataUsageCacheV4 struct { 324 Info dataUsageCacheInfo 325 Cache map[string]dataUsageEntryV4 326 } 327 328 // dataUsageCacheV5 contains a cache of data usage entries version 5. 329 type dataUsageCacheV5 struct { 330 Info dataUsageCacheInfo 331 Cache map[string]dataUsageEntryV5 332 } 333 334 // dataUsageCacheV6 contains a cache of data usage entries version 6. 335 type dataUsageCacheV6 struct { 336 Info dataUsageCacheInfo 337 Cache map[string]dataUsageEntryV6 338 } 339 340 // dataUsageCacheV7 contains a cache of data usage entries version 7. 341 type dataUsageCacheV7 struct { 342 Info dataUsageCacheInfo 343 Cache map[string]dataUsageEntryV7 344 } 345 346 //msgp:ignore dataUsageEntryInfo 347 type dataUsageEntryInfo struct { 348 Name string 349 Parent string 350 Entry dataUsageEntry 351 } 352 353 type dataUsageCacheInfo struct { 354 // Name of the bucket. Also root element. 355 Name string 356 NextCycle uint32 357 LastUpdate time.Time 358 // indicates if the disk is being healed and scanner 359 // should skip healing the disk 360 SkipHealing bool 361 362 // Active lifecycle, if any on the bucket 363 lifeCycle *lifecycle.Lifecycle `msg:"-"` 364 365 // optional updates channel. 366 // If set updates will be sent regularly to this channel. 367 // Will not be closed when returned. 368 updates chan<- dataUsageEntry `msg:"-"` 369 replication replicationConfig `msg:"-"` 370 } 371 372 func (e *dataUsageEntry) addSizes(summary sizeSummary) { 373 e.Size += summary.totalSize 374 e.Versions += summary.versions 375 e.DeleteMarkers += summary.deleteMarkers 376 e.ObjSizes.add(summary.totalSize) 377 e.ObjVersions.add(summary.versions) 378 379 if e.ReplicationStats == nil { 380 e.ReplicationStats = &replicationAllStats{ 381 Targets: make(map[string]replicationStats), 382 } 383 } else if e.ReplicationStats.Targets == nil { 384 e.ReplicationStats.Targets = make(map[string]replicationStats) 385 } 386 e.ReplicationStats.ReplicaSize += uint64(summary.replicaSize) 387 e.ReplicationStats.ReplicaCount += uint64(summary.replicaCount) 388 389 for arn, st := range summary.replTargetStats { 390 tgtStat, ok := e.ReplicationStats.Targets[arn] 391 if !ok { 392 tgtStat = replicationStats{} 393 } 394 tgtStat.PendingSize += uint64(st.pendingSize) 395 tgtStat.FailedSize += uint64(st.failedSize) 396 tgtStat.ReplicatedSize += uint64(st.replicatedSize) 397 tgtStat.ReplicatedCount += uint64(st.replicatedCount) 398 tgtStat.FailedCount += st.failedCount 399 tgtStat.PendingCount += st.pendingCount 400 e.ReplicationStats.Targets[arn] = tgtStat 401 } 402 if len(summary.tiers) != 0 { 403 if e.AllTierStats == nil { 404 e.AllTierStats = newAllTierStats() 405 } 406 e.AllTierStats.addSizes(summary.tiers) 407 } 408 } 409 410 // merge other data usage entry into this, excluding children. 411 func (e *dataUsageEntry) merge(other dataUsageEntry) { 412 e.Objects += other.Objects 413 e.Versions += other.Versions 414 e.DeleteMarkers += other.DeleteMarkers 415 e.Size += other.Size 416 if other.ReplicationStats != nil { 417 if e.ReplicationStats == nil { 418 e.ReplicationStats = &replicationAllStats{Targets: make(map[string]replicationStats)} 419 } else if e.ReplicationStats.Targets == nil { 420 e.ReplicationStats.Targets = make(map[string]replicationStats) 421 } 422 e.ReplicationStats.ReplicaSize += other.ReplicationStats.ReplicaSize 423 e.ReplicationStats.ReplicaCount += other.ReplicationStats.ReplicaCount 424 for arn, stat := range other.ReplicationStats.Targets { 425 st := e.ReplicationStats.Targets[arn] 426 e.ReplicationStats.Targets[arn] = replicationStats{ 427 PendingSize: stat.PendingSize + st.PendingSize, 428 FailedSize: stat.FailedSize + st.FailedSize, 429 ReplicatedSize: stat.ReplicatedSize + st.ReplicatedSize, 430 PendingCount: stat.PendingCount + st.PendingCount, 431 FailedCount: stat.FailedCount + st.FailedCount, 432 ReplicatedCount: stat.ReplicatedCount + st.ReplicatedCount, 433 } 434 } 435 } 436 437 for i, v := range other.ObjSizes[:] { 438 e.ObjSizes[i] += v 439 } 440 441 for i, v := range other.ObjVersions[:] { 442 e.ObjVersions[i] += v 443 } 444 445 if other.AllTierStats != nil && len(other.AllTierStats.Tiers) != 0 { 446 if e.AllTierStats == nil { 447 e.AllTierStats = newAllTierStats() 448 } 449 e.AllTierStats.merge(other.AllTierStats) 450 } 451 } 452 453 // mod returns true if the hash mod cycles == cycle. 454 // If cycles is 0 false is always returned. 455 // If cycles is 1 true is always returned (as expected). 456 func (h dataUsageHash) mod(cycle uint32, cycles uint32) bool { 457 if cycles <= 1 { 458 return cycles == 1 459 } 460 return uint32(xxhash.Sum64String(string(h)))%cycles == cycle%cycles 461 } 462 463 // modAlt returns true if the hash mod cycles == cycle. 464 // This is out of sync with mod. 465 // If cycles is 0 false is always returned. 466 // If cycles is 1 true is always returned (as expected). 467 func (h dataUsageHash) modAlt(cycle uint32, cycles uint32) bool { 468 if cycles <= 1 { 469 return cycles == 1 470 } 471 return uint32(xxhash.Sum64String(string(h))>>32)%(cycles) == cycle%cycles 472 } 473 474 // addChild will add a child based on its hash. 475 // If it already exists it will not be added again. 476 func (e *dataUsageEntry) addChild(hash dataUsageHash) { 477 if _, ok := e.Children[hash.Key()]; ok { 478 return 479 } 480 if e.Children == nil { 481 e.Children = make(dataUsageHashMap, 1) 482 } 483 e.Children[hash.Key()] = struct{}{} 484 } 485 486 // Create a clone of the entry. 487 func (e dataUsageEntry) clone() dataUsageEntry { 488 // We operate on a copy from the receiver. 489 if e.Children != nil { 490 ch := make(dataUsageHashMap, len(e.Children)) 491 for k, v := range e.Children { 492 ch[k] = v 493 } 494 e.Children = ch 495 } 496 if e.ReplicationStats != nil { 497 // Clone ReplicationStats 498 e.ReplicationStats = e.ReplicationStats.clone() 499 } 500 if e.AllTierStats != nil { 501 e.AllTierStats = e.AllTierStats.clone() 502 } 503 return e 504 } 505 506 // find a path in the cache. 507 // Returns nil if not found. 508 func (d *dataUsageCache) find(path string) *dataUsageEntry { 509 due, ok := d.Cache[hashPath(path).Key()] 510 if !ok { 511 return nil 512 } 513 return &due 514 } 515 516 // isCompacted returns whether an entry is compacted. 517 // Returns false if not found. 518 func (d *dataUsageCache) isCompacted(h dataUsageHash) bool { 519 due, ok := d.Cache[h.Key()] 520 if !ok { 521 return false 522 } 523 return due.Compacted 524 } 525 526 // findChildrenCopy returns a copy of the children of the supplied hash. 527 func (d *dataUsageCache) findChildrenCopy(h dataUsageHash) dataUsageHashMap { 528 ch := d.Cache[h.String()].Children 529 res := make(dataUsageHashMap, len(ch)) 530 for k := range ch { 531 res[k] = struct{}{} 532 } 533 return res 534 } 535 536 // searchParent will search for the parent of h. 537 // This is an O(N*N) operation if there is no parent or it cannot be guessed. 538 func (d *dataUsageCache) searchParent(h dataUsageHash) *dataUsageHash { 539 want := h.Key() 540 if idx := strings.LastIndexByte(want, '/'); idx >= 0 { 541 if v := d.find(want[:idx]); v != nil { 542 _, ok := v.Children[want] 543 if ok { 544 found := hashPath(want[:idx]) 545 return &found 546 } 547 } 548 } 549 for k, v := range d.Cache { 550 _, ok := v.Children[want] 551 if ok { 552 found := dataUsageHash(k) 553 return &found 554 } 555 } 556 return nil 557 } 558 559 // deleteRecursive will delete an entry recursively, but not change its parent. 560 func (d *dataUsageCache) deleteRecursive(h dataUsageHash) { 561 if existing, ok := d.Cache[h.String()]; ok { 562 // Delete first if there should be a loop. 563 delete(d.Cache, h.Key()) 564 for child := range existing.Children { 565 d.deleteRecursive(dataUsageHash(child)) 566 } 567 } 568 } 569 570 // dui converts the flattened version of the path to madmin.DataUsageInfo. 571 // As a side effect d will be flattened, use a clone if this is not ok. 572 func (d *dataUsageCache) dui(path string, buckets []BucketInfo) DataUsageInfo { 573 e := d.find(path) 574 if e == nil { 575 // No entry found, return empty. 576 return DataUsageInfo{} 577 } 578 flat := d.flatten(*e) 579 dui := DataUsageInfo{ 580 LastUpdate: d.Info.LastUpdate, 581 ObjectsTotalCount: flat.Objects, 582 VersionsTotalCount: flat.Versions, 583 DeleteMarkersTotalCount: flat.DeleteMarkers, 584 ObjectsTotalSize: uint64(flat.Size), 585 BucketsCount: uint64(len(e.Children)), 586 BucketsUsage: d.bucketsUsageInfo(buckets), 587 TierStats: d.tiersUsageInfo(buckets), 588 } 589 return dui 590 } 591 592 // replace will add or replace an entry in the cache. 593 // If a parent is specified it will be added to that if not already there. 594 // If the parent does not exist, it will be added. 595 func (d *dataUsageCache) replace(path, parent string, e dataUsageEntry) { 596 hash := hashPath(path) 597 if d.Cache == nil { 598 d.Cache = make(map[string]dataUsageEntry, 100) 599 } 600 d.Cache[hash.Key()] = e 601 if parent != "" { 602 phash := hashPath(parent) 603 p := d.Cache[phash.Key()] 604 p.addChild(hash) 605 d.Cache[phash.Key()] = p 606 } 607 } 608 609 // replaceHashed add or replaces an entry to the cache based on its hash. 610 // If a parent is specified it will be added to that if not already there. 611 // If the parent does not exist, it will be added. 612 func (d *dataUsageCache) replaceHashed(hash dataUsageHash, parent *dataUsageHash, e dataUsageEntry) { 613 if d.Cache == nil { 614 d.Cache = make(map[string]dataUsageEntry, 100) 615 } 616 d.Cache[hash.Key()] = e 617 if parent != nil { 618 p := d.Cache[parent.Key()] 619 p.addChild(hash) 620 d.Cache[parent.Key()] = p 621 } 622 } 623 624 // copyWithChildren will copy entry with hash from src if it exists along with any children. 625 // If a parent is specified it will be added to that if not already there. 626 // If the parent does not exist, it will be added. 627 func (d *dataUsageCache) copyWithChildren(src *dataUsageCache, hash dataUsageHash, parent *dataUsageHash) { 628 if d.Cache == nil { 629 d.Cache = make(map[string]dataUsageEntry, 100) 630 } 631 e, ok := src.Cache[hash.String()] 632 if !ok { 633 return 634 } 635 d.Cache[hash.Key()] = e 636 for ch := range e.Children { 637 if ch == hash.Key() { 638 logger.LogIf(GlobalContext, errors.New("dataUsageCache.copyWithChildren: Circular reference")) 639 return 640 } 641 d.copyWithChildren(src, dataUsageHash(ch), &hash) 642 } 643 if parent != nil { 644 p := d.Cache[parent.Key()] 645 p.addChild(hash) 646 d.Cache[parent.Key()] = p 647 } 648 } 649 650 // reduceChildrenOf will reduce the recursive number of children to the limit 651 // by compacting the children with the least number of objects. 652 func (d *dataUsageCache) reduceChildrenOf(path dataUsageHash, limit int, compactSelf bool) { 653 e, ok := d.Cache[path.Key()] 654 if !ok { 655 return 656 } 657 if e.Compacted { 658 return 659 } 660 // If direct children have more, compact all. 661 if len(e.Children) > limit && compactSelf { 662 flat := d.sizeRecursive(path.Key()) 663 flat.Compacted = true 664 d.deleteRecursive(path) 665 d.replaceHashed(path, nil, *flat) 666 return 667 } 668 total := d.totalChildrenRec(path.Key()) 669 if total < limit { 670 return 671 } 672 673 // Appears to be printed with _MINIO_SERVER_DEBUG=off 674 // console.Debugf(" %d children found, compacting %v\n", total, path) 675 676 leaves := make([]struct { 677 objects uint64 678 path dataUsageHash 679 }, total) 680 // Collect current leaves that have children. 681 leaves = leaves[:0] 682 remove := total - limit 683 var add func(path dataUsageHash) 684 add = func(path dataUsageHash) { 685 e, ok := d.Cache[path.Key()] 686 if !ok { 687 return 688 } 689 if len(e.Children) == 0 { 690 return 691 } 692 sz := d.sizeRecursive(path.Key()) 693 leaves = append(leaves, struct { 694 objects uint64 695 path dataUsageHash 696 }{objects: sz.Objects, path: path}) 697 for ch := range e.Children { 698 add(dataUsageHash(ch)) 699 } 700 } 701 702 // Add path recursively. 703 add(path) 704 sort.Slice(leaves, func(i, j int) bool { 705 return leaves[i].objects < leaves[j].objects 706 }) 707 for remove > 0 && len(leaves) > 0 { 708 // Remove top entry. 709 e := leaves[0] 710 candidate := e.path 711 if candidate == path && !compactSelf { 712 // We should be the biggest, 713 // if we cannot compact ourself, we are done. 714 break 715 } 716 removing := d.totalChildrenRec(candidate.Key()) 717 flat := d.sizeRecursive(candidate.Key()) 718 if flat == nil { 719 leaves = leaves[1:] 720 continue 721 } 722 // Appears to be printed with _MINIO_SERVER_DEBUG=off 723 // console.Debugf("compacting %v, removing %d children\n", candidate, removing) 724 725 flat.Compacted = true 726 d.deleteRecursive(candidate) 727 d.replaceHashed(candidate, nil, *flat) 728 729 // Remove top entry and subtract removed children. 730 remove -= removing 731 leaves = leaves[1:] 732 } 733 } 734 735 // StringAll returns a detailed string representation of all entries in the cache. 736 func (d *dataUsageCache) StringAll() string { 737 // Remove bloom filter from print. 738 s := fmt.Sprintf("info:%+v\n", d.Info) 739 for k, v := range d.Cache { 740 s += fmt.Sprintf("\t%v: %+v\n", k, v) 741 } 742 return strings.TrimSpace(s) 743 } 744 745 // String returns a human readable representation of the string. 746 func (h dataUsageHash) String() string { 747 return string(h) 748 } 749 750 // Key returns the key. 751 func (h dataUsageHash) Key() string { 752 return string(h) 753 } 754 755 func (d *dataUsageCache) flattenChildrens(root dataUsageEntry) (m map[string]dataUsageEntry) { 756 m = make(map[string]dataUsageEntry) 757 for id := range root.Children { 758 e := d.Cache[id] 759 if len(e.Children) > 0 { 760 e = d.flatten(e) 761 } 762 m[id] = e 763 } 764 return m 765 } 766 767 // flatten all children of the root into the root element and return it. 768 func (d *dataUsageCache) flatten(root dataUsageEntry) dataUsageEntry { 769 for id := range root.Children { 770 e := d.Cache[id] 771 if len(e.Children) > 0 { 772 e = d.flatten(e) 773 } 774 root.merge(e) 775 } 776 root.Children = nil 777 return root 778 } 779 780 // add a size to the histogram. 781 func (h *sizeHistogram) add(size int64) { 782 // Fetch the histogram interval corresponding 783 // to the passed object size. 784 for i, interval := range ObjectsHistogramIntervals[:] { 785 if size >= interval.start && size <= interval.end { 786 h[i]++ 787 break 788 } 789 } 790 } 791 792 // mergeV1 is used to migrate data usage cache from sizeHistogramV1 to 793 // sizeHistogram 794 func (h *sizeHistogram) mergeV1(v sizeHistogramV1) { 795 var oidx, nidx int 796 for oidx < len(v) { 797 intOld, intNew := ObjectsHistogramIntervalsV1[oidx], ObjectsHistogramIntervals[nidx] 798 // skip intervals that aren't common to both histograms 799 if intOld.start != intNew.start || intOld.end != intNew.end { 800 nidx++ 801 continue 802 } 803 h[nidx] += v[oidx] 804 oidx++ 805 nidx++ 806 } 807 } 808 809 // toMap returns the map to a map[string]uint64. 810 func (h *sizeHistogram) toMap() map[string]uint64 { 811 res := make(map[string]uint64, dataUsageBucketLen) 812 var splCount uint64 813 for i, count := range h { 814 szInt := ObjectsHistogramIntervals[i] 815 switch { 816 case humanize.KiByte == szInt.start && szInt.end == humanize.MiByte-1: 817 // spl interval: [1024B, 1MiB) 818 res[szInt.name] = splCount 819 case humanize.KiByte <= szInt.start && szInt.end <= humanize.MiByte-1: 820 // intervals that fall within the spl interval above; they 821 // appear earlier in this array of intervals, see 822 // ObjectsHistogramIntervals 823 splCount += count 824 fallthrough 825 default: 826 res[szInt.name] = count 827 } 828 } 829 return res 830 } 831 832 // add a version count to the histogram. 833 func (h *versionsHistogram) add(versions uint64) { 834 // Fetch the histogram interval corresponding 835 // to the passed object size. 836 for i, interval := range ObjectsVersionCountIntervals[:] { 837 if versions >= uint64(interval.start) && versions <= uint64(interval.end) { 838 h[i]++ 839 break 840 } 841 } 842 } 843 844 // toMap returns the map to a map[string]uint64. 845 func (h *versionsHistogram) toMap() map[string]uint64 { 846 res := make(map[string]uint64, dataUsageVersionLen) 847 for i, count := range h { 848 res[ObjectsVersionCountIntervals[i].name] = count 849 } 850 return res 851 } 852 853 func (d *dataUsageCache) tiersUsageInfo(buckets []BucketInfo) *allTierStats { 854 dst := newAllTierStats() 855 for _, bucket := range buckets { 856 e := d.find(bucket.Name) 857 if e == nil { 858 continue 859 } 860 flat := d.flatten(*e) 861 if flat.AllTierStats == nil { 862 continue 863 } 864 dst.merge(flat.AllTierStats) 865 } 866 if len(dst.Tiers) == 0 { 867 return nil 868 } 869 return dst 870 } 871 872 // bucketsUsageInfo returns the buckets usage info as a map, with 873 // key as bucket name 874 func (d *dataUsageCache) bucketsUsageInfo(buckets []BucketInfo) map[string]BucketUsageInfo { 875 dst := make(map[string]BucketUsageInfo, len(buckets)) 876 for _, bucket := range buckets { 877 e := d.find(bucket.Name) 878 if e == nil { 879 continue 880 } 881 flat := d.flatten(*e) 882 bui := BucketUsageInfo{ 883 Size: uint64(flat.Size), 884 VersionsCount: flat.Versions, 885 ObjectsCount: flat.Objects, 886 DeleteMarkersCount: flat.DeleteMarkers, 887 ObjectSizesHistogram: flat.ObjSizes.toMap(), 888 ObjectVersionsHistogram: flat.ObjVersions.toMap(), 889 } 890 if flat.ReplicationStats != nil { 891 bui.ReplicaSize = flat.ReplicationStats.ReplicaSize 892 bui.ReplicaCount = flat.ReplicationStats.ReplicaCount 893 894 bui.ReplicationInfo = make(map[string]BucketTargetUsageInfo, len(flat.ReplicationStats.Targets)) 895 for arn, stat := range flat.ReplicationStats.Targets { 896 bui.ReplicationInfo[arn] = BucketTargetUsageInfo{ 897 ReplicationPendingSize: stat.PendingSize, 898 ReplicatedSize: stat.ReplicatedSize, 899 ReplicationFailedSize: stat.FailedSize, 900 ReplicationPendingCount: stat.PendingCount, 901 ReplicationFailedCount: stat.FailedCount, 902 ReplicatedCount: stat.ReplicatedCount, 903 } 904 } 905 } 906 dst[bucket.Name] = bui 907 } 908 return dst 909 } 910 911 // sizeRecursive returns the path as a flattened entry. 912 func (d *dataUsageCache) sizeRecursive(path string) *dataUsageEntry { 913 root := d.find(path) 914 if root == nil || len(root.Children) == 0 { 915 return root 916 } 917 flat := d.flatten(*root) 918 if flat.ReplicationStats.empty() { 919 flat.ReplicationStats = nil 920 } 921 return &flat 922 } 923 924 // totalChildrenRec returns the total number of children recorded. 925 func (d *dataUsageCache) totalChildrenRec(path string) int { 926 root := d.find(path) 927 if root == nil || len(root.Children) == 0 { 928 return 0 929 } 930 n := len(root.Children) 931 for ch := range root.Children { 932 n += d.totalChildrenRec(ch) 933 } 934 return n 935 } 936 937 // root returns the root of the cache. 938 func (d *dataUsageCache) root() *dataUsageEntry { 939 return d.find(d.Info.Name) 940 } 941 942 // rootHash returns the root of the cache. 943 func (d *dataUsageCache) rootHash() dataUsageHash { 944 return hashPath(d.Info.Name) 945 } 946 947 // clone returns a copy of the cache with no references to the existing. 948 func (d *dataUsageCache) clone() dataUsageCache { 949 clone := dataUsageCache{ 950 Info: d.Info, 951 Cache: make(map[string]dataUsageEntry, len(d.Cache)), 952 } 953 for k, v := range d.Cache { 954 clone.Cache[k] = v.clone() 955 } 956 return clone 957 } 958 959 // merge root of other into d. 960 // children of root will be flattened before being merged. 961 // Last update time will be set to the last updated. 962 func (d *dataUsageCache) merge(other dataUsageCache) { 963 existingRoot := d.root() 964 otherRoot := other.root() 965 if existingRoot == nil && otherRoot == nil { 966 return 967 } 968 if otherRoot == nil { 969 return 970 } 971 if existingRoot == nil { 972 *d = other.clone() 973 return 974 } 975 if other.Info.LastUpdate.After(d.Info.LastUpdate) { 976 d.Info.LastUpdate = other.Info.LastUpdate 977 } 978 existingRoot.merge(*otherRoot) 979 eHash := d.rootHash() 980 for key := range otherRoot.Children { 981 entry := other.Cache[key] 982 flat := other.flatten(entry) 983 existing := d.Cache[key] 984 // If not found, merging simply adds. 985 existing.merge(flat) 986 d.replaceHashed(dataUsageHash(key), &eHash, existing) 987 } 988 } 989 990 type objectIO interface { 991 GetObjectNInfo(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, opts ObjectOptions) (reader *GetObjectReader, err error) 992 PutObject(ctx context.Context, bucket, object string, data *PutObjReader, opts ObjectOptions) (objInfo ObjectInfo, err error) 993 } 994 995 // load the cache content with name from minioMetaBackgroundOpsBucket. 996 // Only backend errors are returned as errors. 997 // The loader is optimistic and has no locking, but tries 5 times before giving up. 998 // If the object is not found, a nil error with empty data usage cache is returned. 999 func (d *dataUsageCache) load(ctx context.Context, store objectIO, name string) error { 1000 // By default, empty data usage cache 1001 *d = dataUsageCache{} 1002 1003 load := func(name string, timeout time.Duration) (bool, error) { 1004 // Abandon if more than time.Minute, so we don't hold up scanner. 1005 // drive timeout by default is 2 minutes, we do not need to wait longer. 1006 ctx, cancel := context.WithTimeout(ctx, timeout) 1007 defer cancel() 1008 1009 r, err := store.GetObjectNInfo(ctx, dataUsageBucket, name, nil, http.Header{}, ObjectOptions{NoLock: true}) 1010 if err != nil { 1011 switch err.(type) { 1012 case ObjectNotFound, BucketNotFound: 1013 return false, nil 1014 case InsufficientReadQuorum, StorageErr: 1015 return true, nil 1016 } 1017 return false, err 1018 } 1019 err = d.deserialize(r) 1020 r.Close() 1021 return err != nil, nil 1022 } 1023 1024 // Caches are read+written without locks, 1025 retries := 0 1026 for retries < 5 { 1027 retry, err := load(name, time.Minute) 1028 if err != nil { 1029 return toObjectErr(err, dataUsageBucket, name) 1030 } 1031 if !retry { 1032 break 1033 } 1034 retry, err = load(name+".bkp", 30*time.Second) 1035 if err == nil && !retry { 1036 // Only return when we have valid data from the backup 1037 break 1038 } 1039 retries++ 1040 time.Sleep(time.Duration(rand.Int63n(int64(time.Second)))) 1041 } 1042 1043 if retries == 5 { 1044 logger.LogOnceIf(ctx, fmt.Errorf("maximum retry reached to load the data usage cache `%s`", name), "retry-loading-data-usage-cache") 1045 } 1046 1047 return nil 1048 } 1049 1050 // Maximum running concurrent saves on server. 1051 var maxConcurrentScannerSaves = make(chan struct{}, 4) 1052 1053 // save the content of the cache to minioMetaBackgroundOpsBucket with the provided name. 1054 // Note that no locking is done when saving. 1055 func (d *dataUsageCache) save(ctx context.Context, store objectIO, name string) error { 1056 select { 1057 case <-ctx.Done(): 1058 return ctx.Err() 1059 case maxConcurrentScannerSaves <- struct{}{}: 1060 } 1061 1062 buf := bytebufferpool.Get() 1063 defer func() { 1064 <-maxConcurrentScannerSaves 1065 buf.Reset() 1066 bytebufferpool.Put(buf) 1067 }() 1068 1069 if err := d.serializeTo(buf); err != nil { 1070 return err 1071 } 1072 1073 save := func(name string, timeout time.Duration) error { 1074 hr, err := hash.NewReader(ctx, bytes.NewReader(buf.Bytes()), int64(buf.Len()), "", "", int64(buf.Len())) 1075 if err != nil { 1076 return err 1077 } 1078 1079 // Abandon if more than a minute, so we don't hold up scanner. 1080 ctx, cancel := context.WithTimeout(ctx, timeout) 1081 defer cancel() 1082 1083 _, err = store.PutObject(ctx, 1084 dataUsageBucket, 1085 name, 1086 NewPutObjReader(hr), 1087 ObjectOptions{NoLock: true}) 1088 if isErrBucketNotFound(err) { 1089 return nil 1090 } 1091 return err 1092 } 1093 defer save(name+".bkp", 5*time.Second) // Keep a backup as well 1094 1095 // drive timeout by default is 2 minutes, we do not need to wait longer. 1096 return save(name, time.Minute) 1097 } 1098 1099 // dataUsageCacheVer indicates the cache version. 1100 // Bumping the cache version will drop data from previous versions 1101 // and write new data with the new version. 1102 const ( 1103 dataUsageCacheVerCurrent = 8 1104 dataUsageCacheVerV7 = 7 1105 dataUsageCacheVerV6 = 6 1106 dataUsageCacheVerV5 = 5 1107 dataUsageCacheVerV4 = 4 1108 dataUsageCacheVerV3 = 3 1109 dataUsageCacheVerV2 = 2 1110 dataUsageCacheVerV1 = 1 1111 ) 1112 1113 // serialize the contents of the cache. 1114 func (d *dataUsageCache) serializeTo(dst io.Writer) error { 1115 // Add version and compress. 1116 _, err := dst.Write([]byte{dataUsageCacheVerCurrent}) 1117 if err != nil { 1118 return err 1119 } 1120 enc, err := zstd.NewWriter(dst, 1121 zstd.WithEncoderLevel(zstd.SpeedFastest), 1122 zstd.WithWindowSize(1<<20), 1123 zstd.WithEncoderConcurrency(2)) 1124 if err != nil { 1125 return err 1126 } 1127 mEnc := msgp.NewWriter(enc) 1128 err = d.EncodeMsg(mEnc) 1129 if err != nil { 1130 return err 1131 } 1132 err = mEnc.Flush() 1133 if err != nil { 1134 return err 1135 } 1136 err = enc.Close() 1137 if err != nil { 1138 return err 1139 } 1140 return nil 1141 } 1142 1143 // deserialize the supplied byte slice into the cache. 1144 func (d *dataUsageCache) deserialize(r io.Reader) error { 1145 var b [1]byte 1146 n, _ := r.Read(b[:]) 1147 if n != 1 { 1148 return io.ErrUnexpectedEOF 1149 } 1150 ver := int(b[0]) 1151 switch ver { 1152 case dataUsageCacheVerV1: 1153 return errors.New("cache version deprecated (will autoupdate)") 1154 case dataUsageCacheVerV2: 1155 // Zstd compressed. 1156 dec, err := zstd.NewReader(r, zstd.WithDecoderConcurrency(2)) 1157 if err != nil { 1158 return err 1159 } 1160 defer dec.Close() 1161 1162 dold := &dataUsageCacheV2{} 1163 if err = dold.DecodeMsg(msgp.NewReader(dec)); err != nil { 1164 return err 1165 } 1166 d.Info = dold.Info 1167 d.Cache = make(map[string]dataUsageEntry, len(dold.Cache)) 1168 for k, v := range dold.Cache { 1169 d.Cache[k] = dataUsageEntry{ 1170 Size: v.Size, 1171 Objects: v.Objects, 1172 ObjSizes: v.ObjSizes, 1173 Children: v.Children, 1174 Compacted: len(v.Children) == 0 && k != d.Info.Name, 1175 } 1176 } 1177 return nil 1178 case dataUsageCacheVerV3: 1179 // Zstd compressed. 1180 dec, err := zstd.NewReader(r, zstd.WithDecoderConcurrency(2)) 1181 if err != nil { 1182 return err 1183 } 1184 defer dec.Close() 1185 dold := &dataUsageCacheV3{} 1186 if err = dold.DecodeMsg(msgp.NewReader(dec)); err != nil { 1187 return err 1188 } 1189 d.Info = dold.Info 1190 d.Cache = make(map[string]dataUsageEntry, len(dold.Cache)) 1191 for k, v := range dold.Cache { 1192 due := dataUsageEntry{ 1193 Size: v.Size, 1194 Objects: v.Objects, 1195 ObjSizes: v.ObjSizes, 1196 Children: v.Children, 1197 } 1198 if v.ReplicatedSize > 0 || v.ReplicaSize > 0 || v.ReplicationFailedSize > 0 || v.ReplicationPendingSize > 0 { 1199 cfg, _ := getReplicationConfig(GlobalContext, d.Info.Name) 1200 if cfg != nil && cfg.RoleArn != "" { 1201 due.ReplicationStats = &replicationAllStats{ 1202 Targets: make(map[string]replicationStats), 1203 } 1204 due.ReplicationStats.ReplicaSize = v.ReplicaSize 1205 due.ReplicationStats.Targets[cfg.RoleArn] = replicationStats{ 1206 ReplicatedSize: v.ReplicatedSize, 1207 FailedSize: v.ReplicationFailedSize, 1208 PendingSize: v.ReplicationPendingSize, 1209 } 1210 } 1211 } 1212 due.Compacted = len(due.Children) == 0 && k != d.Info.Name 1213 1214 d.Cache[k] = due 1215 } 1216 return nil 1217 case dataUsageCacheVerV4: 1218 // Zstd compressed. 1219 dec, err := zstd.NewReader(r, zstd.WithDecoderConcurrency(2)) 1220 if err != nil { 1221 return err 1222 } 1223 defer dec.Close() 1224 dold := &dataUsageCacheV4{} 1225 if err = dold.DecodeMsg(msgp.NewReader(dec)); err != nil { 1226 return err 1227 } 1228 d.Info = dold.Info 1229 d.Cache = make(map[string]dataUsageEntry, len(dold.Cache)) 1230 for k, v := range dold.Cache { 1231 due := dataUsageEntry{ 1232 Size: v.Size, 1233 Objects: v.Objects, 1234 ObjSizes: v.ObjSizes, 1235 Children: v.Children, 1236 } 1237 empty := replicationStatsV1{} 1238 1239 if v.ReplicationStats != empty { 1240 cfg, _ := getReplicationConfig(GlobalContext, d.Info.Name) 1241 if cfg != nil && cfg.RoleArn != "" { 1242 due.ReplicationStats = &replicationAllStats{ 1243 Targets: make(map[string]replicationStats), 1244 } 1245 due.ReplicationStats.Targets[cfg.RoleArn] = replicationStats{ 1246 ReplicatedSize: v.ReplicationStats.ReplicatedSize, 1247 FailedSize: v.ReplicationStats.FailedSize, 1248 FailedCount: v.ReplicationStats.FailedCount, 1249 PendingSize: v.ReplicationStats.PendingSize, 1250 PendingCount: v.ReplicationStats.PendingCount, 1251 } 1252 due.ReplicationStats.ReplicaSize = v.ReplicationStats.ReplicaSize 1253 } 1254 } 1255 due.Compacted = len(due.Children) == 0 && k != d.Info.Name 1256 1257 d.Cache[k] = due 1258 } 1259 1260 // Populate compacted value and remove unneeded replica stats. 1261 for k, e := range d.Cache { 1262 if e.ReplicationStats != nil && len(e.ReplicationStats.Targets) == 0 { 1263 e.ReplicationStats = nil 1264 } 1265 d.Cache[k] = e 1266 } 1267 return nil 1268 case dataUsageCacheVerV5: 1269 // Zstd compressed. 1270 dec, err := zstd.NewReader(r, zstd.WithDecoderConcurrency(2)) 1271 if err != nil { 1272 return err 1273 } 1274 defer dec.Close() 1275 dold := &dataUsageCacheV5{} 1276 if err = dold.DecodeMsg(msgp.NewReader(dec)); err != nil { 1277 return err 1278 } 1279 d.Info = dold.Info 1280 d.Cache = make(map[string]dataUsageEntry, len(dold.Cache)) 1281 for k, v := range dold.Cache { 1282 due := dataUsageEntry{ 1283 Size: v.Size, 1284 Objects: v.Objects, 1285 ObjSizes: v.ObjSizes, 1286 Children: v.Children, 1287 } 1288 if v.ReplicationStats != nil && !v.ReplicationStats.Empty() { 1289 cfg, _ := getReplicationConfig(GlobalContext, d.Info.Name) 1290 if cfg != nil && cfg.RoleArn != "" { 1291 due.ReplicationStats = &replicationAllStats{ 1292 Targets: make(map[string]replicationStats), 1293 } 1294 d.Info.replication = replicationConfig{Config: cfg} 1295 1296 due.ReplicationStats.Targets[cfg.RoleArn] = replicationStats{ 1297 ReplicatedSize: v.ReplicationStats.ReplicatedSize, 1298 FailedSize: v.ReplicationStats.FailedSize, 1299 FailedCount: v.ReplicationStats.FailedCount, 1300 PendingSize: v.ReplicationStats.PendingSize, 1301 PendingCount: v.ReplicationStats.PendingCount, 1302 } 1303 due.ReplicationStats.ReplicaSize = v.ReplicationStats.ReplicaSize 1304 } 1305 } 1306 due.Compacted = len(due.Children) == 0 && k != d.Info.Name 1307 1308 d.Cache[k] = due 1309 } 1310 1311 // Populate compacted value and remove unneeded replica stats. 1312 for k, e := range d.Cache { 1313 if e.ReplicationStats != nil && len(e.ReplicationStats.Targets) == 0 { 1314 e.ReplicationStats = nil 1315 } 1316 d.Cache[k] = e 1317 } 1318 return nil 1319 case dataUsageCacheVerV6: 1320 // Zstd compressed. 1321 dec, err := zstd.NewReader(r, zstd.WithDecoderConcurrency(2)) 1322 if err != nil { 1323 return err 1324 } 1325 defer dec.Close() 1326 dold := &dataUsageCacheV6{} 1327 if err = dold.DecodeMsg(msgp.NewReader(dec)); err != nil { 1328 return err 1329 } 1330 d.Info = dold.Info 1331 d.Cache = make(map[string]dataUsageEntry, len(dold.Cache)) 1332 for k, v := range dold.Cache { 1333 var replicationStats *replicationAllStats 1334 if v.ReplicationStats != nil { 1335 replicationStats = &replicationAllStats{ 1336 Targets: v.ReplicationStats.Targets, 1337 ReplicaSize: v.ReplicationStats.ReplicaSize, 1338 ReplicaCount: v.ReplicationStats.ReplicaCount, 1339 } 1340 } 1341 due := dataUsageEntry{ 1342 Children: v.Children, 1343 Size: v.Size, 1344 Objects: v.Objects, 1345 Versions: v.Versions, 1346 ObjSizes: v.ObjSizes, 1347 ReplicationStats: replicationStats, 1348 Compacted: v.Compacted, 1349 } 1350 d.Cache[k] = due 1351 } 1352 return nil 1353 case dataUsageCacheVerV7: 1354 // Zstd compressed. 1355 dec, err := zstd.NewReader(r, zstd.WithDecoderConcurrency(2)) 1356 if err != nil { 1357 return err 1358 } 1359 defer dec.Close() 1360 dold := &dataUsageCacheV7{} 1361 if err = dold.DecodeMsg(msgp.NewReader(dec)); err != nil { 1362 return err 1363 } 1364 d.Info = dold.Info 1365 d.Cache = make(map[string]dataUsageEntry, len(dold.Cache)) 1366 for k, v := range dold.Cache { 1367 var szHist sizeHistogram 1368 szHist.mergeV1(v.ObjSizes) 1369 d.Cache[k] = dataUsageEntry{ 1370 Children: v.Children, 1371 Size: v.Size, 1372 Objects: v.Objects, 1373 Versions: v.Versions, 1374 ObjSizes: szHist, 1375 ReplicationStats: v.ReplicationStats, 1376 Compacted: v.Compacted, 1377 } 1378 } 1379 1380 return nil 1381 case dataUsageCacheVerCurrent: 1382 // Zstd compressed. 1383 dec, err := zstd.NewReader(r, zstd.WithDecoderConcurrency(2)) 1384 if err != nil { 1385 return err 1386 } 1387 defer dec.Close() 1388 return d.DecodeMsg(msgp.NewReader(dec)) 1389 default: 1390 return fmt.Errorf("dataUsageCache: unknown version: %d", ver) 1391 } 1392 } 1393 1394 // Trim this from start+end of hashes. 1395 var hashPathCutSet = dataUsageRoot 1396 1397 func init() { 1398 if dataUsageRoot != string(filepath.Separator) { 1399 hashPathCutSet = dataUsageRoot + string(filepath.Separator) 1400 } 1401 } 1402 1403 // hashPath calculates a hash of the provided string. 1404 func hashPath(data string) dataUsageHash { 1405 if data != dataUsageRoot { 1406 data = strings.Trim(data, hashPathCutSet) 1407 } 1408 return dataUsageHash(path.Clean(data)) 1409 } 1410 1411 //msgp:ignore dataUsageHashMap 1412 type dataUsageHashMap map[string]struct{} 1413 1414 // DecodeMsg implements msgp.Decodable 1415 func (z *dataUsageHashMap) DecodeMsg(dc *msgp.Reader) (err error) { 1416 var zb0002 uint32 1417 zb0002, err = dc.ReadArrayHeader() 1418 if err != nil { 1419 err = msgp.WrapError(err) 1420 return 1421 } 1422 if zb0002 == 0 { 1423 *z = nil 1424 return 1425 } 1426 *z = make(dataUsageHashMap, zb0002) 1427 for i := uint32(0); i < zb0002; i++ { 1428 { 1429 var zb0003 string 1430 zb0003, err = dc.ReadString() 1431 if err != nil { 1432 err = msgp.WrapError(err) 1433 return 1434 } 1435 (*z)[zb0003] = struct{}{} 1436 } 1437 } 1438 return 1439 } 1440 1441 // EncodeMsg implements msgp.Encodable 1442 func (z dataUsageHashMap) EncodeMsg(en *msgp.Writer) (err error) { 1443 err = en.WriteArrayHeader(uint32(len(z))) 1444 if err != nil { 1445 err = msgp.WrapError(err) 1446 return 1447 } 1448 for zb0004 := range z { 1449 err = en.WriteString(zb0004) 1450 if err != nil { 1451 err = msgp.WrapError(err, zb0004) 1452 return 1453 } 1454 } 1455 return 1456 } 1457 1458 // MarshalMsg implements msgp.Marshaler 1459 func (z dataUsageHashMap) MarshalMsg(b []byte) (o []byte, err error) { 1460 o = msgp.Require(b, z.Msgsize()) 1461 o = msgp.AppendArrayHeader(o, uint32(len(z))) 1462 for zb0004 := range z { 1463 o = msgp.AppendString(o, zb0004) 1464 } 1465 return 1466 } 1467 1468 // UnmarshalMsg implements msgp.Unmarshaler 1469 func (z *dataUsageHashMap) UnmarshalMsg(bts []byte) (o []byte, err error) { 1470 var zb0002 uint32 1471 zb0002, bts, err = msgp.ReadArrayHeaderBytes(bts) 1472 if err != nil { 1473 err = msgp.WrapError(err) 1474 return 1475 } 1476 if zb0002 == 0 { 1477 *z = nil 1478 return bts, nil 1479 } 1480 *z = make(dataUsageHashMap, zb0002) 1481 for i := uint32(0); i < zb0002; i++ { 1482 { 1483 var zb0003 string 1484 zb0003, bts, err = msgp.ReadStringBytes(bts) 1485 if err != nil { 1486 err = msgp.WrapError(err) 1487 return 1488 } 1489 (*z)[zb0003] = struct{}{} 1490 } 1491 } 1492 o = bts 1493 return 1494 } 1495 1496 // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message 1497 func (z dataUsageHashMap) Msgsize() (s int) { 1498 s = msgp.ArrayHeaderSize 1499 for zb0004 := range z { 1500 s += msgp.StringPrefixSize + len(zb0004) 1501 } 1502 return 1503 } 1504 1505 //msgp:encode ignore currentScannerCycle 1506 //msgp:decode ignore currentScannerCycle 1507 1508 type currentScannerCycle struct { 1509 current uint64 1510 next uint64 1511 started time.Time 1512 cycleCompleted []time.Time 1513 } 1514 1515 // clone returns a clone. 1516 func (z currentScannerCycle) clone() currentScannerCycle { 1517 z.cycleCompleted = append(make([]time.Time, 0, len(z.cycleCompleted)), z.cycleCompleted...) 1518 return z 1519 }