)]}'
{"doc/source/deployment_guide.rst":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"32ffbf9f8172e6343c8d3ea7cbc836dc733e3aa3","unresolved":false,"context_lines":[{"line_number":910,"context_line":"slowdown            0.01                Time in seconds to wait between objects."},{"line_number":911,"context_line":"                                        Deprecated in favor of objects_per_second."},{"line_number":912,"context_line":"report_interval     300                 Interval in seconds between logging"},{"line_number":913,"context_line":"                                        statistics about the current update pass."},{"line_number":914,"context_line":"recon_cache_path    /var/cache/swift    Path to recon cache"},{"line_number":915,"context_line":"nice_priority       None                Scheduling priority of server processes."},{"line_number":916,"context_line":"                                        Niceness values range from -20 (most"}],"source_content_type":"text/x-rst","patch_set":3,"id":"7f96bb07_26d6d329","line":913,"updated":"2018-01-17 22:06:39.000000000","message":"whoot!","commit_id":"f64c00b00aa8df31a937448917421891904abdc8"}],"etc/object-server.conf-sample":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"7c752712ca7b737de119c5b706dd466aaee7df69","unresolved":false,"context_lines":[{"line_number":376,"context_line":"# logging is per-process, so with concurrency \u003e 1, the logs will"},{"line_number":377,"context_line":"# contain one stats log per worker process every report_interval"},{"line_number":378,"context_line":"# seconds."},{"line_number":379,"context_line":"# report_interval \u003d 300"},{"line_number":380,"context_line":"#"},{"line_number":381,"context_line":"# recon_cache_path \u003d /var/cache/swift"},{"line_number":382,"context_line":"#"}],"source_content_type":"application/octet-stream","patch_set":2,"id":"7f96bb07_c718701f","line":379,"updated":"2018-01-17 01:12:16.000000000","message":"this is in line with the expirer, but I personally mentally map this configuration option to the replicator/reconstructor\u0027s \"stats_interval\"","commit_id":"f41f8205e5b70d379fceed35336e119c3070c288"},{"author":{"_account_id":2622,"name":"Samuel Merritt","email":"spam+launchpad@andcheese.org","username":"torgomatic"},"change_message_id":"7d5c428750c9b33ee1777c5073dc2534e44666e9","unresolved":false,"context_lines":[{"line_number":376,"context_line":"# logging is per-process, so with concurrency \u003e 1, the logs will"},{"line_number":377,"context_line":"# contain one stats log per worker process every report_interval"},{"line_number":378,"context_line":"# seconds."},{"line_number":379,"context_line":"# report_interval \u003d 300"},{"line_number":380,"context_line":"#"},{"line_number":381,"context_line":"# recon_cache_path \u003d /var/cache/swift"},{"line_number":382,"context_line":"#"}],"source_content_type":"application/octet-stream","patch_set":2,"id":"7f96bb07_591be87f","line":379,"in_reply_to":"7f96bb07_c718701f","updated":"2018-01-17 01:28:25.000000000","message":"at least Tim didn\u0027t let me get away with \"stats_log_interval\"; then we\u0027d have three ways of spelling the same thing","commit_id":"f41f8205e5b70d379fceed35336e119c3070c288"}],"swift/obj/updater.py":[{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"4ac8794d628b5760ff0b155cf43c0d34ff80b4ed","unresolved":false,"context_lines":[{"line_number":52,"context_line":"        return type(self)(self.errors, self.failures, self.quarantines,"},{"line_number":53,"context_line":"                          self.successes, self.unlinks)"},{"line_number":54,"context_line":""},{"line_number":55,"context_line":"    def since(self, other):"},{"line_number":56,"context_line":"        return type(self)(self.errors - other.errors,"},{"line_number":57,"context_line":"                          self.failures - other.failures,"},{"line_number":58,"context_line":"                          self.quarantines - other.quarantines,"}],"source_content_type":"text/x-python","patch_set":1,"id":"7f96bb07_e3282b87","line":55,"range":{"start_line":55,"start_character":8,"end_line":55,"end_character":13},"updated":"2018-01-16 00:36:25.000000000","message":"This makes me wonder if it\u0027d be useful to have a timestamp on these...\n\nMaybe with it set to None for since() results?","commit_id":"bc5b910dd624c922aed3e4e65f9d287d688514a0"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"4ac8794d628b5760ff0b155cf43c0d34ff80b4ed","unresolved":false,"context_lines":[{"line_number":94,"context_line":"                           objects_per_second))"},{"line_number":95,"context_line":"        self.node_timeout \u003d float(conf.get(\u0027node_timeout\u0027, 10))"},{"line_number":96,"context_line":"        self.conn_timeout \u003d float(conf.get(\u0027conn_timeout\u0027, 0.5))"},{"line_number":97,"context_line":"        self.stats_log_interval \u003d float(conf.get(\u0027stats_log_interval\u0027, 300))"},{"line_number":98,"context_line":"        self.recon_cache_path \u003d conf.get(\u0027recon_cache_path\u0027,"},{"line_number":99,"context_line":"                                         \u0027/var/cache/swift\u0027)"},{"line_number":100,"context_line":"        self.rcache \u003d os.path.join(self.recon_cache_path, \u0027object.recon\u0027)"}],"source_content_type":"text/x-python","patch_set":1,"id":"7f96bb07_03a517b5","line":97,"range":{"start_line":97,"start_character":50,"end_line":97,"end_character":68},"updated":"2018-01-16 00:36:25.000000000","message":"replicator/reconstructor just use \u0027stats_interval\u0027\n\nexpirer seems to have \u0027report_interval\u0027\n\nauditor has... \u0027log_time\u0027? Is that roughly equivalent?\n\nWhatever we use, we oughta document it in etc/object-server.conf-sample at least.","commit_id":"bc5b910dd624c922aed3e4e65f9d287d688514a0"},{"author":{"_account_id":2622,"name":"Samuel Merritt","email":"spam+launchpad@andcheese.org","username":"torgomatic"},"change_message_id":"1a6e3be86bc9b0e409382d902797587ea0a3e306","unresolved":false,"context_lines":[{"line_number":94,"context_line":"                           objects_per_second))"},{"line_number":95,"context_line":"        self.node_timeout \u003d float(conf.get(\u0027node_timeout\u0027, 10))"},{"line_number":96,"context_line":"        self.conn_timeout \u003d float(conf.get(\u0027conn_timeout\u0027, 0.5))"},{"line_number":97,"context_line":"        self.stats_log_interval \u003d float(conf.get(\u0027stats_log_interval\u0027, 300))"},{"line_number":98,"context_line":"        self.recon_cache_path \u003d conf.get(\u0027recon_cache_path\u0027,"},{"line_number":99,"context_line":"                                         \u0027/var/cache/swift\u0027)"},{"line_number":100,"context_line":"        self.rcache \u003d os.path.join(self.recon_cache_path, \u0027object.recon\u0027)"}],"source_content_type":"text/x-python","patch_set":1,"id":"7f96bb07_be5d5cea","line":97,"range":{"start_line":97,"start_character":50,"end_line":97,"end_character":68},"in_reply_to":"7f96bb07_03a517b5","updated":"2018-01-16 02:01:55.000000000","message":"I like \u0027report_interval\u0027. I\u0027ll use that.","commit_id":"bc5b910dd624c922aed3e4e65f9d287d688514a0"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"4ac8794d628b5760ff0b155cf43c0d34ff80b4ed","unresolved":false,"context_lines":[{"line_number":104,"context_line":"        try:"},{"line_number":105,"context_line":"            return os.listdir(path)"},{"line_number":106,"context_line":"        except OSError as e:"},{"line_number":107,"context_line":"            self.logger.error(_(\u0027ERROR: Unable to access %(path)s: \u0027"},{"line_number":108,"context_line":"                                \u0027%(error)s\u0027) %"},{"line_number":109,"context_line":"                              {\u0027path\u0027: path, \u0027error\u0027: e})"},{"line_number":110,"context_line":"            return []"}],"source_content_type":"text/x-python","patch_set":1,"id":"7f96bb07_23f4d385","line":107,"updated":"2018-01-16 00:36:25.000000000","message":"I wonder, should this bump error count?","commit_id":"bc5b910dd624c922aed3e4e65f9d287d688514a0"},{"author":{"_account_id":2622,"name":"Samuel Merritt","email":"spam+launchpad@andcheese.org","username":"torgomatic"},"change_message_id":"1a6e3be86bc9b0e409382d902797587ea0a3e306","unresolved":false,"context_lines":[{"line_number":104,"context_line":"        try:"},{"line_number":105,"context_line":"            return os.listdir(path)"},{"line_number":106,"context_line":"        except OSError as e:"},{"line_number":107,"context_line":"            self.logger.error(_(\u0027ERROR: Unable to access %(path)s: \u0027"},{"line_number":108,"context_line":"                                \u0027%(error)s\u0027) %"},{"line_number":109,"context_line":"                              {\u0027path\u0027: path, \u0027error\u0027: e})"},{"line_number":110,"context_line":"            return []"}],"source_content_type":"text/x-python","patch_set":1,"id":"7f96bb07_9edd0044","line":107,"in_reply_to":"7f96bb07_23f4d385","updated":"2018-01-16 02:01:55.000000000","message":"I\u0027m not really sure. I went after the spots with StatsD metrics. It\u0027s probably a good idea to include this one, though.","commit_id":"bc5b910dd624c922aed3e4e65f9d287d688514a0"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"4ac8794d628b5760ff0b155cf43c0d34ff80b4ed","unresolved":false,"context_lines":[{"line_number":161,"context_line":"            elapsed \u003d time.time() - begin"},{"line_number":162,"context_line":"            self.logger.info(_(\u0027Object update sweep completed: %.02fs\u0027),"},{"line_number":163,"context_line":"                             elapsed)"},{"line_number":164,"context_line":"            dump_recon_cache({\u0027object_updater_sweep\u0027: elapsed},"},{"line_number":165,"context_line":"                             self.rcache, self.logger)"},{"line_number":166,"context_line":"            if elapsed \u003c self.interval:"},{"line_number":167,"context_line":"                time.sleep(self.interval - elapsed)"}],"source_content_type":"text/x-python","patch_set":1,"id":"7f96bb07_a3b6a3f9","line":164,"updated":"2018-01-16 00:36:25.000000000","message":"Maybe add a\n\n object_updater_stats\n\nkey?","commit_id":"bc5b910dd624c922aed3e4e65f9d287d688514a0"},{"author":{"_account_id":2622,"name":"Samuel Merritt","email":"spam+launchpad@andcheese.org","username":"torgomatic"},"change_message_id":"1a6e3be86bc9b0e409382d902797587ea0a3e306","unresolved":false,"context_lines":[{"line_number":161,"context_line":"            elapsed \u003d time.time() - begin"},{"line_number":162,"context_line":"            self.logger.info(_(\u0027Object update sweep completed: %.02fs\u0027),"},{"line_number":163,"context_line":"                             elapsed)"},{"line_number":164,"context_line":"            dump_recon_cache({\u0027object_updater_sweep\u0027: elapsed},"},{"line_number":165,"context_line":"                             self.rcache, self.logger)"},{"line_number":166,"context_line":"            if elapsed \u003c self.interval:"},{"line_number":167,"context_line":"                time.sleep(self.interval - elapsed)"}],"source_content_type":"text/x-python","patch_set":1,"id":"7f96bb07_1eca506c","line":164,"in_reply_to":"7f96bb07_a3b6a3f9","updated":"2018-01-16 02:01:55.000000000","message":"That gets tricky. All the stats tracking we do happens in child processes, so we\u0027d need a way to plumb that data back up to the parent and then combine our stats.","commit_id":"bc5b910dd624c922aed3e4e65f9d287d688514a0"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"4ac8794d628b5760ff0b155cf43c0d34ff80b4ed","unresolved":false,"context_lines":[{"line_number":192,"context_line":"             \u0027quarantines\u0027: self.stats.quarantines,"},{"line_number":193,"context_line":"             \u0027unlinks\u0027: self.stats.unlinks,"},{"line_number":194,"context_line":"             \u0027errors\u0027: self.stats.errors})"},{"line_number":195,"context_line":"        dump_recon_cache({\u0027object_updater_sweep\u0027: elapsed},"},{"line_number":196,"context_line":"                         self.rcache, self.logger)"},{"line_number":197,"context_line":""},{"line_number":198,"context_line":"    def object_sweep(self, device):"}],"source_content_type":"text/x-python","patch_set":1,"id":"7f96bb07_c3b96fe9","line":195,"updated":"2018-01-16 00:36:25.000000000","message":"...and here...","commit_id":"bc5b910dd624c922aed3e4e65f9d287d688514a0"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"4ac8794d628b5760ff0b155cf43c0d34ff80b4ed","unresolved":false,"context_lines":[{"line_number":203,"context_line":"        \"\"\""},{"line_number":204,"context_line":"        start_time \u003d time.time()"},{"line_number":205,"context_line":"        last_status_update \u003d start_time"},{"line_number":206,"context_line":"        start_stats \u003d self.stats.copy()"},{"line_number":207,"context_line":"        my_pid \u003d os.getpid()"},{"line_number":208,"context_line":"        self.logger.info(\"Object update sweep starting on %s (pid: %d)\","},{"line_number":209,"context_line":"                         device, my_pid)"}],"source_content_type":"text/x-python","patch_set":1,"id":"7f96bb07_039f57b4","line":206,"updated":"2018-01-16 00:36:25.000000000","message":"OK, so this will always be empty in the daemon case, but will change with each pass in run_once.","commit_id":"bc5b910dd624c922aed3e4e65f9d287d688514a0"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"4ac8794d628b5760ff0b155cf43c0d34ff80b4ed","unresolved":false,"context_lines":[{"line_number":220,"context_line":"            try:"},{"line_number":221,"context_line":"                base, policy \u003d split_policy_string(asyncdir)"},{"line_number":222,"context_line":"            except PolicyError as e:"},{"line_number":223,"context_line":"                self.logger.warning(_(\u0027Directory %(directory)r does not map \u0027"},{"line_number":224,"context_line":"                                      \u0027to a valid policy (%(error)s)\u0027) % {"},{"line_number":225,"context_line":"                                    \u0027directory\u0027: asyncdir, \u0027error\u0027: e})"},{"line_number":226,"context_line":"                continue"}],"source_content_type":"text/x-python","patch_set":1,"id":"7f96bb07_0318b7d0","line":223,"updated":"2018-01-16 00:36:25.000000000","message":"Bump errors?","commit_id":"bc5b910dd624c922aed3e4e65f9d287d688514a0"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"4ac8794d628b5760ff0b155cf43c0d34ff80b4ed","unresolved":false,"context_lines":[{"line_number":236,"context_line":"                    try:"},{"line_number":237,"context_line":"                        obj_hash, timestamp \u003d update.split(\u0027-\u0027)"},{"line_number":238,"context_line":"                    except ValueError:"},{"line_number":239,"context_line":"                        self.stats.errors +\u003d 1"},{"line_number":240,"context_line":"                        self.logger.increment(\u0027errors\u0027)"},{"line_number":241,"context_line":"                        self.logger.error("},{"line_number":242,"context_line":"                            _(\u0027ERROR async pending file with unexpected \u0027"}],"source_content_type":"text/x-python","patch_set":1,"id":"7f96bb07_c3816f4c","line":239,"updated":"2018-01-16 00:36:25.000000000","message":"I\u0027m not sure it\u0027s a good idea to conflate the counting for \"found an update and couldn\u0027t split it\" with \"found an unmounted drive which, when mounted, will have an unknown number of updates\"","commit_id":"bc5b910dd624c922aed3e4e65f9d287d688514a0"},{"author":{"_account_id":2622,"name":"Samuel Merritt","email":"spam+launchpad@andcheese.org","username":"torgomatic"},"change_message_id":"1a6e3be86bc9b0e409382d902797587ea0a3e306","unresolved":false,"context_lines":[{"line_number":236,"context_line":"                    try:"},{"line_number":237,"context_line":"                        obj_hash, timestamp \u003d update.split(\u0027-\u0027)"},{"line_number":238,"context_line":"                    except ValueError:"},{"line_number":239,"context_line":"                        self.stats.errors +\u003d 1"},{"line_number":240,"context_line":"                        self.logger.increment(\u0027errors\u0027)"},{"line_number":241,"context_line":"                        self.logger.error("},{"line_number":242,"context_line":"                            _(\u0027ERROR async pending file with unexpected \u0027"}],"source_content_type":"text/x-python","patch_set":1,"id":"7f96bb07_793a8e34","line":239,"in_reply_to":"7f96bb07_c3816f4c","updated":"2018-01-16 02:01:55.000000000","message":"Yeah, we\u0027ve kind of got that going all over the place here. Unmounted drives, directories not mapping to valid policies, any sort of OSError in _listdir (if I make the change you suggested above) including both permission problems and not-a-directory, and unparseable filename.\n\nI haven\u0027t really got a good plan here. I\u0027m open to suggestions.","commit_id":"bc5b910dd624c922aed3e4e65f9d287d688514a0"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"4ac8794d628b5760ff0b155cf43c0d34ff80b4ed","unresolved":false,"context_lines":[{"line_number":272,"context_line":"                             \u0027failures\u0027: this_sweep.failures,"},{"line_number":273,"context_line":"                             \u0027quarantines\u0027: this_sweep.quarantines,"},{"line_number":274,"context_line":"                             \u0027unlinks\u0027: this_sweep.unlinks,"},{"line_number":275,"context_line":"                             \u0027errors\u0027: this_sweep.errors})"},{"line_number":276,"context_line":"                        last_status_update \u003d now"},{"line_number":277,"context_line":"                try:"},{"line_number":278,"context_line":"                    os.rmdir(prefix_path)"}],"source_content_type":"text/x-python","patch_set":1,"id":"7f96bb07_03d67750","line":275,"updated":"2018-01-16 00:36:25.000000000","message":"Should this also be going to recon cache or something? Or do we want to hold off on those updates until a cycle completes?","commit_id":"bc5b910dd624c922aed3e4e65f9d287d688514a0"},{"author":{"_account_id":2622,"name":"Samuel Merritt","email":"spam+launchpad@andcheese.org","username":"torgomatic"},"change_message_id":"1a6e3be86bc9b0e409382d902797587ea0a3e306","unresolved":false,"context_lines":[{"line_number":272,"context_line":"                             \u0027failures\u0027: this_sweep.failures,"},{"line_number":273,"context_line":"                             \u0027quarantines\u0027: this_sweep.quarantines,"},{"line_number":274,"context_line":"                             \u0027unlinks\u0027: this_sweep.unlinks,"},{"line_number":275,"context_line":"                             \u0027errors\u0027: this_sweep.errors})"},{"line_number":276,"context_line":"                        last_status_update \u003d now"},{"line_number":277,"context_line":"                try:"},{"line_number":278,"context_line":"                    os.rmdir(prefix_path)"}],"source_content_type":"text/x-python","patch_set":1,"id":"7f96bb07_391de6bc","line":275,"in_reply_to":"7f96bb07_03d67750","updated":"2018-01-16 02:01:55.000000000","message":"Maybe. It might be worth looking at how the auditor does things with respect to recon stats. I think that\u0027s the only other daemon that both forks and dumps incremental stats into recon.","commit_id":"bc5b910dd624c922aed3e4e65f9d287d688514a0"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"4ac8794d628b5760ff0b155cf43c0d34ff80b4ed","unresolved":false,"context_lines":[{"line_number":341,"context_line":"            self.logger.increment(\u0027successes\u0027)"},{"line_number":342,"context_line":"            self.logger.debug(\u0027Update sent for %(obj)s %(path)s\u0027,"},{"line_number":343,"context_line":"                              {\u0027obj\u0027: obj, \u0027path\u0027: update_path})"},{"line_number":344,"context_line":"            self.stats.unlinks +\u003d 1"},{"line_number":345,"context_line":"            self.logger.increment(\u0027unlinks\u0027)"},{"line_number":346,"context_line":"            os.unlink(update_path)"},{"line_number":347,"context_line":"        else:"}],"source_content_type":"text/x-python","patch_set":1,"id":"7f96bb07_2355b3c6","line":344,"updated":"2018-01-16 00:36:25.000000000","message":"So unlinks \u003e\u003d successes, all the time?","commit_id":"bc5b910dd624c922aed3e4e65f9d287d688514a0"},{"author":{"_account_id":2622,"name":"Samuel Merritt","email":"spam+launchpad@andcheese.org","username":"torgomatic"},"change_message_id":"1a6e3be86bc9b0e409382d902797587ea0a3e306","unresolved":false,"context_lines":[{"line_number":341,"context_line":"            self.logger.increment(\u0027successes\u0027)"},{"line_number":342,"context_line":"            self.logger.debug(\u0027Update sent for %(obj)s %(path)s\u0027,"},{"line_number":343,"context_line":"                              {\u0027obj\u0027: obj, \u0027path\u0027: update_path})"},{"line_number":344,"context_line":"            self.stats.unlinks +\u003d 1"},{"line_number":345,"context_line":"            self.logger.increment(\u0027unlinks\u0027)"},{"line_number":346,"context_line":"            os.unlink(update_path)"},{"line_number":347,"context_line":"        else:"}],"source_content_type":"text/x-python","patch_set":1,"id":"7f96bb07_d962c23e","line":344,"in_reply_to":"7f96bb07_2355b3c6","updated":"2018-01-16 02:01:55.000000000","message":"Yep. (unlinks - successes) can give you an idea of how many obsolete async_pendings your system is throwing away.","commit_id":"bc5b910dd624c922aed3e4e65f9d287d688514a0"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"4ac8794d628b5760ff0b155cf43c0d34ff80b4ed","unresolved":false,"context_lines":[{"line_number":346,"context_line":"            os.unlink(update_path)"},{"line_number":347,"context_line":"        else:"},{"line_number":348,"context_line":"            self.stats.failures +\u003d 1"},{"line_number":349,"context_line":"            self.logger.increment(\u0027failures\u0027)"},{"line_number":350,"context_line":"            self.logger.debug(\u0027Update failed for %(obj)s %(path)s\u0027,"},{"line_number":351,"context_line":"                              {\u0027obj\u0027: obj, \u0027path\u0027: update_path})"},{"line_number":352,"context_line":"            if new_successes:"}],"source_content_type":"text/x-python","patch_set":1,"id":"7f96bb07_e3058bc3","line":349,"updated":"2018-01-16 00:36:25.000000000","message":"So I keep seeing this\n\n self.stats.X +\u003d 1\n self.logger.increment(\u0027X\u0027)\n\npattern -- can/should we find a way to make that less redundant?","commit_id":"bc5b910dd624c922aed3e4e65f9d287d688514a0"},{"author":{"_account_id":2622,"name":"Samuel Merritt","email":"spam+launchpad@andcheese.org","username":"torgomatic"},"change_message_id":"1a6e3be86bc9b0e409382d902797587ea0a3e306","unresolved":false,"context_lines":[{"line_number":346,"context_line":"            os.unlink(update_path)"},{"line_number":347,"context_line":"        else:"},{"line_number":348,"context_line":"            self.stats.failures +\u003d 1"},{"line_number":349,"context_line":"            self.logger.increment(\u0027failures\u0027)"},{"line_number":350,"context_line":"            self.logger.debug(\u0027Update failed for %(obj)s %(path)s\u0027,"},{"line_number":351,"context_line":"                              {\u0027obj\u0027: obj, \u0027path\u0027: update_path})"},{"line_number":352,"context_line":"            if new_successes:"}],"source_content_type":"text/x-python","patch_set":1,"id":"7f96bb07_f9ca1e18","line":349,"in_reply_to":"7f96bb07_e3058bc3","updated":"2018-01-16 02:01:55.000000000","message":"Personally, I don\u0027t mind it too much. I mean, we could wrap them both in a little double-dispatcher object, but that seems like it\u0027d obscure things more than clarify.\n\nIf we wind up with a third type of stat tracker, I\u0027d probably change my mind there, FWIW.","commit_id":"bc5b910dd624c922aed3e4e65f9d287d688514a0"}]}
