channable / opnieuw

One weird trick to make your code more reliable

Home Page:https://tech.channable.com/posts/2020-02-05-opnieuw.html

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Comparision with tenacity

bersace opened this issue · comments

Hi,

Would you mind explain the differences with https://github.com/jd/tenacity/ ?

Great project name anyway :-)

Thank you for your interest in Opnieuw. We used Tenacity previously, but we ran into several pitfalls, which we outlined in our announcement post. Concretely:

  • Tenacity has many parameters, which can lead nonsensical combinations. For example, wait_exponential() in Tenacity takes a multiplier, min, and max, but you can set the min and max in such a way that every or almost every delay hits the min or max, and there is no exponential backoff going on at all. Take this example from the docs:

     @retry(wait=wait_exponential(multiplier=1, min=4, max=10))
     def wait_exponential_1():
         print("Wait 2^x * 1 second between each retry starting with 4 seconds, then up to 10 seconds, then 10 seconds afterwards")
         raise Exception

    The actual wait times are 4, 8, and 10 seconds, which is hardly exponential backoff. This is not obvious from the code, which does mention "exponential". In Opnieuw we solve this by providing just two parameters that control the wait time, and every combination is valid.

  • Tenacity is very modular, with many different wait strategies. But in practice, we almost always want exponential backoff with jitter. Tenacity’s docs suggest that wait_random_exponential may implement this, but if I understand the example correctly, it actually implements a slightly different strategy. (I have not looked at the source to confirm what wait_random_exponential really does.) What we found in practice, was that because there are so many strategies in Tenacity (also wait_fixed, and wait_random), we ended up with many different combinations in our codebase, but very few used exponential backoff with jitter, even though they should have. In Opnieuw we provide a single strategy.

  • Tenacity uses seconds as time units, but the only way to know that is because the example in the docs mentions it. If you are not aware of this, and you see something like

     @retry(wait=wait_exponential(multiplier=1, min=4, max=10))

    in a codebase, then it is not clear whether the wait time will be capped at 10 seconds or 10 milliseconds. In Opnieuw we put _seconds in the parameter name to eliminate all confusion, and to make the code obvious without having to refer to the docs.

  • Tenacity has a "multiplier" parameter to configure exponential backoff. But in practice, we found ourselves thinking about doing "x calls in y seconds", and solving for the multiplier. So in Opnieuw, we accept the retry window and number of calls instead. Also, when trying to solve for the base delay, it was unclear how Tenacity computes wait times. An example in its docs mentions “Wait 2^x * 1 second between each retry”, but it is not clear whether x starts at 0 or at 1. Given that the last wait is about half the retry window, an off-by-one makes quite a big difference here. In Opnieuw we sidestep confusion because the wait time is computed.

  • Tenacity has stop_after_attempt, which suggests that count includes the initial call, but the example of its usage mentions "retries", which suggests that the count excludes the initial call. In Opnieuw we sidestep confusion by referring to the number of calls.

As tenacity wasn't explicitly mentionned, I didn't catch this. Thanks for the complete anwser. Closing :-)