Repository navigation
Pause usage sharing sends after repeated failures - #53
Draft
jwrosewell wants to merge 2 commits into
Draft
jwrosewell wants to merge 2 commits into
jwrosewell wants to merge 2 commits into
Conversation
Usage sharing in Python has no back-off. Every package is sent while the service is failing, and is lost. These tests expect the back-off .NET is being given, at its default settings of ten failed sends within 100 seconds and a first pause of two seconds: - after ten failed sends nothing is sent during the pause, and the evidence processed during it is all sent once the pause ends and the service answers, with no more requests processed; - closing while paused returns at once and sends nothing. The test receiver can now answer with another status, and records every request it gets as well as the data it accepts. The tests fail until the back-off is added.
Python now has the same back-off as .NET, with the same five settings and defaults, as constructor arguments. - After failures_to_enter_recovery sends fail within failures_window_seconds, sending pauses for initial_recovery_delay_seconds. A send fails when it raises or the answer is not 200. - When a pause ends one send is tried. If it fails the delay is multiplied by recovery_multiplier, up to max_recovery_delay_seconds. If it succeeds the back-off ends. - Processing is not affected. Evidence is queued during a pause, and new evidence is dropped only when the queue is full. The new max_queue_size argument sets the size, by default 1000 or ten times the requested package size if that is larger, as in .NET. The queue had no limit before, which a pause would have needed. - One thread sends at a time, as in .NET and Java. It sends until fewer than requested_package_size entries are queued. A timer that does not keep the process running starts sending when a pause ends, so the queue drains without more requests being processed. - Closing cancels the timer and sends nothing while paused, logging how many entries were not sent. ShareUsageBackOff holds the rules and takes the time from the caller, so its tests set the time. send_thread now returns whether the service accepted the message.
This was referenced Nov 7, 2026
Draft
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Python's usage sharing has no back-off, so every package is sent, and lost, while the service is failing. This adds the back-off that 51degrees-dotnet#88 restores in .NET, with the same five settings and defaults as constructor arguments.
Problem
ShareUsage.share_send_usage(lines 225 to 249) hands each full package to a thread pool with no limit on its work queue, andsend_threadignores the status of the answer. While the service is failing, every package is sent and lost, the requests to the failing service are not limited, and nothing records that sends are failing. The queue of entries waiting to be sent has no size limit either. .NET had a back-off from pipeline-dotnet#218, but it blocked processing as well as sending and was removed in pipeline-dotnet#261. The back-off is now to be part of the specification and of every usage sharing element.Change
failures_to_enter_recoverysends fail withinfailures_window_seconds, sending pauses forinitial_recovery_delay_seconds. Successful sends in between do not reset the count.recovery_multiplier, up tomax_recovery_delay_seconds. If it succeeds, the back-off ends and the next pause starts again from the initial delay.max_queue_sizeargument sets the size, by default 1000 or ten timesrequested_package_sizeif that is larger, as in .NET.requested_package_sizeentries until fewer are queued. A timer that does not keep the process running starts sending when a pause ends, so the queue drains without any more requests being processed.max_queue_sizerequested_package_sizeif largerrequested_package_sizefailures_to_enter_recoveryfailures_window_secondsinitial_recovery_delay_secondsmax_recovery_delay_secondsrecovery_multiplierHow it is built:
ShareUsageBackOffin share_usage_backoff.py holds the rules. Each method takes the time from the caller, so its tests set the time. The element usestime.monotonic(), so a change to the system clock does not change a pause.share_send_usagenow starts the sending thread if it is not running, enough entries are queued and sending is not paused.send_threadreturns whether the service accepted the message.ValueError.Tests
The first two tests in test_usage.py use only the default settings, so they ran on rc with only the tests added (run 42348732712). Both failed on Ubuntu and Windows, and the other 34 tests passed.
test_back_off_pauses_then_drains_queuetest_back_off_close_while_pausedThe test receiver can now answer with any status, and records every request as well as the data it accepts.
test_back_off_delay_grows_to_maximum,test_back_off_full_queue_drops_new_evidenceandtest_back_off_invalid_settingsuse the new arguments, so they were added with the change, as was test_share_usage_backoff.py, which checks the rules with set times.With the change (run 42348961812) all 46 tests in the package pass on Ubuntu and Windows.
The full pull request build (run 42349561927) passed on every configuration, at head 94d89a8.
For review
requested_package_size, as before. The specification's cleanup section asks for one, which is a separate change.Related