From b149850bd41921ec4ce0b057646a6194abc20bac Mon Sep 17 00:00:00 2001 From: Dylan Reisenberger Date: Mon, 16 Sep 2019 23:51:15 +0100 Subject: [PATCH] Update readme detail on jitter formulae - new-recommended and prev-recommended (#14) * Readme: minor updates; add credits * Add documentation on new jitter formula --- ...rFormulaRetryCount5InitialDelay1Second.png | Bin 0 -> 16369 bytes README.md | 111 ++++++++++++++---- 2 files changed, 87 insertions(+), 24 deletions(-) create mode 100644 NewJitterFormulaRetryCount5InitialDelay1Second.png diff --git a/NewJitterFormulaRetryCount5InitialDelay1Second.png b/NewJitterFormulaRetryCount5InitialDelay1Second.png new file mode 100644 index 0000000000000000000000000000000000000000..be7a712908d5ba1ab31d2cf01895ef56de468efc GIT binary patch literal 16369 zcmd6O3s{op8#lH2ui3#`nwh5SXa~)VG%Lk*wB~8%@|-TsQ;3SrQwq3RnR#H<<~dn8 zHD_oh2Tg^_1Wk?7gaj28l?)L{1p$HY#kN+P^#88!bbVah#0RMjxdE7G^1X|vx`dvVcxN{Z+vb}ub$ieU8efhly(VJRgj3N>3KOT)g7`@;O zNcQz8E~j!ACRy8iiQc`@&*DJrrHl734NHc2=<=6aOx^8YBG8VH;8`5Flk=+WY4iBl z*!fD7?N?T(Jza9IJ-1E`TjxL3XViB-05auIypfuo#yB7jTkpN zjb7u9gMP^x=d*}n^xM>WG;amE5~d`=3G}{F#@&osRK%%0NZ$epB%=qiNsB)TfhY+_ z$PI!N%ZdgH=lR`u#}V2sXdIuts6zN^@y|CGT7l|!AqGN6hXl8brFK_@xT$GrUEvseu%I=i-pKI0j{*8{^`M13ai*+EakCex zgX1*!2|j}z$l$-`$BrZ}?=Suqv?y7(Lj7x!7LgrQ6r9ynk4`L`%zNfR9f| zzm)1hcfENRf=lj5tKx5)!KIt%eD4o|(rhdbz-qHaF(yT(Tyt4edri1K@1>}vZ`i2H z41JGq6CICk^YT*Q^IL*75^c>YPTg&E!L(}0!xCR7LVP<>%WZzOB+)Z>4*8a+An7(| z$@Ra`mL)?=LCZmA4hQt0##5XV-G`m0&?}6Ks>XzyX4hXzubPNF0ypzT#h!V6OXHWm z!) zhYO}(iG}zpF&@%_^H)*~b&aHjXvUa4sk^*^9gq&C_WfZf^%Vase|r+L7gX_1a`F}r z?ZtzFK4vOX`%C}xQ2tLy{!n-*#M}4+)Of&taxE2H_zPvX|9O3G{NuW*ynAkYy1l>3 zS?Bj!Hq9!*^m*kw&+4~^@N`TmHMoo-zq{`2z0BemJ)&PorQi7qQ=-{cToRs+-ZsVB zPcId)yejz@@9nw#T>4}RX=K9Z(TRA%as=D5;>9a7oH?Fe8DZOi7fEO0Lj6RD^H4q5 zV2u1eOQ8irP%OvGLpZxB+(g)zFoHD>%WWtg7W1q!0}AZOC~u5N>JdKMfb(Nb!n~$N z@eC1jKb{234ak&9cDBoI`S%u0*IrqT20mt5mFx;nfy*Cv)CVA>d_xo!*OxO*LWR&$ zNbC-)M1BkmgBLcD<uEJi*Cl;GV7}!;!Ak6Dkw3-YGmpc4Jlsc_Xt)5aqWoBOWC4pzl1e=9< zaI#N_88PBk#(D*;HdMAW~l0SR6q|<3N=SZ2n7xNsyir{NK z?tGhd!tFrqRW9Ymn3YRfsQinV11-J>ws{};0-Z2ze328MMGr9G0K&o*!XTW~SdTa4 zEz3lW!z`T@ z5ZWVrfP~mjBJX&hbKJ>}OdQe&mOsTpgc=BI{HO$^T>69)BDYK@%gm1`fsvAsRy3V* zZ_hKA#M5nHocS>*$H&VzKaNb|)a2*TEB)F^+5fR;ZI(X4+53~^Z<)~B_~|bK$i-Du z4pQ!4FOStH4tG2tOuzOc28Aw#-u5HI{JUZDEKz$0A&n}BTp1=%?I&w+&bRTy)Jb3y zXnN&zC~qw+h&2T`pPC@t^eEMkx!*J+AQW|sjLgT!tYehhWTIpWkl8!y;YV2#?U}}6sDg3>H_`{cdluoA=RG|fD4TZ`-TiqU$OQtJ5*CtjI zd^x*W5CfvUNx6~0O7VR63$(c+GZdlk$Dmm5tmfAR2}nKpFSZmK5nXLZ@cepQ{XIg2 zECI*JWCgW!yk!s^z1bp2C|ZViX)=7Po!9^|F}3yG`z*T05X!>t?F@DcYt3=5#42mh z{$93B=)RPPF~{IX8fGJPdfZbYYwWl(}1GfDg=+g%pr*%g^{6Go$nf>ShuF=jQJ# ze~>Zh!FpydxVCGYSb`ymn49fejjErgBUqN}#1U&4;JbX4B6oJ|&*b3VPET^FRnq_Q{WWo zbgO;_CzLX+M|1@{p&2IpeBUX7EWJ_>LmujooztE?&8s7{A*D=YD4mWl2$)Kx79l-N zZ8;vSDRcKkyj)*d;VJ8>*>f4LpOFwI9fcvY38oM2OH^^Kt)s=`Kx*2mJHp?0JCzIy zPx73br8l0<>~(s>ynjfRXuXo(UhtE+brdfl-e@5>ncJA$3?o3!gw{>@pAL31G1^Zm zvAwknDyPU_nqckH%P_&!cqh3cgjJbQ1XvVnH%H*xcA!LzWm|IIW>fl(kl}Y>OA$)^ z(xxaD&&)K~fYT9cNbL~4@Pe}LPD9c8c!VAk`nVV_ra;W;NM&VvDU7DD`O=>W3^>Qf z+h=7NalD^~yp~n)*}E!&L*!JLVW(7XP{}t+f#0MPydCseuS!EXqpXexKMCb4nK=~3 z6pw^UC*c_sHfMhd{J_qPqCi3f0^^~~u?!dIxZ4U|%sc>88td0duVwGF=Pc()FU1>F z3cT5+SV111OP%)ga)Qb|IKrBUL5a`;ftjgm4}jHSJ(WZI3mGO zHnPysyVR0`2l4=DtCx3W1B-jzBOA%iphNcV%s5jJOm3jkBU&wKbc)#MFF`N?L0ntw zyq)ue*U@rr)g;t7Oc-%Mc$Lig-NflFa}pZl^q4^b7dlYl2jTkEj+P+SYICeB2jgxM(Ss8DI79@DHF4m5LaAHMVquakt+*=1 z5`mqb43WXxzO_~0hkwfHn{zk~a6OnjfRufA+S}_`CuRSQ7h9xLyw$v3K^lJA?>zda z9D2l!&Ctrz_i?l4OWI$>^@tkaHjPY6AfWAUf>L7)7@-FCiPoeF!Q>iy*6+YJl*nE) zSW#cctHzpjRQMB4Jj`sQQ=B@Dh5%~M5UJE&Uwp6=(0ZPAW}P0x%n&XAQ}MAjwiPsH z&M{#glKq5(v|bN9I9hEUjxnvy)x%GR%ZwG{RNUDfLJ^P{286X5PO}1G4NSs04l*4N z-^1Y^iw<2H;4~<(XN8?;lXcX9eAfb5e*77_6NQ=TUl1~F>lII{?Lql(RpVp;_#+H0Kia|n0{ z%-S4{EWG7tM6D-aiz3MNB59wM=pF%kD6t|i$nfgVwpX!me3yHMaE>)t1`;4 zomS)cswsrwMhci@%ZxS5?yi(Ekp`yiZ#Mk2 z7da@@FFoK~7NM(#Oo4|t*fH*}n7lFyBxvx;R0vGJ5_Zu84Y$RG+S5+ow^Zl}9}wge zKH)4>{|PT!#H!JvP#wwRP#H1fOizi{%2t*ax7hqHpRp_aBEeZ4BKsjpYhnF&n*E@O z$9He8xNP<4D)+VpRvEv0opFf0@sN3gtzJ3=M)Edh@PFc;Bo_;Dt6GpZnO}q-ka!>H z4wF;3%$6A2EK3sH8yB-w#p!S|9!?ZyFyG1o@q9LA4U;DuMk5Z95mMMnrsxVC9TBSk zWWTASezq=JHmzmfnFsfTIdD8U5`fzcUSz^esyT&i4tq&2)iX+2V{bd&AiF;*el)|} z{34=9ST%0Zn1x zr(pqMUgQ$2J^3`9vVt>4U4f8NVJuU}y0b}+v+FuV!{ zC-r3+gE5@*c=IqA(LDMVHrV#^7O6;3S`1r#+bBK|T1X6noM4Z6%PJW+#P zPo_wwH*xqIkC1(^gcpP-)3DcEnt2wFDSK)hvGaYXSYw>bA&lxBw zGG(S&2X>b8FVYXO+}fG3;jN?8E-Arsocdhc&CGTr-wd9bTwdkG@u2W~ru0VrI?=cA zNPXH^X-RvzQ9vgRRYse$jBY7fk{&8YE|-&1B{(=C#%paHxxtIQhT8FfuM_ZE>=?pI z@KF}Wpv|WO^tn0|A$30`Nt$4qQG{}|Hfnt%!v&&QhyuV&dc7)CQlbJ<9+gLP2T_A=5>l3^DV>4D*`;3DKJB9 zM&&xpqJH+)9)QmBq?(bDy;C^kWN*nZ+z!P)vf- z)**=`MPCNtfigt}czk;ufC5yn&Hq<#bo{VjcACc^R{AzPfC|`TGDcd9HnQ!rK}APL z+YY0dyxT{O=JpYP&Q)V#YX})36O9YDBJIqRUY6LgNgzWM@j}9^e55_Uj=VrMvVWVS z&cmX6y?)c{(2R;9!zKLMzz1_7yLT_c=-u8O%*@Fp66kKZTZ9 zKZ#=_;`BK$KZb@naow>`O~1VzF$)?wESIyDa=G8noo%wE6}d}Ye>Avp^Mh+|-%@9t zhaCE5Z`%vq=^7Jmv%WiWXy1a24=mEXf8)&dp94O+vhoMY7RF!M5DUfTth+QWD#{Y{ zHOTHd<5tkno#NGvIuBc(J-Re@Hb-+P=cB7ADE)a;L3sD}PC8j)5%UR=`MvP-Ag{G%(s{oX>Wf`@9z z|No0Tv*tLmtdXA|K_bi3$nti+J3Hw?qV@hiv@p?X`HWhB`?#EwuSHaj2eTDr2+4E` zOF5}j43nsE#LRlN44cs1Q1NO!jgFOfp=4ySO(w5VYoA()kypU$t`|R^Mk5t`1eznA zIILbuN5guvfq;VWGVxJPWdl$l+{XyntX2Z`$+@y9D%)1V2t{DxXL$k?ip{Bk%>Jxa zp79H+#lx9{F%GgpU8C`eSH9c5jJi^2>UXc(PwHXN)b9W!9ws5x6QogNCZ1L=0kg-5 z+RA*jyZv9&zS-9cX8?pY+^^w$45P0hG0t{6Wl%zlVE5TF?G{*P3y|Uz)?RiJN~VjF z<+CbDk{`q~#p*VR{BD7@W}gd4%g7d4zat9GoO)dj^7YfMLnTd0w4#uJ($ppXLO!dG z2k7m|cEURHn*(!Lvf>3-`=Qa!PMt3EXQY{4ui3A;_9JSX1VBf;-$dp@F%$MJ@X!{P zPM6E?0xFaFPLr}*)JVn3ae*~O@+Hepo-J6tF8RN8>F%!rjYR zJqw4Q2G-r)U*Vu^VPO2K4ACgLTp&oScR(j#9$EZ6pVPF{c^DBMK7O_D4Bu{-zQ#m~ zqwEW8VPRy{KwhpItLwu|G$R?0W| z&T;4+>SXN(;{M<=rVzGsi7}G5JgqqQ2kyh_ryd=ADo_crXQX($8u|e5>ZD7w@!X!Q ze9dL|iSJO=2+dbxGfI(Kb)gwHGsP82ky&oVw~SMn^!%&NJD5sG7Q% zZ;q)m56ym`;+nU0W=P^{@iLlvS65K3{Q(=Bo79p~P(IaF2{B#$5G8E|82-tT+G42! zP-s?JtsHhSB)=IwVZJ`e0m27LHq7RYpFKsG|?2fGac9dfyrF z)9)TO9D{ewc8FvzXsT#r^WP?x0&ZY6k?o9HWge;uzGEwg_I<>239TOWSP$?HOguYu@~PHL{X~{v$-&x$bKD9UyU#@n`b$W$XSGybV5SQ8UCH>f4Bp_=>!#}~l!ntI6Z$w{`+t~7K z>62*w>aAM0Ku)phc<1a3LWjD{WRf}T*!*8?K9RZ3v_$;4dvz-n3BC<7oF^H#JOx6R z6Nt#qqp{Ajvhm)!I`Uy34ajO5v1VbxSWsVZWc$1c^B*8XrcJ$UVeD*8pwB?B$#klJ zylsR0s}V!Zerx=^VfnZ^YC1J0xW9v*xxRBWJ&HOELRKmQ^gmolQ_8Xd<(tacP^(7Z zKwfL+tBscp#z9fjY=r=UmTZu$RU=-&h5Odm+^#bVBZ`VJs6=)^#c^cnCuZ(*Crfc2 zJ&IAY|B1oAJ(MApH&49rr#JsEv(J`qy+GwVkY<`v>TbvS%`OKYeQU4YD$jJdU7P(< zJ!MwURe5u-RuILekt`kQ?B#r-WLcE@&Z|2ONt{qdzW9#BT2hTX`(o&@M7*KCLQYF@4B;1!J#;l z6tlMHpK$z8%EU#w9rG1`XEY{^qeg*E0KR<&ZSZH9beUcLU*?ZbP|tEqb2018#9N#= z*XY>h9ezLGnXgpCJZJKE#U~6RMp9f_+H^PlyDnALWe4kNv%9wEN-i9kzl6LZ?jF-B zdf=$ZX#T_$LC7^-u#+`zq_155T`a3A%0YzXWaFLi8;|jCHB$dw=&CBc=Y7H6w(0v5 z^R=z=&5qAsVP5DKcelY#_V1ffx8EEDroCs&=9ya#&qkJfIe)$RJxvzIv)qQKC$l3ov$f!t!}ZcGi99M@AxKZ@S9sL<+e~R)+EBO#*Jr))5U!+?IT! z_u3*|+h1CuC+6cu{2w4B0FTH-d{!N~=w8EjPkec+P<8_{bHJmHH(yLsyD@9PZTiQe zj>cIlW`rDhku4A>jtXlQ&dxpgdhW?Ebvu?_5Cb(0WJhcY9wLmWlb^uF{yGEr&9iZ$ zM@?FRQG4c9H0N!}Vnos6K5_m^jfpj-C85#$RywY*&0+BKrx<8(Z4wIxlddU6qaOzfYu>D&cKguhg5ch12H6riVQN*4-qOi=MD6AtIy}-k-=($F`)HK1GIe%diMxSNzhN&JP5DY6H7TJR zY}Iga;Bzy1|L#d;-o(aSM!PzH_~XUaVI|`yqn3K){INy_%+-d8>~^2ttVo(Kjd=%r zMRm8K&pz|rTyb2n=f92e*$naC6l|*=?DMgl+?We?Po?#Jb4jj#w)^}J^x4a~e=v1>>m~r@g+w8X^E=S*fqn-m%HbKa(lXo^G zY@Sbe)~7)z)$zP?_sVgmOe_$TBF9f+fFwSxvf;>rf-wW1idMQ-py8=aTBpWW?VOJ} zDwx~%Pni3AyIYC4E@MM-!sM|91-dPdHe)tk9s2cCPl_+`;%9{%s>}}cLz%NSg;VRG zF|pkp7PZd6=NxnUby}aXbt5-pKG6YD3GneJ*C%3~cO;2BMf>YOzN3FQR$RQ;+PWti zGhu?+J0H!nLmCsv^H~dL#!lt$YJD^um{;pO)5dML@L4}EfzIcb{6d!1k!77|e-Xd8 zE>7Gqasv6-X`XDhrLcVy!0b}BxO!L8ik=RyGyA#TT40vPqGTthW zUzX^Vx=ZYs=U`|KrWskcc3K(KeUg3nlB@u^S}C67_m`DTVpqkab(nmcWwZ z$WX|9On(R8518*fz1}b={KjtAP2@coNTav=^+x<#&-uarfo(wQ^KMGhMzH(!MMtA5 zuF?Ge(T#X#E=!z4M=ZCgGW*F;J;7)ry5xp6{6L#pxfr?W#ZiE?(>i~-_4{`J&g z^>y5*k=nIp!2CSPoC9XrwFhT;msu}UyPwv)fxku?hv^V|zZo8#Q6s{iPme660qw9; zJs{8S-4U$G@J0lobHS|-{`0Q})$E{VKltcpR(YyA zcfvgL0ei-6J)$JirZbrEys-HA&VRL-%ql>nQ+tw2=jz9#^xVPNE6D8d;`^tLW48Iw zrgHZM1#Q3H`cJJ?ZI0%HSY$T`$wHn16jAZ~xus6b{gni0M>n#U9k!e=w(1v)`p{yz+G>mGf&47%`XtKr$QNff_mO zLWhC#CC(l@D0}U6I?!90H0_FDsX;=q#I4GvZK~{Xu=(GGCchoP`s&Ss>{iy9#iry! z;FsckG)UP%+3~Ie_NdSxLZ_n90b>f7$vyntTuIopN>!$EYdfsNbSgMP7qJD~i=LjF zV9*tu@edvA)a)0=O(k5}noPBN*JSwvuT+pBJ&IE0Y%dT^d4g~yhpdo|qZC0^Y`6Se zS~0{=(N5O6CYuYprxA*`k@kkSzz==X~>^-Tx{5Z;niD(bG`D~58=3(`y0cHz6`23hTid;jO)!x z+$?dxU;~?UUfK1~hwnhn@V5&~n+;+t#2xN?+2DdBECC_gnELHj^+vtyNos4pc)cLBlyxN7LuK1F==i6 z(|eEqQS)6qIgWZ6RpxBXb{1gri$4G|Sp-eSnG#x7QNE&@orM)Dy??ypiI8HAirBQQayZk_&d&f=lSnm{ml6`?TW)wiD9a(2CwD>OMSLC z6PMDoOMrJ?^tQ=%xAj(rMlSa-<_9oX^N4-t>dpQR)~x(L!}MIcTo{vjXuu6F+nSxF zLxbrQH@3y2Uq_dH#olf0no~GheQFjXL4O9m)L*12I#V8L1IBa@P8SERv*`1*Yv-oXkZ(2(3p$P|bWQnsjf~d&=Oj~b~em`?<#H;P$ zMi!XJ?=aL?lb%V-IG3&oR;m0E;E9Gc`$-^p#(TVKgJ!-E-3yNq>4BH(J2!m^7t=C#}xaM{Z&p ztI#b#MeKDRA{GpKz3(HR#th6L6U%n33zAyXSGwl`wg$e$%as_oY_dI9KNoGIzr$do zE3n*+R4^B2m$CQB+uH&!mC4p_>85z6z9*nqv%lIexj*bZBo~*VSGeu-l-IiK79-bt zfY(UDO4`yB2=|n)gwIrQpXP!fJgX%Xq77&TM1X*;OCxkx!P1{{Z%?{maudQ*8Exs= zr313hRgQt*9$B_K8?!9H=QLyU|eQ&_tHJQllt3zPUGVjSRf<@g?^e989AXsRTu6K;2djoLQ z+1rz8NeeD;KR{UPPuD;?R<<(p?5xRi!Jm5v;?4E(rrBi%KVt!c)m{gfDld%>M%+%W zeOYR@KVmvJ*G}6G$c(_8o4K@H#Gxtg9iLGE!qRnzdGUIeGHWBOt zVD5{qVa>jtFnF&4PM4uqqjT(=_&{vfcl$4Tu#onZoSWMSw5ik$eJ3$Cm!@(Pjy#Gi zbG{n@2KVJk3c7>KS0vG+9tX&#FikJaD0sJ_n|jf&@@VwQfjr;~1-XxvZTI8Xl3ag& zM{CN=*BKXEip@?$T7G~(S2O|qE9QD<()LHB4Tq`ZV&HPz_K)X~cZ(QbEwD#;t)IC& z{1a6KuFzqb*%$>$>-YS*9)M$7M19a|y1;FQ zYrnib5nJ|()MZ@A-af}Us>3zwunh5)3ka(}Xl|ca#pMp^yG?8$@tpbsNW;SrJ8j6^ z>UikBfjbazW)96IAQwJz-}ib0+gKZbhxCA!N9tdN$iWdHhTLofG>xm0QqV|r5C(f_ z!n5@YG{1AVLDSu02;y2?P@DH;uIDrSbZ$2VJehiX;3UTF-GZqXhE`shg1)JqP6Fa? z-FF2x{w9D~TAi~--=w-c^P#&C2<&b;rCKce2{@ng)ub+B@!v&Cy{YC#Kn<&bW!*4< zjju==i8Aag1zbq8K7mqifuXJ*%LmKa!Z^8-BafhE&O5G0s2F(!4l8pWBX04=%voep zH<&ScMQDI`I)j~Hvfe}w-4C*kg}gwOe2;n=PxM(!>l3U}ohtI1`mlcRtJvys?d=&k>J)Zyj%0&q1K6H=dA!{o%F)^qjOV`Mwf}yLu~iiyY8{xNEoFz870|962i41w>uVQO3()o}^jm02j5z?Dw~}!*)8Xk1wIQwB~Kw zAJHBhWC&&n?f`D7#VjLO5L~4?P?f#uqoVJ|K;G@wqF0B`*OWlA0iQ*k^ls{o9EmqN zP`GhqWsv40Fyisbchhn2T#&l}Mta>3;6Ke@b#lw>#79*baQf?cRQ;&}8`etBzH^4& zm-+$68#JPFQGi?2kf~hOCM_tZTjr@MVkb7Iesro$g}lVGfY_(Xa*w>&=aWYpvko#a z@9VxWo2UM08n^n?4~6h2)+tH6#ssPOcz~B<9;1 zxA%Vi!;@=3;0gsQ5b|zz3CXOSV-F*!RCPAcZ=+ zf2+0e2OjLBvcgrfKY~qaP<>(abRCfWV)j?+JY2s+-Ed>GWYsrHMZl>`F*;muwO@6p z2(`y<4xF0*SRbYyWay3(yCbNaI6+^pZC=p_snJKW2Y6Ph4S+~-+KuAA{~Z0qoUYmr zl>*ii;fuK&i8cuftm_jdM=B=PwRf@S z?iCFD^5^N!0!QG0EAUFbuKjTCDCpJ`|MkY`A4~f8?%n(D0IG_Q7|n#F*%_g;Ugm#1 z@g_2pE#=MOGLx);-RCDq+A+HgP={CZ3wDz1_W-9=m9B-VHjb@Mh5OlJ4!;pd#A`w!pftK>kfrfM0rZSW?lU)$j{zyMH}%-%EO5EB6U^!UL2Yx_cC?pD@0} zG=Ma)_O#T{pxe!Q)5T(e7A$Y+jYl({^`&k}T>y%=S=+aHC+U@4=K6e5aWi+9XVa+# z2%vB~CX}#t_f!;IGLhbH^_XyrUw_e_eX%Hf;J|J@AQZ2U((e}Sa{!W9xKeJ#ELQ$j z0a*5x*$4M8rAul4n8vd@ewXuvPY*Y5h|ePKb@*-;7S$0~> zS(67->7<>Na=Vst)8rJ7vNcJ2{H``{UB1Dz5qr?h-fwpO#hEXM zqI1TiOJ!)QvnxIM)hv?TlQ_+UEf-@Cy0QlokNTB|IGPtg@gmQjt;x}RIwb8dMlge{ z8UgfiPOT@t1x|FUUcdYfd?yQdeSPBpk3u=AX6?_%zK=c U(sBr_TKU9Lry~`IzQ6E40Hd4Or2qf` literal 0 HcmV?d00001 diff --git a/README.md b/README.md index cd048fd..10bc5e1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Polly.Contrib.WaitAndRetry -Polly.Contrib.WaitAndRetry contains several helper methods for defining backoff strategies when using [Polly](http://www.thepollyproject.org/)'s wait-and-retry fault handling ability. +Polly.Contrib.WaitAndRetry contains several helper methods for defining backoff strategies when using [Polly](https://github.com/App-vNext/Polly/)'s wait-and-retry fault handling ability. [![NuGet version](https://badge.fury.io/nu/Polly.Contrib.WaitAndRetry.svg)](https://badge.fury.io/nu/Polly.Contrib.WaitAndRetry) [![Build status](https://ci.appveyor.com/api/projects/status/5v3bpgjkw4snv3no?svg=true)](https://ci.appveyor.com/project/Polly-Contrib/polly-contrib-waitandretry) [![Slack Status](http://www.pollytalk.org/badge.svg)](http://www.pollytalk.org) @@ -12,26 +12,30 @@ Polly.Contrib.WaitAndRetry contains several helper methods for defining backoff One common approach when calling occasionally unreliable services is to wrap those calls in a retry policy. For example, if we're calling a remote service, we might choose to retry several times with a slight pause in between to account for infrastructure issues. -We can define a policy to do this in Polly. +We can define a policy to do this in [Polly](https://github.com/App-vNext/Polly/). + +While the core Polly package contains [core logic and gives examples for a variety of retry strategies](https://github.com/App-vNext/Polly#wait-and-retry), this Contrib packages up a variety of strategies in easy-to-use helper methods. ## Wait and Retry with Constant Back-off The following defines a policy that will retry five times and pause 200ms between each call. - Policy - .Handle() + var retryPolicy = Policy + .Handle() .WaitAndRetryAsync(retryCount: 5, retryNumber => TimeSpan.FromMilliseconds(200)); We can simplify this by using the `ConstantBackoff` helper in Polly.Contrib.WaitAndRetry var delay = Backoff.ConstantBackoff(TimeSpan.FromMilliseconds(200), retryCount: 5); - Policy - .Handle() + var retryPolicy = Policy + .Handle() .WaitAndRetryAsync(delay); Note that `retryCount` must be greater than or equal to zero. +### Retry first failure fast + Additionally, when using the `ConstantBackoff` helper, or any other WaitAndRetry helper, we can signal that the first failure should retry immediately rather than waiting the indicated time. To do this, ensure the `fastFirst` parameter is `true`. var delay = Backoff.ConstantBackoff(TimeSpan.FromMilliseconds(200), retryCount: 5, fastFirst: true); @@ -46,8 +50,8 @@ The first tool at our disposal is the `LinearBackoff` helper. var delay = Backoff.LinearBackoff(TimeSpan.FromMilliseconds(100), retryCount: 5); - Policy - .Handle() + var retryPolicy = Policy + .Handle() .WaitAndRetryAsync(delay); This will create a linearly increasing retry delay of 100, 200, 300, 400, 500ms. @@ -66,8 +70,8 @@ We can also specify an exponential back-off where the delay duration is `initial var delay = Backoff.ExponentialBackoff(TimeSpan.FromMilliseconds(100), retryCount: 5); - Policy - .Handle() + var retryPolicy = Policy + .Handle() .WaitAndRetryAsync(delay); This will create an exponentially increasing retry delay of 100, 200, 400, 800, 1600ms. @@ -80,36 +84,89 @@ The upper for this retry with a growth factor of four is 25,600ms. Care and a ca Note, the growth factor must be greater than or equal to one. A factor of one will return equivalent retry delays to the `ConstantBackoff` helper. +If the overall amount of time that an exponential-backoff retry policy could take is a concern, consider [placing a TimeoutPolicy outside the wait-and-retry policy](https://github.com/App-vNext/Polly/wiki/Timeout#combining-timeout-with-retries) using [PolicyWrap](https://github.com/App-vNext/Polly/wiki/PolicyWrap). A timeout policy used in this way will limit the _overall_ execution time for all tries and waits-between-tries. For instance, you could configure the exponential backoff for your wait-and-retry strategy to be 1, 2, 4, 8 seconds; and also impose an overall timeout, however many tries are invoked, at 45 seconds. + + var retryWithBackoff = Policy + .Handle() + .WaitAndRetryAsync(Backoff.ExponentialBackoff(TimeSpan.FromSeconds(1), retryCount: 5)); + var timeout = Policy.Timeout(TimeSpan.FromSeconds(45)); + var retryWithBackoffAndOverallTimeout = timeout.Wrap(retryWithBackoff); + +When the combined time taken to make tries and wait between them exceeds 45 seconds, the TimeoutPolicy will be invoked and cause the current try and further retries to be abandoned. + ## Wait and Retry with Jittered Back-off -In a high-throughput scenario, that is where you have many requests firing at once, where the remote service is non-responsive due to load, the above retry options may not help. This is because the retries are highly correlated. If there are 100 concurrent requests, and all 100 requests enter a wait-and-retry for 10ms, then all 100 requests will hit the service again in 10ms; potentially overwhelming the service again. +In a high-throughput scenario, processing many requests at once, a fixed-progression wait-and-retry strategy can have disadvantages. + +Sudden issues affecting performance, combined with a fixed-progression wait-and-retry, can lead to subsequent retries being highly correlated. For example, if there are 50 concurrent failures, and all 50 requests enter a wait-and-retry for 10ms, then all 50 requests will hit the service again in 10ms; potentially overwhelming the service again. -One way to address this is to add some randomness to the wait delay. This will cause each request to vary slightly on retry which decorrelates the requests from each other. +One way to address this is to add some randomness to the wait delay. This will cause each request to vary slightly on retry, which decorrelates the retries from each other. -The Polly Wiki has a very good write-up on how to randomness, or [jitter](https://github.com/App-vNext/Polly/wiki/Retry-with-jitter), to the retry policy. +### New jitter recommendation -However, there are still [drawbacks](https://github.com/App-vNext/Polly/issues/530) with the recommended jitter approach. Worry not! A more robust decorrelated jitter is available a helper method in Polly.Contrib.WaitAndRetry. +Following [exploration by Polly community members](https://github.com/App-vNext/Polly/issues/530), we now recommend a new jitter formula characterised by very smooth and even distribution of retry intervals, a well-controlled median initial retry delay, and broadly exponential backoff. - var delay = Backoff.DecorrelatedJitterBackoff(minDelay: TimeSpan.FromMilliseconds(10), maxDelay: TimeSpan.FromMilliseconds(100), retryCount: 5); + var delay = Backoff.DecorrelatedJitterBackoffV2(medianFirstDelay: TimeSpan.FromSeconds(1), retryCount: 5); - Policy - .Handle() + var retryPolicy = Policy + .Handle() + .WaitAndRetryAsync(delay); + +#### Characteristics of the recommended jitter formula + +**Median initial retry delay:** The median (50th percentile) of the first retry delay generated by the formula will be close to the configured value. + +**Broadly exponential characteristic:** _Averaged over a suitably large sample_, the _medians_ of the timing of subsequent retries will be found to fall close to a pattern of 2x, 4x, 8x etc the median of the initial retry delay. + ++ _Note:_ The above numbers assume tries fail 'instantly'. Actual timings in the real-world will of course be increased by how long it takes the tries themselves to fail. Consider however that in a scenario where a significant proportion of requests are failing, many may report failure after an identical duration - the request timeout. This effect can be thought of as simply 'shifting the graph to the right by a fixed amount'; the underlying logic of selecting retry delays to minimise correlation still applies. + +The smoothness and even distribution of the jitter generated by the new recommended formula is illustrated below, and [third-from-bottom (dark red line) here](https://github.com/App-vNext/Polly/issues/530#issuecomment-441740575), compared against other approaches. + +![Figure: distribution of new Polly.Contrib.WaitAndRetry jitter formula - median initial delay 1 second, 5 retries](NewJitterFormulaRetryCount5InitialDelay1Second.png) + +Credit for this work goes to [@george-polevoy](https://github.com/george-polevoy); included here [with permission](https://github.com/App-vNext/Polly/issues/530#issuecomment-526555979). + ++ Those wanting to dig deeper into the distribution of values from the formula can use the console app in the [DecorrelatedJitterV2Explorer branch](https://github.com/Polly-Contrib/Polly.Contrib.WaitAndRetry/tree/DecorrelatedJitterV2Explorer) and explore the generated values with PowerBI, Excel or other math analytical tools. + +### Earlier jitter recommendations + +The Polly team previously recommended the widely-referenced jitter strategy [described here](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/). + +For completeness and for those wanting continuity with previous implementations, this is still available in Polly.Contrib.WaitAndRetry: + + var delay = Backoff.AwsDecorrelatedJitterBackoff(minDelay: TimeSpan.FromMilliseconds(10), maxDelay: TimeSpan.FromMilliseconds(100), retryCount: 5); + + var retryPolicy = Policy + .Handle() .WaitAndRetryAsync(delay); This will set up a policy that will retry five times. Each retry will delay for a random amount of time between the minimum of 10ms and the maximum of 100ms. -Note, unlike the linear and exponent wait-and-retry helpers, each subsequent retry delay in the `DecorrelatedJitterBackoff` helper has no relationship to the previous. That is, the fourth retry delay may be significantly shorter (or longer), than the first or third delay. +The distribution characteristic of this formula is illustrated [fifth-from-top (purple line), here](https://github.com/App-vNext/Polly/issues/530#issuecomment-441740575). + +### Characteristics of both jitter formulae + +With both jitter formulae, note that unlike the linear and exponent wait-and-retry helpers, each subsequent retry delay in the jitter providers does not have a deterministic relationship to the previous. Due to the intentional jitter, the fourth retry delay may (for example) be significantly shorter (or longer), than the first or third. -Internally, the `DecorrelatedJitterBackoff` uses a shared `Random` to better ensure a random distribution across all calls. You may, optionally, provide your own seed value. +However, both jitter formulae do tend to increasing backoff. That is, averaged over a suitable sample, the timing of later retries will be found to tend to increase compared to earlier retries. - var delay = Backoff.DecorrelatedJitterBackoff( - minDelay: TimeSpan.FromMilliseconds(10), - maxDelay: TimeSpan.FromMilliseconds(100), +### Ensuring optimum randomness + +The Polly team [reviewed the literature on Random on .Net](https://github.com/App-vNext/Polly/issues/530#issuecomment-439680613), including how to ensure optimum randomness and safety in multi-threaded usage. + +Internally, both jitter formulae uses a thread-safe, shared `Random` to better ensure a random distribution across all calls. You may, optionally, provide your own seed value. + + var delay = Backoff.DecorrelatedJitterBackoffV2( + medianFirstDelay: TimeSpan.FromSeconds(1), retryCount: 5, seed: 100); The shared `Random` will still be used internally in this case, but it will be seeded with your value versus the default used by .NET in a call to `new Random()` +## Sync and async compatible + +Examples in this readme show asynchronous Polly policies, but all backoff helpers in `Polly.Contrib.WaitAndRetry` also work with synchronous `.WaitAndRetry()`. + ## Retry first failure fast All helper methods in Polly.Contrib.WaitAndRetry include an option to retry the first failure immediately. You can trigger this by passing in `fastFirst: true` to any of the helper methods. @@ -118,6 +175,12 @@ All helper methods in Polly.Contrib.WaitAndRetry include an option to retry the Note, the first retry will happen immediately and it will count against your retry count. That is, this will still retry five times but the first retry will happen immediately. -## Further Resources +The logic behind a fast first retry is that a failure may just have been a transient blip rather than reflecting a deeper underlying issue. In that case, trying again with no delay can be the fastest route to success. In this view, it is worth starting to back off (introduce delays) after you have had _two_ failures, indicating a more serious underlying problem. + +## Credits -Be sure to read through the material linked from the [Polly readme](https://github.com/App-vNext/Polly/). \ No newline at end of file ++ [@grant-d](https://github.com/grant-d): Original author of Polly.Contrib.WaitAndRetry. ++ [@george-polevoy](https://github.com/george-polevoy): Contributed the new jitter policy via [Polly/530](https://github.com/App-vNext/Polly/issues/530). ++ [@hyrmn](https://github.com/hyrmn): Added documentation. ++ [@reisenberger](https://github.com/reisenberger): Pulled the new jitter formula into this repo. ++ [@reisenberger](https://github.com/reisenberger): Extra documentation for the new jitter formula.