Push model data instead of pull (fixes #1434)

This commit is contained in:
Jakob Borg committed 2015-04-01 11:46:30 +02:00
1 parent a3cf37cb2e
commit 454e688c3d
7 files changed
+244 -47

No files matched your search

+21 -4
View File
@@ -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
+7 -4
View File
@@ -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)
}
}
+159
View File
@@ -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();
+2 -2
View File
@@ -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
+6
View File
@@ -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"
}