cmd/syncthing, gui: Improve completion calculation (fixes #3492)
GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/3493
This commit is contained in:
committed by
Audrius Butkevicius
parent
7114cacb85
commit
7776839c82
@@ -68,7 +68,7 @@ type apiService struct {
|
|||||||
|
|
||||||
type modelIntf interface {
|
type modelIntf interface {
|
||||||
GlobalDirectoryTree(folder, prefix string, levels int, dirsonly bool) map[string]interface{}
|
GlobalDirectoryTree(folder, prefix string, levels int, dirsonly bool) map[string]interface{}
|
||||||
Completion(device protocol.DeviceID, folder string) float64
|
Completion(device protocol.DeviceID, folder string) model.FolderCompletion
|
||||||
Override(folder string)
|
Override(folder string)
|
||||||
NeedFolderFiles(folder string, page, perpage int) ([]db.FileInfoTruncated, []db.FileInfoTruncated, []db.FileInfoTruncated, int)
|
NeedFolderFiles(folder string, page, perpage int) ([]db.FileInfoTruncated, []db.FileInfoTruncated, []db.FileInfoTruncated, int)
|
||||||
NeedSize(folder string) (nfiles int, bytes int64)
|
NeedSize(folder string) (nfiles int, bytes int64)
|
||||||
@@ -583,8 +583,11 @@ func (s *apiService) getDBCompletion(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
sendJSON(w, map[string]float64{
|
comp := s.model.Completion(device, folder)
|
||||||
"completion": s.model.Completion(device, folder),
|
sendJSON(w, map[string]interface{}{
|
||||||
|
"completion": comp.CompletionPct,
|
||||||
|
"needBytes": comp.NeedBytes,
|
||||||
|
"globalBytes": comp.GlobalBytes,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1142,7 +1145,7 @@ func (s *apiService) getPeerCompletion(w http.ResponseWriter, r *http.Request) {
|
|||||||
for _, device := range folder.DeviceIDs() {
|
for _, device := range folder.DeviceIDs() {
|
||||||
deviceStr := device.String()
|
deviceStr := device.String()
|
||||||
if s.model.ConnectedTo(device) {
|
if s.model.ConnectedTo(device) {
|
||||||
tot[deviceStr] += s.model.Completion(device, folder.ID)
|
tot[deviceStr] += s.model.Completion(device, folder.ID).CompletionPct
|
||||||
} else {
|
} else {
|
||||||
tot[deviceStr] = 0
|
tot[deviceStr] = 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ func (m *mockedModel) GlobalDirectoryTree(folder, prefix string, levels int, dir
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockedModel) Completion(device protocol.DeviceID, folder string) float64 {
|
func (m *mockedModel) Completion(device protocol.DeviceID, folder string) model.FolderCompletion {
|
||||||
return 0
|
return model.FolderCompletion{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockedModel) Override(folder string) {}
|
func (m *mockedModel) Override(folder string) {}
|
||||||
|
|||||||
@@ -207,9 +207,11 @@ func (c *folderSummaryService) sendSummary(folder string) {
|
|||||||
// remote device.
|
// remote device.
|
||||||
comp := c.model.Completion(devCfg.DeviceID, folder)
|
comp := c.model.Completion(devCfg.DeviceID, folder)
|
||||||
events.Default.Log(events.FolderCompletion, map[string]interface{}{
|
events.Default.Log(events.FolderCompletion, map[string]interface{}{
|
||||||
"folder": folder,
|
"folder": folder,
|
||||||
"device": devCfg.DeviceID.String(),
|
"device": devCfg.DeviceID.String(),
|
||||||
"completion": comp,
|
"completion": comp.CompletionPct,
|
||||||
|
"needBytes": comp.NeedBytes,
|
||||||
|
"globalBytes": comp.GlobalBytes,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -309,28 +309,8 @@ angular.module('syncthing.core')
|
|||||||
if (!$scope.completion[data.device]) {
|
if (!$scope.completion[data.device]) {
|
||||||
$scope.completion[data.device] = {};
|
$scope.completion[data.device] = {};
|
||||||
}
|
}
|
||||||
$scope.completion[data.device][data.folder] = data.completion;
|
$scope.completion[data.device][data.folder] = data;
|
||||||
|
recalcCompletion(data.device);
|
||||||
var tot = 0,
|
|
||||||
cnt = 0,
|
|
||||||
isComplete = true;
|
|
||||||
for (var cmp in $scope.completion[data.device]) {
|
|
||||||
if (cmp === "_total") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
tot += $scope.completion[data.device][cmp] * $scope.model[cmp].globalBytes;
|
|
||||||
cnt += $scope.model[cmp].globalBytes;
|
|
||||||
if ($scope.completion[data.device][cmp] != 100) {
|
|
||||||
isComplete = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//To be sure that we won't get any rounding errors resulting in non-100% status when it should be
|
|
||||||
if (isComplete) {
|
|
||||||
$scope.completion[data.device]._total = 100;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$scope.completion[data.device]._total = tot / cnt;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$scope.$on(Events.FOLDER_ERRORS, function (event, arg) {
|
$scope.$on(Events.FOLDER_ERRORS, function (event, arg) {
|
||||||
@@ -458,6 +438,20 @@ angular.module('syncthing.core')
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function recalcCompletion(device) {
|
||||||
|
var total = 0, needed = 0;
|
||||||
|
for (var folder in $scope.completion[device]) {
|
||||||
|
if (folder === "_total") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
total += $scope.completion[device][folder].globalBytes;
|
||||||
|
needed += $scope.completion[device][folder].needBytes;
|
||||||
|
}
|
||||||
|
$scope.completion[device]._total = 100 * (1 - needed / total);
|
||||||
|
|
||||||
|
console.log("recalcCompletion", device, $scope.completion[device]);
|
||||||
|
}
|
||||||
|
|
||||||
function refreshCompletion(device, folder) {
|
function refreshCompletion(device, folder) {
|
||||||
if (device === $scope.myID) {
|
if (device === $scope.myID) {
|
||||||
return;
|
return;
|
||||||
@@ -467,30 +461,8 @@ angular.module('syncthing.core')
|
|||||||
if (!$scope.completion[device]) {
|
if (!$scope.completion[device]) {
|
||||||
$scope.completion[device] = {};
|
$scope.completion[device] = {};
|
||||||
}
|
}
|
||||||
$scope.completion[device][folder] = data.completion;
|
$scope.completion[device][folder] = data;
|
||||||
|
recalcCompletion(device);
|
||||||
var tot = 0,
|
|
||||||
cnt = 0,
|
|
||||||
isComplete = true;
|
|
||||||
for (var cmp in $scope.completion[device]) {
|
|
||||||
if (cmp === "_total") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
tot += $scope.completion[device][cmp] * $scope.model[cmp].globalBytes;
|
|
||||||
cnt += $scope.model[cmp].globalBytes;
|
|
||||||
if ($scope.completion[device][cmp] != 100) {
|
|
||||||
isComplete = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//To be sure that we won't get any rounding errors resulting in non-100% status when it should be
|
|
||||||
if (isComplete) {
|
|
||||||
$scope.completion[device]._total = 100;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$scope.completion[device]._total = tot / cnt;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("refreshCompletion", device, folder, $scope.completion[device]);
|
|
||||||
}).error($scope.emitHTTPError);
|
}).error($scope.emitHTTPError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -459,19 +459,28 @@ func (m *Model) FolderStatistics() map[string]stats.FolderStatistics {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FolderCompletion struct {
|
||||||
|
CompletionPct float64
|
||||||
|
NeedBytes int64
|
||||||
|
GlobalBytes int64
|
||||||
|
}
|
||||||
|
|
||||||
// Completion returns the completion status, in percent, for the given device
|
// Completion returns the completion status, in percent, for the given device
|
||||||
// and folder.
|
// and folder.
|
||||||
func (m *Model) Completion(device protocol.DeviceID, folder string) float64 {
|
func (m *Model) Completion(device protocol.DeviceID, folder string) FolderCompletion {
|
||||||
m.fmut.RLock()
|
m.fmut.RLock()
|
||||||
rf, ok := m.folderFiles[folder]
|
rf, ok := m.folderFiles[folder]
|
||||||
m.fmut.RUnlock()
|
m.fmut.RUnlock()
|
||||||
if !ok {
|
if !ok {
|
||||||
return 0 // Folder doesn't exist, so we hardly have any of it
|
return FolderCompletion{} // Folder doesn't exist, so we hardly have any of it
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, tot := rf.GlobalSize()
|
_, _, tot := rf.GlobalSize()
|
||||||
if tot == 0 {
|
if tot == 0 {
|
||||||
return 100 // Folder is empty, so we have all of it
|
// Folder is empty, so we have all of it
|
||||||
|
return FolderCompletion{
|
||||||
|
CompletionPct: 100,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m.pmut.RLock()
|
m.pmut.RLock()
|
||||||
@@ -498,7 +507,11 @@ func (m *Model) Completion(device protocol.DeviceID, folder string) float64 {
|
|||||||
completionPct := 100 * (1 - needRatio)
|
completionPct := 100 * (1 - needRatio)
|
||||||
l.Debugf("%v Completion(%s, %q): %f (%d / %d = %f)", m, device, folder, completionPct, need, tot, needRatio)
|
l.Debugf("%v Completion(%s, %q): %f (%d / %d = %f)", m, device, folder, completionPct, need, tot, needRatio)
|
||||||
|
|
||||||
return completionPct
|
return FolderCompletion{
|
||||||
|
CompletionPct: completionPct,
|
||||||
|
NeedBytes: need,
|
||||||
|
GlobalBytes: tot,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func sizeOfFile(f db.FileIntf) (files, deleted int, bytes int64) {
|
func sizeOfFile(f db.FileIntf) (files, deleted int, bytes int64) {
|
||||||
|
|||||||
Reference in New Issue
Block a user