فهرست منبع

Show scan rate in web GUI

Jakob Borg 10 سال پیش
والد
کامیت
a8a2192cf9
7فایلهای تغییر یافته به همراه78 افزوده شده و 14 حذف شده
  1. 2 1
      cmd/syncthing/verbose.go
  2. 1 1
      gui/index.html
  3. 9 1
      gui/syncthing/core/syncthingController.js
  4. 1 1
      lib/auto/gui.files.go
  5. 3 3
      lib/scanner/blockqueue.go
  6. 6 3
      lib/scanner/blocks.go
  7. 56 4
      lib/scanner/walk.go

+ 2 - 1
cmd/syncthing/verbose.go

@@ -126,11 +126,12 @@ func (s *verboseSvc) formatEvent(ev events.Event) string {
 		folder := data["folder"].(string)
 		folder := data["folder"].(string)
 		current := data["current"].(int64)
 		current := data["current"].(int64)
 		total := data["total"].(int64)
 		total := data["total"].(int64)
+		rate := data["rate"].(float64) / 1024 / 1024
 		var pct int64
 		var pct int64
 		if total > 0 {
 		if total > 0 {
 			pct = 100 * current / total
 			pct = 100 * current / total
 		}
 		}
-		return fmt.Sprintf("Scanning folder %q, %d%% done", folder, pct)
+		return fmt.Sprintf("Scanning folder %q, %d%% done (%.01f MB/s)", folder, pct, rate)
 
 
 	case events.DevicePaused:
 	case events.DevicePaused:
 		data := ev.Data.(map[string]string)
 		data := ev.Data.(map[string]string)

+ 1 - 1
gui/index.html

@@ -228,7 +228,7 @@
                   <span ng-switch-when="scanning">
                   <span ng-switch-when="scanning">
                     <span class="hidden-xs" translate>Scanning</span>
                     <span class="hidden-xs" translate>Scanning</span>
                     <span class="hidden-xs" ng-if="scanPercentage(folder.id) != undefined">
                     <span class="hidden-xs" ng-if="scanPercentage(folder.id) != undefined">
-                        ({{scanPercentage(folder.id)}}%)
+                      (<span class="hidden-xs" ng-if="scanRate(folder.id) > 0">{{scanRate(folder.id) | binary}}B/s, </span>{{scanPercentage(folder.id)}}%)
                     </span>
                     </span>
                     <span class="visible-xs">&#9724;</span>
                     <span class="visible-xs">&#9724;</span>
                   </span>
                   </span>

+ 9 - 1
gui/syncthing/core/syncthingController.js

@@ -321,7 +321,8 @@ angular.module('syncthing.core')
             var data = arg.data;
             var data = arg.data;
             $scope.scanProgress[data.folder] = {
             $scope.scanProgress[data.folder] = {
                 current: data.current,
                 current: data.current,
-                total: data.total
+                total: data.total,
+                rate: data.rate,
             };
             };
             console.log("FolderScanProgress", data);
             console.log("FolderScanProgress", data);
         });
         });
@@ -668,6 +669,13 @@ angular.module('syncthing.core')
             return Math.floor(pct);
             return Math.floor(pct);
         }
         }
 
 
+        $scope.scanRate = function (folder) {
+            if (!$scope.scanProgress[folder]) {
+                return 0;
+            }
+            return $scope.scanProgress[folder].rate;
+        }
+
         $scope.deviceStatus = function (deviceCfg) {
         $scope.deviceStatus = function (deviceCfg) {
             if ($scope.deviceFolders(deviceCfg).length === 0) {
             if ($scope.deviceFolders(deviceCfg).length === 0) {
                 return 'unused';
                 return 'unused';

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 1
lib/auto/gui.files.go


+ 3 - 3
lib/scanner/blockqueue.go

@@ -19,7 +19,7 @@ import (
 // workers are used in parallel. The outbox will become closed when the inbox
 // workers are used in parallel. The outbox will become closed when the inbox
 // is closed and all items handled.
 // is closed and all items handled.
 
 
-func newParallelHasher(dir string, blockSize, workers int, outbox, inbox chan protocol.FileInfo, counter *int64, done, cancel chan struct{}) {
+func newParallelHasher(dir string, blockSize, workers int, outbox, inbox chan protocol.FileInfo, counter Counter, done, cancel chan struct{}) {
 	wg := sync.NewWaitGroup()
 	wg := sync.NewWaitGroup()
 	wg.Add(workers)
 	wg.Add(workers)
 
 
@@ -39,7 +39,7 @@ func newParallelHasher(dir string, blockSize, workers int, outbox, inbox chan pr
 	}()
 	}()
 }
 }
 
 
-func HashFile(path string, blockSize int, sizeHint int64, counter *int64) ([]protocol.BlockInfo, error) {
+func HashFile(path string, blockSize int, sizeHint int64, counter Counter) ([]protocol.BlockInfo, error) {
 	fd, err := os.Open(path)
 	fd, err := os.Open(path)
 	if err != nil {
 	if err != nil {
 		l.Debugln("open:", err)
 		l.Debugln("open:", err)
@@ -59,7 +59,7 @@ func HashFile(path string, blockSize int, sizeHint int64, counter *int64) ([]pro
 	return Blocks(fd, blockSize, sizeHint, counter)
 	return Blocks(fd, blockSize, sizeHint, counter)
 }
 }
 
 
-func hashFiles(dir string, blockSize int, outbox, inbox chan protocol.FileInfo, counter *int64, cancel chan struct{}) {
+func hashFiles(dir string, blockSize int, outbox, inbox chan protocol.FileInfo, counter Counter, cancel chan struct{}) {
 	for {
 	for {
 		select {
 		select {
 		case f, ok := <-inbox:
 		case f, ok := <-inbox:

+ 6 - 3
lib/scanner/blocks.go

@@ -11,15 +11,18 @@ import (
 	"crypto/sha256"
 	"crypto/sha256"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
-	"sync/atomic"
 
 
 	"github.com/syncthing/syncthing/lib/protocol"
 	"github.com/syncthing/syncthing/lib/protocol"
 )
 )
 
 
 var SHA256OfNothing = []uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}
 var SHA256OfNothing = []uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}
 
 
+type Counter interface {
+	Update(bytes int64)
+}
+
 // Blocks returns the blockwise hash of the reader.
 // Blocks returns the blockwise hash of the reader.
-func Blocks(r io.Reader, blocksize int, sizehint int64, counter *int64) ([]protocol.BlockInfo, error) {
+func Blocks(r io.Reader, blocksize int, sizehint int64, counter Counter) ([]protocol.BlockInfo, error) {
 	hf := sha256.New()
 	hf := sha256.New()
 	hashLength := hf.Size()
 	hashLength := hf.Size()
 
 
@@ -50,7 +53,7 @@ func Blocks(r io.Reader, blocksize int, sizehint int64, counter *int64) ([]proto
 		}
 		}
 
 
 		if counter != nil {
 		if counter != nil {
-			atomic.AddInt64(counter, int64(n))
+			counter.Update(int64(n))
 		}
 		}
 
 
 		// Carve out a hash-sized chunk of "hashes" to store the hash for this
 		// Carve out a hash-sized chunk of "hashes" to store the hash for this

+ 56 - 4
lib/scanner/walk.go

@@ -16,6 +16,7 @@ import (
 	"time"
 	"time"
 	"unicode/utf8"
 	"unicode/utf8"
 
 
+	"github.com/rcrowley/go-metrics"
 	"github.com/syncthing/syncthing/lib/db"
 	"github.com/syncthing/syncthing/lib/db"
 	"github.com/syncthing/syncthing/lib/events"
 	"github.com/syncthing/syncthing/lib/events"
 	"github.com/syncthing/syncthing/lib/osutil"
 	"github.com/syncthing/syncthing/lib/osutil"
@@ -143,7 +144,11 @@ func (w *Walker) Walk() (chan protocol.FileInfo, error) {
 	// which it receives the files we ask it to hash.
 	// which it receives the files we ask it to hash.
 	go func() {
 	go func() {
 		var filesToHash []protocol.FileInfo
 		var filesToHash []protocol.FileInfo
-		var total, progress int64 = 1, 0
+		var total int64 = 1
+
+		progress := newByteCounter()
+		defer progress.Close()
+
 		for file := range toHashChan {
 		for file := range toHashChan {
 			filesToHash = append(filesToHash, file)
 			filesToHash = append(filesToHash, file)
 			total += int64(file.CachedSize)
 			total += int64(file.CachedSize)
@@ -151,7 +156,7 @@ func (w *Walker) Walk() (chan protocol.FileInfo, error) {
 
 
 		realToHashChan := make(chan protocol.FileInfo)
 		realToHashChan := make(chan protocol.FileInfo)
 		done := make(chan struct{})
 		done := make(chan struct{})
-		newParallelHasher(w.Dir, w.BlockSize, w.Hashers, finishedChan, realToHashChan, &progress, done, w.Cancel)
+		newParallelHasher(w.Dir, w.BlockSize, w.Hashers, finishedChan, realToHashChan, progress, done, w.Cancel)
 
 
 		// A routine which actually emits the FolderScanProgress events
 		// A routine which actually emits the FolderScanProgress events
 		// every w.ProgressTicker ticks, until the hasher routines terminate.
 		// every w.ProgressTicker ticks, until the hasher routines terminate.
@@ -163,12 +168,14 @@ func (w *Walker) Walk() (chan protocol.FileInfo, error) {
 					ticker.Stop()
 					ticker.Stop()
 					return
 					return
 				case <-ticker.C:
 				case <-ticker.C:
-					current := atomic.LoadInt64(&progress)
-					l.Debugf("Walk %s %s current progress %d/%d (%d%%)", w.Dir, w.Subs, current, total, current*100/total)
+					current := progress.Total()
+					rate := progress.Rate()
+					l.Debugf("Walk %s %s current progress %d/%d at %.01f MB/s (%d%%)", w.Dir, w.Subs, current, total, rate/1024/1024, current*100/total)
 					events.Default.Log(events.FolderScanProgress, map[string]interface{}{
 					events.Default.Log(events.FolderScanProgress, map[string]interface{}{
 						"folder":  w.Folder,
 						"folder":  w.Folder,
 						"current": current,
 						"current": current,
 						"total":   total,
 						"total":   total,
+						"rate":    rate, // bytes per second
 					})
 					})
 				case <-w.Cancel:
 				case <-w.Cancel:
 					ticker.Stop()
 					ticker.Stop()
@@ -492,3 +499,48 @@ func SymlinkFlags(t symlinks.TargetType) uint32 {
 	}
 	}
 	panic("unknown symlink TargetType")
 	panic("unknown symlink TargetType")
 }
 }
+
+// A byteCounter gets bytes added to it via Update() and then provides the
+// Total() and one minute moving average Rate() in bytes per second.
+type byteCounter struct {
+	total int64
+	metrics.EWMA
+	stop chan struct{}
+}
+
+func newByteCounter() *byteCounter {
+	c := &byteCounter{
+		EWMA: metrics.NewEWMA1(), // a one minute exponentially weighted moving average
+		stop: make(chan struct{}),
+	}
+	go c.ticker()
+	return c
+}
+
+func (c *byteCounter) ticker() {
+	// The metrics.EWMA expects clock ticks every five seconds in order to
+	// decay the average properly.
+	t := time.NewTicker(5 * time.Second)
+	for {
+		select {
+		case <-t.C:
+			c.Tick()
+		case <-c.stop:
+			t.Stop()
+			return
+		}
+	}
+}
+
+func (c *byteCounter) Update(bytes int64) {
+	atomic.AddInt64(&c.total, bytes)
+	c.EWMA.Update(bytes)
+}
+
+func (c *byteCounter) Total() int64 {
+	return atomic.LoadInt64(&c.total)
+}
+
+func (c *byteCounter) Close() {
+	close(c.stop)
+}

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است