mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/yunionio/cloudpods.git
synced 2026-09-21 08:33:54 +08:00
153 lines
5.1 KiB
Go
153 lines
5.1 KiB
Go
// Copyright 2019 Yunion
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package progress
|
|
|
|
import (
|
|
"time"
|
|
)
|
|
|
|
// Status can be used by a collection of workers (reporters) to report the amount of work done when they need,
|
|
// Status compute the overall progress at regular interval and report it.
|
|
//
|
|
type Status struct {
|
|
bytesProcessedCountChan chan int64
|
|
doneChan chan bool
|
|
bytesProcessed int64
|
|
totalBytes int64
|
|
alreadyProcessedBytes int64
|
|
startTime time.Time
|
|
throughputStats *ComputeStats
|
|
}
|
|
|
|
// Record type is used by the ProgressStatus to report the progress at regular interval.
|
|
//
|
|
type Record struct {
|
|
PercentComplete float64
|
|
AverageThroughputMbPerSecond float64
|
|
RemainingDuration time.Duration
|
|
BytesProcessed int64
|
|
}
|
|
|
|
// oneMB is one MegaByte
|
|
//
|
|
const oneMB = float64(1048576)
|
|
|
|
// nanosecondsInOneSecond is 1 second expressed as nano-second unit
|
|
//
|
|
const nanosecondsInOneSecond = 1000 * 1000 * 1000
|
|
|
|
// NewStatus creates a new instance of Status. reporterCount is the number of concurrent goroutines that want to
|
|
// report processed bytes count, alreadyProcessedBytes is the bytes already processed if any, the parameter
|
|
// totalBytes is the total number of bytes that the reports will be process eventually, the parameter computeStats
|
|
// is used to calculate the running average.
|
|
//
|
|
func NewStatus(reportersCount int, alreadyProcessedBytes, totalBytes int64, computeStats *ComputeStats) *Status {
|
|
return &Status{
|
|
bytesProcessedCountChan: make(chan int64, reportersCount),
|
|
doneChan: make(chan bool, 0),
|
|
totalBytes: totalBytes,
|
|
alreadyProcessedBytes: alreadyProcessedBytes,
|
|
startTime: time.Now(),
|
|
throughputStats: computeStats,
|
|
}
|
|
}
|
|
|
|
// ReportBytesProcessedCount method is used to report the number of bytes processed.
|
|
//
|
|
func (s *Status) ReportBytesProcessedCount(count int64) {
|
|
s.bytesProcessedCountChan <- count
|
|
}
|
|
|
|
// Run starts counting the reported processed bytes count and compute the progress, this method returns a channel,
|
|
// the computed progress will be send to this channel in regular interval. Once done with using ProgressStatus
|
|
// instance, you must call Dispose method otherwise there will be go routine leak.
|
|
//
|
|
func (s *Status) Run() <-chan *Record {
|
|
go s.bytesProcessedCountReceiver()
|
|
var outChan = make(chan *Record, 0)
|
|
go s.progressRecordSender(outChan)
|
|
return outChan
|
|
}
|
|
|
|
// Close disposes this ProgressStatus instance, an attempt to invoke ReportBytesProcessedCount method on a closed
|
|
// instance will be panic. Close also stops sending progress to the channel returned by Run method. Not calling
|
|
// Close will cause goroutine leak.
|
|
//
|
|
func (s *Status) Close() {
|
|
close(s.bytesProcessedCountChan)
|
|
}
|
|
|
|
// bytesProcessedCountReceiver read the channel containing the collection of reported bytes count and update the total
|
|
// bytes processed. This method signal doneChan when there is no more data to read.
|
|
//
|
|
func (s *Status) bytesProcessedCountReceiver() {
|
|
for c := range s.bytesProcessedCountChan {
|
|
s.bytesProcessed += c
|
|
}
|
|
s.doneChan <- true
|
|
}
|
|
|
|
// progressRecordSender compute the progress information at regular interval and send it to channel outChan which is
|
|
// returned by the Run method
|
|
//
|
|
func (s *Status) progressRecordSender(outChan chan<- *Record) {
|
|
progressRecord := &Record{}
|
|
tickerChan := time.NewTicker(500 * time.Millisecond)
|
|
Loop:
|
|
for {
|
|
select {
|
|
case <-tickerChan.C:
|
|
computeAvg := s.throughputStats.ComputeAvg(s.throughputMBs())
|
|
avtThroughputMbps := 8.0 * computeAvg
|
|
remainingSeconds := (s.remainingMB() / computeAvg)
|
|
|
|
progressRecord.PercentComplete = s.percentComplete()
|
|
progressRecord.RemainingDuration = time.Duration(nanosecondsInOneSecond * remainingSeconds)
|
|
progressRecord.AverageThroughputMbPerSecond = avtThroughputMbps
|
|
progressRecord.BytesProcessed = s.bytesProcessed
|
|
|
|
outChan <- progressRecord
|
|
case <-s.doneChan:
|
|
tickerChan.Stop()
|
|
break Loop
|
|
}
|
|
}
|
|
close(outChan)
|
|
}
|
|
|
|
// remainingMB returns remaining bytes to be processed as MB.
|
|
//
|
|
func (s *Status) remainingMB() float64 {
|
|
return float64(s.totalBytes-s.bytesProcessed) / oneMB
|
|
}
|
|
|
|
// percentComplete returns the percentage of bytes processed out of total bytes.
|
|
//
|
|
func (s *Status) percentComplete() float64 {
|
|
return float64(100.0) * (float64(s.bytesProcessed) / float64(s.totalBytes))
|
|
}
|
|
|
|
// processTime returns the Duration representing the time taken to process the bytes so far.
|
|
//
|
|
func (s *Status) processTime() time.Duration {
|
|
return time.Since(s.startTime)
|
|
}
|
|
|
|
// throughputMBs returns the throughput in MB
|
|
//
|
|
func (s *Status) throughputMBs() float64 {
|
|
return float64(s.bytesProcessed) / oneMB / s.processTime().Seconds()
|
|
}
|