storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/bucket-replication-stats.go (about)

     1  /*
     2   * MinIO Cloud Storage, (C) 2021 MinIO, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package cmd
    18  
    19  import (
    20  	"context"
    21  	"sync"
    22  	"sync/atomic"
    23  
    24  	"storj.io/minio/pkg/bucket/replication"
    25  )
    26  
    27  func (b *BucketReplicationStats) hasReplicationUsage() bool {
    28  	return b.PendingSize > 0 ||
    29  		b.FailedSize > 0 ||
    30  		b.ReplicatedSize > 0 ||
    31  		b.ReplicaSize > 0 ||
    32  		b.PendingCount > 0 ||
    33  		b.FailedCount > 0
    34  }
    35  
    36  // ReplicationStats holds the global in-memory replication stats
    37  type ReplicationStats struct {
    38  	sync.RWMutex
    39  	Cache      map[string]*BucketReplicationStats
    40  	UsageCache map[string]*BucketReplicationStats // initial usage
    41  }
    42  
    43  // Delete deletes in-memory replication statistics for a bucket.
    44  func (r *ReplicationStats) Delete(bucket string) {
    45  	if r == nil {
    46  		return
    47  	}
    48  
    49  	r.Lock()
    50  	defer r.Unlock()
    51  	delete(r.Cache, bucket)
    52  	delete(r.UsageCache, bucket)
    53  
    54  }
    55  
    56  // Update updates in-memory replication statistics with new values.
    57  func (r *ReplicationStats) Update(bucket string, n int64, status, prevStatus replication.StatusType, opType replication.Type) {
    58  	if r == nil {
    59  		return
    60  	}
    61  
    62  	r.RLock()
    63  	b, ok := r.Cache[bucket]
    64  	if !ok {
    65  		b = &BucketReplicationStats{}
    66  	}
    67  	r.RUnlock()
    68  	switch status {
    69  	case replication.Pending:
    70  		if opType == replication.ObjectReplicationType {
    71  			atomic.AddUint64(&b.PendingSize, uint64(n))
    72  		}
    73  		atomic.AddUint64(&b.PendingCount, 1)
    74  	case replication.Completed:
    75  		switch prevStatus { // adjust counters based on previous state
    76  		case replication.Pending:
    77  			atomic.AddUint64(&b.PendingCount, ^uint64(0))
    78  		case replication.Failed:
    79  			atomic.AddUint64(&b.FailedCount, ^uint64(0))
    80  		}
    81  		if opType == replication.ObjectReplicationType {
    82  			atomic.AddUint64(&b.ReplicatedSize, uint64(n))
    83  			switch prevStatus {
    84  			case replication.Pending:
    85  				atomic.AddUint64(&b.PendingSize, ^uint64(n-1))
    86  			case replication.Failed:
    87  				atomic.AddUint64(&b.FailedSize, ^uint64(n-1))
    88  			}
    89  		}
    90  	case replication.Failed:
    91  		// count failures only once - not on every retry
    92  		switch prevStatus { // adjust counters based on previous state
    93  		case replication.Pending:
    94  			atomic.AddUint64(&b.PendingCount, ^uint64(0))
    95  		}
    96  		if opType == replication.ObjectReplicationType {
    97  			if prevStatus == replication.Pending {
    98  				atomic.AddUint64(&b.FailedSize, uint64(n))
    99  				atomic.AddUint64(&b.FailedCount, 1)
   100  				atomic.AddUint64(&b.PendingSize, ^uint64(n-1))
   101  			}
   102  		}
   103  	case replication.Replica:
   104  		if opType == replication.ObjectReplicationType {
   105  			atomic.AddUint64(&b.ReplicaSize, uint64(n))
   106  		}
   107  	}
   108  	r.Lock()
   109  	r.Cache[bucket] = b
   110  	r.Unlock()
   111  }
   112  
   113  // GetInitialUsage get replication metrics available at the time of cluster initialization
   114  func (r *ReplicationStats) GetInitialUsage(bucket string) BucketReplicationStats {
   115  	if r == nil {
   116  		return BucketReplicationStats{}
   117  	}
   118  
   119  	r.RLock()
   120  	defer r.RUnlock()
   121  
   122  	st, ok := r.UsageCache[bucket]
   123  	if !ok {
   124  		return BucketReplicationStats{}
   125  	}
   126  	return BucketReplicationStats{
   127  		PendingSize:    atomic.LoadUint64(&st.PendingSize),
   128  		FailedSize:     atomic.LoadUint64(&st.FailedSize),
   129  		ReplicatedSize: atomic.LoadUint64(&st.ReplicatedSize),
   130  		ReplicaSize:    atomic.LoadUint64(&st.ReplicaSize),
   131  		PendingCount:   atomic.LoadUint64(&st.PendingCount),
   132  		FailedCount:    atomic.LoadUint64(&st.FailedCount),
   133  	}
   134  }
   135  
   136  // Get replication metrics for a bucket from this node since this node came up.
   137  func (r *ReplicationStats) Get(bucket string) BucketReplicationStats {
   138  	if r == nil {
   139  		return BucketReplicationStats{}
   140  	}
   141  
   142  	r.RLock()
   143  	defer r.RUnlock()
   144  
   145  	st, ok := r.Cache[bucket]
   146  	if !ok {
   147  		return BucketReplicationStats{}
   148  	}
   149  
   150  	return BucketReplicationStats{
   151  		PendingSize:    atomic.LoadUint64(&st.PendingSize),
   152  		FailedSize:     atomic.LoadUint64(&st.FailedSize),
   153  		ReplicatedSize: atomic.LoadUint64(&st.ReplicatedSize),
   154  		ReplicaSize:    atomic.LoadUint64(&st.ReplicaSize),
   155  		PendingCount:   atomic.LoadUint64(&st.PendingCount),
   156  		FailedCount:    atomic.LoadUint64(&st.FailedCount),
   157  	}
   158  }
   159  
   160  // NewReplicationStats initialize in-memory replication statistics
   161  func NewReplicationStats(ctx context.Context, objectAPI ObjectLayer) *ReplicationStats {
   162  	st := &ReplicationStats{
   163  		Cache:      make(map[string]*BucketReplicationStats),
   164  		UsageCache: make(map[string]*BucketReplicationStats),
   165  	}
   166  
   167  	dataUsageInfo, err := loadDataUsageFromBackend(ctx, objectAPI)
   168  	if err != nil {
   169  		return st
   170  	}
   171  
   172  	// data usage has not captured any data yet.
   173  	if dataUsageInfo.LastUpdate.IsZero() {
   174  		return st
   175  	}
   176  
   177  	for bucket, usage := range dataUsageInfo.BucketsUsage {
   178  		b := &BucketReplicationStats{
   179  			PendingSize:    usage.ReplicationPendingSize,
   180  			FailedSize:     usage.ReplicationFailedSize,
   181  			ReplicatedSize: usage.ReplicatedSize,
   182  			ReplicaSize:    usage.ReplicaSize,
   183  			PendingCount:   usage.ReplicationPendingCount,
   184  			FailedCount:    usage.ReplicationFailedCount,
   185  		}
   186  		if b.hasReplicationUsage() {
   187  			st.UsageCache[bucket] = b
   188  		}
   189  	}
   190  
   191  	return st
   192  }