Archived
Push model data instead of pull (fixes #1434)
This commit is contained in:
1 parent
a3cf37cb2e
commit
454e688c3d
7 files changed
+244
-47
No files matched your search
+21
-4
@@ -49,6 +49,11 @@ var (
|
||||
eventSub *events.BufferedSubscription
|
||||
)
|
||||
|
||||
var (
|
||||
lastEventRequest time.Time
|
||||
lastEventRequestMut sync.Mutex
|
||||
)
|
||||
|
||||
func init() {
|
||||
l.AddHandler(logger.LevelWarn, showGuiError)
|
||||
sub := events.Default.Subscribe(events.AllEvents)
|
||||
@@ -179,6 +184,9 @@ func startGUI(cfg config.GUIConfiguration, assetDir string, m *model.Model) erro
|
||||
ReadTimeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
csrv := &folderSummarySvc{model: m}
|
||||
go csrv.Serve()
|
||||
|
||||
go func() {
|
||||
err := srv.Serve(listener)
|
||||
if err != nil {
|
||||
@@ -293,8 +301,14 @@ func restGetCompletion(m *model.Model, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func restGetModel(m *model.Model, w http.ResponseWriter, r *http.Request) {
|
||||
var qs = r.URL.Query()
|
||||
var folder = qs.Get("folder")
|
||||
qs := r.URL.Query()
|
||||
folder := qs.Get("folder")
|
||||
res := folderSummary(m, folder)
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
json.NewEncoder(w).Encode(res)
|
||||
}
|
||||
|
||||
func folderSummary(m *model.Model, folder string) map[string]interface{} {
|
||||
var res = make(map[string]interface{})
|
||||
|
||||
res["invalid"] = cfg.Folders()[folder].Invalid
|
||||
@@ -322,8 +336,7 @@ func restGetModel(m *model.Model, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
json.NewEncoder(w).Encode(res)
|
||||
return res
|
||||
}
|
||||
|
||||
func restPostOverride(m *model.Model, w http.ResponseWriter, r *http.Request) {
|
||||
@@ -598,6 +611,10 @@ func restGetEvents(w http.ResponseWriter, r *http.Request) {
|
||||
since, _ := strconv.Atoi(sinceStr)
|
||||
limit, _ := strconv.Atoi(limitStr)
|
||||
|
||||
lastEventRequestMut.Lock()
|
||||
lastEventRequest = time.Now()
|
||||
lastEventRequestMut.Unlock()
|
||||
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
// Flush before blocking, to indicate that we've received the request
|
||||
|
||||
@@ -61,7 +61,10 @@ const (
|
||||
exitUpgrading = 4
|
||||
)
|
||||
|
||||
const bepProtocolName = "bep/1.0"
|
||||
const (
|
||||
bepProtocolName = "bep/1.0"
|
||||
pingEventInterval = time.Minute
|
||||
)
|
||||
|
||||
var l = logger.DefaultLogger
|
||||
|
||||
@@ -613,7 +616,7 @@ func syncthingMain() {
|
||||
}
|
||||
|
||||
events.Default.Log(events.StartupComplete, nil)
|
||||
go generateEvents()
|
||||
go generatePingEvents()
|
||||
|
||||
code := <-stop
|
||||
|
||||
@@ -701,9 +704,9 @@ func defaultConfig(myName string) config.Configuration {
|
||||
return newCfg
|
||||
}
|
||||
|
||||
func generateEvents() {
|
||||
func generatePingEvents() {
|
||||
for {
|
||||
time.Sleep(300 * time.Second)
|
||||
time.Sleep(pingEventInterval)
|
||||
events.Default.Log(events.Ping, nil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
// Copyright (C) 2015 The Syncthing Authors.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/syncthing/syncthing/internal/events"
|
||||
"github.com/syncthing/syncthing/internal/model"
|
||||
"github.com/thejerf/suture"
|
||||
)
|
||||
|
||||
// The folderSummarySvc adds summary information events (FolderSummary and
|
||||
// FolderCompletion) into the event stream at certain intervals.
|
||||
type folderSummarySvc struct {
|
||||
model *model.Model
|
||||
srv suture.Service
|
||||
stop chan struct{}
|
||||
|
||||
// For keeping track of folders to recalculate for
|
||||
foldersMut sync.Mutex
|
||||
folders map[string]struct{}
|
||||
}
|
||||
|
||||
func (c *folderSummarySvc) Serve() {
|
||||
srv := suture.NewSimple("folderSummarySvc")
|
||||
srv.Add(serviceFunc(c.listenForUpdates))
|
||||
srv.Add(serviceFunc(c.calculateSummaries))
|
||||
|
||||
c.stop = make(chan struct{})
|
||||
c.folders = make(map[string]struct{})
|
||||
c.srv = srv
|
||||
|
||||
srv.Serve()
|
||||
}
|
||||
|
||||
func (c *folderSummarySvc) Stop() {
|
||||
// c.srv.Stop() is mostly a no-op here, but we need to call it anyway so
|
||||
// c.srv doesn't try to restart the serviceFuncs when they exit after we
|
||||
// close the stop channel.
|
||||
c.srv.Stop()
|
||||
close(c.stop)
|
||||
}
|
||||
|
||||
// listenForUpdates subscribes to the event bus and makes note of folders that
|
||||
// need their data recalculated.
|
||||
func (c *folderSummarySvc) listenForUpdates() {
|
||||
sub := events.Default.Subscribe(events.LocalIndexUpdated | events.RemoteIndexUpdated)
|
||||
defer events.Default.Unsubscribe(sub)
|
||||
|
||||
for {
|
||||
// This loop needs to be fast so we don't miss too many events.
|
||||
|
||||
select {
|
||||
case ev := <-sub.C():
|
||||
// Whenever the local or remote index is updated for a given
|
||||
// folder we make a note of it.
|
||||
|
||||
data := ev.Data.(map[string]interface{})
|
||||
folder := data["folder"].(string)
|
||||
c.foldersMut.Lock()
|
||||
c.folders[folder] = struct{}{}
|
||||
c.foldersMut.Unlock()
|
||||
|
||||
case <-c.stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// calculateSummaries periodically recalculates folder summaries and
|
||||
// completion percentage, and sends the results on the event bus.
|
||||
func (c *folderSummarySvc) calculateSummaries() {
|
||||
const pumpInterval = 2 * time.Second
|
||||
pump := time.NewTimer(pumpInterval)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-pump.C:
|
||||
// We only recalculate sumamries if someone is listening to events
|
||||
// (a request to /rest/events has been made within the last
|
||||
// pingEventInterval).
|
||||
|
||||
lastEventRequestMut.Lock()
|
||||
// XXX: Reaching out to a global var here is very ugly :( Should
|
||||
// we make the gui stuff a proper object with methods on it that
|
||||
// we can query about this kind of thing?
|
||||
last := lastEventRequest
|
||||
lastEventRequestMut.Unlock()
|
||||
|
||||
t0 := time.Now()
|
||||
if time.Since(last) < pingEventInterval {
|
||||
for _, folder := range c.foldersToHandle() {
|
||||
// The folder summary contains how many bytes, files etc
|
||||
// are in the folder and how in sync we are.
|
||||
data := folderSummary(c.model, folder)
|
||||
events.Default.Log(events.FolderSummary, map[string]interface{}{
|
||||
"folder": folder,
|
||||
"summary": data,
|
||||
})
|
||||
|
||||
for _, devCfg := range cfg.Folders()[folder].Devices {
|
||||
if devCfg.DeviceID.Equals(myID) {
|
||||
// We already know about ourselves.
|
||||
continue
|
||||
}
|
||||
if !c.model.ConnectedTo(devCfg.DeviceID) {
|
||||
// We're not interested in disconnected devices.
|
||||
continue
|
||||
}
|
||||
|
||||
// Get completion percentage of this folder for the
|
||||
// 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,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We don't want to spend all our time calculating summaries. Lets
|
||||
// set an arbitrary limit at not spending more than about 30% of
|
||||
// our time here...
|
||||
wait := 2*time.Since(t0) + pumpInterval
|
||||
pump.Reset(wait)
|
||||
|
||||
case <-c.stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// foldersToHandle returns the list of folders needing a summary update, and
|
||||
// clears the list.
|
||||
func (c *folderSummarySvc) foldersToHandle() []string {
|
||||
c.foldersMut.Lock()
|
||||
res := make([]string, 0, len(c.folders))
|
||||
for folder := range c.folders {
|
||||
res = append(res, folder)
|
||||
delete(c.folders, folder)
|
||||
}
|
||||
c.foldersMut.Unlock()
|
||||
return res
|
||||
}
|
||||
|
||||
// serviceFunc wraps a function to create a suture.Service without stop
|
||||
// functionality.
|
||||
type serviceFunc func()
|
||||
|
||||
func (f serviceFunc) Serve() { f() }
|
||||
func (f serviceFunc) Stop() {}
|
||||
@@ -1,3 +1,5 @@
|
||||
var debugEvents = false;
|
||||
|
||||
angular.module('syncthing.core')
|
||||
.controller('EventController', function ($scope, $http) {
|
||||
'use strict';
|
||||
@@ -20,7 +22,9 @@ angular.module('syncthing.core')
|
||||
|
||||
if (lastID > 0) {
|
||||
data.forEach(function (event) {
|
||||
console.log("event", event.id, event.type, event.data);
|
||||
if (debugEvents) {
|
||||
console.log("event", event.id, event.type, event.data);
|
||||
}
|
||||
$scope.$emit(event.type, event);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -140,19 +140,11 @@ angular.module('syncthing.core')
|
||||
|
||||
$scope.$on('LocalIndexUpdated', function (event, arg) {
|
||||
var data = arg.data;
|
||||
refreshFolder(data.folder);
|
||||
refreshFolderStats();
|
||||
|
||||
// Update completion status for all devices that we share this folder with.
|
||||
$scope.folders[data.folder].devices.forEach(function (deviceCfg) {
|
||||
refreshCompletion(deviceCfg.deviceID, data.folder);
|
||||
});
|
||||
});
|
||||
|
||||
$scope.$on('RemoteIndexUpdated', function (event, arg) {
|
||||
var data = arg.data;
|
||||
refreshFolder(data.folder);
|
||||
refreshCompletion(data.device, data.folder);
|
||||
// Nothing
|
||||
});
|
||||
|
||||
$scope.$on('DeviceDisconnected', function (event, arg) {
|
||||
@@ -215,7 +207,6 @@ angular.module('syncthing.core')
|
||||
var stats = arg.data;
|
||||
var progress = {};
|
||||
for (var folder in stats) {
|
||||
refreshFolder(folder);
|
||||
progress[folder] = {};
|
||||
for (var file in stats[folder]) {
|
||||
var s = stats[folder][file];
|
||||
@@ -241,7 +232,6 @@ angular.module('syncthing.core')
|
||||
}
|
||||
for (var folder in $scope.progress) {
|
||||
if (!(folder in progress)) {
|
||||
refreshFolder(folder);
|
||||
if ($scope.neededFolder == folder) {
|
||||
refreshNeed(folder);
|
||||
}
|
||||
@@ -258,6 +248,30 @@ angular.module('syncthing.core')
|
||||
console.log("DownloadProgress", $scope.progress);
|
||||
});
|
||||
|
||||
$scope.$on('FolderSummary', function (event, arg) {
|
||||
var data = arg.data;
|
||||
$scope.model[data.folder] = data.summary;
|
||||
});
|
||||
|
||||
$scope.$on('FolderCompletion', function (event, arg) {
|
||||
var data = arg.data;
|
||||
if (!$scope.completion[data.device]) {
|
||||
$scope.completion[data.device] = {};
|
||||
}
|
||||
$scope.completion[data.device][data.folder] = data.completion;
|
||||
|
||||
var tot = 0,
|
||||
cnt = 0;
|
||||
for (var cmp in $scope.completion[data.device]) {
|
||||
if (cmp === "_total") {
|
||||
continue;
|
||||
}
|
||||
tot += $scope.completion[data.device][cmp];
|
||||
cnt += 1;
|
||||
}
|
||||
$scope.completion[data.device]._total = tot / cnt;
|
||||
});
|
||||
|
||||
$scope.emitHTTPError = function (data, status, headers, config) {
|
||||
$scope.$emit('HTTPError', {data: data, status: status, headers: headers, config: config});
|
||||
};
|
||||
@@ -325,31 +339,25 @@ angular.module('syncthing.core')
|
||||
return;
|
||||
}
|
||||
|
||||
var key = "refreshCompletion" + device + folder;
|
||||
if (!debouncedFuncs[key]) {
|
||||
debouncedFuncs[key] = debounce(function () {
|
||||
$http.get(urlbase + '/completion?device=' + device + '&folder=' + encodeURIComponent(folder)).success(function (data) {
|
||||
if (!$scope.completion[device]) {
|
||||
$scope.completion[device] = {};
|
||||
}
|
||||
$scope.completion[device][folder] = data.completion;
|
||||
$http.get(urlbase + '/completion?device=' + device + '&folder=' + encodeURIComponent(folder)).success(function (data) {
|
||||
if (!$scope.completion[device]) {
|
||||
$scope.completion[device] = {};
|
||||
}
|
||||
$scope.completion[device][folder] = data.completion;
|
||||
|
||||
var tot = 0,
|
||||
cnt = 0;
|
||||
for (var cmp in $scope.completion[device]) {
|
||||
if (cmp === "_total") {
|
||||
continue;
|
||||
}
|
||||
tot += $scope.completion[device][cmp];
|
||||
cnt += 1;
|
||||
}
|
||||
$scope.completion[device]._total = tot / cnt;
|
||||
var tot = 0,
|
||||
cnt = 0;
|
||||
for (var cmp in $scope.completion[device]) {
|
||||
if (cmp === "_total") {
|
||||
continue;
|
||||
}
|
||||
tot += $scope.completion[device][cmp];
|
||||
cnt += 1;
|
||||
}
|
||||
$scope.completion[device]._total = tot / cnt;
|
||||
|
||||
console.log("refreshCompletion", device, folder, $scope.completion[device]);
|
||||
}).error($scope.emitHTTPError);
|
||||
}, 1000, true);
|
||||
}
|
||||
debouncedFuncs[key]();
|
||||
console.log("refreshCompletion", device, folder, $scope.completion[device]);
|
||||
}).error($scope.emitHTTPError);
|
||||
}
|
||||
|
||||
function refreshConnectionStats() {
|
||||
@@ -412,7 +420,7 @@ angular.module('syncthing.core')
|
||||
}
|
||||
console.log("refreshDeviceStats", data);
|
||||
}).error($scope.emitHTTPError);
|
||||
}, 500);
|
||||
}, 2500);
|
||||
|
||||
var refreshFolderStats = debounce(function () {
|
||||
$http.get(urlbase + "/stats/folder").success(function (data) {
|
||||
@@ -424,7 +432,7 @@ angular.module('syncthing.core')
|
||||
}
|
||||
console.log("refreshfolderStats", data);
|
||||
}).error($scope.emitHTTPError);
|
||||
}, 500);
|
||||
}, 2500);
|
||||
|
||||
$scope.refresh = function () {
|
||||
refreshSystem();
|
||||
|
||||
@@ -187,12 +187,12 @@ func Assets() map[string][]byte {
|
||||
bs, _ = ioutil.ReadAll(gr)
|
||||
assets["scripts/syncthing/app.js"] = bs
|
||||
|
||||
bs, _ = base64.StdEncoding.DecodeString("H4sIAAAJbogA/7RUT4/aPhC98ynmt0JK0C8NUKknRHtod6U99dCteqh6MMkkseTYyB4vQhXfvWOHDRCCtqpaXwjz/z2/sdC1V8LmrSm9wjRxe11QI3WdF8ZiMpsAH/7WZI1SaNPk/hk1fewNSQaV5xxpNKRTV5gtZjBtiLYz+Bmzw0m8Q3BkZUHJatKbu/hcCUexLKxBe6VWfcCzsBC8j5/YtTjLDA7niwKde9DsO81QChLnrcOZz+Fbgxq+vKADi46EJQe7RioEahCUYfuWQQU/I9bYVZQOpB6W21pTcw0XMzfW7Bxa4GBnWoStElQZ2zpuQ95qBwLeLhaQOqmL2GxYrkFRonXQCAcb5Ekr5V2DJewkNbFHV4nnKjHUmmWdSxsIgPNhwaeGx94IJwuh1B5aFDrMKigWO0PXd2Q0SBkIXXYhnDcsuuNYbQhEQT6W5RtgDiqvLvvLCtL/xu4hHLTW2Afd3dPqyt3hvLQfJhd/j6qZYispTb4+ftZ8ZazV1eRqiqN23sNibJTIHF/UvSia9KQgDFIciw+HqXNGsWZNnd7FyLsM4m8uy5cv2oct6L5v4LwCcpU5knSY/Q4x5+sUun+POBXqmgXzBpY/Lov0+zVMZ0ADSlkhT7JF4+mMrjGm4gOQ10ipt4pliPA/JPMIy32IW7BO2NS1no2Skx/XO+3X/EZcFFR6lNWQoAze8bKcjIfBG3JMe/UFudJcVY2K7u8wpCS3WS+Tf8vMcnGTmj+Y7rWpRqcJgv4FAAD//wEAAP//QKHce4MGAAA=")
|
||||
bs, _ = base64.StdEncoding.DecodeString("H4sIAAAJbogA/7RUQYvbPBC951fMtyzY4XO9SaGnkPbQ7sKeeuiWHkoPij22BbIUNNKGUPLfO5ITr+M4bCmtLnFGM2/ePD3pWVgocePr+2fUjmANlVCEq9lM6NorYfPWlF5hmtBeF66Rus4LYzGZz4AXf2tnjVJo0yRCfOwDSQaV5xppNKS3VJgtZnDbOLedw89YHVbiCYGclYVLuOsp3OXnSpCLsExMe6VWfUIgHnYfP/HWYlAZNsgXBRI96DBPz6EUTgxbh3V3B98a1PDlNB1YJCcsS7FrpEJwDYIyHN/yUGGfJ9bYIUoCqcdwW2tqxqBYubFmR2iBk8m0CFslXGVsS9zGeasJBLxdLCAlqYvYbAzXoCjREjSCYIPMtFKeGixhJ10Te3RIzKvEgDXPui1tIAycjwGfGqa9ESQLodQeWhQ6cBUugg2m6zvyNOgyELrsUrhuDLrjXG0ciML5CMsnwBpUXp33lxWk/02dQ1horbEPujun1cV2N+d5/DA7+3t0zS220qXJ18fPmo+MvbqaXbA4euc9LKaoROX4oO5F0aQvDsJgxan8E+rgKl1LC4tVJqPY3qZObyLoTQbxN5fl6cvtw4Xpvq9I0kkwFT1T4gJvAuow/x1lh/cxcPoehVKoa3bcG1j+OAfpL+i4nMccnQlb7Em2aLwb6D2lYXxB8hpd6q1iHyP8D8ldHIs+xGu0TjjUtZ5PipMf34e0fyeu5EVHpkdfjgXK4B3ftpfgYfQIHctefYIuTFtVk679OwopyW3Wy+TfKrNcXJXmD9i9xmqSTTD0LwAAAP//AQAA//8bMA983gYAAA==")
|
||||
gr, _ = gzip.NewReader(bytes.NewReader(bs))
|
||||
bs, _ = ioutil.ReadAll(gr)
|
||||
assets["scripts/syncthing/core/controllers/eventController.js"] = bs
|
||||
|
||||
bs, _ = base64.StdEncoding.DecodeString("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 truncated
|
||||
bs, _ = base64.StdEncoding.DecodeString("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 truncated
|
||||
gr, _ = gzip.NewReader(bytes.NewReader(bs))
|
||||
bs, _ = ioutil.ReadAll(gr)
|
||||
assets["scripts/syncthing/core/controllers/syncthingController.js"] = bs
|
||||
|
||||
@@ -31,6 +31,8 @@ const (
|
||||
FolderRejected
|
||||
ConfigSaved
|
||||
DownloadProgress
|
||||
FolderSummary
|
||||
FolderCompletion
|
||||
|
||||
AllEvents = (1 << iota) - 1
|
||||
)
|
||||
@@ -67,6 +69,10 @@ func (t EventType) String() string {
|
||||
return "ConfigSaved"
|
||||
case DownloadProgress:
|
||||
return "DownloadProgress"
|
||||
case FolderSummary:
|
||||
return "FolderSummary"
|
||||
case FolderCompletion:
|
||||
return "FolderCompletion"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user