cmd/syncthing, gui: Improve completion calculation (fixes #3492)

GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/3493
This commit is contained in:
Jakob Borg 2016-08-12 06:41:43 +00:00 committed by Audrius Butkevicius
parent 7114cacb85
commit 7776839c82
5 changed files with 49 additions and 59 deletions

View File

@ -68,7 +68,7 @@ type apiService struct {
type modelIntf 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)
NeedFolderFiles(folder string, page, perpage int) ([]db.FileInfoTruncated, []db.FileInfoTruncated, []db.FileInfoTruncated, int)
NeedSize(folder string) (nfiles int, bytes int64)
@ -583,8 +583,11 @@ func (s *apiService) getDBCompletion(w http.ResponseWriter, r *http.Request) {
return
}
sendJSON(w, map[string]float64{
"completion": s.model.Completion(device, folder),
comp := 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() {
deviceStr := device.String()
if s.model.ConnectedTo(device) {
tot[deviceStr] += s.model.Completion(device, folder.ID)
tot[deviceStr] += s.model.Completion(device, folder.ID).CompletionPct
} else {
tot[deviceStr] = 0
}

View File

@ -21,8 +21,8 @@ func (m *mockedModel) GlobalDirectoryTree(folder, prefix string, levels int, dir
return nil
}
func (m *mockedModel) Completion(device protocol.DeviceID, folder string) float64 {
return 0
func (m *mockedModel) Completion(device protocol.DeviceID, folder string) model.FolderCompletion {
return model.FolderCompletion{}
}
func (m *mockedModel) Override(folder string) {}

View File

@ -207,9 +207,11 @@ func (c *folderSummaryService) sendSummary(folder string) {
// remote device.
comp := c.model.Completion(devCfg.DeviceID, folder)
events.Default.Log(events.FolderCompletion, map[string]interface{}{
"folder": folder,
"device": devCfg.DeviceID.String(),
"completion": comp,
"folder": folder,
"device": devCfg.DeviceID.String(),
"completion": comp.CompletionPct,
"needBytes": comp.NeedBytes,
"globalBytes": comp.GlobalBytes,
})
}
}

View File

@ -309,28 +309,8 @@ angular.module('syncthing.core')
if (!$scope.completion[data.device]) {
$scope.completion[data.device] = {};
}
$scope.completion[data.device][data.folder] = data.completion;
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.completion[data.device][data.folder] = data;
recalcCompletion(data.device);
});
$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) {
if (device === $scope.myID) {
return;
@ -467,30 +461,8 @@ angular.module('syncthing.core')
if (!$scope.completion[device]) {
$scope.completion[device] = {};
}
$scope.completion[device][folder] = data.completion;
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]);
$scope.completion[device][folder] = data;
recalcCompletion(device);
}).error($scope.emitHTTPError);
}

View File

@ -459,19 +459,28 @@ func (m *Model) FolderStatistics() map[string]stats.FolderStatistics {
return res
}
type FolderCompletion struct {
CompletionPct float64
NeedBytes int64
GlobalBytes int64
}
// Completion returns the completion status, in percent, for the given device
// 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()
rf, ok := m.folderFiles[folder]
m.fmut.RUnlock()
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()
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()
@ -498,7 +507,11 @@ func (m *Model) Completion(device protocol.DeviceID, folder string) float64 {
completionPct := 100 * (1 - 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) {