summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Ohly <patrick.ohly@intel.com>2013-04-24 12:00:45 +0200
committerPatrick Ohly <patrick.ohly@intel.com>2013-05-13 17:49:50 +0200
commit9a4c770d8e8b2ee5d8ac766faf7c7362fde3a82b (patch)
tree205e915497472c3d0a17d88e7d6d4244e547fd96
parent2032d17098fe3baab9eedb1272a2a27cce8a3608 (diff)
engine: prevent timeouts in HTTP server mode
HTTP SyncML clients give up after a certain timeout (SyncEvolution after RetryDuration = 5 minutes by default, Nokia e51 after 15 minutes) when the server fails to respond. This can happen with SyncEvolution as server when it uses a slow storage with many items, for example via WebDAV. In the case of slow session startup, multithreading is now used to run the storage initializing in parallel to sending regular "keep-alive" SyncML replies to the client. By default, these replies are sent every 2 minutes. This can be configured with another extensions of the SyncMLVersion property: SyncMLVersion = REQUESTMAXTIME=5m Other modes do not use multithreading by default, but it can be enabled by setting REQUESTMAXTIME explicitly. It can be disabled by setting the time to zero. The new feature depends on a libsynthesis with multithreading enabled and glib >= 2.32.0, which is necessary to make SyncEvolution itself thread-safe. With an older glib, multithreading is disabled, but can be enabled as a stop-gap measure by setting REQUESTMAXTIME explicitly.
-rw-r--r--configure.ac2
-rw-r--r--src/syncevo/SyncConfig.cpp44
-rw-r--r--src/syncevo/SyncConfig.h2
-rw-r--r--src/syncevo/SyncContext.cpp44
4 files changed, 87 insertions, 5 deletions
diff --git a/configure.ac b/configure.ac
index be784937..9d831e68 100644
--- a/configure.ac
+++ b/configure.ac
@@ -25,7 +25,7 @@ SE_CHECK_FOR_STABLE_RELEASE
# Minimum version of libsynthesis as defined in its
# configure script and thus .pc files:
-define([SYNTHESIS_MIN_VERSION], [3.4.0.16.8])
+define([SYNTHESIS_MIN_VERSION], [3.4.0.16.9])
# Line above is patched by gen-autotools.sh. Handle
# both "yes" and "no".
diff --git a/src/syncevo/SyncConfig.cpp b/src/syncevo/SyncConfig.cpp
index 9631b2f9..382714aa 100644
--- a/src/syncevo/SyncConfig.cpp
+++ b/src/syncevo/SyncConfig.cpp
@@ -32,6 +32,7 @@
#include <syncevo/IniConfigNode.h>
#include <syncevo/Cmdline.h>
#include <syncevo/lcs.h>
+#include <syncevo/ThreadSupport.h>
#include <test.h>
#include <synthesis/timeutil.h>
@@ -1363,10 +1364,28 @@ static ConfigProperty syncPropSyncMLVersion("SyncMLVersion",
"Instead or in adddition to the version, several keywords can\n"
"be set in this property (separated by spaces or commas):\n"
"\n"
- "- NOCTCAP = avoid sending CtCap meta information\n"
- "- NORESTART = disable the sync mode extension that SyncEvolution\n"
+ "- NOCTCAP - avoid sending CtCap meta information\n"
+ "- NORESTART - disable the sync mode extension that SyncEvolution\n"
" client and server use to negotiate whether both sides support\n"
" running multiple sync iterations in the same session\n"
+ "- REQUESTMAXTIME=<time> - override the rate at which the\n"
+ " SyncML server sends preliminary replies while preparing\n"
+ " local storages in the background. This helps to avoid timeouts\n"
+ " in the SyncML client. Depends on multithreading.\n"
+#ifdef HAVE_THREAD_SUPPORT
+ // test-dbus.py checks for 'is thread-safe'!
+ " This SyncEvolution binary is thread-safe and thus this feature\n"
+ " is enabled by default for HTTP servers, with a delay of 2 minutes\n"
+ " between messages. Other servers (Bluetooth, local sync) should not\n"
+ " need preliminary replies and the feature is disabled, although\n"
+ " it can be enabled by setting the time explicitly.\n"
+#else
+ " This SyncEvolution binary is not thread-safe and thus this feature\n"
+ " is disabled by default, although it can be enabled if absolutely\n"
+ " needed by setting the time explicitly.\n"
+#endif
+ " <time> can be specified like other durations in the config,\n"
+ " for example as REQUESTMAXTIME=2m.\n"
"\n"
"Setting these flags should only be necessary as workaround for\n"
"broken peers.\n"
@@ -1968,6 +1987,27 @@ InitStateString SyncConfig::getSyncMLVersion() const {
}
return InitStateString("", flags.wasSet());
}
+InitState<unsigned int> SyncConfig::getRequestMaxTime() const {
+ InitState<unsigned int> requestmaxtime;
+ InitState< std::set<std::string> > flags = getSyncMLFlags();
+ BOOST_FOREACH(const std::string &flag, flags) {
+ size_t offset = flag.find('=');
+ if (offset != flag.npos) {
+ std::string key = flag.substr(0, offset);
+ if (boost::iequals(key, "RequestMaxTime")) {
+ std::string value = flag.substr(offset + 1);
+ unsigned int seconds;
+ std::string error;
+ if (!SecondsConfigProperty::parseDuration(value, error, seconds)) {
+ SE_THROW("invalid RequestMaxTime value in SyncMLVersion property: " + error);
+ }
+ requestmaxtime = seconds;
+ break;
+ }
+ }
+ }
+ return requestmaxtime;
+}
InitState< std::set<std::string> > SyncConfig::getSyncMLFlags() const {
InitStateString value = syncPropSyncMLVersion.getProperty(*getNode(syncPropSyncMLVersion));
std::list<std::string> keywords;
diff --git a/src/syncevo/SyncConfig.h b/src/syncevo/SyncConfig.h
index f051d192..57126fb7 100644
--- a/src/syncevo/SyncConfig.h
+++ b/src/syncevo/SyncConfig.h
@@ -1514,6 +1514,8 @@ class SyncConfig {
/** all flags that are set in the SyncMLVersion property, including the 1.0/1.1/1.2 versions */
virtual InitState< std::set<std::string> > getSyncMLFlags() const;
+ virtual InitState<unsigned int> getRequestMaxTime() const;
+
/**
* An arbitrary name assigned to the peer configuration,
* not necessarily unique. Can be used by a GUI instead
diff --git a/src/syncevo/SyncContext.cpp b/src/syncevo/SyncContext.cpp
index 148f8c68..a2b2b338 100644
--- a/src/syncevo/SyncContext.cpp
+++ b/src/syncevo/SyncContext.cpp
@@ -27,6 +27,7 @@
#include <syncevo/SyncSource.h>
#include <syncevo/util.h>
#include <syncevo/SuspendFlags.h>
+#include <syncevo/ThreadSupport.h>
#include <syncevo/SafeConfigNode.h>
#include <syncevo/IniConfigNode.h>
@@ -2429,7 +2430,45 @@ void SyncContext::getConfigXML(string &xml, string &configname)
" <server type='plugin'>\n"
" <plugin_module>SyncEvolution</plugin_module>\n"
" <plugin_sessionauth>yes</plugin_sessionauth>\n"
- " <plugin_deviceadmin>yes</plugin_deviceadmin>\n"
+ " <plugin_deviceadmin>yes</plugin_deviceadmin>\n";
+
+ InitState<unsigned int> configrequestmaxtime = getRequestMaxTime();
+ unsigned int requestmaxtime;
+ if (configrequestmaxtime.wasSet()) {
+ // Explicitly set, use it regardless of the kind of sync.
+ // We allow this even if thread support was not available,
+ // because if a user enables it explicitly, it's probably
+ // for a good reason (= failing client), in which case
+ // risking multithreading issues is preferable.
+ requestmaxtime = configrequestmaxtime.get();
+ } else if (m_remoteInitiated || m_localSync) {
+ // We initiated the sync (local sync, Bluetooth). The client
+ // should not time out, so there is no need for intermediate
+ // message sending.
+ //
+ // To avoid potential problems and get a single log file,
+ // avoid it and multithreading by default.
+ requestmaxtime = 0;
+ } else {
+ // We were contacted by an HTTP client. Reply to client
+ // not later than 120 seconds while storage initializes
+ // in a background thread.
+#ifdef HAVE_THREAD_SUPPORT
+ requestmaxtime = 120; // default in seconds
+#else
+ requestmaxtime = 0;
+#endif
+ }
+ if (requestmaxtime) {
+ clientorserver <<
+ " <multithread>yes</multithread>\n"
+ " <requestmaxtime>" << requestmaxtime << "</requestmaxtime>\n";
+ } else {
+ clientorserver <<
+ " <multithread>no</multithread>\n";
+ }
+
+ clientorserver <<
"\n" <<
sessioninitscript <<
" <sessiontimeout>300</sessiontimeout>\n"
@@ -2454,6 +2493,7 @@ void SyncContext::getConfigXML(string &xml, string &configname)
clientorserver <<
" <client type='plugin'>\n"
" <binfilespath>$(binfilepath)</binfilespath>\n"
+ " <multithread>no</multithread>\n"
" <defaultauth/>\n";
if (getRefreshSync()) {
clientorserver <<
@@ -2511,7 +2551,7 @@ void SyncContext::getConfigXML(string &xml, string &configname)
" <timestamp>yes</timestamp>\n"
" <timestampall>yes</timestampall>\n"
" <timedsessionlognames>no</timedsessionlognames>\n"
- " <subthreadmode>suppress</subthreadmode>\n"
+ " <subthreadmode>separate</subthreadmode>\n"
" <logsessionstoglobal>yes</logsessionstoglobal>\n"
" <singlegloballog>yes</singlegloballog>\n";
if (logging) {