Add Maxima lists to bandwidth state.

Right now, Tor routers don't save the maxima values from the
bw_history_t between sessions.  That's no good, since we use those
values to determine bandwidth.  This code adds a new BWHist.*Maximum
set of values to the state file.  If they're not present, we estimate
them by taking the observed total bandwidth and dividing it by the
period length, which provides a lower bound.

This should fix bug 1863.  I'm calling it a feature.
This commit is contained in:
Nick Mathewson 2011-01-10 14:11:22 -05:00
parent 28844c8403
commit 105b94b75b
4 changed files with 66 additions and 19 deletions

View File

@ -1,3 +1,10 @@
o Minor features
- Servers now save observed maximum bandwidth throughput rates
to their state file (along with total usage, which was already
saved) so that they can determine their correct estimated
bandwidth on restart. Resolves bug 1863, where Tor servers
would reset their estimated bandwidth to 0 after restarting.
o Minor bugfixes o Minor bugfixes
- Fix a bug in banwidth history state parsing that could have been - Fix a bug in banwidth history state parsing that could have been
triggered if a future version of Tor ever changed the timing triggered if a future version of Tor ever changed the timing
@ -7,4 +14,3 @@
error parsing any bw history value from the state file. Bugfix on error parsing any bw history value from the state file. Bugfix on
Tor 0.2.2.15-alpha. Tor 0.2.2.15-alpha.

View File

@ -443,15 +443,19 @@ static config_var_t _state_vars[] = {
V(BWHistoryReadEnds, ISOTIME, NULL), V(BWHistoryReadEnds, ISOTIME, NULL),
V(BWHistoryReadInterval, UINT, "900"), V(BWHistoryReadInterval, UINT, "900"),
V(BWHistoryReadValues, CSV, ""), V(BWHistoryReadValues, CSV, ""),
V(BWHistoryReadMaxima, CSV, ""),
V(BWHistoryWriteEnds, ISOTIME, NULL), V(BWHistoryWriteEnds, ISOTIME, NULL),
V(BWHistoryWriteInterval, UINT, "900"), V(BWHistoryWriteInterval, UINT, "900"),
V(BWHistoryWriteValues, CSV, ""), V(BWHistoryWriteValues, CSV, ""),
V(BWHistoryWriteMaxima, CSV, ""),
V(BWHistoryDirReadEnds, ISOTIME, NULL), V(BWHistoryDirReadEnds, ISOTIME, NULL),
V(BWHistoryDirReadInterval, UINT, "900"), V(BWHistoryDirReadInterval, UINT, "900"),
V(BWHistoryDirReadValues, CSV, ""), V(BWHistoryDirReadValues, CSV, ""),
V(BWHistoryDirReadMaxima, CSV, ""),
V(BWHistoryDirWriteEnds, ISOTIME, NULL), V(BWHistoryDirWriteEnds, ISOTIME, NULL),
V(BWHistoryDirWriteInterval, UINT, "900"), V(BWHistoryDirWriteInterval, UINT, "900"),
V(BWHistoryDirWriteValues, CSV, ""), V(BWHistoryDirWriteValues, CSV, ""),
V(BWHistoryDirWriteMaxima, CSV, ""),
V(TorVersion, STRING, NULL), V(TorVersion, STRING, NULL),

View File

@ -2856,19 +2856,25 @@ typedef struct {
* bandwidth usage. The "Interval" fields hold the granularity, in seconds, * bandwidth usage. The "Interval" fields hold the granularity, in seconds,
* of the entries of Values. The "Values" lists hold decimal string * of the entries of Values. The "Values" lists hold decimal string
* representations of the number of bytes read or written in each * representations of the number of bytes read or written in each
* interval. */ * interval. The "Maxima" list holds decimal strings describing the highest
* rate achieved during the interval.
*/
time_t BWHistoryReadEnds; time_t BWHistoryReadEnds;
int BWHistoryReadInterval; int BWHistoryReadInterval;
smartlist_t *BWHistoryReadValues; smartlist_t *BWHistoryReadValues;
smartlist_t *BWHistoryReadMaxima;
time_t BWHistoryWriteEnds; time_t BWHistoryWriteEnds;
int BWHistoryWriteInterval; int BWHistoryWriteInterval;
smartlist_t *BWHistoryWriteValues; smartlist_t *BWHistoryWriteValues;
smartlist_t *BWHistoryWriteMaxima;
time_t BWHistoryDirReadEnds; time_t BWHistoryDirReadEnds;
int BWHistoryDirReadInterval; int BWHistoryDirReadInterval;
smartlist_t *BWHistoryDirReadValues; smartlist_t *BWHistoryDirReadValues;
smartlist_t *BWHistoryDirReadMaxima;
time_t BWHistoryDirWriteEnds; time_t BWHistoryDirWriteEnds;
int BWHistoryDirWriteInterval; int BWHistoryDirWriteInterval;
smartlist_t *BWHistoryDirWriteValues; smartlist_t *BWHistoryDirWriteValues;
smartlist_t *BWHistoryDirWriteMaxima;
/** Build time histogram */ /** Build time histogram */
config_line_t * BuildtimeHistogram; config_line_t * BuildtimeHistogram;

View File

@ -1261,8 +1261,12 @@ add_obs(bw_array_t *b, time_t when, uint64_t n)
/* If we're currently adding observations for an earlier second than /* If we're currently adding observations for an earlier second than
* 'when', advance b->cur_obs_time and b->cur_obs_idx by an * 'when', advance b->cur_obs_time and b->cur_obs_idx by an
* appropriate number of seconds, and do all the other housekeeping */ * appropriate number of seconds, and do all the other housekeeping */
while (when>b->cur_obs_time) while (when>b->cur_obs_time) {
/* Doing this one second at a time is potentially inefficient, if we start
with a state file that is very old. Fortunately, it doesn't seem to
show up in profiles, so we can just ignore it for now. */
advance_obs(b); advance_obs(b);
}
b->obs[b->cur_obs_idx] += n; b->obs[b->cur_obs_idx] += n;
b->total_in_period += n; b->total_in_period += n;
@ -1497,15 +1501,21 @@ static void
rep_hist_update_bwhist_state_section(or_state_t *state, rep_hist_update_bwhist_state_section(or_state_t *state,
const bw_array_t *b, const bw_array_t *b,
smartlist_t **s_values, smartlist_t **s_values,
smartlist_t **s_maxima,
time_t *s_begins, time_t *s_begins,
int *s_interval) int *s_interval)
{ {
char buf[20*NUM_TOTALS + 1], *cp; char *cp;
int i,j;
if (*s_values) { if (*s_values) {
SMARTLIST_FOREACH(*s_values, char *, val, tor_free(val)); SMARTLIST_FOREACH(*s_values, char *, val, tor_free(val));
smartlist_free(*s_values); smartlist_free(*s_values);
} }
if (*s_maxima) {
SMARTLIST_FOREACH(*s_values, char *, val, tor_free(val));
smartlist_free(*s_maxima);
}
if (! server_mode(get_options())) { if (! server_mode(get_options())) {
/* Clients don't need to store bandwidth history persistently; /* Clients don't need to store bandwidth history persistently;
* force these values to the defaults. */ * force these values to the defaults. */
@ -1519,18 +1529,30 @@ rep_hist_update_bwhist_state_section(or_state_t *state,
*s_begins = 0; *s_begins = 0;
*s_interval = 900; *s_interval = 900;
*s_values = smartlist_create(); *s_values = smartlist_create();
*s_maxima = smartlist_create();
return; return;
} }
*s_begins = b->next_period; *s_begins = b->next_period;
*s_interval = NUM_SECS_BW_SUM_INTERVAL; *s_interval = NUM_SECS_BW_SUM_INTERVAL;
cp = buf;
cp += rep_hist_fill_bandwidth_history(cp, sizeof(buf), b);
tor_snprintf(cp, sizeof(buf)-(cp-buf),
cp == buf ? U64_FORMAT : ","U64_FORMAT,
U64_PRINTF_ARG(b->total_in_period));
*s_values = smartlist_create(); *s_values = smartlist_create();
if (server_mode(get_options())) *s_maxima = smartlist_create();
smartlist_split_string(*s_values, buf, ",", SPLIT_SKIP_SPACE, 0); /* Set i to first position in circular array */
i = (b->num_maxes_set <= b->next_max_idx) ? 0 : b->next_max_idx;
for (j=0; j < b->num_maxes_set; ++j,++i) {
uint64_t maxval;
if (i > NUM_TOTALS)
i = 0;
tor_asprintf(&cp, U64_FORMAT, U64_PRINTF_ARG(b->totals[i] & ~0x3ff));
smartlist_add(*s_values, cp);
maxval = b->maxima[i] / NUM_SECS_ROLLING_MEASURE;
tor_asprintf(&cp, U64_FORMAT, U64_PRINTF_ARG(maxval & ~0x3ff));
smartlist_add(*s_maxima, cp);
}
tor_asprintf(&cp, U64_FORMAT, U64_PRINTF_ARG(b->total_in_period & ~0x3ff));
smartlist_add(*s_values, cp);
tor_asprintf(&cp, U64_FORMAT, U64_PRINTF_ARG(b->max_total & ~0x3ff));
smartlist_add(*s_maxima, cp);
} }
/** Update <b>state</b> with the newest bandwidth history. */ /** Update <b>state</b> with the newest bandwidth history. */
@ -1541,6 +1563,7 @@ rep_hist_update_state(or_state_t *state)
rep_hist_update_bwhist_state_section(state,\ rep_hist_update_bwhist_state_section(state,\
(arrname),\ (arrname),\
&state->BWHistory ## st ## Values, \ &state->BWHistory ## st ## Values, \
&state->BWHistory ## st ## Maxima, \
&state->BWHistory ## st ## Ends, \ &state->BWHistory ## st ## Ends, \
&state->BWHistory ## st ## Interval) &state->BWHistory ## st ## Interval)
@ -1560,6 +1583,7 @@ rep_hist_update_state(or_state_t *state)
static int static int
rep_hist_load_bwhist_state_section(bw_array_t *b, rep_hist_load_bwhist_state_section(bw_array_t *b,
const smartlist_t *s_values, const smartlist_t *s_values,
const smartlist_t *s_maxima,
const time_t s_begins, const time_t s_begins,
const int s_interval) const int s_interval)
{ {
@ -1567,8 +1591,9 @@ rep_hist_load_bwhist_state_section(bw_array_t *b,
int retval = 0; int retval = 0;
time_t start; time_t start;
uint64_t v; uint64_t v, mv;
int i,ok; int i,ok,ok_m;
int have_maxima = (smartlist_len(s_values) == smartlist_len(s_maxima));
if (s_values && s_begins >= now - NUM_SECS_BW_SUM_INTERVAL*NUM_TOTALS) { if (s_values && s_begins >= now - NUM_SECS_BW_SUM_INTERVAL*NUM_TOTALS) {
start = s_begins - s_interval*(smartlist_len(s_values)); start = s_begins - s_interval*(smartlist_len(s_values));
@ -1578,12 +1603,22 @@ rep_hist_load_bwhist_state_section(bw_array_t *b,
b->next_period = start + NUM_SECS_BW_SUM_INTERVAL; b->next_period = start + NUM_SECS_BW_SUM_INTERVAL;
SMARTLIST_FOREACH_BEGIN(s_values, const char *, cp) { SMARTLIST_FOREACH_BEGIN(s_values, const char *, cp) {
v = tor_parse_uint64(cp, 10, 0, UINT64_MAX, &ok, NULL); v = tor_parse_uint64(cp, 10, 0, UINT64_MAX, &ok, NULL);
if (!ok) { if (have_maxima) {
const char *maxstr = smartlist_get(s_maxima, cp_sl_idx);
mv = tor_parse_uint64(maxstr, 10, 0, UINT64_MAX, &ok_m, NULL);
mv *= NUM_SECS_ROLLING_MEASURE;
} else {
/* No maxima known; guess average rate to be conservative. */
mv = v / s_interval;
}
if (!ok || !ok_m) {
retval = -1; retval = -1;
log_notice(LD_HIST, "Could not parse '%s' into a number.'", cp); log_notice(LD_HIST, "Could not parse '%s' into a number.'", cp);
} }
if (start < now) { if (start < now) {
add_obs(b, start, v); add_obs(b, start, v);
b->max_total = mv;
/* This will result in some fairly choppy history if s_interval /* This will result in some fairly choppy history if s_interval
* is notthe same as NUM_SECS_BW_SUM_INTERVAL. XXXX */ * is notthe same as NUM_SECS_BW_SUM_INTERVAL. XXXX */
start += s_interval; start += s_interval;
@ -1592,15 +1627,10 @@ rep_hist_load_bwhist_state_section(bw_array_t *b,
} }
/* Clean up maxima and observed */ /* Clean up maxima and observed */
/* Do we really want to zero this for the purpose of max capacity? */
for (i=0; i<NUM_SECS_ROLLING_MEASURE; ++i) { for (i=0; i<NUM_SECS_ROLLING_MEASURE; ++i) {
b->obs[i] = 0; b->obs[i] = 0;
} }
b->total_obs = 0; b->total_obs = 0;
for (i=0; i<NUM_TOTALS; ++i) {
b->maxima[i] = 0;
}
b->max_total = 0;
return retval; return retval;
} }
@ -1619,6 +1649,7 @@ rep_hist_load_state(or_state_t *state, char **err)
if (rep_hist_load_bwhist_state_section( \ if (rep_hist_load_bwhist_state_section( \
(arrname), \ (arrname), \
state->BWHistory ## st ## Values, \ state->BWHistory ## st ## Values, \
state->BWHistory ## st ## Maxima, \
state->BWHistory ## st ## Ends, \ state->BWHistory ## st ## Ends, \
state->BWHistory ## st ## Interval)<0) \ state->BWHistory ## st ## Interval)<0) \
all_ok = 0 all_ok = 0