yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/azure/progress/status.go (about)

     1  // Copyright 2019 Yunion
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package progress
    16  
    17  import (
    18  	"time"
    19  )
    20  
    21  // Status can be used by a collection of workers (reporters) to report the amount of work done when they need,
    22  // Status compute the overall progress at regular interval and report it.
    23  //
    24  type Status struct {
    25  	bytesProcessedCountChan chan int64
    26  	doneChan                chan bool
    27  	bytesProcessed          int64
    28  	totalBytes              int64
    29  	alreadyProcessedBytes   int64
    30  	startTime               time.Time
    31  	throughputStats         *ComputeStats
    32  }
    33  
    34  // Record type is used by the ProgressStatus to report the progress at regular interval.
    35  //
    36  type Record struct {
    37  	PercentComplete              float64
    38  	AverageThroughputMbPerSecond float64
    39  	RemainingDuration            time.Duration
    40  	BytesProcessed               int64
    41  }
    42  
    43  // oneMB is one MegaByte
    44  //
    45  const oneMB = float64(1048576)
    46  
    47  // nanosecondsInOneSecond is 1 second expressed as nano-second unit
    48  //
    49  const nanosecondsInOneSecond = 1000 * 1000 * 1000
    50  
    51  // NewStatus creates a new instance of Status. reporterCount is the number of concurrent goroutines that want to
    52  // report processed bytes count, alreadyProcessedBytes is the bytes already processed if any, the parameter
    53  // totalBytes is the total number of bytes that the reports will be process eventually, the parameter computeStats
    54  // is used to calculate the running average.
    55  //
    56  func NewStatus(reportersCount int, alreadyProcessedBytes, totalBytes int64, computeStats *ComputeStats) *Status {
    57  	return &Status{
    58  		bytesProcessedCountChan: make(chan int64, reportersCount),
    59  		doneChan:                make(chan bool, 0),
    60  		totalBytes:              totalBytes,
    61  		alreadyProcessedBytes:   alreadyProcessedBytes,
    62  		startTime:               time.Now(),
    63  		throughputStats:         computeStats,
    64  	}
    65  }
    66  
    67  // ReportBytesProcessedCount method is used to report the number of bytes processed.
    68  //
    69  func (s *Status) ReportBytesProcessedCount(count int64) {
    70  	s.bytesProcessedCountChan <- count
    71  }
    72  
    73  // Run starts counting the reported processed bytes count and compute the progress, this method returns a channel,
    74  // the computed progress will be send to this channel in regular interval. Once done with using ProgressStatus
    75  // instance, you must call Dispose method otherwise there will be go routine leak.
    76  //
    77  func (s *Status) Run() <-chan *Record {
    78  	go s.bytesProcessedCountReceiver()
    79  	var outChan = make(chan *Record, 0)
    80  	go s.progressRecordSender(outChan)
    81  	return outChan
    82  }
    83  
    84  // Close disposes this ProgressStatus instance, an attempt to invoke ReportBytesProcessedCount method on a closed
    85  // instance will be panic. Close also stops sending progress to the channel returned by Run method. Not calling
    86  // Close will cause goroutine leak.
    87  //
    88  func (s *Status) Close() {
    89  	close(s.bytesProcessedCountChan)
    90  }
    91  
    92  // bytesProcessedCountReceiver read the channel containing the collection of reported bytes count and update the total
    93  // bytes processed. This method signal doneChan when there is no more data to read.
    94  //
    95  func (s *Status) bytesProcessedCountReceiver() {
    96  	for c := range s.bytesProcessedCountChan {
    97  		s.bytesProcessed += c
    98  	}
    99  	s.doneChan <- true
   100  }
   101  
   102  // progressRecordSender compute the progress information at regular interval and send it to channel outChan which is
   103  // returned by the Run method
   104  //
   105  func (s *Status) progressRecordSender(outChan chan<- *Record) {
   106  	progressRecord := &Record{}
   107  	tickerChan := time.NewTicker(500 * time.Millisecond)
   108  Loop:
   109  	for {
   110  		select {
   111  		case <-tickerChan.C:
   112  			computeAvg := s.throughputStats.ComputeAvg(s.throughputMBs())
   113  			avtThroughputMbps := 8.0 * computeAvg
   114  			remainingSeconds := (s.remainingMB() / computeAvg)
   115  
   116  			progressRecord.PercentComplete = s.percentComplete()
   117  			progressRecord.RemainingDuration = time.Duration(nanosecondsInOneSecond * remainingSeconds)
   118  			progressRecord.AverageThroughputMbPerSecond = avtThroughputMbps
   119  			progressRecord.BytesProcessed = s.bytesProcessed
   120  
   121  			outChan <- progressRecord
   122  		case <-s.doneChan:
   123  			tickerChan.Stop()
   124  			break Loop
   125  		}
   126  	}
   127  	close(outChan)
   128  }
   129  
   130  // remainingMB returns remaining bytes to be processed as MB.
   131  //
   132  func (s *Status) remainingMB() float64 {
   133  	return float64(s.totalBytes-s.bytesProcessed) / oneMB
   134  }
   135  
   136  // percentComplete returns the percentage of bytes processed out of total bytes.
   137  //
   138  func (s *Status) percentComplete() float64 {
   139  	return float64(100.0) * (float64(s.bytesProcessed) / float64(s.totalBytes))
   140  }
   141  
   142  // processTime returns the Duration representing the time taken to process the bytes so far.
   143  //
   144  func (s *Status) processTime() time.Duration {
   145  	return time.Since(s.startTime)
   146  }
   147  
   148  // throughputMBs returns the throughput in MB
   149  //
   150  func (s *Status) throughputMBs() float64 {
   151  	return float64(s.bytesProcessed) / oneMB / s.processTime().Seconds()
   152  }