[MOD-12069] Add *_pending_jobs metrics#7556
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #7556 +/- ##
==========================================
- Coverage 84.75% 84.73% -0.03%
==========================================
Files 350 350
Lines 54139 54153 +14
Branches 14528 14528
==========================================
- Hits 45887 45884 -3
- Misses 8061 8078 +17
Partials 191 191
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
*_pending_jobs metrics*_pending_jobs metrics
| } | ||
|
|
||
| size_t redisearch_thpool_low_priority_pending_jobs(redisearch_thpool_t *thpool_p) { | ||
| return __atomic_load_n(&(thpool_p->jobqueues.low_priority_jobqueue.len), __ATOMIC_RELAXED); |
There was a problem hiding this comment.
I am not sure this atomic load is actually atomic,
you should use priority_queue_len or priority_queue_len_unsafe.
There was a problem hiding this comment.
priority_queue_len returns a sum of all queues, which is not what i aimed here
| } | ||
|
|
||
| size_t redisearch_thpool_high_priority_pending_jobs(redisearch_thpool_t *thpool_p) { | ||
| return __atomic_load_n(&(thpool_p->jobqueues.high_priority_jobqueue.len), __ATOMIC_RELAXED); |
|
|
||
| size_t redisearch_thpool_low_priority_pending_jobs(redisearch_thpool_t *thpool_p) { | ||
| return __atomic_load_n(&(thpool_p->jobqueues.low_priority_jobqueue.len), __ATOMIC_RELAXED); | ||
| } |
There was a problem hiding this comment.
Bug: Data race reading queue length without proper synchronization
The new functions redisearch_thpool_high_priority_pending_jobs and redisearch_thpool_low_priority_pending_jobs use __atomic_load_n to read the len field without holding the mutex, while all modifications to len elsewhere use non-atomic operations (like +=, --, =) under mutex protection. This creates a data race since atomic and non-atomic accesses to the same variable constitute undefined behavior in C11. While the comment in global_stats.c explicitly notes skipping the lock for performance, mixing atomic loads with non-atomic writes doesn't guarantee correctness. For consistent semantics, either len should be declared as atomic_int with all accesses using atomic operations, or the existing mutex-protected approach used by redisearch_thpool_get_stats should be followed.
|
Backport failed for Please cherry-pick the changes locally and resolve any conflicts. git fetch origin 2.8
git worktree add -d .worktree/backport-7556-to-2.8 origin/2.8
cd .worktree/backport-7556-to-2.8
git switch --create backport-7556-to-2.8
git cherry-pick -x ea0476a8a09e73f02c82b082b46c79ff625b37b1 |
|
Backport failed for Please cherry-pick the changes locally and resolve any conflicts. git fetch origin 2.10
git worktree add -d .worktree/backport-7556-to-2.10 origin/2.10
cd .worktree/backport-7556-to-2.10
git switch --create backport-7556-to-2.10
git cherry-pick -x ea0476a8a09e73f02c82b082b46c79ff625b37b1 |
|
Backport failed for Please cherry-pick the changes locally and resolve any conflicts. git fetch origin 8.2
git worktree add -d .worktree/backport-7556-to-8.2 origin/8.2
cd .worktree/backport-7556-to-8.2
git switch --create backport-7556-to-8.2
git cherry-pick -x ea0476a8a09e73f02c82b082b46c79ff625b37b1 |
|
Backport failed for Please cherry-pick the changes locally and resolve any conflicts. git fetch origin 8.4
git worktree add -d .worktree/backport-7556-to-8.4 origin/8.4
cd .worktree/backport-7556-to-8.4
git switch --create backport-7556-to-8.4
git cherry-pick -x ea0476a8a09e73f02c82b082b46c79ff625b37b1 |
| t.start() | ||
|
|
||
| # Give threads a moment to start and attempt to queue their queries | ||
| time.sleep(0.1) |
There was a problem hiding this comment.
@meiravgri I missed this, I think here we should wait until the query_results length is equal to num_queries.
* align info/* to active_coord * add APIs to get queues length * add to info * fix * test * fix test * catch general error * rename * fix moduleArgs * rename * rename test_active_worker_threads * rename to wworketrs (cherry picked from commit ea0476a)
* align info/* to active_coord * add APIs to get queues length * add to info * fix * test * fix test * catch general error * rename * fix moduleArgs * rename * rename test_active_worker_threads * rename to wworketrs (cherry picked from commit ea0476a)
* [MOD-12069] Add `*_pending_jobs` metrics (#7556) * align info/* to active_coord * add APIs to get queues length * add to info * fix * test * fix test * catch general error * rename * fix moduleArgs * rename * rename test_active_worker_threads * rename to wworketrs (cherry picked from commit ea0476a) * [MOD-12831] `test_info_modules:test_pending_jobs_metrics_search` Extend output on timeout (#7609) * move queries to run_cmds_in_bg add workers stats to state if will fail * refcator run_cmds_in_bg (cherry picked from commit 7db7c2e)
* fix: Avoid Rust cache contamination across platforms. (#7569) * [MOD-12170] Implement ASM State Machine on notifications (#7331) * first commit * fix: handle lock in two phases * inform ownership in regular command process handlers * fix: handle proper init * fix: use conditional variable * fix: add some comments * test: add test proving deadlock * fix: changes as per comments * fix: remove this locking from conn * remove code where certainity is not so high * fix potential issue * test: add first unit tests version * test: add proper testing with the value proposition * test: improve testing * alternative using auxiliary lock * simplify thpool a bit * make sure RedisModule_Yield is protected * fix: fix issue raised by cursor * fix: handle potential deadlock in drain also * fix: fix potential TOCTOU concurrency bug * fix: fix order of release * fix: fix potential overflow issue * fix tests * fix: fix counts in all variables * small refactor * some refactoring of Shared Exclusive Lock * simplify * protect GILOwned simple bool * clarify some comments * set GILAlternativeLockHeld to true properly * fix spelling * add assertion * fix: fix import RS_LOG_ASSERT * test: add more conditions to testing * add some more logic * improve testing to proper signal main thread can finish while other threads may be waiting for the Shared Lock in the loop * test: handl test properly * add another pattern of tests * fix comments from cursor * fix concurrency bug * fix: fix potential race condition at release lock time * fix: add condition * test: add more testing * force testing further to capture more potential errors * parametrize tests * test: make tests a little faster * test: add micro benchmark * fix compile microbenchmarks * fix: avoid potential reentrant deadlocks * fix: avoid potential reentrant deadlocks * test: avoid leak in test * test: avoid leak in test * fix: fix assertion * fix: fix assertion * Simplify Shared Lock internals (#7267) * simplify shared lock * small improvement to set_timeout * fix comment * fix nd improve comments * condition fix * remove lock type from release API * Add lock type back to the release API * remove Unlocked from enum and handle clock init for macOS * adapt to use new API * chnange according to comments * handle PR comments * handle PR comments * fix: take shared lock in other cases * change as PR comments * test: simplify test, do not allow query errors * fix tests as per comments * fix: handle number of high priority jobs running * fix: fix test comment * add ASM to help slot tracking in notifications * test: add some testing idea * change as per PR comments * compile and link test fix * change draining method to drain high priority * add ctests for ASM State Machine * test: add ASM tests * test: complete tests * change as per PR comments * add micro benchmarks with jobs in threads * remove changes not wanted * checkout redis feature branch in task test * remove draining * move atomic to new header * remove _internal naming * fix formatting --------- Co-authored-by: GuyAv46 <[email protected]> * [MOD-12627] Add Debug Support for `FT.PROFILE` Command (#7510) * imp debug profile for SA: introduce in mocule.h: RSProfileCommandImp RSProfileCommand calls RSProfileCommandImp(isDebug = false) for regular execution ProfileCommandCommand_DebugWrapper mcalls it with isDebug=true and skips _FT.DEBUG introduce entrypoint for _FT.DEBUG FT.PROFILE in debug_commands: ProfileCommandCommand_DebugWrapper RSProfileCommandImp calls DEBUG_execCommandCommon is its debug _recursiveProfilePrint skips printing debug RP * pass is debug instead of extracting: module.h: replace declaration: DistAggregateCommand DistSearchCommand with Imp version that receives isDebug expose ProfileCommandHandlerImp align debug_commands introducr _FT.DEBUG _FT.PROFILE * add test for cluster * return res * augi fixes * fix spell check * fix for real * fix test * skip tests according to env * revrt test_profile changes * reove changes from internal_only * [MOD-12694] [MOD-12069] Add active_coord_threads metric (#7546) * Add multi-threading statistics tracking for active I/O threads * fix comment * add cpp test * fix spelling * address comment * remove unnecessary nre line * add "active_worker_threads" metric * fix comment imp tests * test cleanups * add test comne about the num queries and cleanups * fix declartion * remove coord threads * add active_coord_threads expose ConcurrentSearchPool_WorkingThreadCount * make the tests run... * add "active_worker_threads" metric * fix declartion * remove coord threads * make the tests run... * introduce workersThreadPool_isInitialized assert is initizlied in GlobalStats_GetMultiThreadingStats * cleanup * rename workersThreadPool_isCreated * introduce ConcurrentSearchPool_IsCreated * fix test * we dont need workers * remove ConcurrentSearchPool_IsCreated and workersThreadPool_isInitialized * fix merge * [MOD-12789] test: fix flaky thpool test (#7581) test: fix flaky thpool test * Support Multiple Slot Ranges in search.CLUSTERSET - [MOD-11657] (#7508) * support multiple slot ranges * implement and move around helpers * add tests * ignore shards with no slots * improve parsing * better error message * fix flow tests * fix tests * sort by node id * improve error testing * cover missing cases * more error messages improvements * address AI review * stabilize Unexpected argument error path * stabilize more error paths * fix tests accordingly * last fix * add logs to cluster set command * add more logs per @alonre24 request * rename MetricIterator to Metric (#7586) The other iterators are not suffixed with 'Iterator'. * [MOD-12701] Split the execution of Rust and C/C++ unit tests across two different CI steps. (#7587) Split the execution of Rust and C/C++ unit tests across two different CI steps. * [MOD-12519] implement skip multi in II iterators (#7426) * remove it->skipMulti No longer needed as the full iterators have been removed. The query ones always set it to true. * implement skip multi II query iterators are supposed to skip results having the same ids. Test ported from test_cpp_iterator_index.cpp * inline read() and skip_to() * test more ii iterator edge cases Port of GetCorrectValue and EOFAfterFiltering from test_cpp_iterator_index.cpp * Compress layers prior to exporting them to the Docker layer cache (#7529) * Compress Docker layers prior to exporting them. * Ignore boost subfolders recursively * [MOD-12417] Track maxprefixexpansions errors and warnings in info (#7570) track maxprefixexpanions * [MOD-12409]: Port DocumentType enum to Rust (#7590) Port DocumentType enum to Rust Port DocumentType enum to Rust as `document::DocumentType`, removing it from `redisearch.h`. Use `document::DocumentType` in `rlookup` instead of `rlookup::bindings::DocumentType`. * [MOD-12069] Add `*_pending_jobs` metrics (#7556) * align info/* to active_coord * add APIs to get queues length * add to info * fix * test * fix test * catch general error * rename * fix moduleArgs * rename * rename test_active_worker_threads * rename to wworketrs * [MOD-12392] Remove numDocs parameter from non-optimized Wildcard iterator (#7602) Remove numDocs parameter from non-optimized Wildcard iterator * [MOD-12701] Enforce a per-test timeout in the C++ rstest suite using ctest (#7588) * Enforce a per-test timeout in the C++ rstest suite using ctest * Disable problematic tests * Raise timeout to 60s * Raise timeout * Add a timeout for coordinator tests too * Skip ActivateIoThreadsMetric test * Register Cursor Sub-Commands as such - [MOD-12807, MOD-12808] (#7571) * split cursor command * fix and improve tests * cover error cases * fix cursor leaks * Add "TODO: run hybrid cursor" back * remove new empty line * small test improvement * fix FT.CURSOR GC * de-flake test * make CURSOR PROFILE internal only * test the free * Keep just the prints * test on macos and noble as well * Move the free * Remove * Add to non container * Back to regular test * Remove spaces * Moved the remove and add the repo size * Moved it again * Move it to after repo build * Move print after repo build * test all * run the temp flow * add mount to container * Fix curly --------- Co-authored-by: Luca Palmieri <[email protected]> Co-authored-by: Joan Fontanals <[email protected]> Co-authored-by: GuyAv46 <[email protected]> Co-authored-by: meiravgri <[email protected]> Co-authored-by: Guillaume Desmottes <[email protected]> Co-authored-by: lerman25 <[email protected]> Co-authored-by: Henk Oordt <[email protected]> Co-authored-by: alonre24 <[email protected]>
* Reduce merge queue * add ability to run manually * CR comments * allow workflow call for testing * Fix naming in build image + remove quick from flow intel as well * use ubuntu nobel rather than latest * fix noble typo * CR fixes * CR fixes 2 * don't use container for cov and san * restore leftover * remove mac + intel and workflow call * remove macos intel from matrix * change back ubuntu:latest to ubuntu:noble in merge-to-queue as per Jonathan comment * measure disk space * fix step name * Free disk on container (RediSearch#7613) * fix: Avoid Rust cache contamination across platforms. (RediSearch#7569) * [MOD-12170] Implement ASM State Machine on notifications (RediSearch#7331) * first commit * fix: handle lock in two phases * inform ownership in regular command process handlers * fix: handle proper init * fix: use conditional variable * fix: add some comments * test: add test proving deadlock * fix: changes as per comments * fix: remove this locking from conn * remove code where certainity is not so high * fix potential issue * test: add first unit tests version * test: add proper testing with the value proposition * test: improve testing * alternative using auxiliary lock * simplify thpool a bit * make sure RedisModule_Yield is protected * fix: fix issue raised by cursor * fix: handle potential deadlock in drain also * fix: fix potential TOCTOU concurrency bug * fix: fix order of release * fix: fix potential overflow issue * fix tests * fix: fix counts in all variables * small refactor * some refactoring of Shared Exclusive Lock * simplify * protect GILOwned simple bool * clarify some comments * set GILAlternativeLockHeld to true properly * fix spelling * add assertion * fix: fix import RS_LOG_ASSERT * test: add more conditions to testing * add some more logic * improve testing to proper signal main thread can finish while other threads may be waiting for the Shared Lock in the loop * test: handl test properly * add another pattern of tests * fix comments from cursor * fix concurrency bug * fix: fix potential race condition at release lock time * fix: add condition * test: add more testing * force testing further to capture more potential errors * parametrize tests * test: make tests a little faster * test: add micro benchmark * fix compile microbenchmarks * fix: avoid potential reentrant deadlocks * fix: avoid potential reentrant deadlocks * test: avoid leak in test * test: avoid leak in test * fix: fix assertion * fix: fix assertion * Simplify Shared Lock internals (RediSearch#7267) * simplify shared lock * small improvement to set_timeout * fix comment * fix nd improve comments * condition fix * remove lock type from release API * Add lock type back to the release API * remove Unlocked from enum and handle clock init for macOS * adapt to use new API * chnange according to comments * handle PR comments * handle PR comments * fix: take shared lock in other cases * change as PR comments * test: simplify test, do not allow query errors * fix tests as per comments * fix: handle number of high priority jobs running * fix: fix test comment * add ASM to help slot tracking in notifications * test: add some testing idea * change as per PR comments * compile and link test fix * change draining method to drain high priority * add ctests for ASM State Machine * test: add ASM tests * test: complete tests * change as per PR comments * add micro benchmarks with jobs in threads * remove changes not wanted * checkout redis feature branch in task test * remove draining * move atomic to new header * remove _internal naming * fix formatting --------- Co-authored-by: GuyAv46 <[email protected]> * [MOD-12627] Add Debug Support for `FT.PROFILE` Command (RediSearch#7510) * imp debug profile for SA: introduce in mocule.h: RSProfileCommandImp RSProfileCommand calls RSProfileCommandImp(isDebug = false) for regular execution ProfileCommandCommand_DebugWrapper mcalls it with isDebug=true and skips _FT.DEBUG introduce entrypoint for _FT.DEBUG FT.PROFILE in debug_commands: ProfileCommandCommand_DebugWrapper RSProfileCommandImp calls DEBUG_execCommandCommon is its debug _recursiveProfilePrint skips printing debug RP * pass is debug instead of extracting: module.h: replace declaration: DistAggregateCommand DistSearchCommand with Imp version that receives isDebug expose ProfileCommandHandlerImp align debug_commands introducr _FT.DEBUG _FT.PROFILE * add test for cluster * return res * augi fixes * fix spell check * fix for real * fix test * skip tests according to env * revrt test_profile changes * reove changes from internal_only * [MOD-12694] [MOD-12069] Add active_coord_threads metric (RediSearch#7546) * Add multi-threading statistics tracking for active I/O threads * fix comment * add cpp test * fix spelling * address comment * remove unnecessary nre line * add "active_worker_threads" metric * fix comment imp tests * test cleanups * add test comne about the num queries and cleanups * fix declartion * remove coord threads * add active_coord_threads expose ConcurrentSearchPool_WorkingThreadCount * make the tests run... * add "active_worker_threads" metric * fix declartion * remove coord threads * make the tests run... * introduce workersThreadPool_isInitialized assert is initizlied in GlobalStats_GetMultiThreadingStats * cleanup * rename workersThreadPool_isCreated * introduce ConcurrentSearchPool_IsCreated * fix test * we dont need workers * remove ConcurrentSearchPool_IsCreated and workersThreadPool_isInitialized * fix merge * [MOD-12789] test: fix flaky thpool test (RediSearch#7581) test: fix flaky thpool test * Support Multiple Slot Ranges in search.CLUSTERSET - [MOD-11657] (RediSearch#7508) * support multiple slot ranges * implement and move around helpers * add tests * ignore shards with no slots * improve parsing * better error message * fix flow tests * fix tests * sort by node id * improve error testing * cover missing cases * more error messages improvements * address AI review * stabilize Unexpected argument error path * stabilize more error paths * fix tests accordingly * last fix * add logs to cluster set command * add more logs per @alonre24 request * rename MetricIterator to Metric (RediSearch#7586) The other iterators are not suffixed with 'Iterator'. * [MOD-12701] Split the execution of Rust and C/C++ unit tests across two different CI steps. (RediSearch#7587) Split the execution of Rust and C/C++ unit tests across two different CI steps. * [MOD-12519] implement skip multi in II iterators (RediSearch#7426) * remove it->skipMulti No longer needed as the full iterators have been removed. The query ones always set it to true. * implement skip multi II query iterators are supposed to skip results having the same ids. Test ported from test_cpp_iterator_index.cpp * inline read() and skip_to() * test more ii iterator edge cases Port of GetCorrectValue and EOFAfterFiltering from test_cpp_iterator_index.cpp * Compress layers prior to exporting them to the Docker layer cache (RediSearch#7529) * Compress Docker layers prior to exporting them. * Ignore boost subfolders recursively * [MOD-12417] Track maxprefixexpansions errors and warnings in info (RediSearch#7570) track maxprefixexpanions * [MOD-12409]: Port DocumentType enum to Rust (RediSearch#7590) Port DocumentType enum to Rust Port DocumentType enum to Rust as `document::DocumentType`, removing it from `redisearch.h`. Use `document::DocumentType` in `rlookup` instead of `rlookup::bindings::DocumentType`. * [MOD-12069] Add `*_pending_jobs` metrics (RediSearch#7556) * align info/* to active_coord * add APIs to get queues length * add to info * fix * test * fix test * catch general error * rename * fix moduleArgs * rename * rename test_active_worker_threads * rename to wworketrs * [MOD-12392] Remove numDocs parameter from non-optimized Wildcard iterator (RediSearch#7602) Remove numDocs parameter from non-optimized Wildcard iterator * [MOD-12701] Enforce a per-test timeout in the C++ rstest suite using ctest (RediSearch#7588) * Enforce a per-test timeout in the C++ rstest suite using ctest * Disable problematic tests * Raise timeout to 60s * Raise timeout * Add a timeout for coordinator tests too * Skip ActivateIoThreadsMetric test * Register Cursor Sub-Commands as such - [MOD-12807, MOD-12808] (RediSearch#7571) * split cursor command * fix and improve tests * cover error cases * fix cursor leaks * Add "TODO: run hybrid cursor" back * remove new empty line * small test improvement * fix FT.CURSOR GC * de-flake test * make CURSOR PROFILE internal only * test the free * Keep just the prints * test on macos and noble as well * Move the free * Remove * Add to non container * Back to regular test * Remove spaces * Moved the remove and add the repo size * Moved it again * Move it to after repo build * Move print after repo build * test all * run the temp flow * add mount to container * Fix curly --------- Co-authored-by: Luca Palmieri <[email protected]> Co-authored-by: Joan Fontanals <[email protected]> Co-authored-by: GuyAv46 <[email protected]> Co-authored-by: meiravgri <[email protected]> Co-authored-by: Guillaume Desmottes <[email protected]> Co-authored-by: lerman25 <[email protected]> Co-authored-by: Henk Oordt <[email protected]> Co-authored-by: alonre24 <[email protected]> * Revert "Free disk on container (RediSearch#7613)" This reverts commit 9c68740. * add free disk step * rename + remove temp test * remove leftover * better readability in container input Co-authored-by: GuyAv46 <[email protected]> * fix double defaults --------- Co-authored-by: dor-forer <[email protected]> Co-authored-by: Luca Palmieri <[email protected]> Co-authored-by: Joan Fontanals <[email protected]> Co-authored-by: GuyAv46 <[email protected]> Co-authored-by: meiravgri <[email protected]> Co-authored-by: Guillaume Desmottes <[email protected]> Co-authored-by: lerman25 <[email protected]> Co-authored-by: Henk Oordt <[email protected]>
* align info/* to active_coord * add APIs to get queues length * add to info * fix * test * fix test * catch general error * rename * fix moduleArgs * rename * rename test_active_worker_threads * rename to wworketrs (cherry picked from commit ea0476a)
* [MOD-12069] Add `*_pending_jobs` metrics (#7556) * align info/* to active_coord * add APIs to get queues length * add to info * fix * test * fix test * catch general error * rename * fix moduleArgs * rename * rename test_active_worker_threads * rename to wworketrs (cherry picked from commit ea0476a) * [MOD-12831] `test_info_modules:test_pending_jobs_metrics_search` Extend output on timeout (#7609) * move queries to run_cmds_in_bg add workers stats to state if will fail * refcator run_cmds_in_bg (cherry picked from commit 7db7c2e) * fix test
MOD-12069 Multi threaded section
MOD-12696 number of queries waiting in the shards queue
MOD-12697 number of pending jobs in “low priority queue”
Extends the
multi_threadingsection inINFO MODULESby addingworkers_low_priority_pending_jobscandworkers_high_priority_pending_jobsmetrics to track pending jobs in the worker thread pool queues.workers_low_priority_pending_jobscurrently refers to background indexing jobsworkers_high_priority_pending_jobsrefers to queriesContinues #7552, #7538, and #7546 which added
search_multi_threadingsection withsearch_active_io_threads,search_active_worker_threads, andsearch_active_coord_threadsfields.INFO MODULES Output
Additional Changes and Improvements
redisearch_thpool_high_priority_pending_jobs()andredisearch_thpool_low_priority_pending_jobs()to expose queue lengths fromthpool(deps/thpool/thpool.{h,c}).workersThreadPool_LowPriorityPendingJobsCount()andworkersThreadPool_HighPriorityPendingJobsCount()wrapper APIs (src/util/workers.{h,c}).low_priority_pending_jobsandhigh_priority_pending_jobsinGlobalStats_GetMultiThreadingStats()using new worker APIs (src/info/global_stats.c).wait_for_condition()helper for polling with timeout and state reporting (tests/pytests/common.py).Note
Expose
workers_low_priority_pending_jobsandworkers_high_priority_pending_jobsinINFO MODULESvia new thpool/worker APIs, plus tests and a wait helper.MultiThreadingStatswithworkers_low_priority_pending_jobsandworkers_high_priority_pending_jobs(src/info/global_stats.h).GlobalStats_GetMultiThreadingStats()using worker APIs (src/info/global_stats.c).INFO MODULESundermulti_threadingsection (src/info/info_redis/info_redis.c).deps/thpool/):redisearch_thpool_high_priority_pending_jobs()andredisearch_thpool_low_priority_pending_jobs()with atomic loads (thpool.c/.h).src/util/):workersThreadPool_LowPriorityPendingJobsCount()andworkersThreadPool_HighPriorityPendingJobsCount()and expose in header (workers.c/.h).wait_for_condition()helper (tests/pytests/common.py).tests/pytests/test_info_modules.py).Written by Cursor Bugbot for commit 6affd71. This will update automatically on new commits. Configure here.