diff --git a/ChangeLog b/ChangeLog index c6d2d3c199..e84637cc7c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,9 @@ Changes in version 0.1.2.5-xxxx - 200?-??-?? - Start using the state file to store bandwidth accounting data: the bw_accounting file is now obsolete. We'll keep generating it for a while for people who are still using 0.1.2.4-alpha. + - Try to batch changes to the state so that we do as few disk writes + as possible while still storing important things in a timely + fashion. o Minor bugfixes; - Fix a bug when a PF socket is first used. (Patch from Fabian diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index 29a762e887..958894d724 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -2376,13 +2376,16 @@ entry_guards_parse_state(or_state_t *state, int set, char **msg) } /** Our list of entry guards has changed, or some element of one - * of our entry guards has changed. Write the changes to disk. */ + * of our entry guards has changed. Write the changes to disk within + * the next 5 minutes. + */ static void entry_guards_changed(void) { entry_guards_dirty = 1; - or_state_save(); + /* or_state_save() will call entry_guards_update_state(). */ + or_state_mark_dirty(get_or_state(), time(NULL)+600); } /** If the entry guard info has not changed, do nothing and return. @@ -2433,7 +2436,7 @@ entry_guards_update_state(or_state_t *state) next = &(line->next); } }); - state->dirty = 1; + or_state_mark_dirty(get_or_state(), 0); entry_guards_dirty = 0; } diff --git a/src/or/config.c b/src/or/config.c index 9beb10a6c2..5da79db38e 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -3934,8 +3934,8 @@ or_state_load(void) or_state_set(new_state); new_state = NULL; if (!contents) { - global_state->dirty = 1; - or_state_save(); + global_state->next_write = 0; + or_state_save(time(NULL)); } r = 0; @@ -3950,7 +3950,7 @@ or_state_load(void) /** Write the persistent state to disk. Return 0 for success, <0 on failure. */ int -or_state_save(void) +or_state_save(time_t now) { char *state, *contents; char tbuf[ISO_TIME_LEN+1]; @@ -3959,11 +3959,14 @@ or_state_save(void) tor_assert(global_state); + if (global_state->next_write > now) + return 0; + + /* Call everything else that might dirty the state even more, in order + * to avoid redundant writes. */ entry_guards_update_state(global_state); rep_hist_update_state(global_state); - - if (!global_state->dirty) - return 0; + accounting_run_housekeeping(now); global_state->LastWritten = time(NULL); tor_free(global_state->TorVersion); @@ -3988,7 +3991,7 @@ or_state_save(void) tor_free(fname); tor_free(contents); - global_state->dirty = 0; + global_state->next_write = TIME_MAX; return 0; } diff --git a/src/or/hibernate.c b/src/or/hibernate.c index 79c51c7184..bd89240e47 100644 --- a/src/or/hibernate.c +++ b/src/or/hibernate.c @@ -391,8 +391,8 @@ reset_accounting(time_t now) static INLINE int time_to_record_bandwidth_usage(time_t now) { - /* Note every 60 sec */ -#define NOTE_INTERVAL (60) + /* Note every 600 sec */ +#define NOTE_INTERVAL (600) /* Or every 20 megabytes */ #define NOTE_BYTES 20*(1024*1024) static uint64_t last_read_bytes_noted = 0; @@ -575,7 +575,7 @@ accounting_record_bandwidth_usage(time_t now, or_state_t *state) state->AccountingBytesWrittenInInterval = n_bytes_written_in_interval; state->AccountingSecondsActive = n_seconds_active_in_interval; state->AccountingExpectedUsage = expected_bandwidth_usage; - state->dirty = 1; + or_state_mark_dirty(state, 60); return r; } @@ -760,6 +760,7 @@ hibernate_begin(int new_state, time_t now) hibernate_state = new_state; accounting_record_bandwidth_usage(now, get_or_state()); + or_state_mark_dirty(get_or_state(), 0); } /** Called when we've been hibernating and our timeout is reached. */ @@ -827,6 +828,7 @@ hibernate_go_dormant(time_t now) } accounting_record_bandwidth_usage(now, get_or_state()); + or_state_mark_dirty(get_or_state(), 0); } /** Called when hibernate_end_time has arrived. */ diff --git a/src/or/main.c b/src/or/main.c index bfed55b2fe..163cf188da 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -830,9 +830,6 @@ run_scheduled_events(time_t now) * and the rend cache. */ rep_history_clean(now - options->RephistTrackTime); rend_cache_clean(); - /* And while we are at it, save the state with bandwidth history - * and more. */ - or_state_save(); } /* 2b. Once per minute, regenerate and upload the descriptor if the old @@ -935,6 +932,10 @@ run_scheduled_events(time_t now) */ close_closeable_connections(); + /** 8b. And if anything in our state is ready to get flushed to disk, we + * flush it. */ + or_state_save(now); + /** 9. and if we're a server, check whether our DNS is telling stories to * us. */ if (server_mode(options) && time_to_check_for_wildcarded_dns < now) { @@ -1595,7 +1596,8 @@ tor_cleanup(void) unlink(options->PidFile); if (accounting_is_enabled(options)) accounting_record_bandwidth_usage(time(NULL), get_or_state()); - or_state_save(); + or_state_mark_dirty(get_or_state(), 0); /* force an immediate save. */ + or_state_save(time(NULL)); } tor_free_all(0); /* move tor_free_all back into the ifdef below later. XXX*/ crypto_global_cleanup(); diff --git a/src/or/or.h b/src/or/or.h index 9f25dfd59a..053c5a499d 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1655,19 +1655,30 @@ typedef struct { /** Persistent state for an onion router, as saved to disk. */ typedef struct { uint32_t _magic; - /** True iff this state has been changed since it was last read/written - * to the disk. */ - int dirty; + /** The time at which we next plan to write the state to the disk. Equal to + * TIME_MAX if there are no saveable changes, 0 if there are changes that + * should be saved right away. */ + time_t next_write; + /** When was the state last written to disk? */ time_t LastWritten; + + /** Fields for */ time_t AccountingIntervalStart; uint64_t AccountingBytesReadInInterval; uint64_t AccountingBytesWrittenInInterval; int AccountingSecondsActive; uint64_t AccountingExpectedUsage; + /** A list of Entry Guard-related configuration lines. */ config_line_t *EntryGuards; + /** These fields hold information on the history of bandwidth usage for + * servers. The "Ends" fields hold the time when we last updated the + * bandwidth usage. The "Interval" fields hold the granularity, in seconds, + * of the entries of Values. The "Values" lists hold decimal string + * representations of the number of bytes read or written in each + * interval. */ time_t BWHistoryReadEnds; int BWHistoryReadInterval; smartlist_t *BWHistoryReadValues; @@ -1675,11 +1686,24 @@ typedef struct { int BWHistoryWriteInterval; smartlist_t *BWHistoryWriteValues; + /** What version of Tor write this state file? */ char *TorVersion; + /** holds any unrecognized values we found in the state file, in the order + * in which we found them. */ config_line_t *ExtraLines; } or_state_t; +static void or_state_mark_dirty(or_state_t *state, time_t when); +/** Change the next_write time of state to when, unless the + * state is already scheduled to be written to disk earlier than when. + */ +static INLINE void or_state_mark_dirty(or_state_t *state, time_t when) +{ + if (state->next_write > when) + state->next_write = when; +} + #define MAX_SOCKS_REPLY_LEN 1024 #define MAX_SOCKS_ADDR_LEN 256 #define SOCKS_COMMAND_CONNECT 0x01 @@ -1897,7 +1921,7 @@ const char *get_torrc_fname(void); or_state_t *get_or_state(void); int or_state_load(void); -int or_state_save(void); +int or_state_save(time_t now); int config_getinfo_helper(const char *question, char **answer); diff --git a/src/or/rephist.c b/src/or/rephist.c index 20b76679bd..af295facf3 100644 --- a/src/or/rephist.c +++ b/src/or/rephist.c @@ -666,6 +666,8 @@ rep_hist_update_state(or_state_t *state) if (! server_mode(get_options())) { /* Clients don't need to store bandwidth history persistently; * force these values to the defaults. */ + if (*s_begins != 0 || *s_interval != 900) + or_state_mark_dirty(get_or_state(), time(NULL)+600); *s_begins = 0; *s_interval = 900; *s_values = smartlist_create(); @@ -682,7 +684,8 @@ rep_hist_update_state(or_state_t *state) smartlist_split_string(*s_values, buf, ",", SPLIT_SKIP_SPACE, 0); } tor_free(buf); - state->dirty = 1; + if (server_mode(get_options())) + or_state_mark_dirty(get_or_state(), time(NULL)+(2*3600)); } /** Set bandwidth history from our saved state. */