</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--disable-retry-on-network-errors</option></term>
+
+ <listitem><para>
+ Do not retry when network issues happen, instead fail automatically. (Currently only affects libcurl)
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--low-speed-limit-bytes</option>=N</term>
+
+ <listitem><para>
+ The average transfer speed per second of a transfer during the
+ time set via 'low-speed-time-seconds' for libcurl to abort
+ (default: 1000)
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--low-speed-time-seconds</option>=N</term>
+
+ <listitem><para>
+ The time in number seconds that the transfer speed should be
+ below the 'low-speed-limit-bytes' setting for libcurl to abort
+ (default: 30)
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--max-outstanding-fetcher-requests</option>=N</term>
+
+ <listitem><para>
+ The max amount of concurrent connections allowed. (default: 8)
+ </para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>--disable-verify-bindings</option></term>
int tmpdir_dfd;
bool force_anonymous;
char *custom_user_agent;
+ guint32 opt_low_speed_limit;
+ guint32 opt_low_speed_time;
+ gboolean opt_retry_all;
+ guint32 opt_max_outstanding_fetcher_requests;
GMainContext *mainctx;
CURLM *multi;
}
else
{
- g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED,
+ /* When it is not a file, we want to retry the request.
+ * We accomplish that by using G_IO_ERROR_TIMED_OUT.
+ */
+ gboolean opt_retry_all = req->fetcher->opt_retry_all;
+ int g_io_error_code
+ = (is_file || !opt_retry_all) ? G_IO_ERROR_FAILED : G_IO_ERROR_TIMED_OUT;
+ g_task_return_new_error (task, G_IO_ERROR, g_io_error_code,
"While fetching %s: [%u] %s", eff_url, curlres,
curl_easy_strerror (curlres));
_ostree_fetcher_journal_failure (req->fetcher->remote_name, eff_url,
self->proxy = g_strdup (http_proxy);
}
+void
+_ostree_fetcher_set_low_speed_time (OstreeFetcher *self, guint32 opt_low_speed_time)
+{
+ self->opt_low_speed_time = opt_low_speed_time;
+}
+
+void
+_ostree_fetcher_set_low_speed_limit (OstreeFetcher *self, guint32 opt_low_speed_limit)
+{
+ self->opt_low_speed_limit = opt_low_speed_limit;
+}
+
+void
+_ostree_fetcher_set_retry_all (OstreeFetcher *self, gboolean opt_retry_all)
+{
+ self->opt_retry_all = opt_retry_all;
+}
+
+void
+_ostree_fetcher_set_max_outstanding_fetcher_requests (OstreeFetcher *self,
+ guint32 opt_max_outstanding_fetcher_requests)
+{
+ self->opt_max_outstanding_fetcher_requests = opt_max_outstanding_fetcher_requests;
+}
+
void
_ostree_fetcher_set_cookie_jar (OstreeFetcher *self, const char *jar_path)
{
g_assert_cmpint (rc, ==, CURLM_OK);
rc = curl_easy_setopt (req->easy, CURLOPT_CONNECTTIMEOUT, 30L);
g_assert_cmpint (rc, ==, CURLM_OK);
- /* We used to set CURLOPT_LOW_SPEED_LIMIT and CURLOPT_LOW_SPEED_TIME
- * here, but see https://github.com/ostreedev/ostree/issues/878#issuecomment-347228854
- * basically those options don't play well with HTTP2 at the moment
- * where we can have lots of outstanding requests. Further,
- * we could implement that functionality at a higher level
- * more consistently too.
- */
-
+ rc = curl_easy_setopt (req->easy, CURLOPT_LOW_SPEED_LIMIT, req->fetcher->opt_low_speed_limit);
+ g_assert_cmpint (rc, ==, CURLM_OK);
+ rc = curl_easy_setopt (req->easy, CURLOPT_LOW_SPEED_TIME, req->fetcher->opt_low_speed_time);
+ g_assert_cmpint (rc, ==, CURLM_OK);
/* closure bindings -> task */
rc = curl_easy_setopt (req->easy, CURLOPT_PRIVATE, task);
g_assert_cmpint (rc, ==, CURLM_OK);
/* Also protected by output_stream_set_lock. */
guint64 total_downloaded;
+ guint32 opt_max_outstanding_fetcher_requests;
+
GError *oob_error;
} ThreadClosure;
}
}
+static void
+session_thread_set_max_outstanding_fetcher_requests (ThreadClosure *thread_closure, gpointer data)
+{
+ thread_closure->opt_max_outstanding_fetcher_requests = GPOINTER_TO_UINT (data);
+}
+
static void
session_thread_set_extra_user_agent_cb (ThreadClosure *thread_closure, gpointer data)
{
}
}
+void
+_ostree_fetcher_set_low_speed_time (OstreeFetcher *self, guint32 opt_low_speed_time)
+{
+ // TODO
+}
+
+void
+_ostree_fetcher_set_low_speed_limit (OstreeFetcher *self, guint32 opt_low_speed_limit)
+{
+ // TODO
+}
+
+void
+_ostree_fetcher_set_retry_all (OstreeFetcher *self, gboolean opt_retry_all)
+{
+ // TODO
+}
+
+void
+_ostree_fetcher_set_max_outstanding_fetcher_requests (OstreeFetcher *self,
+ guint32 opt_max_outstanding_fetcher_requests)
+{
+ session_thread_idle_add (self->thread_closure,
+ session_thread_set_max_outstanding_fetcher_requests,
+ GUINT_TO_POINTER (opt_max_outstanding_fetcher_requests), NULL);
+}
+
static void on_request_sent (GObject *object, GAsyncResult *result, gpointer user_data);
static void
* by spreading requests across mirrors. */
gint max_conns;
g_object_get (closure->session, "max-conns-per-host", &max_conns, NULL);
- if (max_conns < _OSTREE_MAX_OUTSTANDING_FETCHER_REQUESTS)
+ if (max_conns < closure->opt_max_outstanding_fetcher_requests)
{
/* We download a lot of small objects in ostree, so this
* helps a lot. Also matches what most modern browsers do.
* currently conservative #define SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT 2 (as of
* 2018-02-14).
*/
- max_conns = _OSTREE_MAX_OUTSTANDING_FETCHER_REQUESTS;
+ max_conns = closure->opt_max_outstanding_fetcher_requests;
g_object_set (closure->session, "max-conns-per-host", max_conns, NULL);
}
char *user_agent;
guint64 bytes_transferred;
+ guint32 opt_max_outstanding_fetcher_requests;
};
enum
self->user_agent = g_strdup_printf ("%s %s", OSTREE_FETCHER_USERAGENT_STRING, extra_user_agent);
}
+void
+_ostree_fetcher_set_low_speed_time (OstreeFetcher *self, guint32 opt_low_speed_time)
+{
+ // TODO
+}
+
+void
+_ostree_fetcher_set_low_speed_limit (OstreeFetcher *self, guint32 opt_low_speed_limit)
+{
+ // TODO
+}
+
+void
+_ostree_fetcher_set_retry_all (OstreeFetcher *self, gboolean opt_retry_all)
+{
+ // TODO
+}
+
+void
+_ostree_fetcher_set_max_outstanding_fetcher_requests (OstreeFetcher *self,
+ guint32 opt_max_outstanding_fetcher_requests)
+{
+ self->opt_max_outstanding_fetcher_requests = opt_max_outstanding_fetcher_requests;
+}
+
static gboolean
finish_stream (FetcherRequest *request, GCancellable *cancellable, GError **error)
{
const char *user_agent = self->user_agent ?: OSTREE_FETCHER_USERAGENT_STRING;
SoupSession *session = soup_session_new_with_options (
"user-agent", user_agent, "timeout", 60, "idle-timeout", 60, "max-conns-per-host",
- _OSTREE_MAX_OUTSTANDING_FETCHER_REQUESTS, NULL);
+ self->opt_max_outstanding_fetcher_requests, NULL);
/* SoupContentDecoder is included in the session by default. Remove it
* if gzip compression isn't in use.
void _ostree_fetcher_set_client_cert (OstreeFetcher *fetcher, const char *cert_path,
const char *key_path);
+void _ostree_fetcher_set_low_speed_limit (OstreeFetcher *self, guint32 opt_low_speed_limit);
+
+void _ostree_fetcher_set_low_speed_time (OstreeFetcher *self, guint32 opt_low_speed_time);
+
+void _ostree_fetcher_set_retry_all (OstreeFetcher *self, gboolean opt_retry_all);
+
+void
+_ostree_fetcher_set_max_outstanding_fetcher_requests (OstreeFetcher *self,
+ guint32 opt_max_outstanding_fetcher_requests);
+
void _ostree_fetcher_set_tls_database (OstreeFetcher *self, const char *tlsdb_path);
void _ostree_fetcher_set_extra_headers (OstreeFetcher *self, GVariant *extra_headers);
#define _OSTREE_SUMMARY_CACHE_DIR "summaries"
#define _OSTREE_CACHE_DIR "cache"
-#define _OSTREE_MAX_OUTSTANDING_FETCHER_REQUESTS 8
#define _OSTREE_MAX_OUTSTANDING_DELTAPART_REQUESTS 2
/* We want some parallelism with disk writes, but we also
GVariant *extra_headers;
char *append_user_agent;
+ guint32 low_speed_limit;
+ guint32 low_speed_time;
+ gboolean retry_all;
+ guint32 max_outstanding_fetcher_requests;
gboolean dry_run;
gboolean dry_run_emitted_progress;
* _ostree_fetcher_should_retry_request(). This is the default value for the
* `n-network-retries` pull option. */
#define DEFAULT_N_NETWORK_RETRIES 5
+#define OPT_LOWSPEEDLIMIT_DEFAULT 1000
+#define OPT_LOWSPEEDTIME_DEFAULT 30
+#define OPT_RETRYALL_DEFAULT TRUE
+#define OPT_OSTREE_MAX_OUTSTANDING_FETCHER_REQUESTS_DEFAULT 8
typedef struct
{
const gboolean fetch_full
= ((pull_data->n_outstanding_metadata_fetches + pull_data->n_outstanding_content_fetches
+ pull_data->n_outstanding_deltapart_fetches)
- == _OSTREE_MAX_OUTSTANDING_FETCHER_REQUESTS);
+ == pull_data->max_outstanding_fetcher_requests);
const gboolean deltas_full
= (pull_data->n_outstanding_deltapart_fetches == _OSTREE_MAX_OUTSTANDING_DELTAPART_REQUESTS);
const gboolean writes_full = ((pull_data->n_outstanding_metadata_write_requests
static OstreeFetcher *
_ostree_repo_remote_new_fetcher (OstreeRepo *self, const char *remote_name, gboolean gzip,
GVariant *extra_headers, const char *append_user_agent,
+ guint32 low_speed_limit, guint32 low_speed_time,
+ gboolean retry_all, guint32 max_outstanding_fetcher_requests,
OstreeFetcherSecurityState *out_state, GError **error)
{
OstreeFetcher *fetcher = NULL;
}
}
+ _ostree_fetcher_set_low_speed_limit (fetcher, low_speed_limit);
+ _ostree_fetcher_set_low_speed_time (fetcher, low_speed_time);
+ _ostree_fetcher_set_retry_all (fetcher, retry_all);
+ _ostree_fetcher_set_max_outstanding_fetcher_requests (fetcher, max_outstanding_fetcher_requests);
+
{
g_autofree char *tls_ca_path = NULL;
g_clear_object (&pull_data->fetcher);
pull_data->fetcher = _ostree_repo_remote_new_fetcher (
pull_data->repo, remote_name, FALSE, pull_data->extra_headers, pull_data->append_user_agent,
- &pull_data->fetcher_security_state, error);
+ pull_data->low_speed_limit, pull_data->low_speed_time, pull_data->retry_all,
+ pull_data->max_outstanding_fetcher_requests, &pull_data->fetcher_security_state, error);
if (pull_data->fetcher == NULL)
return FALSE;
* * `n-network-retries` (`u`): Number of times to retry each download on receiving
* a transient network error, such as a socket timeout; default is 5, 0
* means return errors without retrying. Since: 2018.6
+ * * `low-speed-limit-bytes` (`u`): The average transfer speed per second of a transfer
+ * during the time set via "low-speed-time-seconds" for libcurl to abort.
+ * * `low-speed-time-seconds` (`u`): The time in number seconds that the transfer
+ * speed should be below the "low-speed-limit-bytes" setting for libcurl to abort.
+ * * `retry-all-network-errors` (`b`): Retry when network issues happen, instead of
+ * failing automatically. Currently only affects libcurl. (Default set to true)
+ * * `max-outstanding-fetcher-requests` (`u`): The max amount of concurrent connections allowed.
* * `ref-keyring-map` (`a(sss)`): Array of (collection ID, ref name, keyring
* remote name) tuples specifying which remote's keyring should be used when
* doing GPG verification of each collection-ref. This is useful to prevent a
gboolean opt_gpg_verify_summary_set = FALSE;
gboolean opt_collection_refs_set = FALSE;
gboolean opt_n_network_retries_set = FALSE;
+ gboolean opt_low_speed_limit_set = FALSE;
+ gboolean opt_low_speed_time_set = FALSE;
+ gboolean opt_retry_all_set = FALSE;
+ gboolean opt_max_outstanding_fetcher_requests_set = FALSE;
gboolean opt_ref_keyring_map_set = FALSE;
gboolean disable_sign_verify = FALSE;
gboolean disable_sign_verify_summary = FALSE;
&pull_data->timestamp_check_from_rev);
(void)g_variant_lookup (options, "max-metadata-size", "t", &pull_data->max_metadata_size);
(void)g_variant_lookup (options, "append-user-agent", "s", &pull_data->append_user_agent);
+ opt_low_speed_limit_set
+ = g_variant_lookup (options, "low-speed-limit-bytes", "u", &pull_data->low_speed_limit);
+ opt_low_speed_time_set
+ = g_variant_lookup (options, "low-speed-time-seconds", "u", &pull_data->low_speed_time);
+ opt_retry_all_set
+ = g_variant_lookup (options, "retry-all-network-errors", "b", &pull_data->retry_all);
+ opt_max_outstanding_fetcher_requests_set
+ = g_variant_lookup (options, "max-outstanding-fetcher-requests", "b",
+ &pull_data->max_outstanding_fetcher_requests);
opt_n_network_retries_set
= g_variant_lookup (options, "n-network-retries", "u", &pull_data->n_network_retries);
opt_ref_keyring_map_set
if (!opt_n_network_retries_set)
pull_data->n_network_retries = DEFAULT_N_NETWORK_RETRIES;
+ if (!opt_low_speed_limit_set)
+ pull_data->low_speed_limit = OPT_LOWSPEEDLIMIT_DEFAULT;
+ if (!opt_low_speed_time_set)
+ pull_data->low_speed_time = OPT_LOWSPEEDTIME_DEFAULT;
+ if (!opt_retry_all_set)
+ pull_data->retry_all = OPT_RETRYALL_DEFAULT;
+ if (!opt_max_outstanding_fetcher_requests_set)
+ pull_data->max_outstanding_fetcher_requests
+ = OPT_OSTREE_MAX_OUTSTANDING_FETCHER_REQUESTS_DEFAULT;
pull_data->repo = self;
pull_data->progress = progress;
goto error;
fetcher = _ostree_repo_remote_new_fetcher (self, result->remote->name, TRUE, NULL,
- NULL, NULL, &error);
+ NULL, 0, 0, TRUE, 0, NULL, &error);
if (fetcher == NULL)
goto error;
g_autoptr (GVariant) extra_headers = NULL;
g_autoptr (GPtrArray) mirrorlist = NULL;
const char *append_user_agent = NULL;
+ guint32 low_speed_limit = OPT_LOWSPEEDLIMIT_DEFAULT;
+ guint32 low_speed_time = OPT_LOWSPEEDTIME_DEFAULT;
+ gboolean retry_all = OPT_RETRYALL_DEFAULT;
+ guint32 max_outstanding_fetcher_requests = OPT_OSTREE_MAX_OUTSTANDING_FETCHER_REQUESTS_DEFAULT;
guint n_network_retries = DEFAULT_N_NETWORK_RETRIES;
gboolean summary_sig_not_modified = FALSE;
g_autofree char *summary_sig_if_none_match = NULL;
(void)g_variant_lookup (options, "http-headers", "@a(ss)", &extra_headers);
(void)g_variant_lookup (options, "append-user-agent", "&s", &append_user_agent);
(void)g_variant_lookup (options, "n-network-retries", "u", &n_network_retries);
+ (void)g_variant_lookup (options, "low-speed-limit-bytes", "u", &low_speed_limit);
+ (void)g_variant_lookup (options, "low-speed-time-seconds", "u", &low_speed_time);
+ (void)g_variant_lookup (options, "retry-all-network-errors", "b", &retry_all);
+ (void)g_variant_lookup (options, "max-outstanding-fetcher-requests", "b",
+ &max_outstanding_fetcher_requests);
}
if (!ostree_repo_remote_get_gpg_verify_summary (self, name, &gpg_verify_summary, error))
(void)mainctx; // Used for autocleanup
fetcher = _ostree_repo_remote_new_fetcher (self, name, TRUE, extra_headers, append_user_agent,
- NULL, error);
+ low_speed_limit, low_speed_time, retry_all,
+ max_outstanding_fetcher_requests, NULL, error);
if (fetcher == NULL)
return FALSE;
static gboolean opt_disable_verify_bindings;
static char *opt_timestamp_check_from_rev;
static gboolean opt_bareuseronly_files;
+static gboolean opt_retry_all;
static char **opt_subpaths;
static char **opt_http_headers;
static char *opt_cache_dir;
static int opt_depth = 0;
static int opt_frequency = 0;
static int opt_network_retries = -1;
+static int opt_low_speed_limit_bytes = -1;
+static int opt_low_speed_time_seconds = -1;
+static int opt_max_outstanding_fetcher_requests = -1;
static char *opt_url;
static char **opt_localcache_repos;
"Do not use static deltas", NULL },
{ "require-static-deltas", 0, 0, G_OPTION_ARG_NONE, &opt_require_static_deltas,
"Require static deltas", NULL },
+ { "disable-retry-on-network-errors", 0, 0, G_OPTION_ARG_NONE, &opt_retry_all,
+ "Do not retry when network issues happen, instead fail automatically. (Currently only "
+ "affects libcurl)",
+ NULL },
{ "mirror", 0, 0, G_OPTION_ARG_NONE, &opt_mirror,
"Write refs suitable for a mirror and fetches all refs if none provided", NULL },
{ "subpath", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &opt_subpaths,
"Sets the update frequency, in milliseconds (0=1000ms) (default: 0)", "FREQUENCY" },
{ "network-retries", 0, 0, G_OPTION_ARG_INT, &opt_network_retries,
"Specifies how many times each download should be retried upon error (default: 5)", "N" },
+ { "low-speed-limit-bytes", 0, 0, G_OPTION_ARG_INT, &opt_low_speed_limit_bytes,
+ "The average transfer speed per second of a transfer during the time set via "
+ "'low-speed-time-seconds' for libcurl to abort(default: 1000)",
+ "N" },
+ { "low-speed-time-seconds", 0, 0, G_OPTION_ARG_INT, &opt_low_speed_time_seconds,
+ "The time in number seconds that the transfer speed should be below the "
+ "'low-speed-limit-bytes' setting for libcurl to abort (default: 30)",
+ "N" },
+ { "max-outstanding-fetcher-requests", 0, 0, G_OPTION_ARG_INT,
+ &opt_max_outstanding_fetcher_requests,
+ "The max amount of concurrent connections allowed. (default: 8)", "N" },
{ "localcache-repo", 'L', 0, G_OPTION_ARG_FILENAME_ARRAY, &opt_localcache_repos,
"Add REPO as local cache source for objects during this pull", "REPO" },
{ "timestamp-check", 'T', 0, G_OPTION_ARG_NONE, &opt_timestamp_check,
g_variant_builder_add (&builder, "{s@v}", "n-network-retries",
g_variant_new_variant (g_variant_new_uint32 (opt_network_retries)));
+ if (opt_low_speed_limit_bytes >= 0)
+ g_variant_builder_add (
+ &builder, "{s@v}", "low-speed-limit-bytes",
+ g_variant_new_variant (g_variant_new_uint32 (opt_low_speed_limit_bytes)));
+
+ if (opt_low_speed_time_seconds >= 0)
+ g_variant_builder_add (
+ &builder, "{s@v}", "low-speed-time-seconds",
+ g_variant_new_variant (g_variant_new_uint32 (opt_low_speed_time_seconds)));
+
+ if (opt_max_outstanding_fetcher_requests >= 0)
+ g_variant_builder_add (
+ &builder, "{s@v}", "max-outstanding-fetcher-requests",
+ g_variant_new_variant (g_variant_new_uint32 (opt_max_outstanding_fetcher_requests)));
+
+ if (opt_retry_all)
+ g_variant_builder_add (&builder, "{s@v}", "retry-all-network-errors",
+ g_variant_new_variant (g_variant_new_boolean (FALSE)));
+
g_variant_builder_add (
&builder, "{s@v}", "disable-static-deltas",
g_variant_new_variant (g_variant_new_boolean (opt_disable_static_deltas)));