From 36b46f2961c8c7fff561c867d50c49831beff0b9 Mon Sep 17 00:00:00 2001 From: Zhuoran He Date: Thu, 3 Jun 2021 01:11:01 +0800 Subject: [PATCH 01/30] Add CheckTxnStatus Msgs --- DistributedTransaction/DistributedTransaction.tla | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/DistributedTransaction/DistributedTransaction.tla b/DistributedTransaction/DistributedTransaction.tla index 151410c..6a4ac9d 100644 --- a/DistributedTransaction/DistributedTransaction.tla +++ b/DistributedTransaction/DistributedTransaction.tla @@ -103,6 +103,8 @@ ReqMessages == \union [start_ts : Ts, primary : KEY, type : {"cleanup"}] \union [start_ts : Ts, primary : KEY, type : {"resolve_rollbacked"}] \union [start_ts : Ts, primary : KEY, type : {"resolve_committed"}, commit_ts : Ts] + \union [start_ts : Ts, primary : KEY, type : {"determine_txn_status_req"}, + rollback_if_not_exist : BOOLEAN, resolving_pessimistic_lock : BOOLEAN] RespMessages == [start_ts : Ts, type : {"prewrited", "locked_key"}, key : KEY] @@ -111,6 +113,14 @@ RespMessages == "commit_aborted", "prewrite_aborted", "lock_key_aborted"}] + \union [start_ts : Ts, type: {"determine_txn_status_resp"}, + status : {"RolledBack", + "TTLExpire", + "LockNotExist", + "Uncommitted", + "Committed", + "PessimisticRollBack", + "LockNotExistDoNothing"}] TypeOK == /\ req_msgs \in SUBSET ReqMessages /\ resp_msgs \in SUBSET RespMessages From d50703f5260fdc206d99daca1ffb6459afd82420 Mon Sep 17 00:00:00 2001 From: Zhuoran He Date: Fri, 4 Jun 2021 18:34:09 +0800 Subject: [PATCH 02/30] Add CheckTxnStatus, haven't checked by the TLC revert a change DistributedTransaction: fix checkTxnStatus bugs DistributedTransaction: CheckTxnStatus: refactor change a var name Co-authored-by: Andy Lok DistributedTransaction: CheckTxnStatus: format spec update CheckTxnStatus comment Co-authored-by: Andy Lok DistributedTransaction: CheckTxnStatus: update comments --- .gitignore | 4 + .../DistributedTransaction.pdf | Bin 204939 -> 209653 bytes .../DistributedTransaction.tla | 85 +++++++++++------- 3 files changed, 58 insertions(+), 31 deletions(-) diff --git a/.gitignore b/.gitignore index 6e72a9c..a3fd7a7 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,7 @@ states/ **/*.toolbox/*___*_SnapShot_*.launch **/*.toolbox/.project **/*.toolbox/.settings/ +**/*.toolbox/*.aux +**/*.toolbox/*.log +**/*.toolbox/*.pdf +**/*.toolbox/*.tex diff --git a/DistributedTransaction/DistributedTransaction.pdf b/DistributedTransaction/DistributedTransaction.pdf index defaace73fe141666126122e9e337fa280b08f6a..0e4df88e32683e36fbfe70283f0c8ad61b200cfb 100644 GIT binary patch delta 90678 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Ū)%i-GTNyS>NxWLJsgHN=&?rv_M zq6~G+)83HQK4_a-tv^i^)v?@I`O;<$5@*%~LUV2{jnUnF&Jtqr z>gk}RgPF;7-zu=F^&93f%?l!fK2+(ELzIUB@D>`Y0-=jqCwm);T+N-*nR$0H`=LYW zzde7uP+x3_f|D$3Q#0=-P-$H8E>~k$0vOz6sP*m298tO~OaGSX&OT;6`Wq)*uEm#* zGzb=@r&o|Bj@INyqEN=bzog#CD`?mzhzy}=J6aR7>eKkY>OG<|yz{l?!l?1uVeFs+ z=r!=lXj>H8TbgWb&D8~~Y-su@X+Py7jE43#^EfwahEdx#W@Zr{?Nb>@w^4BCET=bj zIAGT@!#Iw9U&+Y?2D4@ByZcIT3#osKcX78Y4sX*atSZ9&Xi+o)GBwQ5sN$)KUDL#<4y3wrQRFC*8E2a25_Qeti zj`RYzb|oQE*(~=vI!ve2ro8WW)Z(?u3S#;2HI394Y6@G&6!0n=0ww4r;psaBXqu z_R0IPo|kt1v1GzwXUUPd=wPtbH!WN;QZ&7MG-b3Hi2S%JYrg$%2gEAzpWmM>Rg5`$ zN?c&-c*e*wP8h`+6C~=4I_CdwPA_Zkx0~L;XVGos_I_}%6l*(;af|KKEukI`7eOA3 zd6MsV(FM<{OR2*Np>##?4!*VnFN+&i-t0Mlp@L3qlc8YfC3raG>9%P^u6DghQyC9c7v0rH?(>f?K`i6D68yL|QzKXO%*wJUL6EPMMJsC(uxwQIs{)hEVwHT)y z*y~P`LgAx|qM-}kSrhB}F7Bq>M7{O$N?Y)j(fVXFdp@g-0Nr`_4JM0y>17FdPs_AZ}ehd~{+3w;uJ4TNM(6$x50IakB%w6uSK*Ju}b}j$)b) z>C9`OB*FwUdEma~Bj_#(SlM5(THZnCARW)VqAMh`Y6gXJo>?Fdu%_z0hgeT;1wA8N z%FPB3-p<@p&0!>2{`n+?b?=0DVJ#K2kMC?93N!VVX`uj4JH6rD$$^`Pg6YWu8|@wV z+`+*;DD|OF75>XaG||G5ZJPeON{fk`>j733mFNf^E0Qs|gKrs@)_ko>Xxb_kOa>oL9h6bD54VI`_#Z^Kf zc$$*mm;kG^6msBS7KurQus2PU2zrgMH4h4huBjxSW9X)Zewc|n{53-pQOaGL&RIU3 zQn?5!U_b&nh#2jFTlYH=^dhP#DYI3l82i)hlpf?5;*fO}lzB&@PKQ_{ z>ybAQOF;`+io%qy|C?vYZ;v`%?IO`}!1HWWSH5aC_1l)2>U%ejLZ}pn#l=&Ju{2KA zRCpjv>9Od6Jni^fpvTlAC%cc_-s1_-xlGwxFzh(`8A3e?Tv!DRik5j(9wS!RBo#<5 zRQZH&-=0_)XylTf!O|IhGw_Nj&9Sx=Jw->5Ak*c7cW$rD2xYFg`PNgu#6*>`i6ESq z?71oP6~U}0TIN$E5&8PyP=G96A|&YQwf!Ry37@n|U%O+G)QPwhgXOC3Bh!u^L-G5fV2!OZGen<2>EbemVE4%Xvbt&m=~x8DUYA zqO0)f<}WarZ(NLfT^kK%(C7NwWSfk&G4&`G>}-4K;E;cZa}m+t5QVWElceqv6zw}u zju>Z=(K^JNgbi7UiKIj|W!t@l!V*s!9grnejrkY(*ruGE2c}Dj%;+25;Ig@E(>6p$x z3u+OKK9TsS|)wJEVam2*E^QQbc(UqA@Psg>Fl<~0yZ{?G8nUpezFY=; zcYpurzX2vCUuAY+4F4XB6Qc`$yl(?pd-S5=SAsUZe6T@jb`IPUQX$tu4>zxZrD=hV zH#c+U zMkg~69xWKfIRJFMQkNc-R@gi*5pNpdTB8R9(u8kYD4C@@9(VrnPz(P#V5GD17{lcItM?GmH}GP8D#Yfjs8gigkW399Nf^%@bk*`OY37vQvDHN z;^gFjT;ai9^U_|?0U58f@duHB<$=ON#^Uv>=~yxF2L>A_^PV0CZ3opG2Pv;W0Xi-y zX&5^2s4e(+1&_&Md37YlNrXxDMj(0kd5QDt5527&C0uPC_-Y$>-$#+c_$aK!tM7SE z)Yp1Nw7~Gj=9`eA?N2+iPmOP8RY*~%kLod1!j?5(LG78_pUd=qj3BO*l$4B^NFdYS zK<=3v0MmDCFAfFh?JfDkuJJ33XHPyqe2~K{2@s#kasMkJ`_0KgTVcP*M7rsvx2>2j zIs5T(5H>m$EI-)k-ciEKsmm*~yceN&-xqmxLJ+;;r%e+m=8yluG+ z>59UgRW37jtp5jkG{_Bu83x~$n;J#RGdBXv!I;^c*g(0rh`hg^IKL07f5rFQn^)F% z@W6C`#I8P>yb=xAep!N)eIY<|Yy($CAf8v+#D1p=iDxV|Z0Vf<%NJn#(3SfBb#~q5 z`T|(re$h$yE-XIUaV>0pei1d*WG7c10brY%iwg%nAp0U$CWc>t7>EBCLM8w7I54_! z`Q=%p7qtZ~vb?$5k$#fCf}OVfS);=vtLXtUkyN4kgW=0E9XLM)qBAO!1r%ZTZ621| z4ty}s=Duz^rcz5M-ro9NI+(s(LAQpEfFq11F39u`4s-f*d=L{VDiej~r%YceS3j=1 zH{lFWfzL}*5VAml;R7Go?G8x;z>jAb_Ojagzz~`l?(64kC^twHDG-L%P5m0$3=(Aw zgyD5le;VwXf-!%8Cs79}yeIOUB7O_qWE=j-e?qtgjs*gH|4rpR8|?nm`%q1gDB6FM zxu-)IEr&dAyitj_Y^GX2t6ncLG zpiz!N*@JcTLDFwSG?zl^w>04QTIR0g|NbRG(3jNjv$_o1Yrdd?4!;%9tvLj=P%m@d zJ`Rk~9>R+DD@P>Z6GsLO`=}n^kq;ex_>x*in0Rw(R6mEv%y~3YwrchCcqJ-X9 zIoW4Plun)K8Qd@5i5;%vc&`J4eZ1_8qL9X|=rFFXQF=rkdG{2FfBxHoG!`x3m}NWX z=wd$5r7)Z~2vMbf?(!FpE>#qujuAgCY*k!LZ`fg3^B`3niA!&?&piP;<#3`LIru)X z#D3_$+srP}X=l(rt<_mZpszR-{;cq%P)JKV$Nh0QMS4<$brAzHzTbmDw~9Dzg3)}9 zZ0$aiLQQUe`G`2NJL+NlWABD0kIZ{`*?vYb}tz!{Z9_ek6#;{P4F?bo6N}lYVkQXc?JkTip9`Og-Ro8y#e+? zUTrxOb)PxFIu3qq$5HZXti|3*!r(MT^Ig>(7HIU^^@ER3nXy%kJ4(iE;O#Ax&J$~= zAX^;85=+bxw8;pxg;II2<|R)hPP)RW<#qiX@`bRYv__J^=Am0Cpt6(U)O{LWKk2!E z;9SHJmQq|U?9>l@t`?6ojm&qJFDc3XK^ksPRRrb5PUb7TwaE0?*?H0vZu?o{%a~Uo z!tghzKvS!|`UOGDmGnM233{z7N>|-JY7S*3d^^>1x6BH~0Fi$L1Cd7|%%wTiw3J$D z)OIbCMz`B_HISIZrcv#XpM-2=>FDtw!US(w54S%j1^X7@{07w0H%^kHN~p@0vOR74 zJrcn05>Lb(g~XRq&wAL5G}t2wxdl8U5;)-R^@Kz@_Uiqz4N2e2&=lg!*2UB`a;-9GvHt{M6}_lPpUlhY<0Eq+@my7P@Kjzq z8&a%FJ35;nYTgcOh-$!Q)w=I6VCORF7_oIAp=jZB9-)v&6y=Yx^gl|>KgM&Jbla@j z!S%S7ew28`qq3*tcOz8SSj<%erMgWPTBRLySV!30&t~b%>%QALlZh$2pPLKtM)+kX zDBB4HYY_L2#g$^4ewq@_4ct7XS=CDABT+P$+_~+>(n))a7!Qo$bc<%VFw3{vN>(?5 zHI$w0ak=?CxyssJFox}<;7dwWx#>PP#}cg5iqoO1-=tB}u}#HC{*?`?B3*Ls)W-BU z$U3QJVoa6x6#aHrw_WKqr&MnVVMdQWiyjRySaCz2NyWTJJzjCbInbs=TaHq)%K1L7 zC0J!w$YQiG>csyrY0AP$oTBT*>3j-LNKPF6e1|IZwl0g02P+6=B@F*($W*taRa|xe zvRi*P;3T6Uwf_+@5KB*VfreKLIM137Jj}y$VQ_8d>{;(AD(vY@*(6RVTZs!+{S{r!)Ulb zWt`n>Gy5#bl))^%BExDSMP{CqU*NN3KE(rpWYpNl!NnrhR<(OGyz)eqpdA27>ksN3 z8QAw)GXpy(5Py&Ue@^Z4v{k?C2{=i9Mi5foBGAXqlPw~}g@qD5BCQM(Y7*=tOX-Oz z8V(!BWkUT+^LP)42pshM_Fu2~wP5WrbxCO1YA|Snna_XVZ>U6?)%;RZ8#LResu`kI zM(VFr?ZWy~bdI?a-)Xg*DO3$$BFeu*Tq|v-u^g9RJjNFX2%EoU#`#*6B>Ax$L%W4Gv3YDo34@Ye~RB!^IDp&+&qVQG^ zIPPx0Uvfd4Z+Qw4@bx+^!tY73*2qo&;f4h!E&=;+d62`4A^&RF-o+<#{de6TKa)&P z>RSg6G5XzGM$B@QlJEq4a=i<-y_~I{$d`rhWSRUrIca?yX-ccgf zqa!(3HF}v89=EUzw2+FBPRl=xi28je_^6HXI;D+AeVpQul8$8=3@Q&n0O-yaTp)@^s!;NcA1f1N;PGyFuRmLL=N&G5awreE6?UQ1vGiY}d_?Pc{lXcu{C z)$OLxv%~4p>wib|g4Z?Q5fpPL%Ba*pB}+r}uKb+nzQE(-TATNS-^_6LIj+_JB$O{d zRHdraaw>>`p?zld$XykV7AE-^=0nE1Y{f|J!lh5G3v2!B3oiThcIQMdbA^ZAbQ2X4 zw1_M}I?1y!QEjjSWH(!-QiWn*Jj0V~IhgAt|4_EckO|ihHcAg0C}dv1sSzM0zUQR@ zWRSksi7*z^JDG_W#5Eq&Dl4QwIn)wEcKS>Z(s)&!c3RU07GXgmA=X)RJWC-%(Odem z@%0O5Jeb)NwEvOXmt+4$&EY}K7ho+C#l>>-D?>rtP(aug@Rh?phkg9;hYY9fuEcy|+!_8^q2e*b|#6YTEmU_ru>9wP?XHNo$A z&qu|rTW-JpK96iRWe$29!{S{_NAo)q5_kMGYGGsdX3aE_Awa`>cu{U1=R^b1KbN*m z{luMP6gdY5kg0IIxSY*#(K_XeX5l(*!Gy?WJ2V-ph123PxIq6#+^Nunm2=zcP^@?v;%Ddlw788@d{*f!~2887xUwzk{g7@h=MJja^Ul zv-trIwDbg~*P*!hF?-J+Z&NMX&{0p|?X58rDF^kf=|zK#isOTqNc zJ6PE7Lu6T|d8Z>Ad7wJCGy9`Sk}z?hRorIE<=ZB`OcfuTHnnn2>$(Hf@rK?P{1U#&Prknc3hLXor) zxvp`%ug_tu>r!>{#>fmTDGSIfG{vOJa%qE~ISt3P6=i`bR>kG#Bqvzm+I2PgkdI1{TN|;Knh?w8-t*)ZN>8 zMVcy#N+({OpgSF}q;Xc}VO&P5tPpK10a_`}5`HBfN>7WO2#B8o+F2#cE3ZLq@I=(1 zB%PGR-GQ$Bunl@%3RwS)Sd^ZVD;qv%?l34W%!dd8S>-<6=)jfiUv8dy*8?!vU6xJl z?hl6SAt_|eczN_#&|$J&12^NUMRn9CrnOXhq1deD;Z}a7SqaOuU76GpwnK58K;H1} zf}wshegnG|7+u}3@q?M7iGa;AhXY|TCyUg$W|0KGLTqX8_N}ludX^xpU`12dLHEm4 z{edRL#zV%02#MXqwUJ0#__FNc5ff)Tu${8oNHrNR`rMo6))!t)uv9*84Ha!9x{qb5 z-lm&*=eSK9ZP+>FvUhx|W2>JZKosK1u~~-ZvcD z&2U4J&3EFe=o4+0Hu@lj4vv)&{`=e~o)~)EZyZ8TQWi zvSH{m#G+-yrOG{nMo72UeY0Fk>R9w2LYdEYsV(b5Q&FH%f4FIzI;p;hK9#<=G0M#C zU*R`w_(L%VelE|TG#o1rgmqfJ!y^3^%&16-gF*oju%ciRiVMk+6P<6 z?U5+-G8Mrg zUx%_!EA|`*qgYc;1hyvuD=gtLkXq=! z)x=j4U1EP30HA15AlkKLRta4(ZFo7$&*91aZe{LTsQz|`E>H}Ykk?8)aD2!1*`YVC z-LQBdLErbY_Ls$P02^a7Nz)=k_M)TT{6&DRQjBMNN{#;X_e5qC#1O{qXdko52Q z_!a3``eQ+Hp%T6ufmc35t9)S@dNbj!jT)h*`reNy1czJwLhhRo?v4E1b5dFUY~yyCYd z6se@{{C)~Hg3FwJgoXX((K)&OjtW}TkSb&!^1CsgG=osW_W~J3$lMDsjU$vgoxb%y z&S9d08VQWMF?wE@a~PygP=$BB7(#&zhhyrjj!(ZSuTjQ5WK}X>Z__;1ksF*j-HFvv z2-4y=Vfa3U0kZs<9b3mxGjN3tEAf|?kE5gdEK$gK`eJFF!Sj`2S$E^Z(wYYBKDGX3 zn?FblCMlEAS)2H5=wFA{E5EYNQg(+}novuZ9bpfDGvbn(qM9RG_s~Hlux&0X#6pDb zO7CMNrOY>}{IauSrr7Vux_Au{{>P?8s;n|r|7aWE1E#e7l_6d0Lc|9|_u`c6S$LEf z$mj(bVh(ovVNJ)IL%K3zes%6-7&t<=&qCH{a2+Dt1?Ryymp z@z2{s2L>cH6e#duL}O_o8sNTuC=ks+GCJ7&31g%e%jI9OGrn)WVZNK@juqxwJsodw z^i9f9a5=bU>*wTr;ar3HbJ4wG@9Oa8fwwnGaNS|R6BWHwYy8&1HIiB$5 z*A#9#x$@rzs@5M&1Dmg4MIz)!2+lLZl$`1u9zl5N4!97B?CaYmzniP%#e)s>=KxRJ zMWNg|0$+}OPsZKL^&(?pA zw8a994mDkK9T~`yp90k3Q!8uNsDQtt5r4`i(K=hr*hCH2r%~ht80dNDG?Ulq&g^-h z!2NiZj(M@{&hNOpRtyOFl$^hz0tgHf5QPd5w>vw(M=|w=p?NDJ>d%#I;o|xo+zV-( zb+RPK{84m60==nKF$2#AT5uMX(x@JdKJu@{K_AqKRfZ6mb} zm08zw(#v|_cZWhSf9gZ(lgy;koj6H8J0~sHFf?<=nZL5v4dVuMK-`B!*C&39!^55ef$FDgmIfzB-Pg8~_0aw1pGLX>tZiGa*JAFNh)87{U zsc3dQ3}S-!_UG0j^>>5cS71lCKn#m=$A=HPMgfvKrOa8i;>VPqV0G=#1 zu@ATET-8L1L04&qf^kOeyf9DgfK>kESC0F%oHv%sOh@q0M|=Fb^#stwbi%8mZp?k_ z&$$>hlG+l}!b5Jeb`o;1wV>K5Q+uGI9Nr|~Oz8{lIx8xuROjPL|1w#X{(Pp$Evwah zzvy7$Q}^>@mg_n(A6m`pEhEt@nGa!ymfGjH0Ec8ByruvPc&^krE&<;rU6RdbKKiFK z$|CX~V+(P<5cbNSe0IPO??tJxr_>q$VvGB4QquL-q{5hR`s7P3w4T@y zv)F9JeiC-}C~^nm)OdIcfNOUU`*HS@v;MMPlQsAi^ zh+irOr-XD#`5XoCk*|t3N#24UkUAc*6SFbhd98xqRzW!h+_mc~jJ&`1n8Q@VKzO#ShXytf@6Z;m= zTLT=Q`~5VM>__!2qsoPVL1Nn;7$ZQNKevkO=it>)(zihTgG2tSVUZOS77D^3dI@eag3wk z<(s#4Snz7gDhJ;=6J>QEN4$*r;oZ6*9U#@yeZDwp@H+T+_Lph{{>+5gavIId({K6tX^8N(y77anAaXu?%^hus#YW+ z`zU4w<>uI@KdK_ z*_w{PcK3i%h#btn98uB~uZ=SV*EeR}zntxt9X8r;vQFxl2Zuz&Tz{rjYYUPU5Z@7g zpV-kX7CEk-w4GBHVeP7=$>)y=IBF>7Ot1#P;M{6(%65m@qFj~)da#y{_RZt4usr$8 zV&*SR54HXV%ybBy%IOwhz?!v&%0U5byHVWzW3QnI)uXYK_hH7iNhJC#2wtoAfwuxQ z-6Oaajd!w{&JK8PCPq&@={H>d2!~A=#zJEF-Nd<%Nn0(116|=6bkuHm-Vy=-AeACd zx3cN2^YK0V*{RbGXlA74-}VMPzf|C9>`M2tV%E;CURIJ4gWuY$kIo`CeJadwruC$m zp>5|4^MarIs&@XR%@oyb0!k?#$$bIE8|JO{ekt$j>(&jFsc!{Xqu`QT<$wIh9x?q0 zB&Qr9y14TXmj?I^)1u5i=0HC9-JW~bcX#`ubFMIKqxbM9>t5zJps_nv(@%#mD#0}k zT-NG)+B#=4WzT}~@+_2PGEv7xP!~|-JWc0lM<5k&ln2{7Fa!qstPaK*A~9bxD1|^^sdP?YeHU z{ISMYpTX1Cy5aLVXtgZTDgLpOw%$I+6~b6haIw#Q!w|7I!S zKg)L-3RY@;8IVc!4bNY_`gR?WGIH+j><1wwMLO+C{X9$nqhj1ULI+QF7Asct%r5bh&Y6>OCh#kd9S{6Y_6m$)Cusg4M=B(^j z>w<^YLG`*u>4X*47Co?kS?+!n0E61s&9E-Kt+V*8Qx#xQa?z9n@kQFvsS;i*C(*|;68^onzN#h`edB9B0%)4W3Q^fJDzIJv%f zK@W{7-bi(9H=Bo{H05b;2-~-FO8Y?oAA$wM6GY$5!@;YZ=`jNWDC133Fzdf6l7k6`|BX(t=gm zQ&2-tG=%uh>I=3rRvUKgFkS z;)YV>+#mcOzO560OPq@7(HoI&@aA$4CKcUtQdpJM@EHj$yUDI3?6)*)$1|1Amv$-T zS~%k0AeYaTW=@l${Wv`(kjsDC&?KYj@}&>(l%BT=>#Lub4v^;x!gipz&d8`3Yuk+w zf$!Ht@G=9gyrZRI_ayoOQ>zZ`-8){_>O83Yq-`0)K(a8lNbgDIa{^QmIvnI2?jNxI zXgf3XI5d0`HWdLDXVa=M_qqf9SYrv^Gr-k)(9H-LDsb)Bzg#Mog6khEbK3)9lQGOS ztpZM~TQUWSZ_e{_G#t-971qX2z)>4^e#15JRtQi>sF`pJ41JI^M|;-o$Qul^zaCo> z2-T-*K%ps>yjxhqX-KhsaG&Q_u3pt1RhBEd8~uLnzby1x#X22M&z50fXQ347bG?b$ z``fSfN5l#HFGAX8$hG|d zrvc!F*N^kGV?qr9nu_pct}%Lj{wwc78Zu^d8)8$R{C#gppWkn12__fRtuu^4WJdI|uVEz`+t7z?87lTy#H`(xtw&@53-*Okn( zYVZc_6s@x&Io>FkYusmjUzqLBbt&^n1yQ=qBc4TTd=+~BcBNvhkuijcTYkO8BBU;{JUU z0}75p*@Wa;)z3C$_*D%@?<(fQ_8CV9w2@vAbMG>EligO+OuF&FTr_lx?3cD-62r0Z zKkn5OfpPU+7D3IsRz1&9Yo~UzczUl3sPPOGq{qCMtJU$W>Gf6EE^9*M&AIS~5jwtZ zQ~pV`a;-7frvx%aRlAF{I1BBwY$t#hXkEQ(rupLi&nxP^eaL1#zMuIqB5b_4uLtHl z73xb^CgLa)LM+xeN`iYXQNLv+vGH=M4ikhCup znqGI0W~Q;${}`)QwsAA^xnEgZZ`6h@+iVdzJG`m{Pp^GFc2PA9_S)cw)}jL^1;Zb{ zy)sIQI%ubTT_ej6ni6ez@K(qIDZ`vwsR~%|3#_y=kM=ZV8}8W=a@*pLtj~=g?=g*Y zS0y76h9(4C1j`}^TG4x-jbrHe%ubSySo*)EEUvIVj64E)J|YRHcG7COWpG>T*z3Fe zK^y;fEgBmj*on+$_QHeqTZ}A!ifH zx?8@72zxBjD{J*Wtj5aDlF}^TcAT!GRrIv>?uz?Ne3%lw=wws|tJDBZ`-NFyrEd^2 zb*vOFKAOszZZAKe!emrnAwn3Mf=m!UUIY`BoiLJQ+=9t(rWQPVEwXcv^VT&3ArmpMcxPeT}>Wmb@-p8@Kp%>kUc|0z|=>L@eGc=lg=C zy^Tm~uJ!|w6JnHdKbuNhSP>Acr3n#qlGum?SkhTnE);-C=??PF{-E8-J0#g&zMKrE zZ>aF@FeRBOvC(h(<%H44c&%-4=OjYBmcy_|ctKs`%2w)H{a*5kbphO*G8Tg)(kAnmF0 zBlgE++DO2n1#JYu%7EEAR^H-VIlyG$OBxY@e_MHHsk^-3`L$2Hj2PcaP4eALz0&sCmt1iy&>>MTmq5M+|JIidFelXO7me`N5FO)= z1P@n}?f6ixo5gRGCit$hyS{qi@^Pe+W^i93sw{c_~3I@;9w%gm;1Ce?U?zm ztOaXa%SX6^tJ(B^Tw6c<@Z8ot;z%)a#|{92z5IjT9z0%|iyHYXtt?v%M&KfD+STk`E4Di zp9P3kUv}=U6tY}sQq~&%@;o-mKRK8fkD1S(4_8PXs8ry6r`JnKo=)UY7S92tn$?X? zdDj+TsLgL7c+VIamSmM91M+h5M!Dusp(T!CHPTVAz@YLB+8$Dxz7BSZLfjr3)B?#BL84^qGU&Bv!bH)a9|yO_{6oUvQbd zKTA-D)w-3a6h!N!OoM^CcN#HmOIs(q3I(|swdL4~z^0v)P*|%G)xB_JM}ceHoh11` zwrT5B{a6dq&dC*b8R$VgfZIKTb$G&+ckA9i)c(~n?DpPo{L8JyCTp`{<}Dzn?H>3Y z7F#k+G>YW#q|W4AsVgi{%)fw{=~A?luMMYxsHwZZaiUjC!O+Q26k_nY(Lirc2GU65 zo#bPn7*9x&Qe-Rr4ofTd`k=sl3WoE`mkrOjXn+6RLV1kP>=PfG73!f?Jco0ILdKpy zqCow%yX-^ymSIhPb>&+(ckuP?T_Ip97}HaY-?_%61f+n~5wNufvWJr7K5kwPQO6(@ za{B#ZKdUJmqdLlS;VR$GWx+ShO-8&B|9r(PIhr#1Sd@%iInd=81WD(c!4fo@@MikZ#)jV5nCrW!+vZ$KZSXRJ8 z3QJx&<4fPqOa3|(CH%A69OZ$tr9?T1simDdo_5!jy>Dc_O=%L&S=63hp z=O{~8l!>SZac?++S<9XSdoX-|qeMP$Pm@Z{Ax#Il$Mw?JcU&M#aO&l{J!t5XaaFcjRHN1c)SUK$*G`7nnb0c@ zZoc=vdZ2Po^LmXiJQ6@gY7X0hOsnB@>&^u}4YyFYF#*M?@{s3zWFMuqz1wA9eAQgZ zw|-@VQ98`Q?bFDPXntx}l(5wr_x?Uk@N5DeUz1Ed+7%xV&y}Une7i^ud7G=kZ~Lw2 zuG`*GHJ5{^Vxl}bNoxL&n$bP4#m3FcfiKS9ZhKp?Y}=Mjoa}%`#j<^?lTdcKN@dTl z+S0S;=XYUQf!c|Q@b){KZbBxN+^(~oM2!RrEBPR?gvz3VYWA9IS;z(=^+1`E_hcoq z0pe%y>%|Iucq8w9d z!nCrNB}CG;7tS-w^^!Fy+kK$1Lt8m+GiG~eo-K*yJ-Dc%hHM`#Y|qHgS=Uz)mDyeu z^k_k?+YQeuvvS-l;*?|($MFr@8{D+bJxyEo_TsBIAK|%?ZkVh7Vx|G?4a3*9;PGU& z8OFzXehug>QW-Rat>_(A5O1-{n%9wFzf}xHh1MTUSrL@9w|&}&2zJ8x0)-x+rT_Ez z-6KKvEt!dC{v7m7?gL!%wZM|lj@$nQU-fqK;3dz)i=3ITCic}n<(HvZHsH4#z_KRzqt|>(i;t{zy{DpbBK)R(8WC5*qF8FSHlRx%ewvNX+6w zNwstC(Ze7*jZ4B7X=S-s4dt`H+3r=AB@PjT`KGMI*Psot|0KARqLoFhrpT2?tZ)1c4PT z2T6;z5wbg%xr8?s{GjCCg*wnL*uKg`B02L}{rwOv`NPEuhrayt9`WF9BslYAtyJ|g zThEoqRP8U3isVmKm;StYtE$CBZkE1+;<6^xa4y_EsUO9Sh;0Ry4cC%{A~nmF-jPFwFE)*r(V0$v~$)K%8y@* zHaVn^E(Ep&8qKFFbUWHgBu4FhzWDDh5_tm$7MkX)R@aAuA)A#Q1XfQXqzqa)-K^nt zqUGaKIXHO#tFW^QiUSG~Ef#{i4#C|AX9f-K!QEkSx8Q@j1$X!04#6dO2o^LD+?~Ln zS$1ppy^noW@3XtQ?$7$<2zv+R+R7?3Q%r7fj~n#zB}vFu#~q)9rlPO2&jzy1$ZoLOw2AM>{w* zf+c|2rC#+!o$1xs7EzQTfp)+A3}3=zgOL1YAQuPxEuek>wK!h_c^8TY3-?uRF z?MH$ZhJ}N|+DZl*c*FeR&3Bqv|27lYA2@4}nH2k>} z_Sc(IWWI9(VZG5q(T?O-ZC_4+SQ($Tp*AvmMEDCk$&3kR4YSf$Io*};rxfmG+}xbc4+T5dM_MIq~&Mkb7)Xs^vVI4Y9yx1SE+57rC))=Pr17N zDn0_+iPit|=brCHmvIsRSUzzDq~(44*Auy}^lr&Jv|=%oo?b(Ujx|(%&1_S)q=m}q zQ!y^~)o6Y?!RUhi0bAxPG|pr_>P%^A6mwt5H(OZbVbr07y|1bmJuLNiHF>vguBJ={ zP3PLEnjJoyaI`ph3?U^^xK|z4u9>p?BIn)m}i-bi>w1wRh&gMeVLk7`VcoZqya*Zd>bu?UJ?cnx*IgtAS%0F zQk<&jQ4m&&)Yls{4tr{_!L$qjzL8kh>fNOZBHxF@&gO^>mKh%pg_)}NbS{<%_p?ba z8lD)@!*bYqx8DWfzL!Vk`!x=1IXIgl90`;ixv#aohCkNzg&kG61Q5${W+>rLv9`S)|FPC?6XB_%jf+LOnVLmlt@K^ul(oyVaVg3x$ z*Ya|Q3I%2CfLwbzk=PUcfny6G?*S472fsCH(LMT>6Zpo{*-j^%on+cLrXVd`Tvxl$ ze@T`x8m(^z&AWZoJy=qV%f#R;@IOX{MJ$|}NDq3&fL@>s41(8*QGKFhoP3x>u^z3S zGdm>UC%63$^4~a_;`hfLk8@yRPYwFW zGU>uT*LXI%^ZwU;u4#{2?H?qB-bV7h7o}qOLL~`kPLMRx$Tw$BtgqWsDwNK2AFs&F zb?|H3Yuw;wA$CQlP$GXI7=i~l=D^SCLa$n;vijhZUjZlTn9YZ4kl`2Lw|Zn;wVAev z)_63Fg%^3mC!qVUE-F2FZwEc!(m$W=nA;d!$XqkB(fWNa4Evm#ll0VLkrqTw69 zzi5#4HlaBjX&Fq0H1CEmtL&wR=lA(v^I-B_LO4|)DHy@4u^-I9!j1OVAX1tg8r*DB@ zvEux&*HTqSek1O{+}Ii0$KF*SM4_5Pb&ztq>|)^#iUkUq(ZtLr(2(jglzu1M_>D?W zOIG2lh2hR!;Gvy_2L^kxlH7=(qyG!vUKBQj2$%rBj4EjC@RJ)TMXK0F zE5I(1`icUk`->HRdExrr0<*>-YlW-}RyWcse6BN=wYL`Vw)G$JM;+=xu8(Q((w36V%PnDYPAdVF z@*#ME>;$vOShQg=EjsVZDE$+lBmuD$LW7cAc+U;}(shMIXdz++A1l83)@65_<=*Wma1JX}?V8otPow7z7j^zx$vdN|ZtvZ1=# z?iu4o%FeHtaMi~iN_w|Wcaf7pFfFhhaB1~4V%)W{7uoarcL8gnx^-7k!%FD$!qTaA z@~%3suo0o29(*W9w(Si<_e0_FqQ8{xSr3q(Zev!QB>Ik2MQi*%7H@PoB>*ZcxO3fp{UwogT3!kJeLRcfFH} z<-S}F3IiSJ?zh8mmBF)qCYechaAy3#!NG;LfVvF0fv+nb%A zK+E^nV_0{ZT(MY!whFHYY^Dsq_Je}|)M53r65NOhm}qW#ho{?<+4FaqxxnccKAf)8 zRYLo{633e$h*Z=s165R3a9)XpTlpuyCpQK>4AGjWKzZmCl?uS z)jG8XP7$MjMV<=DKKISZ*qn>p^Q{~`-z8x84e7FU2$S?2R=c7%77DY_!2v6)7z!aC zNp;k)wmb|@hV65rwM{XKlLQPrsj<3vy=)8CF`~;N^d@YV;>hEf?H!r52+C5Q*(_tK*>PiAPAUBdd?GPLB(NY9BJ3T)61Mv>3B zYN!LVlpEWDAJjngK4A20f|_SxBKrNvcTcMFRozrz z$OlE24c$9@N!V)DfDRX|71lXB{KcHd%rnIjIoOGIvdR%rw|2>IFwysrKD9v|1r)9h z%%skoWu2~RER4Ns+V_2b)(o)X5^;q{nT#`#Qe`oEed9OI9Z~+}{tdQ^)AQ}3v!{ug zTy;0la8SAB_<@2z z0UjP6K31S0BM`{QjK-<#Y$;{t0ij}&0&@eoxq$ztBx3>_QTYQ8J2wYEI~NBpkb|3> zib)fqOQmG%1)=)S+6-=-x1)J7v~Uoa0fJm;oHDjJh2lAy{0hpj8 z1_l~rqj-T!^~pz)zwKlV|ON1;` zh0LBBb{#x*Ra98xb|r=A3YKT+yHdN3=ytw2c z4C;p=ofS2p70~)`yN8s)Wm2J&!TG_>g8O+~lHlCo2#JV@#XW#-I3EHf#2TRHY0MHq zpWp~-G(VECanZdi=!!yv#k55syUZtsS;OR4sgC52fO1`OSVZp$%HSsOFB$1kluWT~ zO9y0On5Ja!gqgEi!tnwLx8cadr;&4!c~TQ$7(8%>B>haah1jc6kZ?>QHZ=TJ#5!7i z?1m5*aZE!CNp({U64ccqa&4$-s_eAt>?}5~c8xj^&k-vf*?qheAZ!RIC-W{QUUR8mB~JVSZpH{RQh%vXdj zN2ckW4phT`FqDQ?8w~Vn5whW&*dZW)ZN$pj0>h%!J@a5y$GEmNNHh-BL*~P4N4w9Z zxx|!khc_=0x#?p2Bcr}~tfZ>8(IJ;WEI?EMT(5X9l_}!Bj{x-`>%$u#TPPNvLMn^(V+Nu23YV#QllKo2HX60sGogF&K;Vq z8?+7h5z}U8Wj`kPeY|Mva3pS@#U9KHod%8lDsXN1k?n}*yFC`tN3#!> zrhbRIqXYUii7t0*Wj&_9Ew(l=P8Bro(POYNQB*j-;803Qn5a7Zsl!D0iv8>e&jI08 zc_D+-so-@Pa*ypX<*G3GLbeYwO_#9DZ*a4AP?R&I8QzS=$+c|jcF~PRgU2((7 zGK3q{j;Lc$@1@SyM6WkNj{arf3`vj*F@YDLaQq?&!*N)KPZqTy37!E`I2|K*i8(UA0&*y$OzV=c|p$jC#)S(Da>iN=?T7O;BKsz}D|NI4Xf za|cO(%_%f>eW{Fx6>2M=%+hTp3p=4!Wv#^R&*5<6WBQ=hRn3WeemXO(z;kfqy$o;r zgoUe0+6tA$lo_z8F8Oqc?>$x=dHh9t`ld#rV23-$=r6#!DDPzbDT&*p9zJKpcTpXe z)9y_?e@i_$_6PAX3bo~vig z6I{))LUnQjp^+ace^Ibybw6+GhjfkPT)shsl>q2X*KB21aoB3BqzHJB$AL;qMArUi znyr**w&D(-c#C)lbvtTnLS_;X!jLD+0B+`fWU4}lx(@XAes|yLzNNfvEmNQ4A~TFk zb4j_=*4x=VdMme9eM#wMh@*09~#L(@{tkMS<47Kc>t3qy6ZOKOT=#DB#DO!`U`>CtwDi<7HX$g3#$0Ld2E z_!EFCP3|N{E9Ly|CTiXay7!vzP@1$M%{65-!uB%%#7M{d*Hd;0`;>bg&e7wX-xxQeHOA z7xtKRKv2QbEZx+WL__)$pW1S+5XfQ!sG?DjKeXlg#HWWvH!sS7CV+SE5+*n-3U7+X zL96~I3x@6;DLS#Aas2W`@5T(<~);Y|4K8_ z8_t7rFs)c(bebAa?2pYvc}P^wF~;1H^!K2h<7W(6&F^7CQZ#k)Snadx3gEr+Q$U*k zhl8Kvw-P;HQEh|NF~`)nUCS@1KStFy*G}wL)-+WM4;(T}x}j5CyK~J@r9dU`Lxbk& zY-`ynI2)&AUiFa}k5HqzF8+-)pFpMZ9(uyZjzRF~eWpKz|tx; z3A>;N>Pl!OdY|i{qA-%zWGFOL+VNeE=cP0nI;%;Cu%cW2P5)cfvqzXZs(IURw4Bj& zCH$R55m^bHXTRr7Jhte)kN~~^Que!TdD_FGOsJ}~r^H4~P!`l#b*~4E^%$3m(aXk` zSWX1+{L8edXrxP!;Vn4H954X8dDaHYs%PQ-9&RG~^kqh_H}>sc_2dxGaK@k}<91Eh z)p1(Wz^|7R%6$6~=6<_++i z=^Azf4q|WJ!hJAt?Wu&Nqv;&uetn=LhG&bGn#MS<_$%& zu1~T{wg_)?dwuWM#;-p4)!(b!rH18SU7Bm_QZy;nLsQxFZDQhr2aiUBz4?VIK8vFv zpJO@)GRqQYxOWp^BUM8OM3X#$lnJs2Gj*Kb=|%IWCcpArMg+rZvrXee9x{fQd*>e# zNn$>28~PKK!Du*!%SfUdJMfmwX`F^Nr-lvJW1P1mVJCgv-7mN(She@6Cx3cD>tcRa zcz=3D4?jK)9|G#)9Xni5kHVj%%&Ze&sL9PlHxIkVc1#@QdoEC8m649z>0xCMxw7a1 zNy#eGPIJl7K_3O~_YOdD9ed2?kvCn`RX&bRAx}=Bkl@qK^rVsa=XGmwR(r&WTO1Gc zjK5Wzkb~`c5ARoN5M_o5(O+h)ck4Ujt%bJ5&%Qz*VmV8k|E#5~RdrApWtb!M3BdT% z_SNI9H{zVF&9$L{to0)fw~|ww6%gkAO~&mDH?XbR0e{i?-qE+_=FKEMlTA7aq=RrAN7!8=;hJ>WL_YaA5mHIA9d_|?-LHINJMA3l_0 zo7P7?k#jcCBIA*zcSB`=@8FXKKuhdW+;^Mh{o404;A!A!KkKWp*FgOC#n$Z6Ipuqs zwgrne&75i2C|AxIpTWb9zi{`&K=+Z=Ihx49s*8I!_`&E=J-%{9>e z?03aM!3y-jCvq#N`eJ>U0dBKS)MPDlB;H`1c`&+a@#L5 zG~WDlcH{h<@@Z_~B0#igU?intp18iGjvD?s;M@Gt#4G;sBMr9DG_53sN=3>AI>KS} z*8ZL5iTwl>}f4aPz3=!eY-ZH_H#i@l>)8xbk&pO*3wHb(8#%O|jPov-ibKQZ}!BYR2=w0jxGYYpYlfxlV$~@&K}ooF?5M zx|6VcS{z|s&~q{k0rl}HP~{)BHxIc{8oMBw^!&Z7^JY&4=Dpu0GZQY>t33a9PR%We z%OiAVU_Id6BfGx6#CgcoC&1WhkxT(oAsF{A)`umWaZd?1WE1H!W@D zO|aHVztGtGKojDIiGcV65adL-{%`op#@k4e+#5os7>Rc zC6WL@jDFF_JLBUE9|X`!#4P@f_`+EQNW`3WY?=Ir!lTvrwS5ec!}C9s#~%}~HsG8R zD?wu9Z5Le;Cd|Q&M9~>eYZxaXnf*mC-yzeI(H@7@M9&*AH0}A>>4tpquDAMyr~xXZ zbCzJYsTI%Bpqi%?y6RBZ6`*xylVCK>((YNZd3NH$ED7wqg+Y21FSGz+Epdp3b;hUE{v84^0JoIf`4Us~f)P zduN6%wPgQ9zJBX*+k>ehV&|Nk(?JuAek*R2>(6Rh`58EE9p+iqehnDk>^_OzGZ4Sn zl`z_SJr9+FbUWkz0^2I;JTwg*iSF9PI&R3LfL9ljM>~ZJzkOeOiRq6qVa?mRS8(PU zvU|0V(%R$-HVsZ(c%mG){RZE9bX}=f5KlPl0$O zT}ei^?$;F^qH+9&lXdY7=%VtZPU)C<6?V`orz_`Vl+7@ZDXGgs)T#*3Wa=-eGu?S- zo;TbNpwm-<H@e&S)Kw--2e2I{ zOGCU(8#NsD)TLR>sxsRE>X^{U?xR~=PKPHW>wK6z`zcjfZT`YQLcRxo65J_F-5TFE zEHC%SfqJ?@Lid1wh0d{*Q2Ti{l6+1jq#-t>4d;j|uQb*p1sI(~F{k(_Cl;MdLP`~C ziQ@IQvH)wyxThL-`97WntAl=#DXCkVK4`sy1vNKP(?Wc_0`R`qNy`A=1xK~)k5p{6 zyx_{eb`2M-3$1wIFs=jDt}9J9Jdj^>sL&2A`c4#-FPOIgQM84SdLRk zcu7fe)rwrIwnXmI+*NG-`+UIcV=#+-&WIvQJcvt?U^mduuNyrhtM`?xet01lpTE9P z_>`5Z6a_)Hvc?Jys=B;=@%pW-uJF4bD~TWZ>bJD=7XOS0&Q~|pe_s5nML|ajL4PQ} zlLA{O$T7Ze)6l_+!0PyINa0O9Q~da$^}ogz*@H3ms7Wa6BKi0W%ffkGDJ2|u-;b*6 zO^PkS{q^F-H{Y|X9QGhpb#V>aAsxzUBgj`5SJmsYi*uG|pYF~F_!!}<(Pw`fy^%^! z#`E!XGCO~J@#C6wks-Kn6-2O54u7}|j4;wv1BD-vsk!|7+$fO8mtX%en$3pqM~Uz^ z-z!&X5xnB~mzG43453-=>c4v3|@*A%i% zl5o~z_i4dwKD?i&gMN3>R7LQDLCh)^HiWjOP19U(2D37jTJjk?35LNXQh&N;I-eB| zrm+3>zW!9$IE2e)x!__!gQ!V^grwz)KNmc>k%emt=@$-(K``!K?57 zTi6W2ZjCv3H*;8GvXss3K(n4gYdf&7Euy)u7w9I5WNFIG^>C+2w~#<+Ig- ziBD91#+I!YUfQPeKb(W@=zr563#U*X#NBe2?+Y@`j^J6lstKodK$(qOurF!8_Oaf$b-hH}y zPNV7SZwtl*u_IeKTFBpK0WtS>DYRKRbElAr;BS0DzVe+zCP+k5+JA@Vp!ryW=3R_k z_oLtL$0c84FA*=@i58H@O>Y3xas!y&&BwRn*?fFiik-kURzBL;nQJEMSnu>*)*AT6 z6v52r^j+tKXYR#!K`L7Nv+q7F-#y6!ioV-4fV%{?0RMM+FjDtk2c`#cV10lCn>`Nf zg_hyL9oQP-T|dbeq<@TiPQH)EfiI`Gx8r%yb5Ikz&|mJmjK@mcHZ4t)6B?Cq50<7` z2_HVAr5y>F8GxDW!~3j(ps1gu3sRPzlkVg3*_+XD^6_qq(`xc9r&U|26)lz`Of>x9 z7K^<{E9%2yLCV_aEc0kA_I@;*-u$nG5S>BbS0^ezM~E&U@PF%E`S;VCn|H&@x;=JP z^cV|c4;?+mJHehF++#paD(b~!2-UTEj`2^(V@^_j*<+f@uL0qFi?hUPN<9!JbS#wA zeOS4URik%@T6yRn6Xs29b|hHy;!4Xlue4@&yY91`2*ITLEPwEQme23AAb)B5z0cD4 zR7`!`XX#LP3V$9`9QuMSm6a1_F6M>i*6uTs+3xdesgwo_LB(jHy*p8pc~CK4v=!O; zcIv4#XedPdZ66v4Xm3%gyFP$`_H_mR6+7ULOI!*P*pf~Zc*d53L5w!Jz^{knn{wtQ zht>YE`bigHbu*lgW~KZOLnEX5FmA21aM*Sx>z&v`1Aj0`-3xDnP!L1wVU86(dq0Y; zVDsSpDDAW$>ZMkRz%aJ9zTyzI0n(d0M=obWy8yx$%8+ zJwTWtKrbp*5UNuwjwB$yNGcW#iTUm9{j3CC41b)#E_X%?k&G zN@=xf>i}q;?NEg{xIpGL2MTd^SCSwWGSn@jdD%`91Wbb9BBh8hzK{Uh?S|H{myE+_ z_Av3;7PM?j&Ln8bK?_6OY!7a!aQsLrLW;!nYB(R3L`N|K=90_YFn-7TK#to4ycrXV zBY&*~`-g&l045wCr=L^j^8h!M>Q`l+gt4XLT)s_Qg zs7-}~H}mBXE0U&Y-|KzxCi}pm)Uqus`lzOuwB$6L5IbAem@aTe18e%=5-DCn)Xfn@ z+B$+*goYl(Ja0n71C9k&tgUce4;4`2MSns%J{YoVHjB2+W))8s2(VKh1$x;7&U2`k zQS<9g+Tx&6govcHwx{(ncU&dDt)GS2@fgp>Ks&({SVg(5p5##xlLYGeC=U=mNv;^7 zSBHfXzdfgLHm@Jzxt-1y-%Tfr=X#>Ai|3rw8o%ok#&fIHv(rQ6%SHCCGGAguVKkvuvj8t zQU+O?3PhlwW#>`)r4Lnvbk@X&40}X$6tLEXJ`i0bWft}8D$#7#FP#Z zKV{Hr5a@M`Ic}O|Jb7QdObIAMk0aTrl(!HyDm?yFdrP8m_2 z+HWW5ZoW9HG@%#sH4=uEhuOSjR!z|MfTmmQt-8Y*ZOK&3EEMo?4}UUbc0in}D8pV- zRi+0mh#PPrm7dq7(vzE5{^#jrRHPT1FS1((87LW$qeEU5@0!|(&$kBnARz)M+3DFA zMy;wH@GQqyEp(q4)ekRy>L`7JYpMg@v%nOuFyZp`gilpXDH>o4*TD;5ZkEU zxn6i9XUB53$@*z~+kd{s+jiFr6&BP5=v~ zVh%~g@5ROXxC_Q!hWBb@zXVPt!*fFG&$aVi9x?kw(WsHK2%aU&|`*_!xZ4jUTlz>kiC0-SWcag#DdgN(GdtI*| zzbSR7m9`Bcsxade`{f5ehNAY)I1L;$qQa3=I@>aja=^{dPl`O9Mt7C3`w@|pS{|TL zgy-QW&RraSIe)NM^lz6J1BL23yZ&ic*O_EJ3#qAR$#R@Yqh2J-H9FX6Z|Ew%W(rYt zEm>IivJoPS!ZV0_gT|`GoyzMnt|G7ZVwm`BC{ESGfnz-w-w?U&z*$P9_~Npaen-Eb z107zD?iP6wDaT_eAa~vCkn1bw5)NUwsy(<5+}SMmG)QXlt!0gx{NgkPg*ah2KY3T19&b98cLVQmU!Ze(v_Y6>zqATS_rVrmLJJPI#NWo~D5Xdp8( zFgBC%8Y_Qu9JdjE=dalKiL4Z19NjqLQYwy1cKJx6$a+aBN)?M-$t;l+O;VBh@AEZg zuUTStW;x)Ba`d8Ikzk|I=;P~d0DAIfq9@<&>DlLBU+jJPt>sBl$y+~p@p@v2lwmvZ z9F%oDd2up%x&NPoS1*3}@>}Ot21XT`K_m}2K0AM%Ufjs2y{9kse%&+pg`Sv+2|;<6 ziLs-}@%i4%S9)@S-~BMric_3?$Rp1uPNgX1&L%(a{j=e#HJyo88ROb~-CQ?}S(kZVy(j>ye}`- z`Bi_`48AV5Vfx#Fwfo1@%bS^8BBYVLCmHIJ(kaY`{-Gcgr9j;;!lb{1m6qAEz*IimuFvZPx}Ga6qPoINS238prS)n811 zE3qD|vdLCqJ$MxYcUXjNnW|XCjIXZ>su6#bj@+ji{xNCB>vvaog-g~did;juWPRT5 zmT=!+915cfldQE0Y-+I1n!z#+V7;Z2bA4D1M2F6& z>~;^u$MMieR9>Qi*=gBMg5wWqR3L)cu}BM~U_=$-kX?&I_wy{)Bo&jXu|#W0xj=u# zC9dlqnT6>8hzN@CV=`|Vn<5}XKE7M~UwdoFD}rnA4q9)l@GZZvIzco+0$gxray6a2 z2Cr*WOK8RgxU&=>`iy1*h{N3?Hak1hp|7v6-=9wp4jt3}hXb4TXWuWT)05`g&AaCF z==j(7r&qIIU*bdWPp_{}&rh#!PLF?^pPs$TKR!OdKX0#_pNVtA7v4WQJ8OROR(?Ib zy>3J^XS=wVJ7$1*v84_4p)qTe_o)gR11ShR&-!+HTd)E{Dn`m?z=Plz*MegNn$AD{lKz`O-^Um8#aVHZQ1O%fir4QW45FHW9|IQ2PzDp7$r?O++)++H?J-JQw8r@?E@28Nk(Q!8|U){0fa_KSjjz@B-TGksMM$zh012w&aO zj=aSJR0fwOAPH@Y<1$2u1gr`v@@puB*CFDat!SI&>Q;2R(|~wPRE76QO50H8y^NqX zgbpyDxD`4cgRAV!T%mW`){vruzl|7Cl;V2!XE&;_ab976XF5Eib$NevdVX|uTk<2I zxO61_D%IuaZSshgFo(!0YBx$48V=CeOo}x%^FDk2{olTReh|Wb$>XzDns|rdwJdt} zN^{$LEP+(Zkb&LU4ww4d0zV8AypbPhf<~ll@l(vTCEc)a$_(EelN*`+6gik0%sUB)OdZVsPCRK*`yBE*Z>ngYgEASth4#59 z8CwagwtxJ4+1#qQVwh5W4-qkH7j5`4zXfP7F9`p}ZW zjzD?|x~@`q(+84)v`x8og=DA;==(09|Ni3mm!pd}(~|;8j*@@D%m)5aSb7WHUu9%| zeOc~ONT4MAr5g!BLk`p-AM z9Oo+Q|2?`Y`u(`75vRo^H5}0v9CB9a6K+>- zS(DgWIY2KlU~iMET_I^RFBOvY&JCr`Vp+RFBK3-s5-vsvg{o766e)}M-wF!OIU|z6CU2 z_KE!?o&kT*K{0?>1>ra&%|R{;r>*6Ng}0BUAM>yMuiBd5VQie8`m(A zf)b#8gVfr*w{3-T5$h41EV$F;SknN5^c|w8)R9XzpBW;+dmGItypuvcGejIP()IS_ zct}sqZq<{cUKL}`X?um`igOgOQC{vxZpy{MAbo$=O}V^*esuE=`ot-j{SJa0ZCpnGLA$9ITvctU8+{6u)o5YHAtb4eKBuye;qjDpp4292?n+LDlkhY4F zsZqHRQj(8-uG|bf+d@`ZPH@TiE61a1HgJE50oo>{?EJXX>!Z`N=}B2RgQ7*R*FUzB zjgx~lu|~y4C`q=lyNXQ(-e(1ye%?Q|g3Wl@%Ud~}iYFvM0 z{&8Da$B_tleYRCZ1B4wAi&~=DcqAe?;D;(x2RXd&I#jBi)=OQ-*RI4l=(Gq>or2)tEY1 z*mNlG@Ldpy zH+`pOV}Sb;QD#a^}lL~kGXnzs)gqAzGW&kYn8=jAk-8*-Ha zIL#pw6dx1Q=!ZKZV6KqjG~n3)>zjk@HRcXBFSs@LgF0o0NK5dU6_|HcuyW{Ne(P)X zD{k38o>qN1OYj&Is%W2sGFgA#Li=2T{p%{)M;f}2I<#*)pgQOXzUe!HZ#gKlewFFr z+x|SD>w@%~U|lv^q~U37^FMLOq&)Q>!Kc9u^p4&?=YCNI&ZUwk{i0RQ>zrEXZv7|4 z;Hd`l$i^f=U1fe_`+Pk2BqySiufaXR|2qJ#Rqi)LSsL5=XKDT0Dx80__$h92e&60d zl3{0KsrUb^G2IVWr#I7QEAM?2N*)N4j(I@upQ`l!YhK$h61?m7{^k6+<|+@1NS7k` z-%abk(zO`tQigan1nio$r|)boJ*LLTJsu!g2fix>$k&+&c1SwT$yT-n3;02?%2rvg zBBi@wW)&9CjNu;7O^ts&(@P$}BGy>2_zx3J=Ym_X&5CxKiZ37fnwq}ZldL+DODX?d zj{9@QUQ9FLNbM{VDh2_rQi7GGm|LCEp z%O!#;yXyQbTZN~hOvvZfD(oG>fhJ^ct-afD4ZUVuEO%?4g9g&+_*88RWNa@hoej~*`-x@ILais z6hS+DIjRQpvzMca%x`~Y=TSTe1@xpt4dKXB#yWLu?6xf#r)d4@)!jV^WkQpiS9lOg zNNJlsw`3Nt!K{BVyz(Ga&$BX1UvTetWd=3nJbbVve7m~vyav%z7yifP&4#kDj``r0AXbKK8Y+L=ZXCxEedkw<^|VC5cHgQm2?WFuWW#n6II@5s zkQ9I>SJHyyk|tM{^!GX4vzM7+&F(CxMKW<-$R*jVsj99%b?uSWyGmBy9mwVPzq~y7 z`ddv^6wzr{y?k3KYlR|RIr2gqs$QN}uO|OKeDm^$ufH{>u`q~01R{CC$@zb5I$xw! z2hU#~{Cc492U)2~c`uyF#K_p{! zrDSzS4steDJ*^eU?SwLTs@)~7woW(*RX|=%5F)5Pd5vqXwIYP|?oK})YCSobUM-fY z3D~RXoJ~tj6f&+&>MKSqMi9AOMj473Wz#0F4t1DZPd{GI7PI-gCmeriB@FCoO`EY_ zZKDXWu>BfXi7tZ-AZ7HDuf+{xy+LTHvhmV(<2AMM(lFySJB=5d`A|eyZ6Jhmys$0P zO}pf%py?LH)0zq0N8yjVP6rO`HKS>x;T<4U0ZkjDgtwm8bW1cP@o~-BQ_f8AXtkDp zKK;b8?zPZS7h&By;XQv1V7)#O+Yh}I0qpE|>Uj|rM3m5!gBx*foYR7c1iza|8DHYe z>&}_K+b(LAFjAEeQN#HdZ&?w$_Gi^v^?v|b;ixNaGou9N`oahf`gP@?ZDae zGqas4Ib{0~7>;vb6Y!1~S0luZ@YlS!Z4eK_*)SZ$!|3>9JcxgXT_C=G#MSj7smUrT zVV-Bbuy#1GKKtwU&;S1N_TE_qI_Xf3-#d%L7!$NM_wL8%|KgGmIDN7O<2@FiO=1MH z?RTbsp3YBSWaXzXzjCNzQ_LFpSLvgjx zf5k<;pk;cZ-YI``q7#1MA1HckRM^uhT2Iyqz;Lz_DZ5O0F0U4|i`mU$cJhQdD9AmW zT+96@MlnZ9*E?G8tlJv*Xbq2VVnu;OkY-$jsRSHICPmVATq||so?BbeKA+7k>4jDw z1>u~RU_MwOweMk`j|;XWvfw^aE0KK+u-O~?0tuupp+WSJ-ax*{>&i?C~hESw^8+BzK#yA6DPt$6dOiqLjnqo&ZGz=^K*ZC@%`Vvd2#6d zgu{N4LRz{t>Sy4gJ`@Y{2zJ z4j<+x?~mv2rl$-^3Mo-pE_~03-U=7$;P2*&ua~6rX7QgUjB{o_V__oY%<|}7+`QwU zEM;twwjC&(rm)_*F-IFG1sye2Ju&R2_ak-i8KSJpS^n4Uzy|1Q#WAEbEY{?E%|Gl)A7` z9Jhb(UgA8zOo#F0j8BaLk!q7!J>|pmMsEBz6TuE4UySkAR^^&edn8uzeBw04rAz%mm+Xu`danqf>>EKArrRDu>yrVJQ2JJ>1 zw9_2gxpABMlpA*o&^GqkLN_-)WzRHX9f^N9;GyL0%XE|dLRjmifOj8jA6BW#y4qQn ztMK<`x-Km%!@MH_zc_&TR*nBL09#-FD!m{;S^?!4l^PAiE2nMciV|-RX5~HUSlTn% zg{FFU%~}Z`0DuV0H4=hRHS3hcgObO>-HwzVQ?HOKGk1(YkjN~o+GlwK~wzuk8 z9wHC)t3rLZD&w@dH-KwJ+dE4bd}lgR_7e|Pzd$6(K`zvTvtUy1b{yiuF_AZ!l=S*m z{;zxU|2vj0fLidIMOcq67tYJE9(R9%kjehZl;GZwL_+S`+~2ME-O_q5>mlY*ffB68 zdU&(NdUKXfcWVl6F#G(xgH3JmRKFR|yiINK9<5VfJj>R)3{cM)w$}CR^5SB);0j!b z7;PL-;G)59ph$sB4JdF)Kn|5HzF4VqTe(2-C6Pi%^#1}^SH~top@vH6IFWzS#{YK} zxCR9|o1#*0jmzJvacL_wm96+19|VOWd*$nF>~RlTkC%5fXWb|7|Kyk2ZYH+6=8op1emZL!1{nuYh--RnWCfChOBVD#H4kilp_2`H)=gz z#MPuVE8=~QTK_)AGHV~nPDMgEAGgjl!J(1r1me9zk=S^|P)4Q-|0w$i!jj39tW zB14Ih$aI6lmf$@o#KOLZ_q~sAVOOy%-!c#x?tdUM zp+c;T0_4bX7Z_p?1}L6Lk#yw-A`99~|3IXT?aXjnkfTi+9D&lL6TyzV&SPc1-<3!a z-3Nx`(8Lts9kwH*<9YvrQ`Pr?A%0M-3-e9!;&@G_5W`2yYI47DvCt4GAd>p7k35DU zP#rCzJX>N2G?Ai6_cnh7T7vcE5NM0_GEJb~c7(CQmQlQc?}~^MTHFt52+o*GirY}5GGT4=SKSWUFW9; zi`m(OM=(G>k3Ob95GVo2aYQ|yeLbz--loXC*aPR#g9qS9E~0~fy1YeSNM&=!H(8@H0ldf}{F$)t@I2CEARMLELDGX3)~#7UlI z&LS<|=MQ%BarHYpIk`Ola51kxAJ0$g44?AK%kpot z*HW3*GCN^NOh13kZWcFUfKI#NO1gYQ+k%yD{Rlq6@6*D%I^%Fe=6ml$ain)nFXUWK zFUv%u=JZlsMCYnp*m)aC4eupy3;ZYwuXPdjoz2r+Ec^XFe_Qu3HxI+$=3&^A+?mAEp|_ zy0(bG+Bkoq^j3k}-M)08@ zfXS)|GziNhx4t;vZBFalb-#CeO;cq>q)mg-si@W`ZQEE~ZeKq{YkIhs$*T422q^fM zo748H?pbh60qj9AelT-oidS1=Fbg|N#kT?*Zqt8zp`vbY_#NxF4iO=BTu$K*Nj62U zL1JVY%Gk-$&a!hSQ&`=`vV{vW--FaeX|$InK3$ zRsMf_yr@6r4ZB&U)=P}E)O?mjtf2JH%Rq1l(PfS-a|u*7cNm7;c{xk-f$3gRx(3)1 zZavJ=R0A@h%{=c)w6m1)uWD8;!oU={d1=BKLon5P*QR`z3P6g0W9VE@$~Du=^TQ*2 zJO3s9aLlGOc^)S2b7z|?2ee-6aQA9Jx{56cfG{L%9Cct*VSyl4B9-!thfSx~J68qd zED}2fWD0IM?MkgB(8zxP8#t($lfOVH0x~y~@fs+9-CIj@sP?OgsNJO=lxh` zt6WLNE;~LXS>0Bf6ik8=OB5-SROJ2l`DOru7;s2X0#>_~i%5y1M)%k6o+iz1GMfE# zL95rlTwQ$kqtKZnPIH}I%`zq#al&R=8N!9iuBO?C(W2O1e!TkSf)cCzfBKi7TEG46 z>f-l*3kIq*V;NJDXr?kPIbqslllu$*0T+fSjgJ{If@SL>n_c{~O*P4)$qaFZvcRHJ zAkIKs5pGKk|Fv2ye#P!o$3n@`x|poi)AIdlS-x&>i}KCGdVZg;Kb61S`Tr`a zUskj7HLnihg^r5NW_~~4Z0D1T&N^Ri^2v684#EMK9nWb=o{gHIgfqTJ4IoN46ly{c z!7Ifsi()(W&|Jru=;Z{PTxRF(sE{Y53^p0HYc` zUG9lZ^5vhl<=bg7nNN$)f*rGjtG&fZN2rrSVaFN42LZaL+XCjaF6Eh5a%~>3DbtgG zVpB@Lz0J2ZUf#$i`QxUDY*CFCtMFOVcITB&U~`)q;?=tT<9)sj$8U@2o6iCw)kHw_ z3PeT)q0|lqB6UQv$^dKNZdZ?s%F6P^W>rE{q1LiRa)!uwwrE7Pu_B7fYzzsUsviBY zzEo@kn*L={zDzyo?|x(^lf+pj{QpXSO(+)`n3t96uPJ5KLDzf;gPU^ytor1;@)+E$ z5n&aP&VtKu;y)0P^N}kLMocq2SnbX&5|lKt&g>5G1cmT!ci;{o&I1=|oGa^NsB(rF zWxc{be5BbFpZx+z75ID#C+{->Ks+~#>^+oUs<-(pt~oJU1X5C0Os@jAcL}h6m?1*f z%zsEQ@5$8%QY3)0s`2><9E}~lO~5pUj23oVYQJLb--##Um^uKQHx3A*jNTo1+r{&C zlaia_t=k=Wmr^@$XZ2Gy+kCxE=*5O;l@9EvTf#$IX4?@1)5azw;l5r%ujg#P%!Kf6s1BsGm0!x*l0{tP zx3in-Svmq0>}!@1WuE-5SIUTAcR7?Kf@yN$zoZz>4p?&T9r* z)(#V8$gjzauUpn$`|Es`vIDf{wHc%Wydlh~&mG^v1qV?@JvXL*m&?Jh4Qy7?vMBuj z5AcW8x$Pgrp$IXILIhfI3r~*}Qaeo?ZzPViVmQ2UnL+|Rnqa4m3MC#_5>*aG)yVr9 zl@C+ssjWjk5-FqNGB`c*-QK7~2z=o%P1=nDTjR#5cct;9tPl~7TX|Ywlr~ljBd~@9 z?4-bv>QL|RBVzG?$0drzN%63)ps|QUPHP#=!H=_Q1N|n|5)rkJSub`-eQpkHr@)9( z6uLg9Y?jj!*~n;%g&&e>Z4mS)xB2p>n5Ie*biN%5g(NU6X;A*seqj;q1_KFckfdlb zEVfb%Y}laoI;*MFC&gd}IBS+kt>KWp?2=iWN53e6j8m0=T~70Do^nETH|o`WP>51x z{blvVV^CRI*4Y10O)>dZIpARt8eV=#O$(GCxK+oOmtzLUnWYn`=AtWg!O{gA#M*?B z#Gv?zG?45+ynnK&lWrjrrm?L+5<46hW!M^`rjx+aw8w>^g@2nW@i-^i)zg}%6h)LS z`vCvKBF`6pt*8{GZ`CC0>lzvbwkJg;ulU{#T3XOUy2b$Q^(wrlM1^OXK!4Hst3aSq z(AWUl5YPsdI@ZRK3NN&M=aqQK&bs9e>Ga|>k?7MUv!N#JV^`FJs{<(}4(%$+FO^-L zkzmBL5=_@93{@>c{SeY>#8YREN*iYw?XOwRNibu7XTs%gq`BA3GtU}NAZ`<79nvC7 zSZa6k2|p!-{Qbrg9Nm+`S>gOdsRDR+(Og~fH?RHA83e=^c8OcTYpQ4}$;P(Tm1SjT zHsGynaJ+8*;Y7fUrceV!%;-uYP6L3Fx8tUCN8_d+;aA$ZOAgN^%(phaU3yK)=&wur zX9c@|zf3xM?He-)!`6HLIx6?!vQ>O<5@oADzcgD(RAQPVYgu@dR(+zLG!Bj~11YZc zU_~ALj=eDSXg_1CLyswE#sh_(sv2&@_QSZ-TsM0DFcNX4b@%w8bcR`~dJ)8m^NX?L zml(B8nQu}?<->Q^4YY>gfOVm4NF%*6sR|K)(UTHNpYVssroK;bJ_c8`J=#4c0V-;W z_EkttOy7E1Nfii5%Fez(dO7wMb)ZoLaBdN#ls&@v^N>eCFuXO%Id#YF6UqS;aYkuE zkxD2{216mvPjYC`hn~Fy6ek1T91wKt?bL2jRKqzitxq+!QSLgoHEwkcilRVm?Z1S7 z8JoYdMjh-}j7n?j7V+aX3ZVo2mnWs{VaK74 zcCz!7g)k|I@iv0L?RF&J7wi8P>tBOH%YonDi%*F?G2^H2ChUpX?maP9?upssdt!du zMzveG&MT&M>2LV)dwOKMFSC>TQ?*b-QUA=b>mg>mRr&8L-`B38Pwew>LD9qN$_Heh9T=Yq9eCwR5+hY|pGUQV z{lVD2bJ|v|k+&;KBy14}r@MrEA-)Q~@N=Gf@sxX=7#4J!fH>O%s+gCJCG=x|&MHNi z)o@~elZ)!w*nZALi8d0X%HSH0!XiW^4%mSam7m3_j@3baIRbPCfQ>8P*QhjXaXxkE z6WTTVv)4Mb3yGciaro7a1g`{xZd)`mV&>Q>xZ~MV8gq2cW4%ag=bmIvG^J4Df-y8Txte4)&S z!V_gTWwIW-u8SD1)v|cg_}W9hTW)_`dI+}jMPeNWj;~qm2xYDDS0D%?`SS7My@&Hx z|J%y{sBe#(>^UD9_j?VMA|{I>U&h}~czk%#wv6xer5)rd6V~phuTTkp629pd=2V!z zdQw!muYETR%?BD&Ez(Qq&&-&9-AA={Oc(jK*lg3gK@!26NrTtkyFtDx_N16REViTM z!VylL=#^X!=dX2gnI-FpMkDmuZsD9y6Kn`L{W`XSNJrs(y<4~gm})|{tKWD^JSiG{ z^3)?-Vgmk5O-L#c_S_IUnvfJu$NATwiPyLfJuwZurd)Lnna@@^Kjxh;WA$*LeDq?+ zX9vsBV@qh3nZ~|${Vy=4{{eC~L`;*zODF<1Gn4TeD1Y5t+j1kf5q;NJ(0-z-Oc?iz zQ%+T!ic|4R9DCo^n-oorBt{guOHwQE-{&-DI2???i!{*2$;*n;LO`R@eQ^2$@zKqQ zkN$eWo3G!!x%m2frAC1TEA8mb^+RzD z#fJ-lUw`K#F_N4!#Z4S;lpf9QFW$c6qbvO94!L1QS|}CY_Pr z*2ARfZeB5oz4Lm+bv+^xGOoQF@R*xIFfQ6DWPfG~`qU1%t3Ewds^SpW>Jm!D*;}BMoJi-#-()5tHMzbXWoLk67AM;b z_~w)-+fVPwP_moi%*(JF*qplVju23(u)7hz2&JQfzRaAi!ce~(d6SW9B9&m;I9Qxu z!GAOgUre{vW}ERIA(_^4WVK<&g+bmUEDMm8rjowzJQ?3n0ZZHeW#2iBN{U-1LJ_?x zSci3o^t#FDixlg1&vcW0LMYWbiO+1)L3-2ERI*0m9Wt|oXF;4&q&%%iO-STy2}No` zA`^xxGMUpvcpQo%twZmmNR5)?V>{#*E`Lo9&JqYY`^03=JDve}o9v-WF8b(XyRNJx zzJbaTbeb<|=m=OOP5-~Ha~hFMw9AAu&G!Y4E{|T45|_UNAXzDxwZpJeLvu>Rsg(PF zy(2f<={j= z`)5XU&%Cj}TV}IfEf&8{XLn!b&_QYeml7!iY;UWG4;NU?P}o3U{@VMl7ZV7E8BNek zfnnfS;?JFD&PlRr2VYxPqiY;RZ_;dd;-IqNfNXKkgi;QZJBGQVgnzrfoG7Emv(K3*-a=F6M<=gpok@#DMZCw~>jUe%xH8~SR-*Qbh*Kh`D$73d%npi zO@?uG^;P{J(_evDVZ2&*rg{YxIXIeXjR%~;mr@X1-M~v~*?;J@RR+Av8ajX2`RD_VZNt**Yz zMF5s*x2H<*29Z(tL|XFGkat9T$Ppiwv)k$Nrn<^SXr8G(L%GdZgJOO!*}Ap`{PQ8> zhn`>pyVu0M{V+dtxI=xM%Blv#?l(6%dnQt}XW=IedbXHX%bY7}&VK}=umaL6t-;e= zD*ealIww7BuS0?kNgs}<(Y&q!uz={^M*u7)d8_wj^;<^kT1n=)Eg`yA4)JzR_S50x zJWCLbVnX*~#A>mrUPcfROIeWV9YF+Nw856T=A~gyHrxHv?8C!+-B6yIAXb~r{C>XK zg5|}Z5(4ALU6#07E`L5X9I*+5wdt%RaMDt?aB{s`XG{>+Pk_Qmn&6*eDDR%5@~(%W z-Q|?RhM@CgV-L86%=l_rhpPSX2o#FTB5PaCq>$;_I~z*WA7HjEFod84w$y_P>5(-eP8fR%p^Ub=#QLG#DX}(q;9)BpoM&x?C>e26gVi;QT ziE-hxJ(!eEMFKYQ77FXwhP{G1bpW*Sj=vhFux=rJVht~vyx@1$Z2GXNTIcI3&Fqwx zi{x^O#QZ9pr$};?R_9H;GjCS9uMjFU$4GJ1E9lo-c=$dlB+gVS>d_vaX&ZB$);n5O zkM)O-F~D1IntvcrxAr`Xxml32CiJ@|=UXV3v15HK$+v^6!IKk zxHh1A!G8|2X9Z%6n@b~F2%&)u@N1}U*3?6!CV+H^Z0O`2f^CawDHw=@dA4do?l$R+ z`nE2eM)V5&Y3{OH;D1}7+JE}u1fo_11gga0YFKlCij%`Vm_d}s z0{@ADrOU=!o8ra$_56Oi{*>!A!F*1iuZq*~s?J8&R4UoKdic@w#zcTN5UEPbNS`lZ zsu7m}BeddRb9PYPQU|aqAE)BpB?;49zEXHh@Akfw*=twJH>Z5)T|V1sndqzcB zvVQ|EPDcj!re}{z!GU}_0H^s~9 zrN`N;0^q8F)g>xPy{Vr`?Re8k%`~@s-!do=66agSp&Qbku0lA=6u0M9Ar#GO7H05h9ox4hx7L z@Y#e^bOa;QoajVuhe@tA2Z&N+~`f8U*{dYCn_A;&O>cc~uYJZ(B zo7B#tYJL*kHKur)-&^hG+Sc5$aFR>8z=Tmd?T~UzDcyK{wGAhKoNsUUDblC;PGw`E z>R2X18fp~Nab6?Ekes8W7zT!xh;6LBU<@ z82G(5rQu%2vPxU#hLKRmI4*daNOSQSLPIJ#IW=5znm{xvcq;j`0>cSV*?&p<-%7M7 z=^|MHrZK0@kkF9&Buo%-D!uFLy`Ih&ZC_YQ5wWoL$5rE=CP|J&i1OJHVj@tIL(R$q z&cKyRbAJb~9Eur2e0t;uzNw^;fiQ@G_vxF;Tm}SNCBGWJHYHP}bm`hflD_4xuLR{1 ze>)iF3yZ_Cws5myu`u7me}8-(qYr%e24)%nd<<}IK$O2PPf#kO@DITPLOGJ$M42o#Jg_m zY^ii@t3HQwX>-}s{9t`4<#~FryL(DhxA*T}D6V#??frvAdJ($LzI}X^$A1Sgw!nM( zMP>$EIs1I{%l_sJA~i%IbY{48%&^=Hf_ZO6oQj)}Yllj^Cde?{*?mJppVaS@L5o~} z3p36RizCx;_J7-xeUXPUPS6WAGZr`zdgD*2?&L)B@ChOegiSB}-4pPqVLLqsDA9a~ z%Z?ga1)>o|B*Xp=PGI0nG?UQJ>OHiFdk-Bm;9rx@s8hX%`uKjGWE>2=#C{zD2!C|L zPkRr=`OSxJaP#4_99KJ+Z$9*lD|M>*P@dfg`I@O>4LcL02)DrL(!*H`V@gifSE-6`b3D0K;pyev>0vC;>K;@ER(A zTU&1;M;3nfujtGZmX=E0FS{CPwUTaOg1vKu0 zOf>UiY}}kab?)Ek4_e$6wD|Ud)~|oNy7>AVAqq#F=DN7LEremjQe9|e2p6ijx+$&) ztB>p3NsTL=q(7n-lCOQhSngmGd-bH$HL$oVC=gGKp%FK{>Bcj69;7i_muljYB*t8&S^AJR_r*a z(A$>^!q{4^^1~(PgK;@u)i$wzaFEklhV+5)+&V74N*F{CrPdduj1WcEL(-&r6Ao`H zYt}~nYbsJTp&RY5aUs;dODBn zT-%$iLoO_lTpXEeZ_3%t|3c+k&QrJLu{mz^Yt36e#3>uRx-3*)OgI zF`NZaf~!4dWfZ?YE8THOY?tmGx(jA~9mt@%;2aSscBsyGMZ{l{MyJ*_7o4zt-MmU@ zhpr>VA?<9d%(b0kkmbC)a}E~@#3xIphM`c`rBg%iN{x53@%?CaSKcHp!%*RC5aYUf<|&A2Wth*&Mw8< zT}nhN3F_ggPc*!L*-YSX%EfQx;`=HILM{34d{r+`>YO$e1QWqN7+o3j!>6=Ox1lVN z{6mJMX2o#p%GasvXB73xwFXp|P#^c*WJD`x+YJTae|;%W$nw95PWjI)st$@6>;{|l zawT&BKB&ZAl*dx7{Ew`M0FoOG|C}=fU<8CJx%-=N2B;Zla$S>ZOxnp5ogf)z!W%I0-H{h9n&NCPQ`4ZHi_(9Z8krP zBt>ia{j_gnKE6)>M-o^|bf}vvP!9l;9t(m%T?*D!SI`C)eJ{T$N{ce5|ESGFf{A+* zfg9arN3O(9k^uV`?@l)<9)!D~N{vdy+!gB$V89*34qRb!4+%IFs@yoVXfT&ryv_P5 zMf2tO5)t-_d=FWYa}wyPSj4IBz;;a_=p4R9vGN-d^o6le5@k#B`E_fqd&ql%qbGJxrpM85<)eYwXDeK*URlc zf8z&ot-#Gnj}cW8-c)8oXz!9?L)8wxe5qn;pt0P(y?+PNj!%ZXphmAz99(T<={dTu z=`Y#7(~#GxHNb8DyTcpsi%!C1KAX=ipmuXYB>MS`72i(K8Y&WVr$qiEv9St=Nkc4WW@5>{(EHe zLGZhl^2bCsHap&J#$;uhP0QPuk6yCzSGRicQYZJB;k`vB$&_zbG!q~Yu^8_Ykd;AO zLBG0#ook|2(t!l@Dy05p?t?P|%owrA(}e70$#=LEH3Ns-@sn1Yt1O;y!PL<*c%}De z1%7Fnh2vxv-Iol!JuvEtjZJ<)@WSP^SZ4ztg zz;#OQRH}N1)_Cf}cP9GCEs7@NsQ-@V$}s5(8eo4DV1j{9Dpwap$Ky+deZ`E6WF^M@Hks$uSgla`{fF%fJF(;oGm4L#WN>C9QY9<< zx5d9TZEk^Y1qjJ@MOePbb}f3>_E^MDa`0G;zuyjDq?gHnHqDAiC@DNXTs%B9O(t?C z;{F0%7Z8iVNQ6;0Xi&@!7@?7eaU(PIugbeeyr!56MoE? zvc{2)N`A*>?gsrd|EY+~uj-$EzP<*6o=P;qYUGwO)Kwe4`b`vI#-t+(BAZc73r#2H za`8{kqpkN&R2li=GvvRpmr8{eY-B+28liD>1PsNO#vfK5L8Z^0ExgPFejvPm8tb3jX`3G04@iB<01iXst@Y z?S`KT>NQ)3vL;91We0RIVlX`&BCNhdlg&C07q2h!o`8FF2Q+LhF_qPl@$)6QIHFnb zct^Cc0?t$kux%odEBmItTmPIFBb7;R$)~(>rZLy`_;Da(8*i+m%P+T-aITBm%)@Rm z>Hq^&p5|XH-`jNbU5*S}Fjw*9}(v_fGk!PDl(x>+71{wI2h1?`ot->Li zjeZYKG*A7lHfY_P)TmHVX#EHDn@%$Szn0v@HWLCc7EYG`OX=(EIvlnk`^_5U?^^S+6 z+$%djH{LEz0T-3EnrOxkQRYijBEPe#`=Qi%GPe!h2%E@xMrV^O6&mH^w{^b?Y1u0Z zXV~hmHA)KuVz^z<5|~sgUl-56{gk6Abzw^FzmDyvF3UVBezfQ(Pc{!IuqY)jC5vim!oKY0AGx91bICB!RxkxRyym3?+%pU zsE3e)aKTbrH;_{!2{Gr|Z}Ce6QAbgh+cBSMlYAu6sD_0t`z(-+8AIaISyhl6!45OeQsV+j8dCkq*u^N8J{GAmrk6PNre-TLc; zqs;Q*0JW*egR15pJ26*LEd@m@`%PfnP_uo3o)N>o^SbRdLCIiXyW&GwRO9}3qKFJM zEk}E!W63(GDB?bl5ur?O2n;E7%7Rk(3XQc7WA2~QQTxsqKCDdM(#ttGK`~7b{kQ`q3~<8RFyr#+Urfq+B7hHo)Kl2V$!rbEHspJfS%}y zfM*A!WYw}Av18aWKRP;_0#lSiC!=&@T(FK8xJe7KUooBRIf0c!1hypfCV@&*CWb}g zGUI#AMP;h!wR;As*$AM_l1byI zI{Ms^k(etFg-Er`0BmE{A!`f7~EUtaL660J-QIT(|Sykm^ORxx>|FXk}j8JTKfCwWSF zYpzN3B@c$z{Tem)rPE0crJJOg*o7D`b=MWm>u1hAI!PKOgCFIvIa})&gDfhB)xJW4 z1cY3t;j;LqW4b$-nGVAzfYJrjh_id6ubzyQ;hP?>Qvq{%>7;YHzL%gz-XSVuHo48F zNB&Y|0rvcy2ss3aZ!G$bW5=d2tK&Zl*rPe4Z6{yyZ)*j^@;b?GN_4K*hHC@E;?WI5_0O>qx9!m-*twx9j zQk?fy;y2B*#`)2h+v6a}`Y#mLfdBPXdW=d){F_ zrafHRiMNmwsa%x?bA(`JSA31wobs9d1B*AyvXs}xU!BonBQOy!JQ*p%u>|561L{v+ zn7-PK!b+~)k6dRIpbw~Vr&N(rkwHI|O%W*~>2fLV7XubEHRjCFtyTHXB8&_4gFYt4 zqaUWk)Vf_yh!^5NhGn1p?Fq{AwQz~% zZr^o0hm#0SD>s-A{>t54WySgTq;}v>N`oD~vkv{~_L=>l zeazu9r7g7KAj3dFy_6MAgRZw=r5zSmZ|Cyn3L;9ULMGcszLVv7cz*NoXYL>jg zOnF#ZAdye5cV#{yQi5g_a^3UQFwaJ8BAF>(jx-bM%zGK6D&{D431*-l0=s4;!l(uQ zvL7NFKxNfwX9Qg1k#c<{Xep@sChq9F52U+ER@Y7;o|+Efti!<^HDbm-6sW zGwoe+l3rUNqKLQLlfCqmE8j6cuTu!)X8D};3~cpIlSx3bN!eLa z`hWsG!>e$YZPwgC>-K7X(_tIG0h=HH<{ zK%w2KSiW!W-gt7JX*FM80RsnUc|x5{vT&6wr?C zR&{-goK80`V{eUY@!p(eVO+DLIihlgL$?Eh=Xk&g-3rP$97aP?|L)xHw_P(n><6Ky zD{w1_LUM5f>jn#OL9cHIoP9#}fy&w^MmSP}GBf`N!cza^Q-7m!qWR6%uH7f`X-V;P zES$+=8;a<7g&Xh?UxAH6=Wt=-fgbO^BzE;!RyVI+LSXHIpy}(Pb$PUm zPR{0jcq8Vbpyzm!%Fhcj7A?qO8(`U`lj%LqMjU=^j{iM(x!>O_`uW^fE!Rv*G+D47 zi@3N%Y$OMSixH<6zeQ|LuEXqWYC4)HXESn-+kzsS*YA;#iZI_C`Si=yh)YN*#{K&! zALOZvd)uERn#|MJs%7;$wJB@1cFjDJu1Z_EURRypKF%{ zRsFW*ytRtYr`z7Fa|~d-fo{-c`~tN}v8OXV3d=fD%-_ut*xlYJbXg;R(#hgxvw+S&WR^k&cT9s}1|N;fr^aA*%Oo_;<@R-_I7p$KwkOgDP!HzB zl`Aqt!>@`&TIe6WlUOr_ZI2@nmeI3aGsyw@igGdr>#X!3mF>)5j=ZR;^Es@oNoqWL z>Jg0?u-*DFDv8>!W)W#9a-#5q;4nN_mVo46=;M)gK~%*umGBe(g5GhB4W(LB=58GD zkslsh(^`u8j!NhCoo8pJ8L-?zN|G`8e|ZYrh)2`wX(ASi7C;gxT13)1(^hMo^g97V z!)G&4l%?0?kJeKZ&BhSUyiA!(Ir$97(dPK)d`mR98$P<_f$i5u9nQHXLZ zb3pEFQz-v*Mbf!zH>-$h`xp$BSgn1X!xd+?c6IX2{+qqGD(l^e@X>G?fCf0vg>w)i zRAF^MimaZ?r`21K9r~^Mza2ZkWFd8>02lTiE;z5k_ywE z*C+Bun9#|rPPo+JXfA|2vJbxX8_1s~c#;=h1@Y5Jl&JBTZ6e5!P*j(kPla6TKP(Ps z{C7P!@N5gZKo;ultXSPa3hJuQle|_QPWpU(FgH-4M}VKMg+FXFDJRpTSFCG0zzsNf z)W0rWyfRd`gOZzzK4C=Yl;i-A6|`}JOv!3bvB#e~z~3MAS%QAu*o2R_%zo(K&OB{f zOu%ve>q}=~KwVm4jJ_awa}34xdU8NX>A>sP=0GPpX~0y|CV4JQpil*EamC8^BZrz{ zZ=h$-nQ$PFlmuW@OD@CWm_hzTG=}~TMo4o3>nB0}RqH*7AOfpI{z!mn9Qn>5VId^) zaliH;pa);1Ia{Q>-U?x$02iEcUMw&$X)^iKAb)8Dj+4Mp|8w7ZE$3Sf+VKg1St2suPmtDpl4Uku|AUxw*p@4 zA{F#W&Z0zgHX8m{#Vh~?c6|z8Qvs@EiNTeC5HC!FwNVgw@{lbcuvHH&W5T%Rr@?bHt>Fe1QwuAuNxY?T**|X5qNaib= zk{l08*{K3@pn6EzkicIu!9g<2 zAzA-^$nxk*ygT7LlpcSH)EJeSm2S%XCHV1rJrWlj4JBvQRnPq>ZY)?n^)pgq6VdaI z6Woqapj=n^^DeS+Ggi~W9Y2ue&*MZg?KMpUtAAP`G06y+2~~T_FMHa*<5=LdI4Hb` z+=5j$HhyD6-4ut-GC0a16=7A3zFWJh(&)~ODtW6$ckHd$)RRuI zSrOHpu>7!EKwdjOt+`tUoe&E?P17w&N{PlEYTf|g+++G6dxJ+lCBG}9At&A?yY1gU_P9(5d~scG(CqEjt-p3j;FFM?-3D3E zMxToTj+H{r?6rIuEi4(@YMK2`X~!yZhIKJs7>#*cRm3v}?4wq2MkL9Gbu?8onz;1! zk|(L4V8QqWf~q}pwTT+F`}3FZ+s$K1&?l=Cn;0);Pxary@TCAzKKRfE2|aHDCr=K5 zNC%u0n=r0h=MU4XF6wVz(nW;9ryZpFiHO>6aGdDvMG4Bp#r{9g;@7zIrUX*=1+7wh za8<-eBct^3UYl)dgLZwA=tR9L8~7$&coE2e#^;MCE08b{9*QmzIivwp9oJ?0j()Gb zKgA7d2A^R5@l)tzqU3Cw44zZQ#nHXqKogl-ni#EUo6`8P`n~n->h05UY-|p+vW{iA zI*!OdASGv_OG>T^LJ0 zOFXR#)@V2*M4&Xv#+Ysto>T$9Ca$+L0#8JzV3?tztWdo^gNg!S;b`24C;O=W@V7{@ zH&a%$0Rr($-r*-ao|HK029-qH%m`9hyPAiF-Je= z(|6#OR$gm>Ja9;o-$KkLHL8@dR)6sbM({gLsUS=KdSHRtcE8H~Sl-#`UMF5CTE5*l z_<;x-1Qzqt=lOAUGHjiUGDCy%lY^iKVZRd5`F&Qv{Pf$z=S%gS`@|QRG!pWz$4rcP zE@ruKY_N?35Q0j38B|nIIK-rGUHGzaV%C?$_pxooA^cm?)OT1IP;wjsQV@5xIl;W1ws30_dAB3|FyB-12z36U`guoo1~&eu`PQSPSL5|6BkV< zg^`ht`#4GKY7vLTvwU{g4hiW}=Oj~_j0*C8HxX98Onk-4DwSJOn~YHr8wEA1wdMXJ z4IqGjQoqbghiCVprrNZ&wdZ5lhUGP9O&$xbfC2t1vzLnCa7 z%8_rwQZzUYzWIYy7j*xX*~{nPTZyl%6*i1r4lRXv_YHX=3gU|8l1i?f(WQflb%88w z1|K7&jm*K2r9}dY^(w9GaMB3wNS_@o7_>wS2zSJB=6q#SvuB#1C|p;X6V~@A+#u<> z!}!YZqfg1= zEgV*%f3SYWz&UBx1e~+v#7aK~awt$2ph6?^((nc2AXi2@$@-}|Y+cIL{z1d&<@_#% z?GL9%rN(mL4GD@*b~C>DDF}EdjZhawMrC0L1PD|Jd^c6U>*D2fN}S`w3buO?Igxpi zvE+O(2I)WDV~+5UIfs%lxScyQLqC*RfsOfKt1(vN9GVso7?FPiY1Ir}Mcb?+Knf+K zT39R5&ymf@+ydn{{#A;#G9=vL$VjNbUwUdPxxpQW4xFA&sVv+Ae23{&j$BXFF0Qic z$dDtWzfMuZ_+w!(31piXCs0PM6=u7m>#fEetm6ENi@tc-ps)BIYLA!IA(-MRn~?4X z-|e;p2V3c_)70A%zd5YNf!R@u0IDL%4d!00xMm+T8Q)ctlBlt=_sP-bk@H-+Qx@c$ zSkpZ-k(;dnL`6@G51)exB521YN9u*^_a(Zi`=z24nIg;T(@?Lz9B&^c>%Ki>1c>bf z!8MLK$I5W6LqM4ypiZG`mhkbmm?4-!k;8Um9P=0p>LG3`b0tOQHJoI3fWJisN<9xn zy*=u%2yL@ez{c>;1IJG2nW!OBF9nYc-nk4rqkV9C1xwR>&#Dm7RQA)uZs~(pmx=QJ zM(*HnorH#J?T_kJ&-p_%JIpk;VpEw^ALYa+-y<8um**#s>#w=}H*Gkl&A+$?Mb-Bt zLQvWb;5a$ZN6?sUK@wqf0N#6tZcKy+q?>!P`NEl|rHsu{ubt3|crH7FX7*&l0N|eZ zIj&bxa$VTD`9nj0lA3t~PjSBfwJ8{o!g7TYQuE@@#cW8p3;8J+qcXCK;SeZM7`B)- zN5IKGcJ4@X26#T*LeGZlwN(*qY4J@8YYrDMwYiY>FjG(Yh}iHnz>Xt5romau?aIQ; z>dow2QjQRl`5k4S_J!&G)^)+Lg@uHC>q&gKtWx92KDlnOxgpT>fwzxEk+acbgOx9@P{8KzuU7BOhog-{g{KX=B{w zC9>+QB1(gJVe!=xemzZh^c{lnR#OpGrc-7u2H>l!2a(NuVC!%V{Olg(G_ILtgPE%Li?vSq<$c2Or48;(+V<04=7m^nEh@J41J%j@s$7Hk(nI zH{5%tpCb3)Dck=KzGY|s-@&&moZSBb82|Bw+r@5Y)nbs5PVh2HvpRV z4HyZ0zeLjFiD#5JIy@i`j#rymf=v=5C+92g6d%@kdZ##YDOK~2Ud;!#J@mV^<-zcG zF*fhFb-L#6x_np-Y->+5?>$@et1&jOJ{Nk`4_%+`ocYrO*QQjv?vf_JbC3keTY!?s zGb0^h%gclAa}0_eDgRl}m)=d1kO9Tl`GVyt@BjE5or>Bu^Gv%qqLV=$5P|^}@}kqj zWn7+)A;I3^kcf@>(mEG*6fv!!(&N9Bw@GWu&?M-wZrd1slPh;T#^pfyb;e)zwY&F5 ziO1p`n+z-5XL)zE29Dqn3cs(c6#tN=jL)OtxKyxY8#wl4#o7%=T2_U2s{qs=oK+dZ zpnla=Ux4!o_X}PXc|LDXo(k&XRiw)bx2=%UgUx>`P1ls0Tc}r^SPC-0GcSOv1Ztvu zf4u=@R?=)3(1*&hU20LLej7JcV@{!vp&2<-e&ha~U%Nx?wP$lpQ8F@-?=6X-NTLd3 zZz#DRDxLwB4K7cmLuF&DulW}|x<$l>mlNoJ*Eh!g4^gdNpaqizt_#8Vr0UjEMnqe1 zLbJ1o2Crl*9#D9qcmYzT6D@5LkYUz;WgN|0m32!C{u{<$(^+H29Orv%+1<-W!2eCW za$EP9o{D^dwokLW_fE(rb&`_`<{J#*`bEsXFC!tDsTN3{)sVrcB>ApS56CL;J+b7nx27%=L%9TJF*REDP zlCC^fI%lKs&tU0X-mAr}2;0Wo3v0=GISZz>lya(lv$*KeLfiBd_|Q$y!In;Ah}z1` znOz$2&Tne|QckIxrxxun^XuH6a6l<{gvnMOG4WIRJA!nwgbZl4M|q5&$)2d@6EAZu z>jQ(ySEYWXC%aBGEC=dh3P_TaGdfjwN_MtGK;3B6>_^S&cKpQR<0cRcjr7}`y#cL8 z8?EEC*LhRzD13WhM{Qu+Lu(O>@h2q)<+1h4nK7%9^rxc+q0SW>Tp{jSyYmVZ6d3YJ zMv}B7iMEDZ1qA?=9t#7Fyg}W{&$6Frqf|`tD{z5Uv<3(8uqV^oETFjtR;Dd&S6j8{ zPCck?ACoaN*&N%+qzmK>;1%HiDFrgcG3zYp(=3hC2zB0@6DNUeV@x|Fn9^nqcJ7jX zVCZ=JBK}2$1Kt5jCCqdLTQS=l$NHru4F-?#ahFK8@(6&3^dbP`z$dEX(f$M4$?X8I zhAQF7*y#-CftsXdX&!9%^L99Sll#rZY@S6kf=&{K*Mz0$o%`m@^-}~*%EEoL2mDB1 z4o6L)@+x}ZW7QpO9s58o#smtiYDN5~9Pe&;s6&qt#V`~2uCW2vQ_t_!s&Q?GP||ly z<7WDH?g_B59&=+)APSqrL}UwXU>Nvf_4-zl!pf3zVmaz?dO!U1?&g_pxs0(nZQP=# zu{^PCo?~}0Lobu^Y4l$A4+5DbH+jr^I|Ru6YBKny$ir2lnBdJw)MTO zpJt<0!((qafVOaa9>?=E;&iNmkcdw|vTSM#Fi%|%J_+06;}!^HcB zi4mrP9w9gTs*njvR0&8LwI|=*AyV-=paMk8ole|OT|{f`Zv^J9fbRm3eT(sa5-K)s z#}LtG3lryK0R1H~RvfN@61_6x&|n4VHU_2y&Zj}&arfxu&i)~X1VNL*e7sTy}n9;!}KP@^7Dda*42A3%@_xVnR1 ztkYSYl|?7#6#*Xuw7>oIGLqAHB8o0&v0yI+lHc{vhOaiPF42<^dq6MM3svDz#OlL8 z5BK3|h0;$j$={Osomba^iG}(#48TXtNXh#O-Q-Ua&e6mN1E*A41Fm)TMTSBeo zoPrL_D*$41J=znK<3`a` zjOq2hxS$C*T`5?HoRcd45kkBp*@B}_{_U^<`W{M#P&b@MC#T0$j!dQGHCXn1#f|VB z%cvrfH!50hn|s1bgY4%t%i{ccry8&O8CuhEN?ELSU(~)DD)3$!oy;L?NZbi62!*DX zb*Dr)5hadHpV0#A%wfLAWHSGI2E^}tyO)hKJAnQ?{T75;IRsIgdkt0EYN-X*$rqlu zhQA`E46hI-;bKKYRe8(|W_7d{#0FQ9YbudPYT~~~cOxtkPwRrJR1rj16$5^g$F0;? ztx*`6Yf3AFbpr61LoxsPVa}G4_2ffWZv`(X54ra+L|9N4J~e4+w_qOQ0t&VnLQ-5t zKoBeCSz0E4@ssqqB)Q8&6m-|(x~ z_XIw^m!03z^OpjJ>x`OgJ>O^V=c9-srZHU8u5P}m%*0{y9E%f33~Fw^nZ7t*8aqG# z5NqX=wLqpO<00c}@Aqo}AYBf{BK=mdF$?RQMZBqLpEH-*Y<|x5QxVH3!6Gr{m@o)i zQ;!vtm)3A5Ow|eJ2O%WT4UWG4H3I@P2Wt+AS|>;`JfG>e9S$gNC=x&7R5L+Dy>a2z z&s)mvFxz^%{A?@I|213UWzSNbzs~8S$$YLzT^y*~zZ%Y@# z0)c~_FkEB9!DB+U2!?M%j?TsuW5tKFf?+i_WINOH4Y#>^tI&pcmZc0@erTdnowCU5 zskEc!2>R+(?)8xaa2t!Yp|hLe=hwywbuPyn`GW_^rP$i3UK+Z4*I~ptUU%fd7DF;?SaC zTL&T2(cWJo(C*%6RJlMzxrH<+o#AD{a$%Cq-1=&th^xl{C`HDVpO~r#I?s&vcgyBk z7$?uZ)=tZzam71X45GpD#W(-ybeh`WPY~;}V%l-}y@UAtgTpSTgt=`Ua0Au1l6Lrz zk(-WBKv*qHTV*`ZDio6DeUg7lW#VL2N#e9WCz?0NF|QJ2wH7Hg0bKOZC>oeUMlB==(J_v_15!*M43<{C3c|Ex_y%`Z) z-u0B{S-G)wul6iWK{9j7cruRhCq!t64hrQ_?fBG-;omtGw`j8cDOTcD-0WiBK`@S~ zhk`qRWX;j1JtK5=zYkFm{4*u;467YdUu@WS>J%WKfw@iSM)Zb%{WYDUn`;Mb4}Rxo z6lu}XMR5H^{vd*}>$=OvL!I{-=R&$0CI<_6VY2WdLP!YA-k{!jJ|54gBh9-z)opym z5_)Qz@1K7)^T;ZV1R5L7mX>Oo+y-A-=#lsVUOFPpyqyPHgs-+pSa@LB3BFhiI!?j< z#t&u(pX;!-+VPdBje_0kN^5xZ8w$drfim7+rE{8R^{Pr?K&0YndpDJ5q(gpBm`5*G z>;|wqUvu4jTgcu$14igd!hOE zeOw`Z{zw*V(ZFoGJHe0^yeXMp(2zS}tSj$spVHCd=GipKud2w~cc!P1S$0GGy<;fk zEBu+yRf9tZl5aBsom%^^9yG>s6A%;VMmve`b57&b#j-=WMtOTpHdleUW8ZH;R&7No z^)uFQt*$6a6&8cnqEt6)G|g7tgaDMaEIO~6_()rJve@Dpd2UN<-*@hnxqg%T z1u$t1#{GIMQM_vY+UUK)%q0pbK-kW8|Ez1#C7!^e(S1W#a{LX70Q!44#^@0JGWZg7TC7X8KfPb86Qm^KXsUA3LaBmy?GhqgKaiRZT|B zM@Rndi9GJmuy|XnRx#9v>1fW46-U}RvH|Gon~jn1*`~NU6Jpsql%i@`BK~QvLkAPy zn6@lrO*Z(^9eNE237sCWpB8V}i4ViEJIHBq?OuVDT_6MZ@r=~hGj5%fLf@6YP{qnoj)@XmNUbWEHF&ExRvu|)jXAkm1Lggg0< z7{}!qxFW@1QGO)zLd7A8+%AVT4)_XyUK4TaWbaLmapteBal?FoVqYmwi~$e6Cw`vm zpub1A_Up&sCrefEx#`Bg1{@6fP-AjavFSU`hWoEH+`i){$$4vwz4((-4J=gyC4rkP zm*28UTOD(2zcc1~&60uxOCrQR=K?{iu?#K>%qWQ3%nE=guE)6KEq<4?=%r3{KBy4a_#oagb=9 z>JIROPIMFEZ{m~&f)^RNC{zff?oJ6r112Osl}hDGf1GdQ3dgui6=$#ZLDJl3>Zr> zysHXz5LY>kUen*6n&T^5Dkj|+3}S>g)OTYIjBJbd0sV1tl`NzBgzpz7`{}<#(gK1& z?j|Lp0VC1{4o+?E?i2U)b-)~QpaeXEjL7euUIe-U+d7F7ea5vwZ4rhuLR<#`)!AP| z3GV(}y_SlD`~l&RvY|8lQzu#=^{t8zNoxDE);8%b>IM8l%ylgcJT`o@OZywI+jfrX zU)*!>Pmckxov$6eyl9Rf>dz)6W#}`A&l}huU;$w)Fn^JJq&)=t{g+vm4M)JyE(g)i zxVrFq0kH1pjhoZyV{HomH;ey6El-<20Ao??&@vK;>u1oJ6$;Kc`a|faxBZ*<_($R{ zlIDjo<;PuA=2}6acRQ>1>n9ZVI?Cn#8`s~Ynir!K2%b#=vg`+eUF1`_8e#zA?D}(8 zADs@b6ihn_KW5rT$;dD4mJom!t}Nn3Y}*vCW%D7J^%jJyYdH-aE@D~8JQg_$o(dlD znTklsWFxr+8rYBg5DOdAUk?BAfx~DErl9z9DT-_s-O`SfLnH_0-`U>ZE$yfo+i2*| zh*qkE8@%dZ4GQu}#fFgzvS5K91+4dq#|n86S4j)V6;zXp;OmES;Hgirc-&XR$)MPl5@5sL!((V6 z8S$b?(qrcQH8YCn5P_vv%jv6{YaQU5%8>o2}5 z?Fo&lN<4D)XqOmwo&p>U+v9s|$jzfo4=r%G;d>e0hvL<4mgIUh>{2Or6ymJpt*_~7 zC*&&(QklQHDr0`EROkS}pMzpZz2~_ZOsPjMl_Sh?Dl<$Ena5FQVItmYcCNK2+)7gZ z5~;`TCEwWYYsrX*+e?5O4ZsYp^EKqI4-;fssr6_~EZwtt;Q-p{I$X%M?>)mETF<8Z z0->VuSqRnz$=8cW0lBHq#d8GsY0h+DHt@*u;{+Dbb&>bY@mZ=1@U}j@=<~cX-md|M zJ?`t_O`dHzte}r`-{Rx;{(#&n{U%ZC_GW6rUS$O11fJIHh|NVD5wBMb>lGNxAh2!L2PHw~0G1A&I2T=3rD7!GzWne!sHiy9|$J+3d4U@xgDMzxv7T-jY)|mJ=rx$*oFf2SsQbEKDqrzr^448cVf++ zItRpc?f@jSb{mIRLmBXnVy`+Mj@$gEFr9{N^37Gz_uC9?lWu3#h`0H7uaG1VA-Pz5 zB6cc}uMG_RJotV3TyF8MSsm3A&^C(v`_q9u_WI#)9PXc_g(3Domguwv1|ORA(&8S3 zyUA(M5)TMA*Oiv0TthnFk4$2R)&8B-SHB}WT>){a{;g+BCgY0P#f(`wA`45{A|jjN z9|%;tz8ikdCh{|g$g~z zG&y&AV~NoRsEg9X*c2pIuC9l$*sn&yd$CpVoU;@%{imyo%~V>nJzh9oJt1WRAIVk{ zrT}i$^0m4?rH5%Z&Zb1&%mfeorOF^zyBgQjhLGF3r(YJ44)Rbz?|)o8FTWT)4NwBR zX#{o9Fw|gu^-)!P_qrOGEsh3f<1AVS4v=WQk2S6b>xpAWjj5p~Xlo#Se`oK=yy)o& z#<{nY?D293$5#< zUtX0C)=Kh)RgEulaFiITTIWK4j7tbwVmnb_ub8{KEQ}aAt&ikf3`NQ{zcO5}*^FHR z-g`m^H?bq|l35S6LfehF9bRZ0pLY8l$!BG7UCiPPL6Bx9N3E7$qQPNM z5Uh7ZV$i(;eEim5nQosqoeZIbB1v=@6AAG949Yg&&Q3Uiy>p&3V3p>f*+# zsvM#0uR7&i1?oj>%v&>ViI)R=5h)HK_fPk;50{}k7FR!fX~DW%CgJxevQEaW!z{%k zersB;xbUEhk05L_PXq4hCKC^~O?$wt@d0Qgery(wf{@!OH$T9&TuW}JZBv8fgVe-P z(aI*uX{S7FA@m$adT{C}k#;acNS?IH5`nG-nNaU-qpmmN{4hDxs9=%yFA0x*ylhlo z6A`i0`vixyUBNVtZz;1uJ(v1kDny@dz2-p5FiJvCLZQ~c9_aQhRg)Z{4>5RK%Q`=* zkVjzcFiBli9e#&&&GRamFRWvBW+Iyk@^M;ys8rTJ%+Y`{pItkgY?}ym!Qo|W?pa28 z6v_guQ7Y{(k7mM7yXkX^^Vj9oaaA`b7%h2hnJJZ~<8A7wF`{!E;&QpSFYGuDA=jqj z?%ZO0+0&-^0Kj$od~@;Q@uNLWQ%h*W`s5Rct1w(S?n4{Q_NnDKi$I^bd2>! z%rY+64B1T5rmSP@86-0slj7*8HmOPYMor=Y0+*P~T1)sK-#B2bpP|f5rtVB7?2*Vv zVy>!ST_FBUPBFoiwf5s`d=>6!#JtQke5F}0l0Il2FyYp&M5RHt3(-rc@X$CcS56pq z)+&KhR7Jac36(dqOYEc$EDIM69cHwZSD3oG;4()}kK(;P6W;_$YDX%1DQvUS=!mRY ziohXv(>2B0r;B)WY2}mKGq=S#r{*{g(BXq$sG1DeIilF%>QT>nN_ShRt#qIY zoxTPqmQdO+EwqHBGJn4Q_90b&aeZQ#Q;ISROWWk;N1Z?a#l4k5GKk+6M!$bJN*(w; zTjA-cMnv&2FsfmHhn~7rSZJd`&0BPfGyppQ{H5MA+?6{*tm>C*k5QpG&=ah_)r2%V zH%FJ_Q6|>CAzLfe{po3}48r!ET%DdT%>8Ip7o z5UgpjWFz-d`k^7hZN?I&45)ScQk9iJw;!jS6VHjxEsMUq>9T@E6Ty*0uiAyFl5Yx5 z`a=bt zdF1^Uz5XS*_^?WR56gPwC|gJST~MzE2ra04on!eNlX;f%ifUArIpBpw-<0Gai#sb` zPPj$)g^+yTYIW?={Zn=x23en zIx3M(*C~3w>z68G1dO;WJ!6dXlJYG}Yu z7Rx^MdN_p)aDq0GY?}o^j{u_%jn}((O?NN0B9Xe2_gbt3tD?Z$(j}@XtD~4$c`T9> z8XIGUgC0VW7<0zzLXJ23lVqCv6z_$QBF6-8)Z`c0%c=MG;?RC(m5VlDBV_#!yO6_$%0W3EuBcd{{7g zx;s?#m>Foh|0tRMS%EYWe%aq=w5u&8<0sT0Mfzz$4)RC;ZzHLI=3AcoBnuKVvAmJ9 zGvUM2iZ#vq5=HcG(QwHJF=~^%=SMhxjiQJ8OQl5A$Rlphql#B z1qR=lqB^)qz$$`P4ONe-Y1+hG>xMUW$Db;{%D-;i9@|;_o4L-Xr$rdt-icOXU#`#D z-qG3wGR2Id`tY{ztE}?oUc$*0F`c(Jrc35U7;^3ipZCnCeJ60s0rq44jeob~XNFx8 zI-D5HO}^E=n%^bxc-H|eZ+M|ej8Z#!Cjr>?&~wpmkWHV{l?UNqP+-;-fi8qeSNF z*RdtY6FOVK9o7%2PS>5_$FLcy!`S?0)>5MmIjh~ptMkqn^+%!)$AvN5S*^I!5sWSW zEat3v{q`DXJ;bR6ozu=QN`{0^yio{f#?UiYZF@n?v*i0bwO8|DH8Q>}-j9@4#ovGY zZntd8cUPEMOX42O&mLK_HXIBTd=ra`=5Tx|%EMX!!sY-yUXQP~Q`L%t(gGP2>;j{w z9sf!xkbl%<-GyEmFVYK9YDjuCHu+%Uaw5bk(ZSq1v0V1Xv^sO4TGj7QJ-5C^S@_l%aAhNz*-QhpG1LK>q=g=rDm+{+@Y$67m%PLxfW zjd9I=K1X?KSQ}tNRIKoNSfT=(SD69dV~|PN^;S;aq#Nc%V>MH?vM^IFNNY!*(9$V~pqv_Qs#mi{PwF)G#|E=IX2GYOM{k*da6R^uyybAwOQ-6X>d zq|32q@$=YuYue-_%!$lcv_0i)@GQNq8K|~u11Vwp{~78 zQ&!g-$4T@^&RvWzr>PFinKwmLuGIzZBbqT+&wk+2vJp9LbJuB^rteWs#>}<<5hXS1$cCP z;u?zzmRSj!GQ3$Q4JBt&Og9PdPM(|$TS%tb_7ka+rnMY?dhEm`wYAZR^E~`;|GYU| zty>X*3iwnwxM*S$^}jkRZBPDmGK5}`DwIQO{`_oAnUnt=2UFDSe?g|UkA!;)xPF$z zH-I@|j?iYN?i(eTj4IgVmMS`}U3$^x94*125{pPLJ~q=P+I1#Quz5_v*L8e0RMrTw zx9DyY-WD49N+ssQNtJHBkGK$0^W@2>#+I#+J$aIRgLb(O5Jr>4MD%K9Y}6%P^E1 z+*b!g$d3s=Ut3j$dQU|P?>+}?o8wuST{Z0W?xOn=)8=72B~aIey#3XjP^78?!LJ8{ z;>C&Iq(634U>P>U=9e7VU&bNietL^cyM{rZqq0MKc$#oH|1{HtJNH zi}_FYdlUEcg<7PRi0ZZisOhNL;zjq9e&fl%LfTxuL!8!>Ik}EP?#~mp#DDSR1p_cWqk2N%fF}!T|c1eNl{F@n}R_V(JGZVSjW>i9UhSc;qO83XX z4fE{1uiUrd?B;X|pe+o${G8M5`ZEK=UH;F^Il@x!QcsFjc3bUvzZT(XnVc4gW#Ghq zlO2CTREFAS$w|Q-0nna(Yx0{pmO8SW8RacNz4~RE-RW>vT?-9P_aLHoJlA;NAV*WR zM@_^1wi?zaYBHXeid8bxtHh&;>p$z0i}v?HbTdhoQ0`VXpegz=cK^e`j?0|0qn4>r zs5|1ygx*GeKu`SG@~Jd+SYezQGS>3`@$Xw^SzzMbzx;a0MWw5jw@A-i&805I_gDl& zWo`*lX>8Z)hC*!@hbGX^ggI?58|7S>&ajpb2;6PI-07t2Lh{sIh-rPW^{c%Rq}8jR zv;k|DrTa^DKo;T!pPV7p{V>zR%0q6hvL>=Vo<`X%_@WQyKrL$(QWq8_cf{cK%xYmDj(KqoSHih5E*a?>+yE{ivAIt=%9 z@dQI+yC#d9fj+EOa17<+NV+L+oX@GmN)g4Sg1bGVr&gx;b-GYhE9Xp?&?h4HW%QE8 zdIqdnh~u7FmmB>v@#gyB43@u0^1o#b{)A?$MxhMSSGpse2=mU+kTzYzHyM{Fh^JNh zF-u}IfLbF~-HdxCs^_qm<9J@@IppOn4noyLYYOC)f*-j~J|ZakVYA7V-I(+4p;ONG z*~S{WV#b|2)jVLuNV(?sotAhJ?C`r=imlsi)soPur}%pv4ip3;eNYgiHb!3I?5n_u zxR-fWDzi9uzbrnP;-5>sv$Uy!q_cliC|qNkz<=4|B2Rf)^kw=oNxurQ=g-Z9;2q(T z3nq&D3y64M<(b1X;N7+rvF3Qv^H@BG9NEd}`>vvQSad-i*AF-`NFR~u0#9O*_Qhm; zU!HO>CK0AMgm=O7RHYd`5eOwcb^2`+vChW+3WJ;~v@|-o;bUWZ~WqZ0# zr=-MxC)kgWM{t|cJDGI)_&AAgmf7 zKP>JN@kwR*$py*E{2?qS6ev{GcOS>6S?&3hay?)4VCL>Ko6bE4#rGtqxZl_T^QKMf1OdU0bxVTzgQ`IUCUvHPW^ zMaQS5VZ;OoP0bBKy|kdm2!Wp**jU<+yd!m$qRoyT>XMrs+}V-a+e60zj!mGowV)at zo|>&5X=$L9laoB(#unFu@W{+g4J?qO%wQ%LmwwX@==o2G*>WjIm~_{QU&m+ywnE?r zySuyj-xK&mHt;U>ER6NwqquDvAhz42>Da3vDwnb}{&d;j`MF;PyL)@R6N3j=SJM_I z=aUxKCUgSANDeJ7bs(hxNS42xoq%%hwG$|uSaWX=oN-VANU5d<*Eg+N6T7n`yAv4L z79?8=xd8XEJG45taxU5qFPng>7UF+~ZR7j8#+5zzpI$%<+`!P}m3|ZG1kg0sJ`Zh7 zjEwBh4NQ(LEzJ=b8X7^t(8&(4b$N77K*ijOdwxXQWL|E6MsEO7G`3Oyg(ZARZNLx_ zS0XU{sk!D~&N=BLOTBXl7<+EF^f46Bv*` ze>E6B$<^;&K)!&Y59yEWp8o{W#7&W79ih8O0~eY29e*}Ou#v#Gebt_ckKs%&8t4$H?aPharj-o z^UX~NtB#w|)40&H_)S<9UsV)+3_M)FpPk)xzu!GwQ}kW|8CGE5eARaX%F^oV-&M(O zjDPsSfqu6mwr|_~qK2R%=vkGqxuLin>aTadRgE>!3e)W9yU2@Q zm3-+sHRS%C@2d!`n=l;+MQ>rQWB>GN*Wly;6_b+_%XYXX^%chhu}^t6wt#zhu?_^4 ziE?|QyQcGdzVik(dy;eLIUXAWH9-BMeZzGG!2XJS5z&Dpo)8XxcIyGiNPdzZJtsbS ze}8=usevRe5Dr2aBzy&K?T>v4UZWg*=eo-ceUn_d%N6mTWGNpa{W(_o3|s+~u>xkf zOO61P8E&Sxz%6TruRx8h_{8{d)|=09cIQ`8^Y@yM>&?%aJzl}YZ;}{yCN!^%=dn*9 zHYn?}WpL?9zIU6{l<5=N8wvJfesFvA-=ojrH+JW;#B1`09T2 z1Q_+Kck}yxEA*IO-H3l)o!@+ctpcCc2p2Tp@n2pr7uS+po%*;>-xlZZ_Y(&%<}%j@ zG1X6W0?Ni20AqdKx8g@eQuHx@IuNPp>%MVHc+;gn4PfqUj^S8Xzh09W7<$UzAPv0- zj~p27LVPHHyepbHIktR<4tVA0{x{M4#S_vC!O$lmDe(OC0_|P$`~mF^&~Kj0S8Szq z`pE&IZ_7=FduI<@8mT?ikI&qPz>PjV3-Spli@&yBE&DEfbNLUujvCBick`8;ot&bT zWtk$w8$CCx|Ed84f>{|fJ*ae+6N2V~piqW9SLmzu# z4yYYeH4OcHXSinopVK}#i9~qHe_qZ9WJ~IjJS(4WZP$Nc zW{_Id_&_FmmF(-kC7~yFF0^vU>|oC+{>z|Mf^8=I1*>qOnQG0HK`__TnjW2Xih)M6 zjc(g&6_4;%Jy~+Dw4TNJikRS)qy$#4be2KotvkzecG^+6X+pC4)+ks%BR0-2^cmo_ zr_cRNZA~OMz%!WutU1>2CI%eh7d(Zj8!7$tmHLw?J@C!SK~V6AUiQ$Y(|=p5_T)Gx z0O_z`0rn~`s>mQeY*j^*m`BgaS(#L+im`TGTB^VdMW3gPJ|f}*^a(Ol%XFDJt$QLZ zM9zvi!hRXEFcee!MnQn8A&bQ(N{F~PIW}Yb#R^gk6^>U7C}D4-{^v34026eny_CfT5kx_H#ATxQL>}Dmm zvFB;^ON_abfbq8iU-D)F6u6IGKZf#OvuOH&a)$w~lx#UU7J~_JQxtd9hm4Fjp|<0G zE&kb2b(dg~&?{d*(1y*Ru-9>Rw->T(BG9&e^?yl+KtaqI#4vGul~UX)hu5PH{13zO z8gG#hrQ4f$&U2NjACaDuD=~vpgN|HtvC-bpgXllv#J2VSZT%o4bWvo4<)AS|Z<>G@ zKIZKG*GTfug$o93bjFTdg9Fvq!Qbwcbzgw+)FjXkC8_$Cd^Q#-7E@_xn5+CcTtJ=d zLQXQ2GSHXFyA$G$&T{Q7x-5`ZsH{2soa*g3`h-3}9+&QNf8z!tz#KmiFxq46XwQ9i zs*xi&a6S;_XPdy0qRvpF1{u8p8U#E2Pav}i=B^2LtRNO6Gm_HAH}PV&Jshn;gs)B< zqD5FhrCdkN_FQpM3nOr$iLWaJ1ct^|uyn33-~YJpFOO^?tUF_*qw;MHHiBk0h<%t<#Q z_5I01(NGWYvg~C#ex)P3g^7Z(miz@J0&p92qR6*P=hsoYog7HMFQ(%kRFOOv`p9|M zb=G5snr8b{o{b(hhTCxy2E`6-W-iqQ(7hV{#bLD8<3Uk436estVkt^RerKg#aIcnA zQ1CuybT&f(>JjjPYc8-NJ};OKUje*vFQiciZIyWn{}dXYRNz8}7Kf*~F#Mad00~ov zT38dTyz_s3-AGf0`Erjl9NJiVkkDS?M)SmF3nwiC+vR9_c-Xg+$bz?iaWS~gG!VWE z6<5imLi^fgb8%%>;;f>xu}%tqK=B>0`;Bp)=eKjs-VR34><^3+g)b(;op}zlxbjKN zK6FCE`JkG9%E;H?kpvjs$S@+N0E5@$TuS*sQ-)x@%3v;u3$oI z&+b_LXbC(@qeWG|wgcOoO$@Byl{?dksUM z*W3Ey9qwzBjQkZcA5ToDL6CL^_DNR5Z)cjAWa{AeRX((8{L;~ACHP-Ovi=co`Y>+U zR~yDMpLX#}SO%1e)=yTE?)0zV3tHjsCWD8WUBc4_*r;{y;w_f@bpVlp1apPK1mQ0*ch;FRb8+*F8i(I z>=20p|4;rk>fbW7EM8W3gvX^|_S8B$^b_G`b2jtBrcRWw-{;?h?|13Oz4z5)cYDepn8$GMSc;9F^h8k z1=Ie1TMU7k*B<>7AGi3^kj+t3b7L{8zu-7*Ed?D4T?Hb0ZDmYx9c3BgVPB*TJ@0kL zi4aCE4_w{O$QRN+y1%}jq4>AOy64G9r`z33FX8yo0^eU{UI5Dds~l2BxWIiwEc_>u zFuKYUwO!#^W!h{p!1M3q+E{so$n|qx50Y3<}^!Vy7)o>QyTIMoKZY znUWlG(f<&$W~&jci{^)iHB(lDK9yLNC4}T}-4=-bs`A0Ger_-Yx3)A8+uZpQCtsp< zOhfye%ZKV3xC5H1^D)B1N0Pw&ooNdQ73qo#t^+Jt-WhV~bFv!)25d&-tXy7T5}f;a z9opUR$v?}&C=ESX$GpN08{X^Z2^D90M-=~d41%sl?_9x>H+@M4shmCJuG zbE46E*BLKJK&^IgOH2Fwoc(cYqwW&uP-ecJDN^EM)c}eTR&tL5Zgy1BIliAB&=dq6 zlw$NUzEFQVrLw#@IK_PlW$pQ|5S`@hxs;HjTtH@i(mUEE51NRO5}*YWchRbOW|=3rByn~jPy0yS)`p`bpEnlCPtajVrs8H zQ^G7`<^&3q_fgt31m=82wBBZNGd@8*WR3UEVp+x+OC#0H+-4mM5HbBJ-{~+cz0v;U zEa%&|dCH%34Om;h>i3$)4FzhrvQiIebq>9?Eos4ew%gy9nSim?Ul%{+jA~RD{#Jn164T)oewXL+mVU)STl{)H;)X5Vvp~47Dn(@bLQfjjn7N z{`6yW(>r7MtJtO6BxGx3ag3DZ+9s>5{|6%-vSsJMIRtqcCAO?w9N{PS+#)5hjp*YB zUNh|V1jo6YFHz+$cLTCOS389JoxHd#fJZQTay!ry} z4X>rgbx2hz=9BUuz3|&i8@`!Z&uMOOj5Lnt{UceL3SFMVl%NEk!$)_wi(54fm$a0T zyIeuwI@hHlrPYu*9^Bq6dy3+7t$Q_#=C6EuGZj+@1N9N7Y9x_6IXwtPx7_xOof6PF zd+Cf2dW;*39s|)f^X;J#{g2_w-;&0w@68)@3G=nE2gs}OgwCU@>{C6>yMP}2)q$Lf z!)&|6N<5zjh3E)~(+|3T>0~Wpm7HBy#E*%l z-UK~(V4SeQ4s>BP+jVuRy(OBIqEVP1Y%GKK&J^8Gu*29gG<PsP@8&~x&kdKH;-%_&KIJm@mtfz*zhEm{9zZ%_ISATH*^WoB4eQ8S!8wAoXLtXh{ zm0=+7nJ}@(!c)K_v~M8dZijknB)4bODA=Fg)B`nm+|O#+pYp~T`$xbw*F|lKW{}3a z7foV0>m^*MGY;zoY#v+0$nH|d%*M)5AX5bWE%(Z^D9;mP zNW6`}USFgLm?6vDu_+Ii!a$SsaXiojlHM2Lb{EKASITOHLbsMNZQ(Vs0 z7)`bW;h48AJ}>BqZF~kS% zgg_b(Ijr7|4Nv(>^6p<`Pp;}Xn%MG55W6s#nxe}Qe|01K7YWW-wwilPI;=&+3X7c~ z=d~xG+2IVFWll*SehjeA%cpmRMV;ikmd&Kk^zlTWbJeSby6rJ;NwMrgH!4?ArO9ZV zPx(jMgx|m3b8}}lRuMQDEBW>Xm)0f^kBCEePvcdWxdSe^8E?P5OBc~S3xRD!SPap? zbYhYE5z^kLQG~C3Yg6g9O5@zCn-JwZCMqo@35LqR82|X=k`D+;_$X;P7!Y;L%*~VK zmXfo_kyT(JEQ-}G#n+%UG`2(ZgPV zTUbTwk~iJ43<0oC7fKbXOqtd6Aw)=(H7rC!=X>m=3QT=M`WW0io!}b4m?i(YT(mmF zA}=I2|N7doXy?_=z4UdAOwiNs2^nKT7NJmS#xgn4<}nsG-&{uUVx!LR=Ma6q*w~X1 z|Ae5@i{}{>!lKIYxKfv`lQ-2TU>`o=%}wx=;1(uU^9Q8)6_@jT>qDC>S!VG=^lc!M zfAw*zwS282Ac3hZzlF9K#vPs98%^+$@Rgt(nxL~S)ul~T;Fdw8pgtoJMnP~n(DMiP z#;9`nqI|=6(kyVhH&~x=gT|CxF4tPf^-nngyU8w=X_Vz0_E3fyT2AoYgv}-x{q z4gWDqoB;J1k~1FZ9frH39pdDpWbCkb$tn|q2aK-&RCUZC=gf&)eL$mVuNXD*U^pEv zAX!9R%X4EtHCloj(@n2mC+zyTIX)g<-5J$g3)EWu6p+XVW;$ z1=wYDL^JGnci(-6gXbcIRZo=9S)`W)sIAQm>3A{-DRs7F;QMZrA;!XI-O{Y32r>V3 z*xpfB7f7ha+C6F-n`{nj91#S)7P6bAyy6CU-S+w===)&3s~HBS%tVDraG=ytX|l1FTN& zI|Os1_*Z-nSqqv{rn$R6(aX!_?|vkski({4T?QbUf)s>h(8wcqv}}7|BzvGXPi@)W z9=!#^Ac2*5$`@u)F%@0E{teQB3Pt-*!BbSLAlfcmFjyqdOCYJ@;12G);1J2JH;3RN zC5nqF!e=>g8gwVU390wC7>J!iO#y-Z5Yj~V}1De;1yh1lt<7dnaf_eT`}u7f0MGsGiqm3&2174`{X26)&<3JJm&l+`y zUHFJ;pRQx*oYIhqp#eGuI`mRhv+(7P)&M7(P?MGIz(n7>65-;(%FU(7X4P_4PfR@m&XTT|6nv5RaKc|*w((%S)G%jEntWfF z^jz(2*O^IKb0!!XAb(z|6>jlw9QTz{BIW`C*XW>Y)6p*ihc%|}HKQbSsK~>;(QiK| z${GDfs+W^LqgUpArY2;sYG;V;H)M?tjRt&GZ$p2*#zN0qm%mR2L3nYhkW^llcih#? zLGzqT$j&vSoH{<+!Wq0fA`aySD56To*pxN@r;Jx;ubdYEsFUDZfP5JX~(2QAK4eFqKUHSY|34v!Y%5hAIEHoqfgGC>?p#ZbHS!Uq$S_N)K*`x z$6ehh!r1d}-6p5}b@2_CE_&7GH1?_Lkgl33lk;yQzX~TY+QB_F_a_kTIjqskYr2@# zK#S)cPZ(|hNMFK=(%8#i44q_=GS}|}lynz;;?OAxt7Y~x`&<)lY{u8x01+fewbu;hh`|dzc=cmRQ1enrmke0X2$zUEYh9hitN<}x_)B7} zn!`zRD9y@G9Q7;ihO29a$Z>dMUt;s?$3l_cNk^}M1fL*EX)m7H+0N5q`3)losLk4^ zAj8hQM#{LisyAC{1-K;s)dKbT7G}i(+~2q<6m%(b{9HRKJ-1L&W&+Q>J|_Bd-bi@* zn6_fNiX_N(|4HobytaGxtB|{5FfeW?Qo?tjSlC z-k6OubI)@GEb8gBk%pkb)MM9W#GmGxW}$p|?T6qB}gawhFGtKnM>>u{D2$tE9_ z?q?usn1qd1o2BSL9%{{6P~EUDSh9ofryeQnG_F?RJ;q5FCiI{}wXM~T^2oCk)O)B5 zzQN)Mo)A7cB36vl$e#jALzrJVzO$qjWTM>aE6q+Q%b+U?UWK2lGt)Eknde>Vq7;V! z>dTgse+hf}*AQ13<4)89Zw8^q^BIaY67J>UuG*OX`U1KxMNS!&eR<{vU zvj`+b*c~U>cvFmzja`elnh;{meAK?EQF$I2%%X4?OrwCJEW}IdL%c$dv#TKN7?Ni= zfr=1AL5vP9tW0pH=O^XfRqiAqij>j9T30_$QfT^Dj4zl-cVn3-y$)T>>w^O{T7aD? zU+oLp4KD7jC*bocF?&jH+vuLyH<0z58itAk- zK2c~Vvv_$&2)!|?=|76SB4EW$bqL;v(1R+Q6Advi-JS-Z{c2#GXWn=d;#n-e7i}lVB^Z&`4b#XmROG+=xmO|$*M<0+ zi#iAuj!{90mCg?~zizeX+j?i*YWzza3Vt3F=hN)TNaN0&^-09m?a%!%Z zrPm9Vuk4oe2!*O`dw|iP_LMlp9WDJAf?zJ-BA{xSh=5sGkgu_V{D+Pu;CBdqS@z9S zJnbaU!g0~UXE3q;X~~%3b=awKw_+DpWqip;r;(XXv!;fiV`2@cdK$%Wi*>n#=#4#+ zMUW4Yj9csdC+qtv0~6W7b-@-d z<~8>EU-o?DW67tgg;gwflJfJ}f#nAyJw!eWom~ARx6RK|+kx?^^$^ixVG*ymbk4CE zS}?i1(dmYu%~v77;-b)0Lm_&&pXmoE?Y$1HmUG66>;>H9V z(&Zm7qL`3{7vfL#KQ$G(v5U;*dhfG~KZ}mUo;@MuhHeg4eBHVo7-r|?6u&fZWPCy#bNNv z2b^&!Tv?IXDNBeBdA`Iem8xw6j2Hz`D%4su76_`>WA2Ii5wJ!GyjXasr6|zt!Af5i z!P5tCTXHK_2F@cyI%|m)Vbs^=uK$qj5tPx*1{Vf`gw_)r3nUc~&M>{tOtzY{W?z)B z7ihSCt7M7;A$ZhVbY*H||J9YM(`@}Q2Z0=(-VUjBXy*!sZc;TP54dohEZ`lE7ys-M z#57Tiq4U5)e?J%tmH_qC*qH~Jc}`$K{w#c=ri8jZ>di4k+vcGMGyN6yle&Xoh+>RH z{2#g`jTeRtxkKOjj(3H}i$!tigCfB~gMD7rax9h_pukYL(9Qqo?K)n6K+ekkcwTQ@ zlZ_Nj46gNNNZ_>QX1nbNfB6qM1`$7tDPpnPxNME?f;u6d<4LvN7bkqsq^3&?Me*+OMDOiajD&am%i$!)dHh@+ zWWc{uK(c$VrB9Hm>n@}a;`*A2zw+tXxPJkAONLiPn?T`Bz1Q*nx39&qOjaCOooln? z!D3xQaNSY)cEx#2R#Jfqx1DhNX7X!O!DyS5c16xJ?cKBZN4WqAas5rYuwW)h#9D!PB&W z(me2LT!_IvUG663*QQn3#K2Hs>d;|B%{>OLXibF0{@f_-pZmP9L@;|k8JukX$-l}3 zJz`i=5y}PGii=LZCoYOyPuzlVS%8tDb*uf)@rcLIY&3iX?wH{@T7RrS`kC;2T!l(X z;8_F%dc?3i;9V;V(HueAxx$h|s!ca?GFzr@xMJ}=(f`=F0yNThX27aV_|5B^u;WhvdGXa(yJaTyFN`DkawKpn41ar6XtyFGCt8E0 zIVPj!Lg~q^5!wPKM&}5G7gctHXRm<`fNe!M>6W$@=F^>|1SRjhfi>SOQ2)>?*1fr; zunS3&Hf|Q-Q2o=24`*n*0-5u!a83B25JEq+aqqBdz&T_4L;}T?t2o^bNqGvd2_ew2N6#7)uO=e{X-BK>f>1Z_%ji&*iDf99@SQ}nFEuXA zrdso}reZ;Te>2vNu~#hhmt-4vz$?=khR@$pxZn>4^!Rw3;$nsNp!!U5w;DLhq#Bby z=2z?`EUc>%V)e+>Ju~DFi_pSuvygVYgegzC3vi78ME*JllBc(_lQ+5M@Xp$xl&+Xa zPIN|*EWVZdM5#=~U*&4>b0ja>aK{5Vh=AbpR&W|7q81Ai)wnVRQctkFwsNJ@!u%`|hS*HWrV zw-SaDZ7>wP*?Lzbiq2w18WB%t{N~f5Pvdo&D0r@-tscL1a3I@rX)RyD`d2Qoe%B0SaH@Ole9AgqasmNnM- zqb0ruj_2{`%+XBhiz#S5vk_{sO4i*h%=_`~sIZ(9E9sn}C_AI3fva*ykFVe>DzRWG zLL2l5dT%pmNXLqR%BUqa=^_#j4dV3%g;!JFA0=SYE%f-)&n-w}QYr{S`ihdsSv}>~ zjq$gOoxO`onPj!BEBwcRAAE#=)Brn`Qw_=n?Da}+I-v$v#OrQDurc2mG`=JQP9BTN zO~Y`oAp=YlqmfVz5V@RrTQ?kLzQD=V3vT6r!16V`l)!$cDBovS@flS;TXhlYPEUl)_{c*}pMs}8WOt&w}Bnnz|cubbNEDjSU@ zpSYZV51ETzJ)w+q4ptuC&$_QA}!vLa{PG%3&g@=`6$P{W946kmS4hDQYzt?0$yN(ynL>VJSlU>uV{vad}YD% zqqItcaMxI4pRyw-M3@`j841F`?*($Ur*FpRrivLU5jWrc93?0G{SW<@Fw(WqMHdV3 zF?$@;A2Q<@Lp2#>?Fz$YunTz#xWQX}^8}SObvujPz@aZ`v*1>HrL`Ky0T~JtKKXQK z=yHZ~!1lJEG+nEX$iaiG-j@h3935ta1hH39Z!~LT*@&*LjvT27b<<`(-~EqqV>L=2 zKdpYowW}rhfB!O~>#UW@uUdp<&qf%w>OQB(Rj&8GXTwHbob&!w+xMR?q#z^Q+@{=& z3Jh_B(P2iXSffh3#>|&9mc|x1)R#_@Bf*_b0LZ*)g6pWjyjtI7U;l$?NUVAb(u`Zt z@A?5SO(JqcINHw7CPuG(!GX$@oTj>R&jaEmFG3HkSfhY77`YFZ6!Jik_95a#xVm?U zF6TBY?^kx>kumH2EVLRxMR|bd4#+zaKPIg6%six(?76{YH(l!ZB zATAQWSbZf{q6_k%Oe290oPt5JCDN+8|gvNl9rlQW*PNnSXU;};C z8u5cn`qG1^D#f16by)TGB=?R3(3*MX#kN_&87Tv=^(olEyP9;DE*9vlGpEGhS3QXa5mS5eX*BjwO<~ek*(uf^+_d z?$H0V?2EVHcS-uk0*`)JmyUjM1a&>GShSRzL~t-E$JJR=^Iif$9n_`hD(8xp64G=J z`GG1v5&IxfjpL^kH*Lcb;0Lwn!YZ07H?DIM%1|XRYq8q!Id=2}HUnKBVIiSlT2EOn zBzAc@JO@?P#(vD$+QbeKLw~i{a6-ZR6T8O+T>7u=#aEG=D_1eKL#WaOAumf{LF0ex zDPgG?=%E~*KQ?BTPTuB04reCq`Vc=Ja!(AD;6j>(+@Xcekf@29019w+Nt_*STEcZr zI#KA7T47t?vIcVH=l9s38Ilhrw6C?+DJRB6D9T2!A)nYZ?BGv6oitNXP|THeV%mi4 z9fzN*>r<7)k@9cORCsHA9*~f;rSUgKTz2NG8WBfmoRs)3iQ?Iu>k>Y>Hbu9;ztEKB zQ7#XXwnrY&K5dD&0Tz5CgvPJF_C#LY0LOes^%bdp-9K_`{o7uK= z9-m)3z2SPrne1hJ=rb36J{hIRnlT|$S6_T}BeJhjm3WVyUfDVkwn!@yYJ~@QRL;f) z1^WyiG!?8NA(~e9&Ovkent>UA7diRDjkw_&E!3U_fxbOuz^Q7OjfPtkl(W-KT}XwX75k#9qq%)2I!&EPy3)ARY`Q1h8|vQf-$hi z5pv;n0_BQucv}=^bu#T>IcGR1_qyGdFJQln zic@QpXEl>T0&w+3J}c3MsYX+heNa0>JZSC8$XlKp$u%a?%izZXdB}`))LLhZ;8->L z&-s?ejY)l~D*hi^?-U$p7e(8~cG9u!j%{_&v2EMV7k1pS)v;~cwr$(G|3O{UJujU?1u9m~sxRYaZ&g38AGSjhn|S zPEms>y=?Wbtt}I?9kT`fBY(8^q^O?X_4ElFhs6&AJQiW#%UI*-M&X!PFc69rkl5&L zk>~fZ)C{GI6_oR}Ofv3!*WnIo-cXR4Sq@ zj-Mp0#!!A~pWotRZ6Jw4TXp?;)h^`C$gg`L(r3NI-l{zW@?3+D?ZQCYzZ2jE0nl&OjK#1SYb$qN%-W(}m#${?pY zaT;uEh`w@vyZ%R!SE1_Nc@5Jo#}^98kWQ}(`PC)R)PVjV3^FYC7AYtI(U#o%ho{{^ zLn!gjMAAZu`rTttlXC3XX#pHAn|SNYXgxHyG_GwU+^P%(o>oICLq0lc6=$~9<-}je zIzZEr+idk9prS-iU+4;De=610Ut4EvGSuD>G7`Ez*Ck}@TbIE@1`8FP_FDSnnN7a8+R3_%j-Vn_d@EnJ+ z#q+TkjF6em#;db^Sz$>E3b_wcM8wR(BL-t7@eI=UNt&v``?Rq{@wlImqB*Cc{lH3d zE`<3Q8+R%Be&-HCA1CY+RwBBwPjmB+8kJu8F|5Phf*&e5Yzo6{B`t!pBH6Y==e6`= z3$37q*wb~ddSnC~XcQ8)31CKZxK(Zn$cW-To2=Q3BE@H__>+Nol)X4r4fChk(P!Zi z0z31XNF5ANCsH&V>8bHMMr%|~rGUM7%!W`_CobEo+X4G0v696f4RnglxiX{~vhq@y zRi&bgwZOPS9qgwOd5!fGDaobT%)r7TCPF==+45Kzu0*CF$JhAhQW?f z^(l-lYzwOJ&6Fo_@>PT0yex2n`S4v{Rvzzx*?K@G-`s$wm*b3RirGeOSHI=)be^r^+pu0W}MZJ5`}5`?%c^vnt^AtrWK2V8^>8 z!k>ioR_d%PDwTq}GZyW30SR*p9djsQan5au&O#gyBaob$Gi^ zY5l&e;S}b(+qa$oQwKnF)oXSS|Z2KlVjWOrd4HR3NsI_E&3^lje zr;aGi%iEvwpSa!d%sdDFQ{UmiGtbGsUI z*FRIf^J5KN&a_%cU~S^xkl18)Ui(a6(B2@c;ro@vLA@8ZRntDW^J8&9k}<%8KVa$Z z#rhfr|DLuEmur&hIg+h!K%*HZXS*04>t(KHNma|{>%k7<>UZ7!=4#6OS)v^%@KC5h z{fBYJnd8ey%B0K8AHuWz+mJ^K)i9E&LFrI$S=F`7a861wpKF%OSM&Hl6#; zN{~kw7_A3U@Jq;I+Y#k+*xbN^HlI^Q1IEQVgTw|ZsoTI}Impqv&Mo6NBS~(tU0GBK z`!BCkm zH{By+yW$KX8yw;_J<0E}l# z*S9TT)AVwmv0VZ7rALycdsl00I3H7Nh&s!Yl!y0X_>`5S^zvs6s2zM(PU8wqhyV5Z z9LShk)_WkFKgQ^Dg)+dH$n?~QEY%&I&ojvLYZ=$)ly5kn_=Ih2pAjPqueo zd*BpVB&x#08X`SX(>$yqGaiM@TCgt8KU!E=M$yq4Xwd-R_pMAeb5PP$hg92v5*hG? zVpf%7j2fa2E7yy6t;CB*D#t>&3o>VL7QxeC`g$7BMJOtURGvuOmGginW=Pb&*xaE+ z!WkhiNHXh-8tg?VhSorB3iDE($=uF~DVlmj)H_|8a*?4Q9L=vKv8~Ts~PtlWR;pjcIqDe{;Zw1;cr@7hHE!bz0`sdp8IaWsbKCfY9TcDj=KdipfsPosY_V?wM; zPrQmylR&bV{{!C##_}1A|9X-qUIrQUWpKyRGRlpzQlAxz67RrY26mcP6W+9esdsfr zyH53-$7-}b;yP15`?EAEAgGdT3@iHKF|=Gii*32JtNSBihfWKleHrL*|GTWf`YE=t z!UV`o(~K3qw$@Ccw~2sYtcIhz|1KKp#S)*0c=D@qdSPa{S;jB+3%2)#;+Z=!Z)&n> zV5g!|DcXewFRqE(Kd3bI(2RG+BZWpOA74H!Hiy7x$PA)3O1yKr^aYIjY?7gC>@Zz_ zf{-@_p?a5>6#^5I6Tdm~U|>Bp@;&>_=z)#`-gLT1o+7YaU)N1~_a^+6X5&qR5Gz3e zO=R-bQ}g9K1)>e#zWeq26vb|38E!|zc#bzRRix|lbZM(36naetk9XKg-F)gzLr(}_ zBG-s^oj{=<6I{D+X&rAb%7pOsw-7)%mx-H?gCpKH$-T}uRBQEs-Dh=y!lH#fGLVCm zB^^b4-+{@-UL5lw5L|@EANJR(HqpVCT)g75jRPhTB@P_xtIcOvp{)pMe-Fjhzx7zu zPOHBq*P7;M<#dkeF6}Gq@D%Blp3aGJ94%JdZzL^{dXhn?+9p4hH@5ppP;Xy$RR`PH zG>mdSXFubMKqEsouo$rSDgrmSZGfx0+&k8xPGeUA(eGQ_;O`Ye>7d*LbpB3aeYQki z%TE#Tjib=c?jwSkdw*S-M_!Ly3YLz5iw!OJmB~0iwJk+6YgdwlJ*vb!Umbqg5H&*A z&U|rngzlLLY;dqJzF{?_xbDx7#8^};IvZ5U@57XBZCKp_3GPQDFb*FCKft#9srRDG zYWi1~horwrJ+BQXMY5q7UwuuK`6H+67Tv6{CzyRw&3TUYXJMVsXuV&A&Ew0lbPkoS z6XoXlnhg2N=v3E#eJixUwT?#3ok0lI6uKcY^Bz9AwSmeUBotkx>0H~k+J7?4U$m8b zP!dNxNDf&h;&@>>jL=Ne9>AkpK6c@({4is@R_w$-&3Ya_?954QUT8>qM3Z=-e41g1 z==q-xOiw?eZ}i~7gP!+B2DDE2!OU@}Ff|vM9h+7B`n=qmaq918l?oTd%o5Q5u9Y9J zk_L|B)0}JLo)k$%$Nnnxz$|`8fS@Kp+8B>C*w9oe|7*1s;B^Ymi2yj3=gJ<;T&Tih z9&y6D6Td!F&8z(;V!lCn{1HKxAO(MLA+5>pAE7f0Q$Rg!hS!ZrNpqkqM#Qb{-!@OK~$hJ*uwSeu4r7=Mbp}!^0B|xCLxHqG#wsDVfg3}NrvxYMgNDG`*nrs%KG0W zu^-~nsFdxDevhMdjOskp)J@>erHC4U(L3a{sBClQmTD`py{r0;wprtOn=`hpc;Jl+E_nRKu`C;Dp>2 zoovq;j@Eip9Ds2NF#^eSR4Ne?CB|SFgUt`v>3_bkewqb`PZncwQXJDB%ekI>Y*Tt- z_Q`9ZG0rfMc7SasMu73D$+pD&P)Lz}HYyS4+E0`<8Z?h!qYV6ETJ?4N%iUEjB{;=_ z>7g0pThZ6$2-%X&Zzq7 z#RSm8k$;wC$1B)=fkwhvuG_tZ?|PIFP1`?t4(#d^u1)Z43z)otLf)lYWFlzLCe&mu z?8jCpe2B0&@J`sgcD)n(v8DU0zL_Qc@)4isC9obxT1_%?`D4Wh>(1UTvC=g1w9nq- z400kY1lTpn=sJNrrFIV3tc*pqh%bG@=I^xE-;5Dlf5kj~A}SalhD=oA0K7v@UvH$| zPOlydNZ^ z3pBc;CJLihfISjSR+fKv>=2OUJl?FlWM}6|+f>4%lRG-tf zN?h)RA~7?sOLHkEXNYy>zbYe{xqn=R1Smp}r6knRdDuGLHMp2c5#jlv9b*ckhA~=C z$2%&k@-}?!O3V(<(R=fj&`jH7j3_IPhsr`Fe;0&IrsNx~f1M%y~h*uGTD+j8AZ6bk!K%hv6OZy5e#+m&-;BsQV-2yIP?)nX$*5PvU61|Um0 zNl+TYd_&{mEfa-9FtB0~P<-eBHE{dtTiSvOS3~S>auqp}1aen*CNfM6R zMW<>FO_^l<7}nM+|2b}22>No?gabC z$$2AWb$JsBbBp#>CU+Buet%=wWx+rQzU}V9y?ct8GdE_vg>C73%41)|ZHoD`)t%rM zjM#6<{)6jYhvfC$ntcc=mCcY;3IBJ518)g&q}^my!_+Z!3Ul0sI`7Ore>L_&DD1cu zlHLmNv%bMCWH3$5j-wt~0Y9-2zGt$qz|5PplJV0oFE1c_3FK%3InG@r?a2)@r2feJ zW{#I3oiFTQ$gRjvK+FBKlF%QXu)l18zJ$kB%Eh%0WziGSWz#QU&z>o0huzN>;NuUe zDqZ)qZ{254feSeSd?cXc>Gz6;GhDcDv^kn^?pOaqQ=jHF_f}jI1K%#0c~G2W3#^8a zX0v*FRFP}cKfU8f>SMP8h-g<+KO(bcCGS+~yXg8l7+3ZwIw^zMpu^MuowrB!%vNJW z3!74nE`>K$=R%_Xl@?&r;^q&pr2D6&gO)_VO$nB`h)uU9)$SXK_gpaUkhYy4o_bRQ zX>H{P>aw!+8{AFu3Gj=}dB;rR2w(c)SQ=3OpQJncxST@)`-xw-n8(NCQoufr$|fF9 zG|v9{vaXdRdS>T86V~04LBOhZ^pPd9GTm5DcNdQeK8dj$y|;=tT^XrXSPz4}V*bYu zt9rP+nRDW5VMO`!?5yCXA_r4sRkF%G%!g71IOk3sIDuMINI+Ed?^tPtRARjIt&_E4 z1BZK-sBOFFE7ua7OAp#JyVT!QXHo%qdw*vimO~BF2!(X{>>s)RMIRlDlbh)9i{G^7 z4gClEd?n7XM&_t5b(<+nJSe6Iw84@i85@v%kKqL*^s>x5D(d<@HrV583d=oEuM+50 znqbi;QS%c;t^vHby&p)$u9zc(nKeqBPqb0OBMH^z733cM->{vKB5x8l3K@+ZzGh%4 z>0hNcHQ=^Zzdjm?boIPXxw>{vVtopj8TW!mCD~|Lp6{DLMc|p@BOg0HyDY3Afv12I!b;8 zjr9@dR!ElUnwmjzypS370X9UnW_+(%p>r%JTL!>w_BovR(oz(KMm=Ego5`ANJ(H_=_{Dn+8cbA6cCy%!2^rpA=wf>0(fz&a{&R|9x2a zB>?1EFS5K0Z}a}u9iEHgRU6B{ne6(r7aZajN5ZDqq7Owi#F!$eXz9U^z1#0^qj)_w zCN2fZIx8(p$}6nT?|Dq}Rriv`1M%irEpHw7U+Z|i(d8)I>IDm?w8W1q<4-n16iPlV zS?BNJwR1`S_&OT>O zZS8iY!x!rxi3;OzMoHK#{eE&_*)b~>ffw&4wb1%>HP|x8wn!<}N4&zD$5fCkt-1Z} z)!U_RF#+~BB2VSkn$>!c209$%>^$L)9{mdq>T-$L9%M&=(^4M0~ayh;M*W zoH$v?pM_bSqc(i9eZeEBVO|aRWCi?RL!JW$4ZK>2Fpwy=Mz3#{gI=QffeV`z?7{U` z7p;QSny!kPU!U#KpbMfnrAzRgZ$WSZVIYkd`-DCj01boXu`z`oA2F8~1s+BX?6`J! zUr?QZMdWQ<3zDNP@NI%G18pfXy8^7!bUV2k>(!^`;IipI%tlDPfb4h7YwJa83p(xa z3wI4c1(h}WkX>s=Y%s@j!_3-Rl<>gOQ?_w%9d9Gn$D^!1x)5O+3vSRf;^jTsR)cQP z*{OFU&Ug$rW@lNq=LMz=k6@&=fs0 zdtdiiHVBGRc`>T3KsA zzZMyxT|jo-n&p^9f+zNTa4THTm{A)(C&&Gs6}?s~aIfF)uo}4AuGj#ls%lqLult8h zKa1UVrSnl%7f>zzY<_3BO^|8Q^{bbkPz(0DM@wt@zk+tjoRzqNG$l+8q;}ff8^g-- z@k;GX%@v>BV1@?Hi5C#)XUHUDDAeboV(!V3e*CmQot&WrdNpNADItcjeGL?XMDi~g zBm81&yWsZCphO*6Z_kf5_popfp1W5)J_!HCU&Jn{X%|*Mo zlM(vNKfixBPHEOJO*mg}J$F5IW*h!fT;7Xq2u=$+G*IAz=A=@9Ag;_S97Kcw3Fyca zB1VP+@0WquApB^?4-$sHxBv?f7Wb=E&_Xr5uG1g{%EZC5Z!%z!Wc9efI7g#!`FT9 zBE`CZ@QTt4Dfq*|*nzuwj97ryL0k0F6C1P~10qb3t3A7aNp*CtuC6kPp|Rvr>l?Y? zd3y-JngLLYK;51xI|G@X>SWL|V(1(FN=G9m5WO`aoi7!O-inl^{?nr?E^2WG-G1h{g|7m^$|W{+FN)AppfY_BE)Ns4xh`DIOPOMl=+7JT}@z za13sDXcM{s9!DPOCiK2+lq^>jH%s zD*YM_PtQgQ5kt6I1Amx))p?>Ae$drJ^@Dy05i7uqz=QCD_7Gxn=yaoXJh(u7=N^1f z0DdRE%j>8|Fg0El;1}?UhbSum6?t=g-6enj7nh_{;YEUJKLKKiM z7}l!>?1jGait>T{>4AQ81o!V2_~yeAWST&OYJC1!{)j%aGc zz1Y*Mu7ErCf*b*!b`S^9T6{i^--7j@$SL1~@!t`5z~Sq+8u_u6{dZ{gt9;OR4B-Jt z?eoX@UO-FLO(=yz0DlG810d_#3)oe`t)T6l-E?b-bDbFCkmoKvedCi4h$8`epiL^F zJq$Kq2je+Tm--oI$N_W(j$`n_dpzz|M(u!Lpi|1IKEBH=@0*bcL3_3`3dC-s<5B%!1)rpc=g{{ z`ucAy@9Mwwm@%H%-;n&g_#fxn?RcWt1UdwyCbs5gXzku_#hbg&@|T3fZwU_t?r$g} zY1daoIFJ@UPQQ|kN6r_8)^+=vT&HJ%WLmT|8|e9-nCM$veFb*U-i)gWq24&Z_Ol^^ z_Otqum15p~E zRn$bZ2x4=z9R=u>+nT(K{vfJk%z)}^2Z0vag=Cavf#j25DR;$Umd^R(2l*ww4a14K zy~?!sSeW;8gFHYjc~Ph$!?xOZTxMvqmJ$-u)!fNX(?R9=vyB#FOayiWrZdr^4r~k(shsEZ&R^&ebl9xql!;~ z+!6@PSoyIEV5GTb9_kd>9J% z`8@H>Bz5v{ZqLBcb-a)-^;tgDBf^2C#?SWaY#^gbK>ksr8Hq#>A(L5;PaKq`&Nw<< zFx`HZKBrg~njQdae0kQ{!k(*ZY^=AzQdMKtsQ|(54@rmhndv`zSxD{bdcPs`=1KAa z&2?j|$V)!--_uL|@*YZml(Q4_70PBmpvSX1emc^a56mB2!_4QiAI%S%1_QK@7d^Nl zfa?7K6H@pf9Bm;Yjl^|=U&xh)^M1>;x61W{9W1ilHFsUO9CuE!G~%e~g90s!vy_Kn zbCO!G&N-E96VrCd(bXK;FUot-WXvH0a>DN{l36fy|7ykZ|y8^pfULZ{$7h^ll})mWubAia>*!SsbCdi(^K3- zHukRpqtmXybg^HrhBA<-I_QgvM}DYoyv9b1M<*?0)`M9u9-hVL+;i-UrHzZ5SMYwO zNq_mF8#Rk318o7+P$x=A6o8BIrIJM~XVi1&~AtqbQ&(Vqbd{H;}|!t<1cKHoyyNO6}+A56ZuB8h|j z?YDtKn9SXoJtqJ=b8 zv`eh*u2}Lsf-36R8G>i`0LxbQOP#XeO{3UGu*o6z!OdP->?V<60dLzmGaXnto5_ib z9dw%g!mk%EY$mlPu)T7nz;Gah`@}VqRHOy_&u|Zmuouxrle|;!J_I@3;sIngQob|y zlglMYaL!7M-`Hk5MG>|1&%W&<#WdQMzTsR6YltwO?giNBfR7eEFHKQKch2f#SEm2~zat%W;8-8Zk==I{-*yO=6f zNpU_3%>KxP+n93Um)GM2t$j-*HlyW2=1U4LFjh*qN`Ky;RxK|2u^>G9C;E6TWN=Yb zQn{XfopxwM+NuBwNW7RUUI7l?apo$CrSH)67(}1#VXCKC_dz5$W+}24O5S1`{PeEE z9A*QCV*1@VDMM1_1_df36F~6}D)hxN)e&jqUT#oASIMp*@-8-oD+WuQyNn}KO(AUF zxRn^xB6CnNc^X}%yahyRt5JUwG2M@qT`5vGsnXi&OM&EG?)VrrLl!hBB{`_<`~*E-jMw$v`14-qV_D6Wde*` zn-awRIa~`S+4m+j)Ffl}(8c#GYG|xc)Y;Q!YW|hJ6^>RVi?V58lCEk#PJmZyDj6SC zA)RgBOKc<{X~#s{6E7n*Bg@8!PN6o$?cEu2?NuC(SK!4v1x42auP75fto&H<;?$QS zTxLx@gCik+gRS(Z!@U9hm=ut@VP!s$^#P4@1}YM#)Ex?f(&{~WTTw#(F71>nli2qb zQ;2p+@_6G1ai378sM~?IPW2~NKRca<7$Hj{^6LZl?G~ki%d3}<)5>fv28;h0Yv81{ z>)7(V9(=o%MSemZI;+MbK`A+c3&w;BX%uAmf;Ck3m_uCwa zGICpx^91{wZh}UpSRMiE`xc*Cdl_f{ByA`GKVPIO3=hRCJg9v0zL4@Q>n}4`cuLZ_ zOE7$kff=KE_q1@|2v>fNx;fg4fxCEoVT@&^xf!ZdU{N}7$% zV*i)W!M4SZqV7u*UUKatx(GwHSuzSh@?;-ed%!ZEI?gb20AfBDM&LbgrbLA88htbUWOVQ0k_k~N$jLS{tFw%rW>{QSjX2eO+{!C zZ_@-umNf1q!#>IqFDQKt&xjvC90(-QuS5y}iHJq*kHuf3-7xJO9a|D_^JIN%DdwpT zX%qia&;-p)EGGt{Y}1n!)|ZI@vSo{+{R8j6*Me|QLfl0EB5hx7k{YYK@gFwW-lzV| zmYm1?jcRdLZm^*nPSS+RyJ(mL96>G}PAdOS&9xTcgNL&HW+nXMnQYpsX@aqvEc?pw zDb9!cBuz*8s8Y7yHayvH1bz$9bkR;kC2=qDl=+DWya=?@ZEmauvUR@$gBtCeS2g%} zFEsyY-j~;LUXK1!UG+p?=f$ZtAD=_@kaOa(`x1vi8k6cTz9bgaJ(yQRS!NUn`^(C7 zaUuLjs~C~5BzZO941gE<%jmMLLyn;|{XG)H&XzY%I|E)_bNl$2P~vK2*!(kUe|_0E z^x>}ZtGSVN`_QeN^Iw!t2Mx1*j8qC|>>7DfSK_j61jKaaiGklCM)@v`=*Rgwkw$xRuF6iJ;9}DdEuNF;aP_4qb#l)^YjaR z+{HY(ho2dS#^V4?D8ceccf3Vfi|W+bDCa_gO$((OX&}=80QF)imU zb(o_B8HZAQN^)cm=oBvr>EI2r6VP;G0(xr6aY(GUi=lv@C+2O4 zbRR)vI1W&LqCA-=A6QwfDsG8%Fd3 zM-KP)1>N#}0&7%Bg5e|YA&?bnzF4k6X!8u;2E5iuUnGGW&|%x4a_4w&b<-hnV1ocl z6@A zD`628=y}S>K2dyCY4irB@3p&RMjb%iZ0OS(8CB;hg(&3$=R}rW z5C#)@3(?Sh-Z}r3TUNgO^^wGn;#G8iNRJ>)^~|FXfKyZN{p#;az$Ep*taXA3@nG~* z(POe&lYNg}#0oOkZ3cwHnjm~~Rw1%&akXg=x4l|2x+YQih8*Z(dSUgc!rHMJ+oJYA zoyC#SgJ-f~JSk+4DA;aWoTXDM$UNSI7EH}Gm8#7gY^gZ6K_kOZn ztzri~00$~4kMDLo@g|{65#J?I8G=-0Rrnqk(49)NraX2kdCzQ~MaBPhL$8x*v4^w% zqoz<_anc&17ON^pTp;NmQ^15}W`4B1Txb0DOySB!`UmMw{t4;KlZ#@9YK^A%n<-w9 z?wpFPfdR>GT0mzhCu^zQp(6^px;u)DQQ>YW5b)(?)m_n&I1yYc_Fd&5K|GyT=js|^ zNiWcw@Z(+K$d802E7jNWjQVf`8#)}^*I&mfd}tH-_UmP)Il#LtaoVJ#{a<6=oQF`` zz^SnAZTfQj;dN`eUWS*JP6%-7R|aCNZD4!v^T+AAlo~NFu2*KQpUIcU_#`{$epZmI<6|A=5hfN*YTq2#D97>Ok=-qn@-|OAb_!La)>)h$~aA@ zcn@+lt!-?H718euM278egra*hN4j-5(1=3scV}v=YdzcFG_sqZ{lwibJ~b6?cDtE4 z*SwS#aUnJ9^r%z81Inko<87s=4IW$=?t01&g5mFFEp5l?m zD9Q6P9*)v=9&$i{Ci=;$7!HEhLX+xjT&mZD@Wf9+9=QBpq`!VgW|Tni=IK+(SNW|5 zL;1{9B^}9|nfFd?^{^o@Bp{uC0Ks^Na*mUz&@I|FNqvUAvzCX6nf$5ln~Gx*{(HiNtNV|x{unD@<`=~c0F1+3;bXDBx4*UI_ ziU^SnJ>6}C@NV6A>uL@|Dq{%nVqK?tlu8+ByPA_MKIa$5PK@sy3srr-~8595I*z@Pf$>Y^VK1pKG%)A@(s?)>b5NzMq)W<{)B59)~B0ag5fd zIjm%;Mti;*5@8KHS5-%0fIm6r+KXif;+~TYji!N$F{yI1oLp=gly=9-DMgX3!s5@Wu2Q=m64O-!;pfdlo+XrNn-j2 zC+NlZCUbJIJF+VN^r347P&Ls@5=#xa>ZT9(NzN%2}2c0S$KOw^)8Jkp@y4 zH@Snloe8L=*kzRYxtCT2)5+Q&Cb<;fgSgy*UB5LEMs=$C!ybCF;G#C^GJQU@^0!hS zcm6E@lf=11=6)hcFpT zHfhdlh`c$n2_Qa*>F`6KZ&9;yjl?EmP&HP_r%Np!dFX~Q&1 zB|EiH6&lqX@vTpnV%4O_gcUC;c1J)w3-gbTtX+$nGPfUI0p5r$4NKng79UDwHw=)d zXZF3jb|Kg86pOIGLnorO{%ca}dFV%=JR;9fakJfTC=8t1!(#|gv2BOu{>uy87dbNZlgZPPoy(Rn zpx*83jVr@z(?J|tbEn>tiu`=mI7#pD#qhL)5aL|zRiV!oB#00^Zss=)Xy3j$mWu`Z z%c2b`1v8tqvaO22yL0~qMO}YGALG&Ab`;|TazZDns}w(tYDF5p&OgBr1hnOE0*(EH znSaPn)4>oLsP<#0RRvPoeZJD`V`&D_0WH=|hWk@LtUi(Ek>{?>9X}>|Hi@l~haZZC ziu-FRojU3g1IzPC%^{Ip@N>!jG(8|yi5Oo~)_d+wKK;9>d$wBh#w1)$Zlk=KV1o85 zAv%J;5U|RwTUz&z#nUAJlsPFMW)jLk@9#B}dyWUd{BUKPm_U~a~pA4KX@{D3U*lFv8ECC9a6P7Io;`UNvkO7Oozed6@EK1bVAjh(o zOEm9d#1hwY&#jYxM?JDzUeL^A0N98fxyZ{V@>z@qoPOCst|%)K!d9N-PY8o+4m}Z} zC#rAmV|^sKF3>q<8`$^Ru*unF_sHRQJ8!qYi82D2D)L0jz@}7ghi&yuGtmY0M2BS% zX=pq8MH@LBPe{o96XS6TamhB|V8d0D&Uc?>m2fuJz{4?(?C(2_$@TMu0G2E94sA(--rOu#b^wt#!InL`83!fi6A7VbDFF}1Yf!!yuBtV2_Pdt$ zhDj--*s$-H)-FipgHRz7*)FD3@4Fmm<@&NdMQ~39w^_t!ZX}WL7iJCa7dmAICtuLX zwYQ*3hb6V!L3@GSpN~gdAbd&4wU8C%;YSze#M2^d6VKfmwbWB7__<_{&&El@(DZ53 ze(+n?l)afz$623|x3i-d^mxLbLrW66BH1Olpt%pIcgc>fcS>0M{%VYy*ELE(P>-?S zI9*vD&Q?;G`w4lyb031OoC zxZ%%VwONid9BC3}X3S#y`rbjqi`XeA6Pzz2ScRAQ_K;Hazyp^x5#bu+8i9xftcdX* zMqyKpwandc1Xt>s6_i4_s%+2v(X;@u_U4k$?&%-doEX*h!4;u%ScE#muywo&9kL4j zGloV4^GlqX8<)n$)6gL=XMC_?)jsRWpPO@M!Du>r;X8QGX5{;!XE_<7K8E=N(0I(- z;wS>yuDn>J_OO4J9(Off$ww#J9Ok$bWmJT5$ zw(7jHA#4Kq1%4$hG2$iC+pK!^bTG+plQ9oV){bC>Qdhh4RT_{yog^aOYt0}dV>kA7JDFI1p7 z?8Z<_9@h{I(2|*P;~~8dgfi^E znW1;N_+0E_n;cFQUXzZAaiU%X8@^T*AL2eyHuz;YCv{FU5jS@C)VO7F3m6cWcCc(X z{&fQprotpuB9;pENY_RsJidT(*m7l>4-$K^W2Q|}KMoY2tq9w?nccXH&}1%pIYrG2 zs$f2upgIW;Lqpxl^m-xM15n8U$0l0!Zl z{HE~ZFPbuyhkh<4G-zS^)H>nnVxFAaAJRGE#_fp|Se+51$E>#B|H?yh-_xg}5&U8{ znXg20Az(`lkvot0}o(k31C&D^*_#>PFU z%7t@R(WHkxB_=nb!hcGEJ5u>sPvz)YJA%Sx8wNwL;snkDP;b{K={-lBrdOD3LUk+Z zfz{M;^+{$FG3~Tal7|G{Y+tXpk--9voT)8!$Y;^u@^N>yff8zo^kb+;VapAb+Jc4z zc=&nowfJF*l4z5nVPrEk3!F#_?4*kL%Ns5H%NVV(G5M5HI=-Nxqpe+&tjf+AIaJ+ zFpuK>IK$SK6d|f~l*Bpjs9V0eLesTXZF%$w;o>^nhCD+6zrKGT1;V8WvBSi;VEy=J z=DbFM{H~g|@&VE{?@%M&Qm9mVmS`=;nL)AQ=nKm6z(y?!QPf%I6)p}e&2qozhtWcl z7v0+L=N>|u1)E%T;egrC!F?*s#JBm%o?nL2KUx*EvWO(jbq!^O+Bfz3r9-e%#my|| zEj1#wr_nxu$HLsYZHSR-Y@yx82y!v5{6Zf4D&}^TrWpoI;DkTNg< zRhOe5Bf%$&Hp2&QokXgQO=dONOw2rbj@eRWpk0%)c7Q<9pZw*NP~&N)s0WXIon>u@ z{c$(s*^_1@$fs0$NKES#C>cKcEaZ)Mo^Ox%&2J^aG#48!kN}=iN8jc&6!7e%q<0dC zOoc>h0S>14cryMr)azL1PXJ`&D~spjy_+O0?FdXQ?Yt1k|3$5?uBt%6gu!|@!BRS( zz5n~o?W#W8N~b+~eLTB*{EteL6A&n-7$Ur~FojKRXnsD?(>F2!j*_IF^aWvHW@c<+ zW)@QXPXUr!8~oe&Pq6|t4^O~_jpx@P@fi%8lV5W<4#3_0KDxdNW};I8N^b*#DMyIO zNN8pTLdVMb{LQt-eFriSr6XJf;wj-DmBlavn=P_1we?>##cubbYx0{9tVGrVgnnZ~ z=K%->54QFHON>sY?cWhs4>@z>`wN}S+CL%-7UtyQwp&TzUXzP={ho=rwY8NQclG}Q zlt630I5FBenhVf>&;#5+&K3Y=pcBy16=(|ht6;!qBOBm9l`+Co08}kNPXE|d?980q zj2wZ0cY`&^1ZeB@9^qnZ3Uma#Zw^qAmIEl*18x6tmixzn9`K*D0kAN#{5RcyqW?++ zvi&>Q$i&3X#@@)*17vFsFaucw0SXdwjLz=P^Z+AU)4v>ljjWyQ-s6p2jX>5$#_tY) zD>nj22rB`M-V6Lsc}^ydAbV#gMkkQVfo{^9q_cLccubeP|Jj|IT|*XO@adhhLJYG-Th z@yGq|`(+YVk`WOWrTeGi|E`IM*tr9|7}z)f46Gb~%m5Y^Ze{@2`-9K_PNQf9`i~6e zKe5ubW_AGXf7HGo)Bh-T{Z9i>|7SR80RKDJXS?^V1p=u5G`TJ_2eZlhAC~{mt^Rk& z|Gy6ZE6e{|lmA~q5-!%(e=k%2z5f5RY-9tn_V|y(d(*l&zYl=C-TN5W{$E!O;6Fnv z4>SdTx!C;QRcU9V_dyW0HGgkr1{QWkX7+#VASVfsJJ3`SHu-<=F#Vzjk$eh&e>*XRR(Ftc-n|La;gIo_M+FVjE71z-~WL!SUl zVtlJym$=`x82zDlEyjQ7-P7d1^w(W5nf?n|08GGt zf8o10vwz{cI`eKzjXiN( z*F&X1QNG7(wt_FK!o7<+J&(}RI_((sRu5$$Df zGLHpPfA(|Y$r{cA7AW@m#c`hFr~_|0g9E6IohDWIT9uV!8RfqEnRXnz`R-${#liQT z3dx&f`6RU~Bi7=}F_Z%Q$qo1sfcSyNwENqQNX{dqp!9_BjHrtEC=qSa-)ioWRe%NHJ zT#vBC@kj+nmnh5q){yc^ewMTT9*}fJmaT*%#qS_jrCgsMslJk=aO+Qh@==^<%)a@& z_)1@S8(fzn3k?5RQ$-KKPBtSXS98+NS~rTlanw|q|K)T2hRrn&tltK>BVwZ`suR)5 z$L|(wvIg=Y&ML6!QL=&wRi(`^sH+LU$ETL3sVe=fGY>@Oj$W&~Wd~U=m`9SDNDp?q zAB-00C(&%P))I7InB<^;a%wNB!WOgm;g09g+b%0PR=3MVuk zEmKc?f`LnD*G!d&LaI8nLd#jo^_7RZ2*~nNp|4~-IDX|6vVxOb1}Kh%KhzR8R08?x z5J<0SDR_b{!#pL+M2S=PJKjv-{ddzJ1&LsDKv_#fOr&35I@c;stYG(Pk^0(VC^|&^ z$g|nIlSviOxTMa1-Gcl(_smHGHG*H7#%80)mT)9D!_>n=&hi_O-Yf z=TGWl`2a`uqGpe;Ul8-BznWtUyfr43+sF4<=WTiAg$^p+>gEoe*Vu`wkfy1qZ;R3* zmsvEpJeCrSZI!vDNykEjaPT@n##YqDu+TI$<(#6)qvBkDwW-0Xe#~P(l)s@?tzb?g zfe8HQgkmC@>LTZl~cH``fu`1SfNnh z1qh9#ziRX{(gVE>tZySE-@+=95M;=e^|QXQOzPl&`nk`x*rL*e5MDO;Ir7I9rvA=m z!_OVUMqaQtO1^2us0G=ztLYK6_EdYhrnL7K#&AoHqM<(bC&o~gYq{7ckGyuu^3xNSOC&p`jQLjh*8AN-v}FutG6d2Dojqza7keUCeqQUK zgHoJ-nss}Qs)byGpX08yRy+GjS+2)gB+JM(ciuQ+-6zxMAyKkwM#+y~f1GbqB{EV# z^jO9{Q7@x$;U?Qer2;^bezS5R>EVF#vvLKF70x!XDQBXaAv>R>aC z*-Sb)5h;%U@a%bzLqMn4NM-f1B(uC&BadZ=e8sGpqG-L8{x0-%zw;JGEn&mC$^Z8F z5-QciFs9*C!OO*i|BEk{L(Kz9+DrN4CyE8q1RGq|A|9=TQ3+#+af#*Pbu^LcPq49n z3ik0p6ER&Zx!Gku5&0wO#7gLH6shT!c|zxCdf5b2Aq;0QqTNbpDbE9_l*py35KNz| z*oS0XcZ`F!OL05qyaZ&?(V1RO$L&Z7pzRN0sy0LgpDU#=@WutxO@1|sJX+g3S4tS5 z=cEPpTg_J1!m?<)`-P3-tb=iry z`{L}8Zy$m+Ku$R57}x~_uLQcPb6dnORUS1`9nI*=Em+sQR`l&3l4ba2E@TtQQymEw zC&G)HohV5tKF)?_g3=TS4f`!qs1jO6>Eu|_hkn>^czO$6Y+KLf&q`yhe1gt@Me^f; zAF~P`q=8EHpERETNlUt-dHO)>xQC3g4M}I5s%(Z&U>x2&Cqd(k?aVqGs<;$8qVtiW zTONw3QYCJfo5i^}+u;$l?hIx|+9>z3@?+&q5SB<)Gx~Jb;JB_Kce7PJMbUlYRai5V zGQ!Bmr9`yGFB)+p`@E)}o*Dgrw>nmnT6bofGyp6^J+^h27Aebifx&D4=(F^|WkhOV zzKlA7pnWfe!RcFE7cBYOOc4f>7`h+#;i&t8hi>XG*b1%MbK|+*B7mqUk?w=sbNA=d z0T{1pM+qL*dpn2LNSARWFUHHwz5r4}fW8|cf7fwL_Ze7f1Gqy_gGc9o890k&M`=j9 z+)_d=3>N>aV1}^LhpeR~6Cz~`(lxF<;?{%_&`6S2+UoEPy!J6s2L(u_vjDa!Djk{o>C2F02S)%iiMS>R8QTSly_dcVP=Q8QamUg8Q0q{22^YA~7oLa5w zzymZ^4Mr*$^yAqkpteo=8ToU)b!rBl*2NJ z0!Kz!HGlg>5BslbB$*y#A}@F`x(q)sAn_)eG?f`Ymj8lpLy9*x_Gwv4L}WWcX?;x? z?hEorgJZv}QBN6vb8}@d?M)_Z0Dn2$LBKMG5L~C3U_6Np9iqlDK->(EmFlNGOYdDy zY91}(FQTsKNX_EwDK$_;XAxTxUXPk!@puuS=$a%Tzm`J2U8cpV&D_{8nYv3c-XVM? zfNcMcHZT3EF7QoxAC!S#r}XH7n9L^E=_c*bkgc}Y9CNFGmS;}bJ#RL7BKtY}_N$Eo z(i861*rRHhq0$!Qlyi%!PmTMvWl6mege*cUxjbt3T)2bud23N26JD}RZ4j7?5T>*` zVMkDAo1i6iRt1JK6=TBDIQ3==C5wh5b%5)uF%DUYFE8j)KHgSUS}Un@J;RQ=6;rWJ zN{CX#!8S^NHD;gw)zRZeE3pabPU-Qn2bRojT45>XXx*6^{k`V3vCUs)R zzu0eo3!xN){IaQ|`00jeqECKNg?Vczy&GG298qymxZ2NE5sZ$U&P`pVlq$JUfML-1;nwvb-rPnBZ2t}K* zIwV_~dY8`CVd2;D7RjiHm@ePfEoR@hJ!eu$C)^*W{B!)L_L`&%B1GM9H7~8Jkf4Kq z@sh;PGwsiK`v?JYnBtoP0zBv^Gf=zr5A;ENd=54M@QY8ff}hN~wwM z^81NW6gG_MLkg^4^_PSz(8{8wv9mgVCY_(0JIonC&Fe?%8YBBeBu$dsnI%Aylb_9q zDH0?2QHaGBU`!Xj88v*nV4H3tY1pLRdELBdvUc@zwE0Y~BX)Qy{9-lI?rY~fKCV9Y z7QnSGpN-VE<0@Z{>9AdVRc+)qt9GJ{qZ(iPI{JE9Un2v0`uP@Cc3M@DIdHXqnG(C4 zvZ1Z9z&3_#yQwVhaY?aGk<|#!5EO4x%SdpzSpSa68#>YBBPGkh`)(*I%40q4FP~qk zo>CExKIlsg4Xcvu0R)y%NeRhlwaPWw2661Z37pm7d~TPZ}#bi-d;>2l~@g2 zwfEVN-R;^)j2vOBZzMT9VD)FEb3+`CZBZysjz~V~{0a83-WsYI-V;}UYDUW_5TtK9 z<=n`2l4`T5ms}JAwGrT=QT<%DEPZp(#Rd@pUyXqx8CgFS4@;q$S%Z}flX3~L_LNDY z94~D5%Up3otv-LBM+CvR`+dwObjWW5W`MUnZr1O=E()sl;brFDda>UEzk#yIX{;8l zKauh~jZbjn+OlC!(Jlvny2ubcCIXag8b^XTsT6-@tGz_2TDe2m65to%9WpYZiXHeQ zGo>y}kv>vWUGwFt6AbJRYaxBUa3(m4tdT~r*w4^g@{{iWbwS%7O{}uKv@@t4^Bg^? zVkbd;NrpM#Yjm(%!dA1{<4Gd($#~bmP!|E-F0T)`!qFAk1F-IYS!e#HUVJ-6A|vbp zThwoeOKD1lLG^^ly0^5CB4^T%mVr)r(sSa8zTz^6rd;T9jJs2HWKoMdJvPF{dxPr8 z8|-+Gae7xIt@&tb_!e$ZKCuqz6#hK*65%p&gpxY&xqMlJbm|OI52Cd5l<6b(Ta;E@ zhV>k{7~EFZbawK893wFk9$HFU45pe%7-cj#_e)MPxbT@V=2O~dgAe>-d&q4Q$)(Yx zXo4>`0UL`*Iqjitv)aVL5mjU>c#}6CCGG1x$g&2j9~G-B=E&h5`NMfGdFk=@0;X)Ae}bS;&Nt&m_z?x7_{A$3x> z6uf36J?Cxz_(XUtj!aQWWkGW{+wt}WQ%cd#)v0Fy)KjAB((EZ=)4R_)iO>v?DzsTJ zATAM_f|R0vi^+{@8)NlubEpUlNFZmyGx*VRVwHqvcEueK3X7dgRdGu4oouzovgod) zeEVi9h$H2-;Lz%=J$nk`BR#L?U2TD6A#T0WDvy-!L(A$gkGOArzZ4mTaqBt08Y2-tJ7*R!b?ZV%GGBU<=fUBIlsiMnk(5KI~-!c1K zd02R8oyMS>t+f~1x!|Oi(p)%w4RDN%lNOrcwd}c?&M)sGb}_HL)8>)UbAk>_U(|oA z1Y%AN53}Ug$Cz6iFoTeDMdB-cY^1Dcl?g0lehoPE-kDW>rZ_~kNx=<*Sh*uOR;p!rH zgk8W+kSzBNQ69Cg>y%~r&13kQ%q+q!lRbNXAJTAUD9m2#ltm(!CPhzbEFxJm1ly3u z=bH|L)lvVU2rW%tY0t)n9daotIz+egm-UFjCK4@{ew``DoQK|R45_=m6khUYeV5E;mF}4ELkv%@FCLV8QPFVFSlxvH+F3*W)0%C=OljP+Y3>rvURi$g1TvWcYdKr(z-Q@!y^GX< z1B*OZPo0festjAG#_zUxBqe^1531gn1rK(~d`?~lSBO;0Q?U$w;2w!Rt`7Zw@sB&~ z=7?YqMZP(+k5{H2Y@z2DUAwcKi)MtJwm=ZL*}B5+wlH+G(@fssD96V$$~*MIALy|w z({skTDeWXq!kSH>JL1d>ZQkA-dx|$cdXtoqX2TEWC~OXk_3KKg9vT>c>R0xy=@ppF z(20*717+2F&Fg{5De#A;D*C^Fk>Hxzlz2b$3W?5NOJ6V{WT3WB?!d>{d1TD7_63x~_EB)5 z66WL~2??v3N4lRL3}Jze^f$7yeXa;|KSnq#n3t@)+5)&rL(5&^nt3aKd*6xdUll5d ziY`-l;B|JcUVJhjn>4PfO!QdaAi^KK=v}Awd}S z;s$;9pxQ^ZVP)cRwBN*&oo()_hc7O=!Xv(peG&v!t>Bj3<%g^Bvu2v=^&qJ*drWD zt2s0he;f;ED+!m~IgD(5Z`-=^3HWH>PTkqL>F8GD21B%Amoe6v7NhP7teu`m$R}}$ zj-BckN-_IThHy3v>x)}CGNu(nmfp6TKPkT8t?Bc_@JK^6`WMW9okG_$r)4ZRak(h~ zUoS3!r5}m9ykl3D22Nb^>vpJTn=#2s!woyrWL~V6HLXeX6AvwtP&f=NUpA*G9s#VQ zvc?7fz4Nadjs0V2-i{cz01SD>ctUm;wTHq={3!#!P5@2TH$3dh2Pf;XCm#PrB%KMd z;L2st(ucL88C*zzKG4*~hbt6^Pmd>~`)T(m-l6>?uYE@3XN4XPnqfXxKprm4_Uc0j z@yo61-xmeB43e2r%fa3qicKfY==u9R0_*!$4(T^8J;_8Cw`9hZJ*2c4!B8kv`kraW zEh7Box{N;JSsc4E{f-UbMWY6m4?_^Floj{0bSpNdEE2AVvjpwlb6H+$05 zy$gNh>>JXo@N-$XNU`2nsD@8@N)`i;HspS1<31;=5*LRaOK2AHN>T2-1fj{6`-^%e z8jBj<>3@Zx3{RW>dF*75ZEi};8g`~Dp9}~J)#t^}n-tU@@CY){?JUUSzWbPv{e99- z8+*1itf||7&>EiP{2)Xyv{Kbbe`EEM(VovZe%Z2n1>ufw(_mi~v_aav#`Od-O?^_; z4AeQNLMbX8VT%h&xa3m|RhEp*^>q#tq3E8QsU;(1yAv zR&p=I$MBWp{*HNuudyq_*BSwK8cFI&+zwm`iQ<-J=)M1EOOe$`?9=rsg3$I0eLtA! z<2)81HBLE)mub)nFExpJ$Bm_&PR``l)W^AD#-zKn*DC{XKj%x(hb1IFgITuMk6h9Y zteHT6NHIL0+fw)7C^Elm#oxXpBWqUcYJp~9+a=}-+A7B5gDrQ^iURjQh4H&UT59Bc zG+PS}u#tAukf*SF0<{Jv$4mq?a)ZTRV89D1+bYzIUCNldic;L_1ayiS0tMq(x zs_jPT(p9OEQ0u9>e8fns8b155teT2ixYU4utFU8BADK|K%nNP-6$h2YthvSWfF08_(6Pz=|AB=?^%&R4t5|r5yHAw}_g#y97HK2$L%4cP+(# zdegtZ;R=sJ)iT%Lb|%cG*z9TcH&UhWWA zJFA-o)`98SsC5pDFc5(5B5NE>zF}uRK*r>J&9wPadJXB(&RiQ#$DHDGT$3Q30Kb_} zsh*#)Q7lL$o$GmM<**ui;BR%7&_JGlO+x%lt5Y#_=3q`YPC&IV?u^H(oGI8e76%os z7?`dmT|;M3k-CCDrjl_&;Xz>e!y_$l@qlVt(sjdAF3S36pl6&aD+J71t*hm!KJ~O$ zKTnW&><`GbmmdN38`%7MK~GgV1msN}CY7P!nZRHEI8zemzA_XntHM6g~nu>-M8gs9O3)CPm$$nZLreP*_6(?o92PBTyLc%d`>b3iaeu z>k>{arK_7AWpt%~e_V#M;;!nzB^QP+fgPV;Djh=HwDZTYq64`Pfsh%vqGt}`?TetW z&PjuUvX+&9DjXj)Oj#QgFlg$38=6!w%2CB7#iKX-rW?5I#fUE*e{nN0ee{L1#^xCG z7gxQnjr%C1OQZw@&z86fZalU^+ElGa&9NgZ$lk3%qc!74M-ZAYpsJCr`PB$xN$&R) zZal*Y4XmA^Gb;M+%bk^7G-;>aG0tV5fZaIs^)=pbc z9AN8zwi({-M=>s(oxhZhkXBC2JK6l4Yj2$Oi*Jk)6JqoR1~Fyp>zoX<@PJ+h^ouuE zZ}RSMSVq8+oy{Z@E7CoG?%}}f>v=>N$yXBkiq$!KXk+~{>aq2o=wVt3%Mj7|Pb&u? z$tx`oa0q5MQrY--o1wKp?6KH@#FD6~2sZ`8U4M8!t)JuR1qpa`pipDjZ5PyW3~SJI z<&^kU*W`hL$GioF)N@wC$K;gT*YvhkdUK;`9&Nw9gjoDJ!y@T_imn}6OrKR&cn;Us zX$l44Yfz{NNeMNn)>l}-mG-54$@iD*XP9p#0o3{IZV3tyNCTX5ds_m4xlnVs@fq1V zmnBv5JB`)*NA=<+nbGq8-N%_Hr#ZdfS*FEw_alu|fnX5BiihbbXdk+*hteBM+g8cF zojHU0XNK^Mf7DEWstsJT-#zB!*OMfd+sMJjdcd$^EG*LHt9X)13+1&W>cP;*c_a-= zEPg;MeaTi9oFGNLov_QkO(6^GLqByBp2ko!&YLK6y!Zx!)r6lQMK9q|41PMI3|q*y zLLG7>5AHT2d6T$2t1~O&sAeJoa&SCI`|D zRsg=^GTk2u+LxC9~6u)u}JqNv|}gE!Z%kY?b!coz%wbl;7Qd%OfH!(}Qw$ zaK2yIFL0}ee`$Cu_K!}xKKJZ9OZmq4nuba00K@tAs*w;-{NZs@K{PC`i;X55n|MIB zhAOQ3{b#{{0=HB%e)G5AG?xL=K`;848qCm}hK${!uunHGE`MP}WE9oVeBz^7=n1W$ z-Nc@co+SxliC%jIhu9031}LdwXB2|WVji2rc4`}nUcy@*FE}gs@*)2Jf3nS&O~ny*XZAW@4;`Q*6(b-%ld5=~WdAfa}IyL4`YqH7W4Pp&kkVow12*Y$+~ zAMOPrl4#8ko&rtU!b@hoPh8Op+zULGlXEL29;hE4u%a_Y@A)Q)1>EmT!q$D)HZbUm zMBILVG_oo;xf|)l=FYehdUAcL38o)S-(D~DMfn<`^~EEVGfjMuu)YF*n#SQX$~}>% zSNO{aO^=|V)=~kfuGvh*QQx&hBdTi00SZ$%;&2?zY!$*|v?pfR=rytzmNUMLe0|l-Ix39A#l8=S|gND^GLJ zGW>;uTH<4uXaC-A-I~9mG#Hhsr@|@(B`$G_4>{~hmzE&IVo7Y%eNOAw5nFY#V&@`7 zeWNe>TeKJS6+!JB>Td1m`Sy03Aa7Z8j<$)y&H&#Q0;9MMRS~4aNSr8xLM=rltG?BL z`mHkvNRMQ4AFeIM`S@TRkU?}Ulkk%YBc6x0)C7MY+eW@+k6w{M69EKbxR_x3@92Cx z`7*Olt}hr#&ih_u6YJhm0)n3&1~ES#K%k^$ovZ1i>eyWiS0qHR+xRpyu_}Y1RH`vc z@WhRlESv7}F);J{Y&;ndKVst)#X&89QJqY)j%Q=`h?0^mw4s}##34ka?9u4)E&z69 z+f}OjYik~V1Awg}k-wd~FzFOLaEQ^Oew_;+JdRlY8rdFS+nmBQ$fT9UuV@C5HElh; zwE0AQ7S)K~gEK-HJs`ALXm0HXPQ4hb^ZhJ)?(1+60)JXYXcaBuI0oruj^&olXGK#UQyLNv zwX+u_8If1h-g51{$#~k>V*5 zTCW?{)E?FzfB6H=>n+lflLp&;vjrF27%A*>g)BeIN|+G?3aC@^2uQ)rJ9NpYm}UOUmD;RBthnXC15giO49 z7Ks~gEl+8T5s$8+Uc05tN^8Z<9;GqhoW2ULgYsdTZnE^i+LJqBJ3ONXF(Jbv3W$bD z+}uCjN}`zf+wg96(P)`}?cCLNwkn>-a)FQf+c1>@)t}(LSedR$-sI2()Rmqnml4DU zKK)8o1rRS&&#!^%oQ1|^aDNS9pI125DY?wVSvOdkyI&L{Qy^b`eu7oxC@G`gJ3BFT zDX95SdGCYi*%`l=4iAy3g3yv2_A4sDLj=*E#k#Wb?Bdi5`tg$g65-n17D%NmgK4~%LFx{R7?Dvd*veW!Aa(U7Tc$U!Ybuk!Fhv3_-l)txUAP% zH)Bak_$U3WZ=T%vO_~o=@axSgx@!46&%5(w2j>YL04r7A*v;j+4BG0w>%=YG?B2 zW4$NrjnL@@HjLeWo8AB?vO3m6t&y~K^)ICdds^`M`0bf zS1VGagcT*enr0oIV8oz6rjhVXDCdlic#)OS)-hm0;>Bo_@(l$SO2w9rXeKdO_IK_eMHu?(K_FkIN$p6*IL7Twed6 zNfz#xhUxbE&--e!t|-(7#lePrS?*CZ?fowf@!#etC+tPw*7@V&#tL!=U5`)sf<+B+ zv>}geo4;VQ5wmJ((B)UmTY^#q%TO zRm~6>ZtPj?N~BqONem$|jN7DFqB-9?iR*jlLl{=V=2R8F6i~E+eq9^#@tnYlZwEt3 z3)=R7SIZeYx^ilkiOv!RO<4pu&6Bs&KaQC~xIrQik?)yUy!x8zKlpbAl|-M59<~ub z%qkD+Njx7|jq(}VYfL)=l~>d%@1~Dn=2{dsnbbvGajtvea^Z51Z`yorLsNZElx^m~ zRX&im*l-)v(~JeSQ6Iz(^YMXx&j?P?Y^vb_c}(A=O0;w8&hDyz z;Sh44n$O@fuJ?q)Euu`P#3#)W*LnoRI>aPFIj}kuI#E4BqdIdgsem3-8`@s#XSvfH z)+p18E{jBl=kMw)9ig0&UXve<(r>+ED-%aB4aK=1(~|~%HoK@+JVKnRg@HaiV3we- zNkkrojq>y-E)3i{mY{Nl#b3iIo+(6mKp4rODtWH7L+Hh*e2_}tx_A^fpvruy0rAf7WPH3 zMY~O^ZAe0m>GrA36G$s%XS7!d%Z^iHtrnC7eL~)eBj;On)$5bd7sVbG!X`X_esLGK zlhoIf(@6Td+Hz!%1l-GUd8OW*#a@*IID*#scEQzX*@)nd@i#DGktg- z@7UR7d(d2v0&UgP8-*DMq5sXW7sAgENbrHPL43uNsGwYNHUiW-2Duy-bS!UrMH6f` zO@@-=G;=(lrNFdB=c8j@1Eko0$VK=)V-ctxDzMK|>*hYGc1-YPEwv3SdTh?>C`>>_ z=J4}`j(z#*xabwEe?qCT-+W$Aha|+tpmQZdk-5kxooB+;>w9E zP2N!G)k>zq6zzHJ5UDTbO^C+_1#C(_oi_XYfpqlLSS^V<4ru&8nf3z}d|C zXH=9^w??KKuv|P+GeMO=`{%L%aIkt$Cm~5e<%uLhp9)s3JlDlAK|Mhij!!5_nzAEL z#QTHmz>W+Z36;lOvCRb0Nr$k+a#X`Ee~PMln<7FmAc2Mv)T_X0YAE< zup4D=a1Nba=(?dx14L9*=ooy*OKaF`k?%xSFB7J#`SEnMqL+J>OnAN>picT2 zRTWW1SQiF{E$~6G(6PaA# z!ZTNHs#Yn6y(Gtfib|z}ET;QHMA|V;X6;jMTw_O?!z^&aC%)^4r280z#wB1i>U`x# zJQ9}23%DS4wI)@Q8`JnnI26H>BUGX74}LPuq;VIGoT2{a0v7TOe2OXMTsMCadul#o znI}T{OY0KLq8R~cL=;~02lr58@%*4^@c))01RW5y*D(X+m=KX$Qc{ix)pDJH zjOQ?&4AfwM#SEc`eG&oNeZDM#a3^!p36NiI_eAsRD}1~raB>_tZC=h|;kD4tIZkwI z&uHoT1a~*e>A3SzvDZNL)#nmApP4acp@o6=`DMY%F!+E(Tn2joC={YlOR*aeKD56SVNE#Tt%Fgq-O^0*|<)u)Ea8;YIs{3bF_ZOb*-}>$$jZ``C7}+@wakvc&ZwV`G&1 zWM9B|cUTm1j9V(Fgs3%@XqF`^xTRnQ`^%Gd?i zDer*CFN@#vvOg}75kAP*;}%IG3_e&Vxqsa>BIY~D7>9j($dpeAANd_a)^>DgOA3$L zPa9b(1r|u7iH_>H!{|I;PdrFUr*0*GauXF}|MeFDudG5Fx$mL>w@GWIS1ky0iW&e)`dy&q|tcP(_ef{)|-9wl&nreF6rGDEvp2UO2a;sbZ)ua6dI z!>DwgEN@!Gkxw-cKPgb?S-kEc4ORPq+`kV9`{+M@@B)r??-5o&#mywFz0IG0*CuA+ zg|<~MkSuw9S_7RBy32d$C(Iw&2KgrnRw<~ zX}svFQMAfJ(7D${OI^^UHH8d+-tD11lyKNeXg5`N!2l}iM137{aN{`UkF_(sw6s2U zyTRHt;mQWGA32D%r4ym2oGSNq+UlXjAzx{nioT(_i{p%1o#_>~RH1UeWF45hdtH@l z>+2R^;@>D;aqe7UGbw6*(bvO9>o;SKjtoiZDEAhypG!@r zyTmBkKX06k%v!NknZWR#?Cp%<*QJ3x`$W@dmXI+qb`f}NusE(%Xns%yP4IM# z=Cx#D;b|xd6c)%O`P3?0-60D(@PjPpS)rGmoStRHo>vUw`jcBAOH@^Y)p9!9-R+|; znK8>JQ^ENh+4sucu8_~>{H5r~v z^bXzTi^KKh#$vt1;o!gm#9OE)#BefOA*!`hU*67teo7=QQ9d8V>0ZeAkoi1brhr#} z8Kp2e_n?DxYE|(XHD1tumrCH&t-PBcG~f0`k+?V-7WR!RI2Y%C=iS5^H@raF?))?e z`c04X7P89m0&%33gdcj_%^Cr95aS%v;gh9ja%Ra{T@bGb6dh)Bnu>?N<5w*h+Sys{ zrhxlVFN{XV4M z1DqC*p}zPYVSf03M4DWlZ1#k2!MR->8hVnU1#45qC|gku4QntCxXrsBMc<%c<>mM- z2CgD2QQ)znmVpJJaDJFL@Yq^b&o@TtvkHzT*ujd8}`}pGTbHw_RhpSqA zFS}3QA2!qQ=+Qw;MeKb|z_3^+A40UtuZHt4sRl=v`0Uev|MTXiwfFqkpk&3~xa{O+ zI!lz-cIz0?_<42;@7C76?omKfhl(1Em}MOi+_37E9KQ`?KUJ71B{_*@<{?IXmogu|pht4`wP5M5UjdXaAA$u8z9*=Qn94!I>wh~meP&I9=N44J z2FyP&uga2t;A&PoJDytz5NW+VAAJt;Ye0bUj8~yNyg)Ma*uE+g3Z?BIn(MvWpe%5k zT--3V_h2=HI)m?*fkzBrCM5WXWnkdPGujI277@4}lyb>#m60VR+^NiE$d(2D9e&lM zZ>yp4&ePGOxe%5*tifX7*RaV`VonZs9ZA`Y0CaVKUWRJYSqjO;+2cS?A!z_FbinHZ zvO!a8t>akNDOYHjJnR}i$k;>sCNwHsgo*jr`L(1Zx zlwyt`ES?s>YqKS@2sxd~-9J~S8Zj;D>w=2|cpA z33IwjsQZX;kgF4irJD_IQa?kXQh~V#C=(=Kw9Y5nVdEp$Ta7Im+}&t->6fmfkhFHt zf;^LY*cI9U3x_Ksoxfu?e=z3j4lt^t_j3q;s-d-sQb-B-u>8FZCok62{LA3YTPY1{ z&*+LDK|j4|V~0k}rTL0wf@pG$UpDy=@q=A!HE#<5?*Xy(tNRcx%{V1^rwgG#F_>lM z?;)_L@IIU(y=xRH3dDzprT#h0etj{LU&1lEB{;*m`o|2NzbmFU68-FQ7SuKuhm=Bp z=eK0Lc5j8zAzN5X*W!1coK6RiD+v-V?iBFrur!9%6;nP3$fRSCGdA2bb$4aEH)MyS z%@J0Cp;07VjB329#+`h#`!?9{K|AQi=b_Y|2$XiAy^IpH4dlxh$|@I?hn~UgrO=<*hPh2xLHm>n zuDC5JR?pBabGuG;U^laf22EP9i{!T&zKm>+(f0idLkKi2mK~>&Zh;ES!sr`LSWg%h%=`dSNIZ55J)mtGW~9-9+_-N~Sl@LxiJt z(l>;9Eb$wtUPZ?wd~rU=?H|W1!t@U5dlM5P%o5_=I*8PI(5`r3rS4?`f%=P|or?U* zxgvw0gyf+1v~9eFJ-Nw>|N7%xy;aE)em4F5Eaa9{jLEXy&0*8iOP5FzM+hqr+GE zg3T@R`q?-)a3INRbUW_x*xM07tXq6IC(wWh;Mw+FFlfPofRg8bcX}s^gnWR6ky$~? zSp81?*0?L&+7@^Y9@e@fs|E;`{f8UOI{iii-%l-guU|D(xqBzV8rNMo;oe-K*k6vU z4N3Vl=Y4$_#q-%cO|7Ew5k{{Seotl-TWo%*WfxJz!=$ArPaD@EGK4z{+k?DgEO&VX zGgnT2DGwp=Sk2{s!g@_>yNE~***s0mvMiV2$auoqm!5_)81G@!&5jGD{rOnJ5<9u9 zo_47U@s#A&SN!p2=J2hcFseV~6t0WjB#JyTge-!f#W{9-vK-pf@~_o4qKuw@=46esgSb~xr%E@u5+5@| z_g0c>SHU;1!M9wx8el4RxqXKA)F5pir1rR)pu^)1q1x&<7xCw@_ipMPPyKsnRan?< z=$s@`hLNcqBWv?(hz-l$?%f6@mF;JdhvS$&o#L%qE2EINxdapc7O5$c*&E>TE|(&O zD!ue22+ae3E2MKEqlV|x51589qSik_wIz+up-C1n76}E%G(WR^L+l?>#Hj3@UMJcH z7RD(KFP!o?{BRrYoHrNoy7>w!%oL54{tWqA(Er3P{`t!B7g+BJ- z4GWUa%^LI5W%A5TuYYSLSnI>XX>LAr_+1Wt$G}5>ItAAEHybwAFhzWD_>+Xv>h^nu zOTbS&5{_w>PDYN!*%>Npa;u^0?cT=>aI%=12F))Fdz6$aS(Q&}MDv7W2d2yneL-%5 z3Lm`EgZ5|{#z&RU0?S%kW8k$=O6RIkZ3at{$2oeiM4N5~CG{(bRU(`=JyVza`(6v< ztNpHjCz*YMgO-KAq#fp-3;3X|aGzO~0j@8i%N5p|4}W#>wZ5?}mF*DO(81^I(j{b* zjfUIfDTHcNj6`ikT{>u(bC0o-4=hj0ZEt5-W{OkuCB;W-(^Z4Vpo3nY(TK`(O93ic zatpNu>r<*=7X{U~J>{n=kxUVqQ2Gp{u{(D=#Xie(^jHEt40w26Y=tJtw~DnpjfdOy zlb!v@5n$$@1k^(=^AS1EwQuK06-@63ei6kBKmR|^;qaH2u`mM@0x&j{z@8MhYX$?w z8UZkuF(CsY0Wp_UAp<8YG%+wTFfcPUI4dwXConK4DGD!5Z)8MabY&nYL^?7sGBGeT zF)%VPFf%ncm!}~EO&cvTE;cPOE;KMMGBF@1RApEoOlfvyATlsAFqaS_10n%3mop** zDH=C93NJ=!a&vSbHZ(R0FHB`_XLM*FGc-0hlkplTf6ZHMPa8=R{_bBfAMdmZ{r)CJ zD+LG_vV^dKkljT2;J^fYXKZ4dB)h-<>Sf068GB-Ei0-7&)O1%@bv;#G)jdpfAq!O` zXkwvVEL13rlZDoWGw@3l9!MRE04&-UG3o;Pl{CPf3Z;zf8B-{oVqtWl46qqrCp^dVIb%nOl_iTcdfP4!oJj`fO2(2qkAAu57Lj9@6jqN6FI1fkIZ z8R0nq8DkL>;H_N&rX&scfy!h-PlB!h>qr6cRiG7Y2A~7B3Vu8Q2hZ@6$OQneQV1@r z4G0Yg!f&r(neaQ<)Kl0IVcqDdlwfJ#DWD3NptV7qfiM6VOjg>$A{9E*OVkNOFz72F ze!BRy=)Lhyw^se~TkXn!;5i#H6o#!HpFOJ0aF2c&z4ihz!#f zJy6pM@u_Guuoeg=vPxKtIGKzuPRm*G0Sw(gD8yO$eO7#|*SQh_m6jj~55r(#VwVAn zMYoJu3Ez64GJqk_w0sC2dMPBddMrHF8`p(!?#$MF`8gcxF~}ll<(AtP1KY;H=ts+m z0nH3D^P6mi)%!cg>!-!P2}>BbdtqQ=2L_K|?P?qwDK<-`kBWy~+7~8079L%BE(hoT zVPDv#ePQ*(Fj(o&^%{85I_kS~d+>z&3`mYiGHAY+7=j4)o@j>>j_f#H< z63?y`ZSiEg9HH6nZDh7p{@xY@*9O6vhdFt5CfX>9yBwZD*2n;J__hUM>2%}nce-)u z5wiPwv$|1TQy6}X#|JOX4qUsf-Emc$_ZS~{OrxKB@_r)|w8^<0xCMK)lPb=JWPd4l z;HDVePQYt1avBXSjehKX2sZ2hrXS$SN6)|y*H?o&<`mpgq17Smz%4&U(8Dcq1nRBE zh?{8~yYG+AWvB$}WvMo3u!CFgJy_6a4Xz<*9CctBv>xa3XpoCFR}zwiQD+s5M!d`u zM=*Xz)v}nj4%g49MoDczFOb~C9)HQ#p%l#0b3`&Kv&)hsr*MX~w33WA615Q?)T?Pd zn&exO$2}n}W-hq7HDsBIhN1-5RB0gEQ$N$xg0GDRywLZ_L2uL^Wg7*2L}N7Y6U4@R zgy=}?<#|?Nv~iowu11Zs(9blFKBQ$im1OW%tXC}+A)@A<&s!fNa0VYRI)6-{9fCWd z;=%V{ygr@}9N<5ggD-5tjv|f_E&7@zF-=k7l0+FA_g4TsbtP3v*o3aR44cq3GF8Sb zsN5{4B!y{&1#;l#w9%-#f`t0DLp?geGi)7IJx6OWh>0WT=GJ%(CRzZ&P{4#*M@$F{ z8N)QS5C{zOLs}(4sjGi=kALLuZjLmjHv1S_ZhtTdx2CSUa@X*#<`yded$sZ{aqxmT zhPcg5V9n8DKE^x)fO^cbQBz!ciBUsY)Ej*LihIIg#s#n??mbCzjoiUkbsm=yJ$f_3 zrUs8#+`JLgLIRQ=6Fu}mlthmrK~*fg^JE+BH<;jXRY5w6d$Wl3j(;}CFVDJpn>hyS zM)J6N(1x!;BiWE>50E8PJ7CukmsR9gT4sbg=>uYuC>JqD)S7ZR<4SbYxFX&}B)`%~ zt&)Kt&AL3nYiJ8P*svAF@lJH!V-xNVZu1`I$@}xU+^>U0Z%|t21?GFJlI+k#OG(>+ zGhj5-V}TPUgbCOU2*JKDu4alav@)CYxMJjQ}c+# z^MS&W{F9GYIs-r7+?7+X&~aK|S3&AX^&!aVdFeyG`iOxXl{4v&9KIT_`e<$?osL%= z8kKzHa~NcLuHMyvEDR+Iz)>>sVbw#Wi;W~*9ZRUKjt6}W%Ba~$#npC5&i!|;QI zvp8)u%5~QXDSzYqU!9N|=eq2K#64_$0Jo~zY~GC;Qe3T7hy5dLec%)w1ZS&nsmoBEuLt~jDgbW$DVjLy`Z-q z3K;kLw=c@U)%6VTbv=PXM)5@T^ICSodP;!b=O-^E}5`^4zlTpOF_!fCqG+$RbLbBmuKIBRXb7~kCV#nq(j&&r7ymeZ-YEhmHV zWq-rQxQUG?XD6?Z&&kHP+C*J@R4MsbCdT`jn41;lCT1O;zkmI57tDIo z0cOo()u--QorzDA{>6XF*|7XPrzgk?1`o$0LDNL}<*q*zp9fzdsK<8`F&d0YF@~R8 zd~rx~iQ<{J!@H@NmS4+}m=3;++4ZC>#eeMESo~d1#trE7CeXb+dwYKV2SVpoBd2QQ zjI?WzllMX{b|bf64rl#_Ep`)I_TTTncz%esbOg8Y^K>O2B?^M?wB?!D6?f%3Ohl>M8hpHu zHF9=M*O@A9D^q*oy8-Y+@DOw-{t_& z6O*5SX*%pruPINJ^Ndv`3h20jWx@U#O!3cU1ISfXRp-QorUF5l(SFS}2aC`B z?f^ds6SD1V)1r;C?6cor?mpWii{d(er$@Whn6>_1?d|i2dV8ZB_kZl|?%un@Uyh!2 zS0I(pQjLFCLi-okG*W`>yuaVG@;KQvqa(M0sJl-ydc;nUvD=@P^x4Gn8;OS5_{lqc zkZ~}WOlPRMND$#i{pvw!5cY0xIlG?XSMnQcZ@(aDDNk+XE@sNTUg$QP>uz1#o2sK) zQk_<-_FI_Ux?HzZn}4=y41u}oWENJ-%~dxqr!CE{ty$&1Ug%Yu>up{nTY8s*Of?ki z%+Y#t)y?ZrOZ8HS8I!3t3$Xnbn%j4mrtY*9W5#yuEx$#4w=N4U-RAzfx5z5(LwZa3 za-8M+TH%yhdD=pG^9bXvhH{w-IjmQ%B2!EPokm zt4{u~(%E&=RV9~n7uHTSH820ihUyd8>LbwB%)LIGqQ+M}7BN=_JpTpNF9Q0Pu`mM^ z0Wp`xFasigGF=KUQ)zl-ATcpA3NKe6TQMLrATeDEFH&!BbRaP>IUq0~QVK6gZf0*F zF)=V8Fd$M2FG)loTRcTKLOD1!Lqan{L^L!tL_{_;K`}Q&ML|P3HbXHtGekZhJViG` zIXE>#LNh}|G&D6tL^d=*F*id+K|?t&3NK7$ZfA68ATuyCAd~SLD1RNy z$u2{29L4e9xmQgst+|RCTk}{omex$EgpG|2D~Z?%JC;~j8TtsG082ZONFP8XUO^%f zDKKY*CKlhrM{W)fS5CdUNkVP^{l4P1pkvXzV7RW3~zzyu^+ca^B1M_8cn3=1~u;ig;3Gu "cleanup", + /\ SendReqs({[type |-> "check_txn_status", start_ts |-> l.ts, - primary |-> l.primary]}) + primary |-> l.primary, + resolving_pessimistic_lock |-> l.type = "lock_key" + ]}) /\ UNCHANGED <> -\* Clean up stale locks by checking the status of the primary key. Commmit -\* the secondary keys if primary key is committed; otherwise rollback the -\* transaction by rolling-back the primary key, and then also rollback the -\* secondarys. -ServerCleanup == +\* Clean up stale locks by checking the status of the primary key. It +\* can be divided into two cases: pk_lock exists or not. Note that it is +\* hard to model the TTL in TLA+ spec, so instead, the TTL is considered +\* constantly expired when the action is taken. +\* The client does not care about the resp message, it cares about whether +\* the lock is resolved, and resolving the lock is actually resolve the +\* status of the corresponding transaction. Since, in the TLA+ spec, +\* we ignored the `check_txn_status` response message. +ServerCheckTxnStatus == \E req \in req_msgs : - /\ req.type = "cleanup" + /\ req.type = "check_txn_status" /\ LET pk == req.primary start_ts == req.start_ts committed == {w \in key_write[pk] : w.start_ts = start_ts /\ w.type = "write"} IN - IF committed /= {} + IF \E lock \in key_lock[pk] : lock.ts = start_ts + \* Found the matching lock whose TTL is expired. THEN - /\ SendReqs({[type |-> "resolve_committed", - start_ts |-> start_ts, - primary |-> pk, - commit_ts |-> w.ts] : w \in committed}) - /\ UNCHANGED <> + IF + \* Pessimistic lock will be unlocked directly without rollback record. + \E lock \in key_lock[pk] : + /\ lock.ts = start_ts + /\ lock.type = "lock_key" + /\ req.resolving_pessimistic_lock = TRUE + THEN + /\ key_lock' = [key_lock EXCEPT ![pk] = {}] + /\ UNCHANGED <> + ELSE + /\ rollback(pk, start_ts) + /\ SendReqs({[type |-> "resolve_rollbacked", + start_ts |-> start_ts, + primary |-> pk]}) + /\ UNCHANGED <> + \* Lock not found or start_ts on the lock mismatches. ELSE - /\ rollback(pk, start_ts) - /\ SendReqs({[type |-> "resolve_rollbacked", - start_ts |-> start_ts, - primary |-> pk]}) - /\ UNCHANGED <> + IF committed /= {} THEN + /\ SendReqs({[type |-> "resolve_committed", + start_ts |-> start_ts, + primary |-> pk, + commit_ts |-> w.ts] : w \in committed}) + /\ UNCHANGED <> + ELSE IF req.resolving_pessimistic_lock = TRUE THEN + /\ UNCHANGED <> + ELSE + /\ rollback(pk, start_ts) + /\ SendReqs({[type |-> "resolve_rollbacked", + start_ts |-> start_ts, + primary |-> pk]}) + /\ UNCHANGED <> ServerResolveCommitted == \E req \in req_msgs : @@ -480,7 +503,7 @@ Next == \/ ServerPrewriteOptimistic \/ ServerCommit \/ ServerCleanupStaleLock - \/ ServerCleanup + \/ ServerCheckTxnStatus \/ ServerResolveCommitted \/ ServerResolveRollbacked From 0a218896117408b4a66664e860d3b9468c4b6f64 Mon Sep 17 00:00:00 2001 From: Zhuoran He Date: Tue, 22 Jun 2021 15:57:45 +0800 Subject: [PATCH 03/30] Add SI check DistributedTransactions: upd min_commit_ts DistributedTransactions: fix tla parse err DistributedTransactions: upd tests upd lock failed DistributedTransactions: upd read write keys DistributedTransactions: upd Test2 DistributedTransactions: fix many bugs --- .../DistributedTransaction.tla | 215 ++++++++++++++---- .../DistributedTransaction___Test1.launch | 3 +- .../DistributedTransaction___Test2.launch | 5 +- DistributedTransaction/Test1.cfg | 5 +- DistributedTransaction/Test1.tla | 5 +- DistributedTransaction/Test2.cfg | 5 +- DistributedTransaction/Test2.tla | 5 +- 7 files changed, 191 insertions(+), 52 deletions(-) diff --git a/DistributedTransaction/DistributedTransaction.tla b/DistributedTransaction/DistributedTransaction.tla index 29b49df..07869e0 100644 --- a/DistributedTransaction/DistributedTransaction.tla +++ b/DistributedTransaction/DistributedTransaction.tla @@ -1,16 +1,17 @@ ----------------------- MODULE DistributedTransaction ----------------------- EXTENDS Integers, FiniteSets -\* The set of all keys. +\* The set of all keys CONSTANTS KEY \* The sets of optimistic clients and pessimistic clients. CONSTANTS OPTIMISTIC_CLIENT, PESSIMISTIC_CLIENT CLIENT == PESSIMISTIC_CLIENT \union OPTIMISTIC_CLIENT -\* CLIENT_KEY is a set of [Client -> SUBSET KEY] +\* Functions that maps a client to keys it wants to read, write. \* representing the involved keys of each client. -CONSTANTS CLIENT_KEY +CONSTANTS CLIENT_READ_KEY, CLIENT_WRITE_KEY +CLIENT_KEY == [c \in CLIENT |-> CLIENT_READ_KEY[c] \union CLIENT_WRITE_KEY[c]] ASSUME \A c \in CLIENT: CLIENT_KEY[c] \subseteq KEY \* CLIENT_PRIMARY is the primary key of each client. @@ -59,6 +60,10 @@ VARIABLES key_data \* (2) LockType \in {"PUT", "DELETE"} /\ for_update_ts > 0 <=> \* type = "prewrite_pessimistic" \* (3) LockType = "PESSIMISTIC" <=> type = "lock_key" +\* +\* There's an min_commit_ts field to indicate the minimum commit time +\* It's used in non-blocked reading. +\* TODO: upd min_commit_ts comment. VARIABLES key_lock \* key_write[k] is a sequence of commit or rollback record of the key. \* It's a record of [ts, start_ts, type, [protected]]. type can be either @@ -70,7 +75,7 @@ VARIABLES key_write \* client_state[c] indicates the current transaction stage of client c. VARIABLES client_state -\* client_ts[c] is a record of [start_ts, commit_ts, for_update_ts]. +\* client_ts[c] is a record of [start_ts, commit_ts, for_update_ts, min_commit_ts]. \* Fields are all initialized to NoneTs. VARIABLES client_ts \* client_key[c] is a record of [locking: {key}, prewriting: {key}]. @@ -97,32 +102,62 @@ SendResp(msg) == resp_msgs' = resp_msgs \union {msg} ReqMessages == [start_ts : Ts, primary : KEY, type : {"lock_key"}, key : KEY, for_update_ts : Ts] + \union [start_ts : Ts, primary : KEY, type : {"get"}, key : KEY] \union [start_ts : Ts, primary : KEY, type : {"prewrite_optimistic"}, key : KEY] \union [start_ts : Ts, primary : KEY, type : {"prewrite_pessimistic"}, key : KEY] \union [start_ts : Ts, primary : KEY, type : {"commit"}, commit_ts : Ts] \union [start_ts : Ts, primary : KEY, type : {"resolve_rollbacked"}] \union [start_ts : Ts, primary : KEY, type : {"resolve_committed"}, commit_ts : Ts] - \* In TiKV, there's an extra flag "rollback_if_not_exist" in the check_txn_status - \* request. If the primary key lock is missing (and no record in the write column), - \* there are two cases: the prewrite request of the primary key is delayed, or is lost - \* due to client (TiDB) crash. To distinguish these two, it must wait until the - \* TTL of the lock to be resolved expired. In the TLA+ spec, the TTL is considered - \* constantly expired when the action is taken, so there's no need to model the flag. - \union [start_ts : Ts, primary : KEY, type : {"check_txn_status"}, resolving_pessimistic_lock : BOOLEAN] + + \* In TiKV, there's an extra flag `rollback_if_not_exist` in the `check_txn_status` request. + \* + \* Because the client prewrites the primary key and secondary key in parallel, it's possible + \* that the primary key lock is missing and also no commit or rollback record for the transaction + \* is found in the write CF, while there is a lock on the secondary key (so other transaction + \* is blocked, therefore this check_txn_status is sent). And there are two possible cases: + \* + \* 1. The prewrite request for the primary key has not reached yet. + \* 2. The client is crashed after sending the prewrite request for the secondary key. + \* + \* In order to address the first case, the client sending `check_txn_status` should not rollback + \* the primary key until the TTL on the secondary key is expired, and thus, `rollback_if_not_exist` + \* should be set to false before the TTL expires (and set true afterward). + \* + \* In TLA+ spec, the TTL is considered constantly expired when the action is taken, so the + \* `rollback_if_not_exist` is assumed true, thus no need to carry it in the message. + \union [start_ts : Ts, caller_start_ts : Ts, primary : KEY, type : {"check_txn_status"}, + resolving_pessimistic_lock : BOOLEAN] RespMessages == - [start_ts : Ts, type : {"prewrited", "locked_key"}, key : KEY] - \union [start_ts : Ts, type : {"lock_failed"}, key : KEY, latest_commit_ts : Ts] + [start_ts : Ts, type : {"prewrited"}, key : KEY] + \union [start_ts : Ts, type : {"get_resp"}, key : KEY, value : Ts, met_optimistic_lock : BOOLEAN] + + \* Conceptually, acquire a pessimistic lock of a key is equivalent to reading its value, + \* and putting the value in the response can reduce communication. Also, as mentioned + \* above, we don’t care about the actual value here, so a timestamp can be used + \* instead of the value. + \union [start_ts : Ts, type : {"locked_key"}, key : KEY, value_ts : Ts] + \union [start_ts : Ts, type : {"lock_failed"}, key : KEY, latest_commit_ts : Ts, + lock_ts : Ts, lock_type : {"no_lock", "lock_key", "prewrite_pessimistic", "prewrite_optimistic"}] \union [start_ts : Ts, type : {"committed", "commit_aborted", "prewrite_aborted", "lock_key_aborted"}] + \union [start_ts : Ts, type : {"check_txn_status_resp"}, + action : {"rollbacked", + "pessimistic_rollbacked", + "committed", + "min_commit_ts_pushed", + "lock_not_exist_do_nothing"}] TypeOK == /\ req_msgs \in SUBSET ReqMessages /\ resp_msgs \in SUBSET RespMessages /\ key_data \in [KEY -> SUBSET Ts] /\ key_lock \in [KEY -> SUBSET [ts : Ts, primary : KEY, + \* As defined above, Ts == Nat \ 0, here we use 0 + \* to indicates that there's no min_commit_ts limit. + min_commit_ts : Ts \union {NoneTs}, type : {"prewrite_optimistic", "prewrite_pessimistic", "lock_key"}]] @@ -131,17 +166,31 @@ TypeOK == /\ req_msgs \in SUBSET ReqMessages /\ key_write \in [KEY -> SUBSET ( [ts : Ts, start_ts : Ts, type : {"write"}] \union [ts : Ts, start_ts : Ts, type : {"rollback"}, protected : BOOLEAN])] - /\ client_state \in [CLIENT -> {"init", "locking", "prewriting", "committing"}] + \* The reading phase only apply for optimistic transactions + /\ client_state \in [CLIENT -> {"init", "locking", "reading", "prewriting", "committing"}] /\ client_ts \in [CLIENT -> [start_ts : Ts \union {NoneTs}, commit_ts : Ts \union {NoneTs}, - for_update_ts : Ts \union {NoneTs}]] + for_update_ts : Ts \union {NoneTs}, + min_commit_ts : Ts \union {NoneTs}]] /\ client_key \in [CLIENT -> [locking: SUBSET KEY, prewriting : SUBSET KEY]] /\ next_ts \in Ts ----------------------------------------------------------------------------- \* Client Actions -ClientLockKey(c) == +ClientReadKey(c) == /\ client_state[c] = "init" + /\ c \in OPTIMISTIC_CLIENT + /\ client_state' = [client_state EXCEPT ![c] = "reading"] + /\ client_ts' = [client_ts EXCEPT ![c].start_ts = next_ts] + /\ next_ts' = next_ts + 1 + /\ SendReqs({[type |-> "get", + start_ts |-> client_ts'[c].start_ts, + primary |-> CLIENT_PRIMARY[c], + key |-> k] : k \in CLIENT_READ_KEY[c]}) + /\ UNCHANGED <> + +ClientLockKey(c) == + /\ client_state[c] = "reading" /\ client_state' = [client_state EXCEPT ![c] = "locking"] /\ client_ts' = [client_ts EXCEPT ![c].start_ts = next_ts, ![c].for_update_ts = next_ts] /\ next_ts' = next_ts + 1 @@ -170,13 +219,32 @@ ClientRetryLockKey(c) == /\ resp.start_ts = client_ts[c].start_ts /\ resp.latest_commit_ts > client_ts[c].for_update_ts /\ client_ts' = [client_ts EXCEPT ![c].for_update_ts = resp.latest_commit_ts] + /\ IF resp.lock_type \in {"lock_key"} + THEN + /\ SendReqs({[type |-> "check_txn_status", + start_ts |-> client_ts[c].start_ts, + caller_start_ts |-> next_ts, + primary |-> CLIENT_PRIMARY[c], + resoving_pessimistic_lock |-> TRUE]}) + /\ next_ts' = next_ts + 1 + /\ UNCHANGED <> + ELSE IF ~ resp.lock_type = "no_lock" + THEN + /\ SendReqs({[type |-> "check_txn_status", + start_ts |-> client_ts[c].start_ts, + caller_start_ts |-> next_ts, + primary |-> CLIENT_PRIMARY[c], + resoving_pessimistic_lock |-> FALSE]}) + /\ next_ts' = next_ts + 1 + /\ UNCHANGED <> + ELSE + /\ UNCHANGED <> /\ SendReqs({[type |-> "lock_key", start_ts |-> client_ts'[c].start_ts, primary |-> CLIENT_PRIMARY[c], key |-> resp.key, for_update_ts |-> client_ts'[c].for_update_ts]}) - /\ UNCHANGED <> - + ClientPrewritePessimistic(c) == /\ client_state[c] = "locking" /\ client_key[c].locking = {} @@ -188,17 +256,28 @@ ClientPrewritePessimistic(c) == key |-> k] : k \in CLIENT_KEY[c]}) /\ UNCHANGED <> +\* Add a function like `ClientRetryReadKey` (?) +ClientCheckTxnStatus(c) == + /\ client_state = "reading" + /\ \E resp \in resp_msgs : + /\ resp.type = "get_resp" + /\ resp.met_optimistic_lock = TRUE + /\ SendReqs({[type |-> "check_txn_status", + start_ts |-> client_ts[c].start_ts, + caller_start_ts |-> next_ts, + primary |-> CLIENT_PRIMARY[c], + resovling_pessimistic_lock |-> FALSE]}) + /\ UNCHANGED <> + ClientPrewriteOptimistic(c) == - /\ client_state[c] = "init" + /\ client_state[c] = "reading" /\ client_state' = [client_state EXCEPT ![c] = "prewriting"] - /\ client_ts' = [client_ts EXCEPT ![c].start_ts = next_ts] - /\ next_ts' = next_ts + 1 /\ client_key' = [client_key EXCEPT ![c].prewriting = CLIENT_KEY[c]] /\ SendReqs({[type |-> "prewrite_optimistic", - start_ts |-> client_ts'[c].start_ts, + start_ts |-> client_ts[c].start_ts, primary |-> CLIENT_PRIMARY[c], key |-> k] : k \in CLIENT_KEY[c]}) - /\ UNCHANGED <> + /\ UNCHANGED <> ClientPrewrited(c) == /\ client_state[c] = "prewriting" @@ -299,6 +378,7 @@ ServerLockKey == \/ /\ ~ \E w \in latest_commit : w.ts > req.for_update_ts /\ key_lock' = [key_lock EXCEPT ![k] = {[ts |-> start_ts, primary |-> req.primary, + min_commit_ts |-> NoneTs, type |-> "lock_key"]}] /\ SendResp([start_ts |-> start_ts, type |-> "locked_key", key |-> k]) /\ UNCHANGED <> @@ -312,6 +392,21 @@ ServerLockKey == latest_commit_ts |-> w.ts]) /\ UNCHANGED <> +ServerReadKey == + \E req \in req_msgs : + /\ req.type = "get" + /\ LET + k == req.key + start_ts == req.start_ts + IN + /\ IF ~ \E l \in key_lock : l.type = "prewrite_optimistic" + THEN + /\ SendResp([start_ts |-> start_ts, type |-> "get_resp", key |-> k, value |-> Ts, met_optimistic_lock |-> FALSE]) \* TS here is not defined now... + /\ UNCHANGED <> + ELSE + /\ SendResp([start_ts |-> start_ts, type |-> "get_resp", key |-> k, value |-> NoneTs, met_optimistic_lock |-> TRUE]) + /\ UNCHANGED <> + ServerPrewritePessimistic == \E req \in req_msgs : /\ req.type = "prewrite_pessimistic" @@ -351,6 +446,7 @@ ServerPrewriteOptimistic == \/ \E l \in key_lock[k] : l.ts = start_ts /\ key_lock' = [key_lock EXCEPT ![k] = {[ts |-> start_ts, primary |-> req.primary, + min_commit_ts |-> NoneTs, type |-> "prewrite_optimistic"]}] /\ key_data' = [key_data EXCEPT ![k] = @ \union {start_ts}] /\ SendResp([start_ts |-> start_ts, type |-> "prewrited", key |-> k]) @@ -389,19 +485,25 @@ ServerCleanupStaleLock == \E l \in key_lock[k] : /\ SendReqs({[type |-> "check_txn_status", start_ts |-> l.ts, + caller_start_ts |-> next_ts, primary |-> l.primary, - resolving_pessimistic_lock |-> l.type = "lock_key" - ]}) - /\ UNCHANGED <> - -\* Clean up stale locks by checking the status of the primary key. It -\* can be divided into two cases: pk_lock exists or not. Note that it is -\* hard to model the TTL in TLA+ spec, so instead, the TTL is considered -\* constantly expired when the action is taken. -\* The client does not care about the resp message, it cares about whether -\* the lock is resolved, and resolving the lock is actually resolve the -\* status of the corresponding transaction. Since, in the TLA+ spec, -\* we ignored the `check_txn_status` response message. + resolving_pessimistic_lock |-> l.type = "lock_key"]}) + /\ next_ts' = next_ts + 1 + /\ UNCHANGED <> + +\* Clean up the stale transaction by checking the status of the primary key. +\* +\* In practice, the transaction will be rolled back if TTL on the lock is expired. But +\* because it is hard to model the TTL in TLA+ spec, the TTL is considered constantly +\* expired when the action is taken. +\* +\* Moreover, TiKV will send a response for `TxnStatus` to the client, and then depending +\* on the `TxnStatus`, the client will send `resolve_rollback` or `resolve_commit` to the +\* secondary keys to clean up stale locks. In the TLA+ spec, the response to `check_txn_status` +\* is omitted and TiKV will directly send `resolve_rollback` or `resolve_commit` message to +\* secondary keys, because the action of client sending resolve message by proxying the +\* `TxnStatus` from TiKV does not change the state of the client, therefore is equal to directly +\* sending resolve message by TiKV ServerCheckTxnStatus == \E req \in req_msgs : /\ req.type = "check_txn_status" @@ -409,10 +511,12 @@ ServerCheckTxnStatus == pk == req.primary start_ts == req.start_ts committed == {w \in key_write[pk] : w.start_ts = start_ts /\ w.type = "write"} + caller_start_ts == req.caller_start_ts IN IF \E lock \in key_lock[pk] : lock.ts = start_ts - \* Found the matching lock whose TTL is expired. + \* Found the matching lock. THEN + \/ IF \* Pessimistic lock will be unlocked directly without rollback record. \E lock \in key_lock[pk] : @@ -421,13 +525,30 @@ ServerCheckTxnStatus == /\ req.resolving_pessimistic_lock = TRUE THEN /\ key_lock' = [key_lock EXCEPT ![pk] = {}] + /\ SendResp({[type |-> "check_txn_status_resp", + start_ts |-> start_ts, + action |-> "pessimistic_rollback"]}) /\ UNCHANGED <> ELSE /\ rollback(pk, start_ts) /\ SendReqs({[type |-> "resolve_rollbacked", start_ts |-> start_ts, primary |-> pk]}) - /\ UNCHANGED <> + /\ SendResp([type |-> "check_txn_status_resp", + start_ts |-> start_ts, + action |-> "rollbacked"]) + /\ UNCHANGED <> + \/ + \* Push min_commit_ts + \E lock \in key_lock[pk] : + /\ key_lock' = [key_lock EXCEPT ![pk] = {[ts |-> lock.ts, + type |-> lock.type, + primary |-> lock.primary, + min_commit_ts |-> caller_start_ts]}] + /\ SendResp([type |-> "check_txn_status_resp", + start_ts |-> start_ts, + action |-> "min_commit_ts_pushed"]) + /\ UNCHANGED <> \* Lock not found or start_ts on the lock mismatches. ELSE IF committed /= {} THEN @@ -435,15 +556,24 @@ ServerCheckTxnStatus == start_ts |-> start_ts, primary |-> pk, commit_ts |-> w.ts] : w \in committed}) - /\ UNCHANGED <> + /\ SendResp([type |-> "check_txn_status_resp", + start_ts |-> start_ts, + action |-> "committed"]) + /\ UNCHANGED <> ELSE IF req.resolving_pessimistic_lock = TRUE THEN - /\ UNCHANGED <> + /\ SendResp({[type |-> "check_txn_status_resp", + start_ts |-> start_ts, + action |-> "lock_not_exist_do_nothing"]}) + /\ UNCHANGED <> ELSE /\ rollback(pk, start_ts) /\ SendReqs({[type |-> "resolve_rollbacked", start_ts |-> start_ts, primary |-> pk]}) - /\ UNCHANGED <> + /\ SendResp([type |-> "check_txn_status_resp", + start_ts |-> start_ts, + action |-> "rollbacked"]) + /\ UNCHANGED <> ServerResolveCommitted == \E req \in req_msgs : @@ -481,17 +611,20 @@ Init == /\ client_key = [c \in CLIENT |-> [locking |-> {}, prewriting |-> {}]] /\ client_ts = [c \in CLIENT |-> [start_ts |-> NoneTs, commit_ts |-> NoneTs, - for_update_ts |-> NoneTs]] + for_update_ts |-> NoneTs, + min_commit_ts |-> NoneTs]] /\ key_lock = [k \in KEY |-> {}] /\ key_data = [k \in KEY |-> {}] /\ key_write = [k \in KEY |-> {}] Next == \/ \E c \in OPTIMISTIC_CLIENT : + \/ ClientReadKey(c) \/ ClientPrewriteOptimistic(c) \/ ClientPrewrited(c) \/ ClientCommit(c) \/ \E c \in PESSIMISTIC_CLIENT : + \/ ClientReadKey(c) \/ ClientLockKey(c) \/ ClientLockedKey(c) \/ ClientRetryLockKey(c) diff --git a/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test1.launch b/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test1.launch index aec9d79..0dbb4b0 100644 --- a/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test1.launch +++ b/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test1.launch @@ -41,7 +41,8 @@ - + + diff --git a/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test2.launch b/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test2.launch index 3e2e636..e9b8857 100644 --- a/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test2.launch +++ b/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test2.launch @@ -5,7 +5,7 @@ - + @@ -29,7 +29,8 @@ - + + diff --git a/DistributedTransaction/Test1.cfg b/DistributedTransaction/Test1.cfg index 93f0ac2..9eb4d4d 100644 --- a/DistributedTransaction/Test1.cfg +++ b/DistributedTransaction/Test1.cfg @@ -6,10 +6,11 @@ CONSTANT c3 = c3 CONSTANT - KEY <- Key + KEY <- Keys OPTIMISTIC_CLIENT <- OptimistiicClient PESSIMISTIC_CLIENT <- PessimisticClient - CLIENT_KEY <- ClientKey + CLIENT_READ_KEY <- ClientReadKeys + CLIENT_WRITE_KEY <- ClientWriteKeys CLIENT_PRIMARY <- ClientPrimary INIT diff --git a/DistributedTransaction/Test1.tla b/DistributedTransaction/Test1.tla index d267bbd..63c9f14 100644 --- a/DistributedTransaction/Test1.tla +++ b/DistributedTransaction/Test1.tla @@ -4,9 +4,10 @@ EXTENDS DistributedTransaction, TLC CONSTANT k1, k2 CONSTANT c1, c2, c3 -Key == {k1, k2} +Keys == {k1, k2} OptimistiicClient == {c3} PessimisticClient == {c1, c2} -ClientKey == c1 :> {k1, k2} @@ c2 :> {k1} @@ c3 :> {k1, k2} +ClientReadKeys == c1 :> {k1, k2} @@ c2 :> {k1} @@ c3 :> {k1, k2} +ClientWriteKeys == c1 :> {k1, k2} @@ c2 :> {k1} @@ c3 :> {k1, k2} ClientPrimary == c1 :> k1 @@ c2 :> k1 @@ c3 :> k2 ================================================================================ diff --git a/DistributedTransaction/Test2.cfg b/DistributedTransaction/Test2.cfg index 1473b5d..623a2f4 100644 --- a/DistributedTransaction/Test2.cfg +++ b/DistributedTransaction/Test2.cfg @@ -7,10 +7,11 @@ CONSTANT c3 = c3 CONSTANT - KEY <- Key + KEY <- Keys OPTIMISTIC_CLIENT <- OptimistiicClient PESSIMISTIC_CLIENT <- PessimisticClient - CLIENT_KEY <- ClientKey + CLIENT_READ_KEY <- ClientReadKeys + CLIENT_WRITE_KEY <- ClientWriteKeys CLIENT_PRIMARY <- ClientPrimary INIT diff --git a/DistributedTransaction/Test2.tla b/DistributedTransaction/Test2.tla index f36a89e..057194f 100644 --- a/DistributedTransaction/Test2.tla +++ b/DistributedTransaction/Test2.tla @@ -4,9 +4,10 @@ EXTENDS DistributedTransaction, TLC CONSTANT k1, k2, k3 CONSTANT c1, c2, c3 -Key == {k1, k2, k3} +Keys == {k1, k2, k3} OptimistiicClient == {c3} PessimisticClient == {c1, c2} -ClientKey == c1 :> {k1, k2, k3} @@ c2 :> {k1, k2} @@ c3 :> {k1, k3} +ClientReadKeys == c1 :> {k1, k2, k3} @@ c2 :> {k1, k2} @@ c3 :> {k1, k3} +ClientWriteKeys == c1 :> {k1, k2, k3} @@ c2 :> {k1, k2} @@ c3 :> {k1, k3} ClientPrimary == c1 :> k1 @@ c2 :> k1 @@ c3 :> k3 ================================================================================ From 913a5c39c657cff46fc960592af6659e1e75640c Mon Sep 17 00:00:00 2001 From: Zhuoran He Date: Wed, 7 Jul 2021 21:13:23 +0800 Subject: [PATCH 04/30] DistributedTransactions: extract unlock_key function DistributedTransactions: avoid client check txn rollback itself DistributedTransactions: add ClientCheckTxnStatus to `Next` DistributedTransactions: amend pessimistic lock DistributedTransactions: add read SI check DistributedTransactions: upd pdf version DistributedTransactions: remove ServerCleanupLock DistributedTransactions: refactor ClientReadFailed CheckTxnStatus DistributedTransactions: upd server lock key DistributedTransactions: fix ClientRetryLockKey bug DistributedTransactions: fix ReadSI def --- .../DistributedTransaction.pdf | Bin 209653 -> 230845 bytes .../DistributedTransaction.tla | 255 ++++++++++-------- 2 files changed, 146 insertions(+), 109 deletions(-) diff --git a/DistributedTransaction/DistributedTransaction.pdf b/DistributedTransaction/DistributedTransaction.pdf index 0e4df88e32683e36fbfe70283f0c8ad61b200cfb..e020e0dcfc670a3d3cb3803a50395a74e8d8d91d 100644 GIT binary patch delta 119352 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

cEZX>SlTdX#&mWo&P@gWx|b?HN-( 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 z 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 delta 98192 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⋘@$qJiUi1E_wGIoRPmTJ(wTvStbj5CgUehz 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 SendReqs(msgs) == req_msgs' = req_msgs \union msgs SendResp(msg) == resp_msgs' = resp_msgs \union {msg} +SendResps(msgs) == resp_msgs' = resp_msgs \union msgs ----------------------------------------------------------------------------- \* Type Definitions @@ -136,9 +137,9 @@ RespMessages == \* and putting the value in the response can reduce communication. Also, as mentioned \* above, we don’t care about the actual value here, so a timestamp can be used \* instead of the value. - \union [start_ts : Ts, type : {"locked_key"}, key : KEY, value_ts : Ts] - \union [start_ts : Ts, type : {"lock_failed"}, key : KEY, latest_commit_ts : Ts, - lock_ts : Ts, lock_type : {"no_lock", "lock_key", "prewrite_pessimistic", "prewrite_optimistic"}] + \union [start_ts : Ts, type : {"locked_key"}, key : KEY, value_ts : Ts \union {NoneTs}] + \union [start_ts : Ts, type : {"lock_failed"}, key : KEY, latest_commit_ts : Ts \union {NoneTs}, + lock_ts : Ts \union {NoneTs}, lock_type : {"no_lock", "lock_key", "prewrite_pessimistic", "prewrite_optimistic"}] \union [start_ts : Ts, type : {"committed", "commit_aborted", "prewrite_aborted", @@ -173,6 +174,7 @@ TypeOK == /\ req_msgs \in SUBSET ReqMessages for_update_ts : Ts \union {NoneTs}, min_commit_ts : Ts \union {NoneTs}]] /\ client_key \in [CLIENT -> [locking: SUBSET KEY, prewriting : SUBSET KEY]] + /\ \A c \in CLIENT: client_key[c].locking \intersect client_key[c].prewriting = {} /\ next_ts \in Ts ----------------------------------------------------------------------------- \* Client Actions @@ -190,7 +192,7 @@ ClientReadKey(c) == /\ UNCHANGED <> ClientLockKey(c) == - /\ client_state[c] = "reading" + /\ client_state[c] = "init" /\ client_state' = [client_state EXCEPT ![c] = "locking"] /\ client_ts' = [client_ts EXCEPT ![c].start_ts = next_ts, ![c].for_update_ts = next_ts] /\ next_ts' = next_ts + 1 @@ -215,35 +217,38 @@ ClientLockedKey(c) == ClientRetryLockKey(c) == /\ client_state[c] = "locking" /\ \E resp \in resp_msgs : - /\ resp.type = "lock_failed" - /\ resp.start_ts = client_ts[c].start_ts - /\ resp.latest_commit_ts > client_ts[c].for_update_ts - /\ client_ts' = [client_ts EXCEPT ![c].for_update_ts = resp.latest_commit_ts] - /\ IF resp.lock_type \in {"lock_key"} - THEN - /\ SendReqs({[type |-> "check_txn_status", - start_ts |-> client_ts[c].start_ts, - caller_start_ts |-> next_ts, - primary |-> CLIENT_PRIMARY[c], - resoving_pessimistic_lock |-> TRUE]}) - /\ next_ts' = next_ts + 1 - /\ UNCHANGED <> - ELSE IF ~ resp.lock_type = "no_lock" - THEN - /\ SendReqs({[type |-> "check_txn_status", - start_ts |-> client_ts[c].start_ts, - caller_start_ts |-> next_ts, - primary |-> CLIENT_PRIMARY[c], - resoving_pessimistic_lock |-> FALSE]}) - /\ next_ts' = next_ts + 1 - /\ UNCHANGED <> - ELSE - /\ UNCHANGED <> - /\ SendReqs({[type |-> "lock_key", - start_ts |-> client_ts'[c].start_ts, - primary |-> CLIENT_PRIMARY[c], - key |-> resp.key, - for_update_ts |-> client_ts'[c].for_update_ts]}) + \/ /\ resp.type = "lock_failed" + /\ resp.start_ts = client_ts[c].start_ts + /\ IF resp.lock_type = "lock_key" /\ ~ resp.lock_ts = client_ts[c].start_ts + THEN + /\ SendReqs({[type |-> "check_txn_status", + start_ts |-> client_ts[c].start_ts, + caller_start_ts |-> next_ts, + primary |-> CLIENT_PRIMARY[c], + resolving_pessimistic_lock |-> TRUE]}) + /\ next_ts' = next_ts + 1 + /\ UNCHANGED <> + ELSE IF ~ resp.lock_type = "no_lock" + THEN + /\ SendReqs({[type |-> "check_txn_status", + start_ts |-> client_ts[c].start_ts, + caller_start_ts |-> next_ts, + primary |-> CLIENT_PRIMARY[c], + resolving_pessimistic_lock |-> FALSE]}) + /\ next_ts' = next_ts + 1 + /\ UNCHANGED <> + ELSE + /\ UNCHANGED <> + \/ /\ resp.type = "lock_failed" + /\ resp.start_ts = client_ts[c].start_ts + /\ resp.latest_commit_ts > client_ts[c].for_update_ts + /\ client_ts' = [client_ts EXCEPT ![c].for_update_ts = resp.latest_commit_ts] + /\ SendReqs({[type |-> "lock_key", + start_ts |-> client_ts'[c].start_ts, + primary |-> CLIENT_PRIMARY[c], + key |-> resp.key, + for_update_ts |-> client_ts'[c].for_update_ts]}) + /\ UNCHANGED <> ClientPrewritePessimistic(c) == /\ client_state[c] = "locking" @@ -256,9 +261,8 @@ ClientPrewritePessimistic(c) == key |-> k] : k \in CLIENT_KEY[c]}) /\ UNCHANGED <> -\* Add a function like `ClientRetryReadKey` (?) -ClientCheckTxnStatus(c) == - /\ client_state = "reading" +ClientReadFailedCheckTxnStatus(c) == + /\ client_state[c] \in {"reading"} /\ \E resp \in resp_msgs : /\ resp.type = "get_resp" /\ resp.met_optimistic_lock = TRUE @@ -304,10 +308,13 @@ ClientCommit(c) == \* Server Actions \* Write the write column and unlock the lock iff the lock exists. +unlock_key(k) == + /\ key_lock' = [key_lock EXCEPT ![k] = {}] + commit(pk, start_ts, commit_ts) == \E l \in key_lock[pk] : /\ l.ts = start_ts - /\ key_lock' = [key_lock EXCEPT ![pk] = {}] + /\ unlock_key(pk) /\ key_write' = [key_write EXCEPT ![pk] = @ \union {[ts |-> commit_ts, type |-> "write", start_ts |-> start_ts]}] @@ -328,11 +335,10 @@ rollback(k, start_ts) == IN \* If a lock exists and has the same ts, unlock it. /\ IF \E l \in key_lock[k] : l.ts = start_ts - THEN key_lock' = [key_lock EXCEPT ![k] = {}] + THEN unlock_key(k) ELSE UNCHANGED key_lock /\ key_data' = [key_data EXCEPT ![k] = @ \ {start_ts}] - /\ IF - /\ ~ \E w \in key_write[k]: w.ts = start_ts + /\ IF ~ \E w \in key_write[k]: w.ts = start_ts THEN key_write' = [key_write EXCEPT ![k] = @@ -354,43 +360,64 @@ ServerLockKey == start_ts == req.start_ts IN \* Pessimistic lock is allowed only if no stale lock exists. If - \* there is one, wait until ServerCleanupStaleLock to clean it up. - /\ key_lock[k] = {} - /\ LET - latest_write == {w \in key_write[k] : \A w2 \in key_write[k] : w.ts >= w2.ts} - - all_commits == {w \in key_write[k] : w.type = "write"} - latest_commit == {w \in all_commits : \A w2 \in all_commits : w.ts >= w2.ts} - IN - IF \E w \in key_write[k] : w.start_ts = start_ts /\ w.type = "rollback" - THEN - \* If corresponding rollback record is found, which - \* indicates that the transcation is rollbacked, abort the - \* transaction. - /\ SendResp([start_ts |-> start_ts, type |-> "lock_key_aborted"]) - /\ UNCHANGED <> - ELSE - \* Acquire pessimistic lock only if for_update_ts of req - \* is greater or equal to the latest "write" record. - \* Because if the latest record is "write", it means that - \* a new version is committed after for_update_ts, which - \* violates Read Committed guarantee. - \/ /\ ~ \E w \in latest_commit : w.ts > req.for_update_ts - /\ key_lock' = [key_lock EXCEPT ![k] = {[ts |-> start_ts, - primary |-> req.primary, - min_commit_ts |-> NoneTs, - type |-> "lock_key"]}] - /\ SendResp([start_ts |-> start_ts, type |-> "locked_key", key |-> k]) - /\ UNCHANGED <> - \* Otherwise, reject the request and let client to retry - \* with new for_update_ts. - \/ \E w \in latest_commit : - /\ w.ts > req.for_update_ts - /\ SendResp([start_ts |-> start_ts, - type |-> "lock_failed", - key |-> k, - latest_commit_ts |-> w.ts]) - /\ UNCHANGED <> + \* there is one, wait until ClientCheckTxnStatus to clean it up. + IF key_lock[k] = {} THEN + /\ LET + latest_write == {w \in key_write[k] : \A w2 \in key_write[k] : w.ts >= w2.ts} + + all_commits == {w \in key_write[k] : w.type = "write"} + latest_commit == {w \in all_commits : \A w2 \in all_commits : w.ts >= w2.ts} + commit_read == {w \in all_commits : \A w2 \in all_commits : w2.ts <= w.ts \/ w2.ts >= start_ts} + IN + IF \E w \in key_write[k] : w.start_ts = start_ts /\ w.type = "rollback" + THEN + \* If corresponding rollback record is found, which + \* indicates that the transcation is rollbacked, abort the + \* transaction. + /\ SendResp([start_ts |-> start_ts, type |-> "lock_key_aborted"]) + /\ UNCHANGED <> + ELSE + \* Acquire pessimistic lock only if for_update_ts of req + \* is greater or equal to the latest "write" record. + \* Because if the latest record is "write", it means that + \* a new version is committed after for_update_ts, which + \* violates Read Committed guarantee. + \/ /\ ~ \E w \in latest_commit : w.ts > req.for_update_ts + /\ key_lock' = [key_lock EXCEPT ![k] = {[ts |-> start_ts, + primary |-> req.primary, + min_commit_ts |-> NoneTs, + type |-> "lock_key"]}] + /\ IF ~ commit_read = {} + THEN + \* Actually there's only one msg to be sent. + /\ SendResps({[start_ts |-> start_ts, type |-> "locked_key", key |-> k, value_ts |-> w2.start_ts] : + w2 \in commit_read}) + /\ UNCHANGED <> + ELSE + /\ SendResp([start_ts |-> start_ts, type |-> "locked_key", key |-> k, value_ts |-> NoneTs]) + /\ UNCHANGED <> + \* Otherwise, reject the request and let client to retry + \* with new for_update_ts. + \/ \E w \in latest_commit : + /\ w.ts > req.for_update_ts + /\ SendResp([start_ts |-> start_ts, + type |-> "lock_failed", + key |-> k, + latest_commit_ts |-> w.ts, + lock_ts |-> NoneTs, + lock_type |-> "no_lock"]) + /\ UNCHANGED <> + ELSE IF \E l \in key_lock[k] : ~ l.ts = start_ts + THEN + /\ SendResps({[start_ts |-> start_ts, + type |-> "lock_failed", + key |-> k, + latest_commit_ts |-> NoneTs, + lock_ts |-> l.ts, + lock_type |-> l.type] : l \in key_lock[k]}) + /\ UNCHANGED <> + ELSE + /\ UNCHANGED <> ServerReadKey == \E req \in req_msgs : @@ -414,19 +441,22 @@ ServerPrewritePessimistic == k == req.key start_ts == req.start_ts IN - \* Pessimistic prewrite is allowed only if pressimistic lock is - \* acquired, otherwise abort the transaction. + \* Pessimistic prewrite is allowed if pressimistic lock is + \* acquired, or, there's no lock, and no write record whose + \* commit_ts >= start_ts otherwise abort the transaction. /\ IF \E l \in key_lock[k] : l.ts = start_ts + \/ ~ \E w \in key_write[k] : w.ts >= start_ts THEN - /\ key_lock' = [key_lock EXCEPT ![k] = {[ts |-> start_ts, + /\ key_lock' = [key_lock EXCEPT ![k] = {[ts |-> start_ts, primary |-> req.primary, - type |-> "prewrite_pessimistic"]}] - /\ key_data' = [key_data EXCEPT ![k] = @ \union {start_ts}] - /\ SendResp([start_ts |-> start_ts, type |-> "prewrited", key |-> k]) - /\ UNCHANGED <> + type |-> "prewrite_pessimistic", + min_commit_ts |-> NoneTs]}] + /\ key_data' = [key_data EXCEPT ![k] = @ \union {start_ts}] + /\ SendResp([start_ts |-> start_ts, type |-> "prewrited", key |-> k]) + /\ UNCHANGED <> ELSE - /\ SendResp([start_ts |-> start_ts, type |-> "prewrite_aborted"]) - /\ UNCHANGED <> + /\ SendResp([start_ts |-> start_ts, type |-> "prewrite_aborted"]) + /\ UNCHANGED <> ServerPrewriteOptimistic == \E req \in req_msgs : @@ -441,7 +471,7 @@ ServerPrewriteOptimistic == /\ UNCHANGED <> ELSE \* Optimistic prewrite is allowed only if no stale lock exists. If - \* there is one, wait until ServerCleanupStaleLock to clean it up. + \* there is one, wait until ClientCheckTxnStatus to clean it up. /\ \/ key_lock[k] = {} \/ \E l \in key_lock[k] : l.ts = start_ts /\ key_lock' = [key_lock EXCEPT ![k] = {[ts |-> start_ts, @@ -476,21 +506,6 @@ ServerCommit == /\ SendResp([start_ts |-> start_ts, type |-> "commit_aborted"]) /\ UNCHANGED <> -\* In the spec, the primary key with a lock may clean up itself -\* spontaneously. There is no need to model a client to request clean up -\* because there is no difference between a optimistic client trying to -\* read a key that has lock timeouted and the key trying to unlock itself. -ServerCleanupStaleLock == - \E k \in KEY : - \E l \in key_lock[k] : - /\ SendReqs({[type |-> "check_txn_status", - start_ts |-> l.ts, - caller_start_ts |-> next_ts, - primary |-> l.primary, - resolving_pessimistic_lock |-> l.type = "lock_key"]}) - /\ next_ts' = next_ts + 1 - /\ UNCHANGED <> - \* Clean up the stale transaction by checking the status of the primary key. \* \* In practice, the transaction will be rolled back if TTL on the lock is expired. But @@ -524,11 +539,11 @@ ServerCheckTxnStatus == /\ lock.type = "lock_key" /\ req.resolving_pessimistic_lock = TRUE THEN - /\ key_lock' = [key_lock EXCEPT ![pk] = {}] - /\ SendResp({[type |-> "check_txn_status_resp", + /\ unlock_key(pk) + /\ SendResp([type |-> "check_txn_status_resp", start_ts |-> start_ts, - action |-> "pessimistic_rollback"]}) - /\ UNCHANGED <> + action |-> "pessimistic_rollbacked"]) + /\ UNCHANGED <> ELSE /\ rollback(pk, start_ts) /\ SendReqs({[type |-> "resolve_rollbacked", @@ -561,9 +576,9 @@ ServerCheckTxnStatus == action |-> "committed"]) /\ UNCHANGED <> ELSE IF req.resolving_pessimistic_lock = TRUE THEN - /\ SendResp({[type |-> "check_txn_status_resp", + /\ SendResp([type |-> "check_txn_status_resp", start_ts |-> start_ts, - action |-> "lock_not_exist_do_nothing"]}) + action |-> "lock_not_exist_do_nothing"]) /\ UNCHANGED <> ELSE /\ rollback(pk, start_ts) @@ -620,6 +635,7 @@ Init == Next == \/ \E c \in OPTIMISTIC_CLIENT : \/ ClientReadKey(c) + \/ ClientReadFailedCheckTxnStatus(c) \/ ClientPrewriteOptimistic(c) \/ ClientPrewrited(c) \/ ClientCommit(c) @@ -635,7 +651,6 @@ Next == \/ ServerPrewritePessimistic \/ ServerPrewriteOptimistic \/ ServerCommit - \/ ServerCleanupStaleLock \/ ServerCheckTxnStatus \/ ServerResolveCommitted \/ ServerResolveRollbacked @@ -726,6 +741,26 @@ MsgTsConsistency == req.commit_ts <= next_ts /\ \A resp \in resp_msgs : resp.start_ts <= next_ts +ReadSnapshotIsolation == + /\ \A resp \in resp_msgs : + /\ resp.type = "get_resp" + /\ LET + start_ts == resp.start_ts + key == resp.key + \* As mentioned before, the value is just a timestamp + value == resp.value + met_optimistic_lock == resp.met_optimistic_lock + IN + /\ \E c \in CLIENT: + /\ client_ts[c].start_ts = start_ts + /\ IF client_ts[c].commit_ts \in Ts THEN + /\ \A w \in key_write[key] : + \/ w.type = "rollback" + \/ w.start_ts > start_ts + \/ w.ts < start_ts + ELSE + /\ TRUE + \* SnapshotIsolation is implied from the following assumptions (but is not \* necessary) because SnapshotIsolation means that: \* (1) Once a transaction is committed, all keys of the transaction should @@ -739,11 +774,13 @@ MsgTsConsistency == \* PROOF BY NextTsConsistency, MsgTsConsistency \* (3) All aborted transactions would be always not readable. \* PROOF BY AbortConsistency, MsgMonotonicity +\* TODO: Explain the ReadSnapshotIsolation SnapshotIsolation == /\ CommitConsistency /\ AbortConsistency /\ NextTsMonotonicity /\ MsgMonotonicity /\ MsgTsConsistency + /\ ReadSnapshotIsolation ----------------------------------------------------------------------------- THEOREM Safety == Spec => [](/\ TypeOK From 2b21d1402040354f971d303357fba2bb49f86fb8 Mon Sep 17 00:00:00 2001 From: Zhuoran He Date: Sun, 11 Jul 2021 03:13:09 +0800 Subject: [PATCH 05/30] DistributedTransactions: upd max read times DistributedTransactions: upd max read times DistributedTransactions: fix bug DistributedTransactions: upd DistributedTransactions: refactor, upd some comments DistributedTransactions: refactor, upd --- .../DistributedTransaction.tla | 140 +++++++++++------- .../DistributedTransaction___Test1.launch | 9 +- .../DistributedTransaction___Test2.launch | 1 + DistributedTransaction/Test1.cfg | 1 + DistributedTransaction/Test2.cfg | 1 + 5 files changed, 94 insertions(+), 58 deletions(-) diff --git a/DistributedTransaction/DistributedTransaction.tla b/DistributedTransaction/DistributedTransaction.tla index 6798be5..0581d46 100644 --- a/DistributedTransaction/DistributedTransaction.tla +++ b/DistributedTransaction/DistributedTransaction.tla @@ -14,6 +14,8 @@ CONSTANTS CLIENT_READ_KEY, CLIENT_WRITE_KEY CLIENT_KEY == [c \in CLIENT |-> CLIENT_READ_KEY[c] \union CLIENT_WRITE_KEY[c]] ASSUME \A c \in CLIENT: CLIENT_KEY[c] \subseteq KEY +CONSTANTS MAX_CLIENT_READ_TIMES + \* CLIENT_PRIMARY is the primary key of each client. CONSTANTS CLIENT_PRIMARY ASSUME \A c \in CLIENT: CLIENT_PRIMARY[c] \in CLIENT_KEY[c] @@ -83,6 +85,11 @@ VARIABLES client_ts \* haven't been acquired, "prewriting" denotes the keys that are pending \* for prewrite. VARIABLES client_key +\* client_key_read_times[c][k] is a number representing the number +\* of times the client c have read the value of key k. +\* We limit the read-times not exceeds MAX_CLIENT_READ_TIMES to reduce +\* final TLA+ states, this variable does not exist in real world TiKV. +VARIABLES client_key_read_times \* next_ts is a globally monotonically increasing integer, representing \* the virtual clock of transactions. In practice, the variable is @@ -90,10 +97,11 @@ VARIABLES client_key VARIABLES next_ts msg_vars == <> -client_vars == <> +client_vars == <> key_vars == <> vars == <> +SendReq(msg) == req_msgs' = req_msgs \union {msg} SendReqs(msgs) == req_msgs' = req_msgs \union msgs SendResp(msg) == resp_msgs' = resp_msgs \union {msg} SendResps(msgs) == resp_msgs' = resp_msgs \union msgs @@ -131,7 +139,9 @@ ReqMessages == RespMessages == [start_ts : Ts, type : {"prewrited"}, key : KEY] - \union [start_ts : Ts, type : {"get_resp"}, key : KEY, value : Ts, met_optimistic_lock : BOOLEAN] + \* We use the value NoneTs to denotes the key not exists. + \* This convention applies for many definations below. + \union [start_ts : Ts, type : {"get_resp"}, key : KEY, value_ts : Ts \union {NoneTs}, met_optimistic_lock : BOOLEAN] \* Conceptually, acquire a pessimistic lock of a key is equivalent to reading its value, \* and putting the value in the response can reduce communication. Also, as mentioned @@ -168,42 +178,41 @@ TypeOK == /\ req_msgs \in SUBSET ReqMessages [ts : Ts, start_ts : Ts, type : {"write"}] \union [ts : Ts, start_ts : Ts, type : {"rollback"}, protected : BOOLEAN])] \* The reading phase only apply for optimistic transactions - /\ client_state \in [CLIENT -> {"init", "locking", "reading", "prewriting", "committing"}] + /\ client_state \in [CLIENT -> {"init", "locking", "prewriting", "committing"}] /\ client_ts \in [CLIENT -> [start_ts : Ts \union {NoneTs}, commit_ts : Ts \union {NoneTs}, for_update_ts : Ts \union {NoneTs}, min_commit_ts : Ts \union {NoneTs}]] /\ client_key \in [CLIENT -> [locking: SUBSET KEY, prewriting : SUBSET KEY]] /\ \A c \in CLIENT: client_key[c].locking \intersect client_key[c].prewriting = {} + /\ client_key_read_times \in [CLIENT -> [KEY -> 0..MAX_CLIENT_READ_TIMES]] /\ next_ts \in Ts ----------------------------------------------------------------------------- \* Client Actions -ClientReadKey(c) == - /\ client_state[c] = "init" - /\ c \in OPTIMISTIC_CLIENT - /\ client_state' = [client_state EXCEPT ![c] = "reading"] - /\ client_ts' = [client_ts EXCEPT ![c].start_ts = next_ts] - /\ next_ts' = next_ts + 1 - /\ SendReqs({[type |-> "get", - start_ts |-> client_ts'[c].start_ts, - primary |-> CLIENT_PRIMARY[c], - key |-> k] : k \in CLIENT_READ_KEY[c]}) - /\ UNCHANGED <> +\* Once the get request is sent, it exist permanently in the req_msgs, +\* so we have to limit the read times in server side. +ClientReadKey(c, k) == + /\ ~ client_state[c] = "init" + /\ SendReq([type |-> "get", + start_ts |-> client_ts[c].start_ts, + primary |-> CLIENT_PRIMARY[c], + key |-> k]) + /\ UNCHANGED <> ClientLockKey(c) == /\ client_state[c] = "init" /\ client_state' = [client_state EXCEPT ![c] = "locking"] /\ client_ts' = [client_ts EXCEPT ![c].start_ts = next_ts, ![c].for_update_ts = next_ts] /\ next_ts' = next_ts + 1 - \* Assume we need to acquire pessimistic locks for all keys /\ client_key' = [client_key EXCEPT ![c].locking = CLIENT_KEY[c]] + \* Assume we need to acquire pessimistic locks for all keys /\ SendReqs({[type |-> "lock_key", start_ts |-> client_ts'[c].start_ts, primary |-> CLIENT_PRIMARY[c], key |-> k, for_update_ts |-> client_ts'[c].for_update_ts] : k \in CLIENT_KEY[c]}) - /\ UNCHANGED <> + /\ UNCHANGED <> ClientLockedKey(c) == /\ client_state[c] = "locking" @@ -212,7 +221,7 @@ ClientLockedKey(c) == /\ resp.start_ts = client_ts[c].start_ts /\ resp.key \in client_key[c].locking /\ client_key' = [client_key EXCEPT ![c].locking = @ \ {resp.key}] - /\ UNCHANGED <> + /\ UNCHANGED <> ClientRetryLockKey(c) == /\ client_state[c] = "locking" @@ -228,8 +237,8 @@ ClientRetryLockKey(c) == resolving_pessimistic_lock |-> TRUE]}) /\ next_ts' = next_ts + 1 /\ UNCHANGED <> - ELSE IF ~ resp.lock_type = "no_lock" - THEN + ELSE + /\ ~ resp.lock_type = "no_lock" /\ SendReqs({[type |-> "check_txn_status", start_ts |-> client_ts[c].start_ts, caller_start_ts |-> next_ts, @@ -237,8 +246,6 @@ ClientRetryLockKey(c) == resolving_pessimistic_lock |-> FALSE]}) /\ next_ts' = next_ts + 1 /\ UNCHANGED <> - ELSE - /\ UNCHANGED <> \/ /\ resp.type = "lock_failed" /\ resp.start_ts = client_ts[c].start_ts /\ resp.latest_commit_ts > client_ts[c].for_update_ts @@ -248,7 +255,7 @@ ClientRetryLockKey(c) == primary |-> CLIENT_PRIMARY[c], key |-> resp.key, for_update_ts |-> client_ts'[c].for_update_ts]}) - /\ UNCHANGED <> + /\ UNCHANGED <> ClientPrewritePessimistic(c) == /\ client_state[c] = "locking" @@ -259,10 +266,9 @@ ClientPrewritePessimistic(c) == start_ts |-> client_ts[c].start_ts, primary |-> CLIENT_PRIMARY[c], key |-> k] : k \in CLIENT_KEY[c]}) - /\ UNCHANGED <> + /\ UNCHANGED <> ClientReadFailedCheckTxnStatus(c) == - /\ client_state[c] \in {"reading"} /\ \E resp \in resp_msgs : /\ resp.type = "get_resp" /\ resp.met_optimistic_lock = TRUE @@ -270,18 +276,21 @@ ClientReadFailedCheckTxnStatus(c) == start_ts |-> client_ts[c].start_ts, caller_start_ts |-> next_ts, primary |-> CLIENT_PRIMARY[c], - resovling_pessimistic_lock |-> FALSE]}) - /\ UNCHANGED <> + resolving_pessimistic_lock |-> FALSE]}) + /\ next_ts' = next_ts + 1 + /\ UNCHANGED <> ClientPrewriteOptimistic(c) == - /\ client_state[c] = "reading" + /\ client_state[c] = "init" /\ client_state' = [client_state EXCEPT ![c] = "prewriting"] - /\ client_key' = [client_key EXCEPT ![c].prewriting = CLIENT_KEY[c]] + /\ client_key' = [client_key EXCEPT ![c].prewriting = CLIENT_WRITE_KEY[c]] + /\ client_ts' = [client_ts EXCEPT ![c].start_ts = next_ts] + /\ next_ts' = next_ts + 1 /\ SendReqs({[type |-> "prewrite_optimistic", - start_ts |-> client_ts[c].start_ts, + start_ts |-> client_ts'[c].start_ts, primary |-> CLIENT_PRIMARY[c], - key |-> k] : k \in CLIENT_KEY[c]}) - /\ UNCHANGED <> + key |-> k] : k \in CLIENT_WRITE_KEY[c]}) + /\ UNCHANGED <> ClientPrewrited(c) == /\ client_state[c] = "prewriting" @@ -291,7 +300,7 @@ ClientPrewrited(c) == /\ resp.start_ts = client_ts[c].start_ts /\ resp.key \in client_key[c].prewriting /\ client_key' = [client_key EXCEPT ![c].prewriting = @ \ {resp.key}] - /\ UNCHANGED <> + /\ UNCHANGED <> ClientCommit(c) == /\ client_state[c] = "prewriting" @@ -303,7 +312,7 @@ ClientCommit(c) == start_ts |-> client_ts'[c].start_ts, primary |-> CLIENT_PRIMARY[c], commit_ts |-> client_ts'[c].commit_ts]}) - /\ UNCHANGED <> + /\ UNCHANGED <> ----------------------------------------------------------------------------- \* Server Actions @@ -343,7 +352,7 @@ rollback(k, start_ts) == key_write' = [key_write EXCEPT ![k] = \* collapse rollback - (@ \ {w \in @ : w.type = "rollback" /\ ~w.protected /\ w.ts < start_ts}) + (@ \ {w \in @ : w.type = "rollback" /\ ~ w.protected /\ w.ts < start_ts}) \* write rollback record \union {[ts |-> start_ts, start_ts |-> start_ts, @@ -390,11 +399,16 @@ ServerLockKey == /\ IF ~ commit_read = {} THEN \* Actually there's only one msg to be sent. - /\ SendResps({[start_ts |-> start_ts, type |-> "locked_key", key |-> k, value_ts |-> w2.start_ts] : - w2 \in commit_read}) + /\ SendResps({[start_ts |-> start_ts, + type |-> "locked_key", + key |-> k, + value_ts |-> w2.start_ts] : w2 \in commit_read}) /\ UNCHANGED <> ELSE - /\ SendResp([start_ts |-> start_ts, type |-> "locked_key", key |-> k, value_ts |-> NoneTs]) + /\ SendResp([start_ts |-> start_ts, + type |-> "locked_key", + key |-> k, + value_ts |-> NoneTs]) /\ UNCHANGED <> \* Otherwise, reject the request and let client to retry \* with new for_update_ts. @@ -407,8 +421,8 @@ ServerLockKey == lock_ts |-> NoneTs, lock_type |-> "no_lock"]) /\ UNCHANGED <> - ELSE IF \E l \in key_lock[k] : ~ l.ts = start_ts - THEN + ELSE + /\ \E l \in key_lock[k] : ~ l.ts = start_ts /\ SendResps({[start_ts |-> start_ts, type |-> "lock_failed", key |-> k, @@ -416,23 +430,38 @@ ServerLockKey == lock_ts |-> l.ts, lock_type |-> l.type] : l \in key_lock[k]}) /\ UNCHANGED <> - ELSE - /\ UNCHANGED <> ServerReadKey == \E req \in req_msgs : /\ req.type = "get" - /\ LET - k == req.key - start_ts == req.start_ts - IN - /\ IF ~ \E l \in key_lock : l.type = "prewrite_optimistic" - THEN - /\ SendResp([start_ts |-> start_ts, type |-> "get_resp", key |-> k, value |-> Ts, met_optimistic_lock |-> FALSE]) \* TS here is not defined now... - /\ UNCHANGED <> - ELSE - /\ SendResp([start_ts |-> start_ts, type |-> "get_resp", key |-> k, value |-> NoneTs, met_optimistic_lock |-> TRUE]) - /\ UNCHANGED <> + /\ \E c \in CLIENT : + /\ client_ts[c].start_ts = req.start_ts + /\ client_key_read_times[c][req.key] < MAX_CLIENT_READ_TIMES + /\ client_key_read_times' = [client_key_read_times + EXCEPT ![c] = [client_key_read_times[c] + EXCEPT ![req.key] = client_key_read_times[c][req.key] + 1]] + /\ LET + k == req.key + start_ts == req.start_ts + + all_commits == {w \in key_write[k] : w.type = "write"} + commit_read == {w \in all_commits : \A w2 \in all_commits : w2.ts <= w.ts \/ w2.ts >= start_ts} + IN + /\ IF ~ \E l \in key_lock[k] : l.type = "prewrite_optimistic" + THEN + /\ SendResps({[start_ts |-> start_ts, + type |-> "get_resp", + key |-> k, + value_ts |-> w.start_ts, + met_optimistic_lock |-> FALSE] : w \in commit_read}) + /\ UNCHANGED <> + ELSE + /\ SendResp([start_ts |-> start_ts, + type |-> "get_resp", + key |-> k, + value_ts |-> NoneTs, + met_optimistic_lock |-> TRUE]) + /\ UNCHANGED <> ServerPrewritePessimistic == \E req \in req_msgs : @@ -628,25 +657,28 @@ Init == commit_ts |-> NoneTs, for_update_ts |-> NoneTs, min_commit_ts |-> NoneTs]] + /\ client_key_read_times = [c \in CLIENT |-> [k \in KEY |-> 0]] /\ key_lock = [k \in KEY |-> {}] /\ key_data = [k \in KEY |-> {}] /\ key_write = [k \in KEY |-> {}] Next == \/ \E c \in OPTIMISTIC_CLIENT : - \/ ClientReadKey(c) \/ ClientReadFailedCheckTxnStatus(c) \/ ClientPrewriteOptimistic(c) \/ ClientPrewrited(c) \/ ClientCommit(c) \/ \E c \in PESSIMISTIC_CLIENT : - \/ ClientReadKey(c) \/ ClientLockKey(c) \/ ClientLockedKey(c) \/ ClientRetryLockKey(c) \/ ClientPrewritePessimistic(c) \/ ClientPrewrited(c) \/ ClientCommit(c) + \/ \E c \in CLIENT : + \E k \in CLIENT_READ_KEY[c] : + /\ ClientReadKey(c, k) + \/ ServerReadKey \/ ServerLockKey \/ ServerPrewritePessimistic \/ ServerPrewriteOptimistic diff --git a/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test1.launch b/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test1.launch index 0dbb4b0..6bb8e55 100644 --- a/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test1.launch +++ b/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test1.launch @@ -7,12 +7,12 @@ - + - + @@ -21,7 +21,7 @@ - + @@ -46,9 +46,10 @@ + - + diff --git a/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test2.launch b/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test2.launch index e9b8857..8b6340a 100644 --- a/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test2.launch +++ b/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test2.launch @@ -34,6 +34,7 @@ + diff --git a/DistributedTransaction/Test1.cfg b/DistributedTransaction/Test1.cfg index 9eb4d4d..c59a4db 100644 --- a/DistributedTransaction/Test1.cfg +++ b/DistributedTransaction/Test1.cfg @@ -12,6 +12,7 @@ CONSTANT CLIENT_READ_KEY <- ClientReadKeys CLIENT_WRITE_KEY <- ClientWriteKeys CLIENT_PRIMARY <- ClientPrimary + MAX_CLIENT_READ_TIMES = 3 INIT Init diff --git a/DistributedTransaction/Test2.cfg b/DistributedTransaction/Test2.cfg index 623a2f4..4f6eae5 100644 --- a/DistributedTransaction/Test2.cfg +++ b/DistributedTransaction/Test2.cfg @@ -13,6 +13,7 @@ CONSTANT CLIENT_READ_KEY <- ClientReadKeys CLIENT_WRITE_KEY <- ClientWriteKeys CLIENT_PRIMARY <- ClientPrimary + MAX_CLIENT_READ_TIMES = 3 INIT Init From 07caa8d9a0a7124ecc7867a3d6f0a536997348da Mon Sep 17 00:00:00 2001 From: Zhuoran He Date: Sun, 11 Jul 2021 19:00:35 +0800 Subject: [PATCH 06/30] DistributedTransaction: Add max client check txn times DistributedTransaction: upd lock min_commit_ts check DistributedTransaction: rename wrong name to current_ts DistributedTransaction: reformat some code --- .../DistributedTransaction.tla | 120 +++++++++++------- .../DistributedTransaction___Test1.launch | 8 +- .../DistributedTransaction___Test2.launch | 1 + DistributedTransaction/Test1.cfg | 1 + DistributedTransaction/Test2.cfg | 1 + 5 files changed, 86 insertions(+), 45 deletions(-) diff --git a/DistributedTransaction/DistributedTransaction.tla b/DistributedTransaction/DistributedTransaction.tla index 0581d46..0d2bd3b 100644 --- a/DistributedTransaction/DistributedTransaction.tla +++ b/DistributedTransaction/DistributedTransaction.tla @@ -20,6 +20,8 @@ CONSTANTS MAX_CLIENT_READ_TIMES CONSTANTS CLIENT_PRIMARY ASSUME \A c \in CLIENT: CLIENT_PRIMARY[c] \in CLIENT_KEY[c] +CONSTANTS MAX_CLIENT_CHECK_TXN_TIMES + \* Timestamp of transactions. Ts == Nat \ {0} NoneTs == 0 @@ -90,6 +92,11 @@ VARIABLES client_key \* We limit the read-times not exceeds MAX_CLIENT_READ_TIMES to reduce \* final TLA+ states, this variable does not exist in real world TiKV. VARIABLES client_key_read_times +\* client_check_txn_times[c] is a number representing the number +\* of times the txn of client c have been checked(check_txn_status). +\* We limit the check-times not exceeeds MAX_CLIENT_CHECK_TXN_TIMES to +\* reduce final TLA+ states, this variable does not exist in real world TiKV. +VARIABLES client_check_txn_times \* next_ts is a globally monotonically increasing integer, representing \* the virtual clock of transactions. In practice, the variable is @@ -97,7 +104,8 @@ VARIABLES client_key_read_times VARIABLES next_ts msg_vars == <> -client_vars == <> +client_vars == <> key_vars == <> vars == <> @@ -134,7 +142,7 @@ ReqMessages == \* \* In TLA+ spec, the TTL is considered constantly expired when the action is taken, so the \* `rollback_if_not_exist` is assumed true, thus no need to carry it in the message. - \union [start_ts : Ts, caller_start_ts : Ts, primary : KEY, type : {"check_txn_status"}, + \union [start_ts : Ts, current_ts : Ts, primary : KEY, type : {"check_txn_status"}, resolving_pessimistic_lock : BOOLEAN] RespMessages == @@ -149,7 +157,10 @@ RespMessages == \* instead of the value. \union [start_ts : Ts, type : {"locked_key"}, key : KEY, value_ts : Ts \union {NoneTs}] \union [start_ts : Ts, type : {"lock_failed"}, key : KEY, latest_commit_ts : Ts \union {NoneTs}, - lock_ts : Ts \union {NoneTs}, lock_type : {"no_lock", "lock_key", "prewrite_pessimistic", "prewrite_optimistic"}] + lock_ts : Ts \union {NoneTs}, lock_type : {"no_lock", + "lock_key", + "prewrite_pessimistic", + "prewrite_optimistic"}] \union [start_ts : Ts, type : {"committed", "commit_aborted", "prewrite_aborted", @@ -186,6 +197,7 @@ TypeOK == /\ req_msgs \in SUBSET ReqMessages /\ client_key \in [CLIENT -> [locking: SUBSET KEY, prewriting : SUBSET KEY]] /\ \A c \in CLIENT: client_key[c].locking \intersect client_key[c].prewriting = {} /\ client_key_read_times \in [CLIENT -> [KEY -> 0..MAX_CLIENT_READ_TIMES]] + /\ client_check_txn_times \in [CLIENT -> 0..MAX_CLIENT_CHECK_TXN_TIMES] /\ next_ts \in Ts ----------------------------------------------------------------------------- \* Client Actions @@ -195,9 +207,9 @@ TypeOK == /\ req_msgs \in SUBSET ReqMessages ClientReadKey(c, k) == /\ ~ client_state[c] = "init" /\ SendReq([type |-> "get", - start_ts |-> client_ts[c].start_ts, - primary |-> CLIENT_PRIMARY[c], - key |-> k]) + start_ts |-> client_ts[c].start_ts, + primary |-> CLIENT_PRIMARY[c], + key |-> k]) /\ UNCHANGED <> ClientLockKey(c) == @@ -212,7 +224,7 @@ ClientLockKey(c) == primary |-> CLIENT_PRIMARY[c], key |-> k, for_update_ts |-> client_ts'[c].for_update_ts] : k \in CLIENT_KEY[c]}) - /\ UNCHANGED <> + /\ UNCHANGED <> ClientLockedKey(c) == /\ client_state[c] = "locking" @@ -221,7 +233,8 @@ ClientLockedKey(c) == /\ resp.start_ts = client_ts[c].start_ts /\ resp.key \in client_key[c].locking /\ client_key' = [client_key EXCEPT ![c].locking = @ \ {resp.key}] - /\ UNCHANGED <> + /\ UNCHANGED <> ClientRetryLockKey(c) == /\ client_state[c] = "locking" @@ -230,22 +243,28 @@ ClientRetryLockKey(c) == /\ resp.start_ts = client_ts[c].start_ts /\ IF resp.lock_type = "lock_key" /\ ~ resp.lock_ts = client_ts[c].start_ts THEN + /\ client_check_txn_times[c] < MAX_CLIENT_CHECK_TXN_TIMES + /\ client_check_txn_times' = [client_check_txn_times EXCEPT ![c] = @ + 1] /\ SendReqs({[type |-> "check_txn_status", start_ts |-> client_ts[c].start_ts, - caller_start_ts |-> next_ts, + current_ts |-> next_ts, primary |-> CLIENT_PRIMARY[c], resolving_pessimistic_lock |-> TRUE]}) /\ next_ts' = next_ts + 1 - /\ UNCHANGED <> + /\ UNCHANGED <> ELSE /\ ~ resp.lock_type = "no_lock" + /\ client_check_txn_times[c] < MAX_CLIENT_CHECK_TXN_TIMES + /\ client_check_txn_times' = [client_check_txn_times EXCEPT ![c] = @ + 1] /\ SendReqs({[type |-> "check_txn_status", start_ts |-> client_ts[c].start_ts, - caller_start_ts |-> next_ts, + current_ts |-> next_ts, primary |-> CLIENT_PRIMARY[c], resolving_pessimistic_lock |-> FALSE]}) /\ next_ts' = next_ts + 1 - /\ UNCHANGED <> + /\ UNCHANGED <> \/ /\ resp.type = "lock_failed" /\ resp.start_ts = client_ts[c].start_ts /\ resp.latest_commit_ts > client_ts[c].for_update_ts @@ -255,7 +274,8 @@ ClientRetryLockKey(c) == primary |-> CLIENT_PRIMARY[c], key |-> resp.key, for_update_ts |-> client_ts'[c].for_update_ts]}) - /\ UNCHANGED <> + /\ UNCHANGED <> ClientPrewritePessimistic(c) == /\ client_state[c] = "locking" @@ -266,19 +286,23 @@ ClientPrewritePessimistic(c) == start_ts |-> client_ts[c].start_ts, primary |-> CLIENT_PRIMARY[c], key |-> k] : k \in CLIENT_KEY[c]}) - /\ UNCHANGED <> + /\ UNCHANGED <> ClientReadFailedCheckTxnStatus(c) == /\ \E resp \in resp_msgs : /\ resp.type = "get_resp" /\ resp.met_optimistic_lock = TRUE + /\ client_check_txn_times[c] < MAX_CLIENT_CHECK_TXN_TIMES + /\ client_check_txn_times' = [client_check_txn_times EXCEPT ![c] = @ + 1] /\ SendReqs({[type |-> "check_txn_status", start_ts |-> client_ts[c].start_ts, - caller_start_ts |-> next_ts, + current_ts |-> next_ts, primary |-> CLIENT_PRIMARY[c], resolving_pessimistic_lock |-> FALSE]}) /\ next_ts' = next_ts + 1 - /\ UNCHANGED <> + /\ UNCHANGED <> ClientPrewriteOptimistic(c) == /\ client_state[c] = "init" @@ -290,7 +314,7 @@ ClientPrewriteOptimistic(c) == start_ts |-> client_ts'[c].start_ts, primary |-> CLIENT_PRIMARY[c], key |-> k] : k \in CLIENT_WRITE_KEY[c]}) - /\ UNCHANGED <> + /\ UNCHANGED <> ClientPrewrited(c) == /\ client_state[c] = "prewriting" @@ -300,7 +324,8 @@ ClientPrewrited(c) == /\ resp.start_ts = client_ts[c].start_ts /\ resp.key \in client_key[c].prewriting /\ client_key' = [client_key EXCEPT ![c].prewriting = @ \ {resp.key}] - /\ UNCHANGED <> + /\ UNCHANGED <> ClientCommit(c) == /\ client_state[c] = "prewriting" @@ -309,10 +334,10 @@ ClientCommit(c) == /\ client_ts' = [client_ts EXCEPT ![c].commit_ts = next_ts] /\ next_ts' = next_ts + 1 /\ SendReqs({[type |-> "commit", - start_ts |-> client_ts'[c].start_ts, - primary |-> CLIENT_PRIMARY[c], - commit_ts |-> client_ts'[c].commit_ts]}) - /\ UNCHANGED <> + start_ts |-> client_ts'[c].start_ts, + primary |-> CLIENT_PRIMARY[c], + commit_ts |-> client_ts'[c].commit_ts]}) + /\ UNCHANGED <> ----------------------------------------------------------------------------- \* Server Actions @@ -415,20 +440,20 @@ ServerLockKey == \/ \E w \in latest_commit : /\ w.ts > req.for_update_ts /\ SendResp([start_ts |-> start_ts, - type |-> "lock_failed", - key |-> k, - latest_commit_ts |-> w.ts, - lock_ts |-> NoneTs, - lock_type |-> "no_lock"]) + type |-> "lock_failed", + key |-> k, + latest_commit_ts |-> w.ts, + lock_ts |-> NoneTs, + lock_type |-> "no_lock"]) /\ UNCHANGED <> ELSE /\ \E l \in key_lock[k] : ~ l.ts = start_ts /\ SendResps({[start_ts |-> start_ts, - type |-> "lock_failed", - key |-> k, - latest_commit_ts |-> NoneTs, - lock_ts |-> l.ts, - lock_type |-> l.type] : l \in key_lock[k]}) + type |-> "lock_failed", + key |-> k, + latest_commit_ts |-> NoneTs, + lock_ts |-> l.ts, + lock_type |-> l.type] : l \in key_lock[k]}) /\ UNCHANGED <> ServerReadKey == @@ -450,18 +475,20 @@ ServerReadKey == /\ IF ~ \E l \in key_lock[k] : l.type = "prewrite_optimistic" THEN /\ SendResps({[start_ts |-> start_ts, - type |-> "get_resp", - key |-> k, - value_ts |-> w.start_ts, - met_optimistic_lock |-> FALSE] : w \in commit_read}) - /\ UNCHANGED <> + type |-> "get_resp", + key |-> k, + value_ts |-> w.start_ts, + met_optimistic_lock |-> FALSE] : w \in commit_read}) + /\ UNCHANGED <> ELSE /\ SendResp([start_ts |-> start_ts, type |-> "get_resp", key |-> k, value_ts |-> NoneTs, met_optimistic_lock |-> TRUE]) - /\ UNCHANGED <> + /\ UNCHANGED <> ServerPrewritePessimistic == \E req \in req_msgs : @@ -473,8 +500,8 @@ ServerPrewritePessimistic == \* Pessimistic prewrite is allowed if pressimistic lock is \* acquired, or, there's no lock, and no write record whose \* commit_ts >= start_ts otherwise abort the transaction. - /\ IF \E l \in key_lock[k] : l.ts = start_ts - \/ ~ \E w \in key_write[k] : w.ts >= start_ts + /\ IF \E l \in key_lock[k] : + l.ts = start_ts \/ ~ \E w \in key_write[k] : w.ts >= start_ts THEN /\ key_lock' = [key_lock EXCEPT ![k] = {[ts |-> start_ts, primary |-> req.primary, @@ -555,7 +582,7 @@ ServerCheckTxnStatus == pk == req.primary start_ts == req.start_ts committed == {w \in key_write[pk] : w.start_ts = start_ts /\ w.type = "write"} - caller_start_ts == req.caller_start_ts + current_ts == req.current_ts IN IF \E lock \in key_lock[pk] : lock.ts = start_ts \* Found the matching lock. @@ -584,11 +611,17 @@ ServerCheckTxnStatus == /\ UNCHANGED <> \/ \* Push min_commit_ts - \E lock \in key_lock[pk] : + \* We must ensure that this is not the last chance for this txn + \* to be checked, otherwise, we would make a deadlock. + /\ \A c \in CLIENT : + /\ client_ts[c].start_ts = start_ts + /\ client_check_txn_times[c] < MAX_CLIENT_CHECK_TXN_TIMES + /\ \E lock \in key_lock[pk] : + /\ lock.min_commit_ts < current_ts /\ key_lock' = [key_lock EXCEPT ![pk] = {[ts |-> lock.ts, type |-> lock.type, primary |-> lock.primary, - min_commit_ts |-> caller_start_ts]}] + min_commit_ts |-> current_ts]}] /\ SendResp([type |-> "check_txn_status_resp", start_ts |-> start_ts, action |-> "min_commit_ts_pushed"]) @@ -658,6 +691,7 @@ Init == for_update_ts |-> NoneTs, min_commit_ts |-> NoneTs]] /\ client_key_read_times = [c \in CLIENT |-> [k \in KEY |-> 0]] + /\ client_check_txn_times = [c \in CLIENT |-> 0] /\ key_lock = [k \in KEY |-> {}] /\ key_data = [k \in KEY |-> {}] /\ key_write = [k \in KEY |-> {}] diff --git a/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test1.launch b/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test1.launch index 6bb8e55..c15fbc9 100644 --- a/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test1.launch +++ b/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test1.launch @@ -12,7 +12,7 @@ - + @@ -21,7 +21,7 @@ - + @@ -47,6 +47,7 @@ + @@ -57,6 +58,9 @@ + + + diff --git a/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test2.launch b/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test2.launch index 8b6340a..b97a557 100644 --- a/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test2.launch +++ b/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test2.launch @@ -35,6 +35,7 @@ + diff --git a/DistributedTransaction/Test1.cfg b/DistributedTransaction/Test1.cfg index c59a4db..6fda2fc 100644 --- a/DistributedTransaction/Test1.cfg +++ b/DistributedTransaction/Test1.cfg @@ -13,6 +13,7 @@ CONSTANT CLIENT_WRITE_KEY <- ClientWriteKeys CLIENT_PRIMARY <- ClientPrimary MAX_CLIENT_READ_TIMES = 3 + MAX_CLIENT_CHECK_TXN_TIMES = 2 INIT Init diff --git a/DistributedTransaction/Test2.cfg b/DistributedTransaction/Test2.cfg index 4f6eae5..272e387 100644 --- a/DistributedTransaction/Test2.cfg +++ b/DistributedTransaction/Test2.cfg @@ -14,6 +14,7 @@ CONSTANT CLIENT_WRITE_KEY <- ClientWriteKeys CLIENT_PRIMARY <- ClientPrimary MAX_CLIENT_READ_TIMES = 3 + MAX_CLIENT_CHECK_TXN_TIMES = 2 INIT Init From 33cb86e5479e4c8c281e2955ac07ff9a97cae87b Mon Sep 17 00:00:00 2001 From: zhuo1ang Date: Mon, 12 Jul 2021 20:14:45 +0800 Subject: [PATCH 07/30] fix wrong pessimistic lock amend DistributedTransactions: fix pessimistic lock amend bug upd upd upd upd upd upd upd upd --- .../DistributedTransaction.tla | 204 +++++++----------- .../DistributedTransaction___Test1.launch | 66 ------ .../DistributedTransaction___Test2.launch | 45 ---- DistributedTransaction/Test1.cfg | 2 - DistributedTransaction/Test1.tla | 2 +- DistributedTransaction/Test2.cfg | 2 - DistributedTransaction/Test2.tla | 2 +- DistributedTransaction/Test3.cfg | 29 +++ DistributedTransaction/Test3.tla | 13 ++ 9 files changed, 126 insertions(+), 239 deletions(-) delete mode 100644 DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test1.launch delete mode 100644 DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test2.launch create mode 100644 DistributedTransaction/Test3.cfg create mode 100644 DistributedTransaction/Test3.tla diff --git a/DistributedTransaction/DistributedTransaction.tla b/DistributedTransaction/DistributedTransaction.tla index 0d2bd3b..653e1d3 100644 --- a/DistributedTransaction/DistributedTransaction.tla +++ b/DistributedTransaction/DistributedTransaction.tla @@ -1,7 +1,7 @@ ----------------------- MODULE DistributedTransaction ----------------------- EXTENDS Integers, FiniteSets -\* The set of all keys +\* The set of all keys. CONSTANTS KEY \* The sets of optimistic clients and pessimistic clients. @@ -10,18 +10,16 @@ CLIENT == PESSIMISTIC_CLIENT \union OPTIMISTIC_CLIENT \* Functions that maps a client to keys it wants to read, write. \* representing the involved keys of each client. +\* Note that "read" here stands for optimistic read, and for +\* pessimistic client(s), the constants equals an empty set. CONSTANTS CLIENT_READ_KEY, CLIENT_WRITE_KEY CLIENT_KEY == [c \in CLIENT |-> CLIENT_READ_KEY[c] \union CLIENT_WRITE_KEY[c]] ASSUME \A c \in CLIENT: CLIENT_KEY[c] \subseteq KEY -CONSTANTS MAX_CLIENT_READ_TIMES - \* CLIENT_PRIMARY is the primary key of each client. CONSTANTS CLIENT_PRIMARY ASSUME \A c \in CLIENT: CLIENT_PRIMARY[c] \in CLIENT_KEY[c] -CONSTANTS MAX_CLIENT_CHECK_TXN_TIMES - \* Timestamp of transactions. Ts == Nat \ {0} NoneTs == 0 @@ -87,16 +85,6 @@ VARIABLES client_ts \* haven't been acquired, "prewriting" denotes the keys that are pending \* for prewrite. VARIABLES client_key -\* client_key_read_times[c][k] is a number representing the number -\* of times the client c have read the value of key k. -\* We limit the read-times not exceeds MAX_CLIENT_READ_TIMES to reduce -\* final TLA+ states, this variable does not exist in real world TiKV. -VARIABLES client_key_read_times -\* client_check_txn_times[c] is a number representing the number -\* of times the txn of client c have been checked(check_txn_status). -\* We limit the check-times not exceeeds MAX_CLIENT_CHECK_TXN_TIMES to -\* reduce final TLA+ states, this variable does not exist in real world TiKV. -VARIABLES client_check_txn_times \* next_ts is a globally monotonically increasing integer, representing \* the virtual clock of transactions. In practice, the variable is @@ -104,8 +92,7 @@ VARIABLES client_check_txn_times VARIABLES next_ts msg_vars == <> -client_vars == <> +client_vars == <> key_vars == <> vars == <> @@ -142,25 +129,24 @@ ReqMessages == \* \* In TLA+ spec, the TTL is considered constantly expired when the action is taken, so the \* `rollback_if_not_exist` is assumed true, thus no need to carry it in the message. - \union [start_ts : Ts, current_ts : Ts, primary : KEY, type : {"check_txn_status"}, + \union [start_ts : Ts, caller_start_ts : Ts \union {NoneTs}, primary : KEY, type : {"check_txn_status"}, resolving_pessimistic_lock : BOOLEAN] RespMessages == [start_ts : Ts, type : {"prewrited"}, key : KEY] \* We use the value NoneTs to denotes the key not exists. \* This convention applies for many definations below. - \union [start_ts : Ts, type : {"get_resp"}, key : KEY, value_ts : Ts \union {NoneTs}, met_optimistic_lock : BOOLEAN] + \union [start_ts : Ts, type : {"get_resp"}, key : KEY, value_ts : Ts] + \union [start_ts : Ts, type : {"get_failed"}, primary : KEY, key : KEY, lock_ts : Ts] \* Conceptually, acquire a pessimistic lock of a key is equivalent to reading its value, \* and putting the value in the response can reduce communication. Also, as mentioned \* above, we don’t care about the actual value here, so a timestamp can be used \* instead of the value. \union [start_ts : Ts, type : {"locked_key"}, key : KEY, value_ts : Ts \union {NoneTs}] - \union [start_ts : Ts, type : {"lock_failed"}, key : KEY, latest_commit_ts : Ts \union {NoneTs}, - lock_ts : Ts \union {NoneTs}, lock_type : {"no_lock", - "lock_key", - "prewrite_pessimistic", - "prewrite_optimistic"}] + \union [start_ts : Ts, type : {"lock_failed_has_lock"}, primary : KEY, key : KEY, lock_ts : Ts, + lock_type : {"no_lock", "lock_key", "prewrite_pessimistic", "prewrite_optimistic"}] + \union [start_ts : Ts, type : {"lock_failed_write_conflict"}, key : KEY, latest_commit_ts : Ts] \union [start_ts : Ts, type : {"committed", "commit_aborted", "prewrite_aborted", @@ -196,20 +182,18 @@ TypeOK == /\ req_msgs \in SUBSET ReqMessages min_commit_ts : Ts \union {NoneTs}]] /\ client_key \in [CLIENT -> [locking: SUBSET KEY, prewriting : SUBSET KEY]] /\ \A c \in CLIENT: client_key[c].locking \intersect client_key[c].prewriting = {} - /\ client_key_read_times \in [CLIENT -> [KEY -> 0..MAX_CLIENT_READ_TIMES]] - /\ client_check_txn_times \in [CLIENT -> 0..MAX_CLIENT_CHECK_TXN_TIMES] /\ next_ts \in Ts ----------------------------------------------------------------------------- \* Client Actions \* Once the get request is sent, it exist permanently in the req_msgs, \* so we have to limit the read times in server side. -ClientReadKey(c, k) == +ClientReadKey(c) == /\ ~ client_state[c] = "init" - /\ SendReq([type |-> "get", - start_ts |-> client_ts[c].start_ts, - primary |-> CLIENT_PRIMARY[c], - key |-> k]) + /\ SendReqs({[type |-> "get", + start_ts |-> client_ts[c].start_ts, + primary |-> CLIENT_PRIMARY[c], + key |-> k] : k \in CLIENT_READ_KEY[c]}) /\ UNCHANGED <> ClientLockKey(c) == @@ -217,14 +201,14 @@ ClientLockKey(c) == /\ client_state' = [client_state EXCEPT ![c] = "locking"] /\ client_ts' = [client_ts EXCEPT ![c].start_ts = next_ts, ![c].for_update_ts = next_ts] /\ next_ts' = next_ts + 1 - /\ client_key' = [client_key EXCEPT ![c].locking = CLIENT_KEY[c]] + /\ client_key' = [client_key EXCEPT ![c].locking = CLIENT_WRITE_KEY[c]] \* Assume we need to acquire pessimistic locks for all keys /\ SendReqs({[type |-> "lock_key", start_ts |-> client_ts'[c].start_ts, primary |-> CLIENT_PRIMARY[c], key |-> k, - for_update_ts |-> client_ts'[c].for_update_ts] : k \in CLIENT_KEY[c]}) - /\ UNCHANGED <> + for_update_ts |-> client_ts'[c].for_update_ts] : k \in CLIENT_WRITE_KEY[c]}) + /\ UNCHANGED <> ClientLockedKey(c) == /\ client_state[c] = "locking" @@ -233,76 +217,62 @@ ClientLockedKey(c) == /\ resp.start_ts = client_ts[c].start_ts /\ resp.key \in client_key[c].locking /\ client_key' = [client_key EXCEPT ![c].locking = @ \ {resp.key}] - /\ UNCHANGED <> + /\ UNCHANGED <> ClientRetryLockKey(c) == /\ client_state[c] = "locking" /\ \E resp \in resp_msgs : - \/ /\ resp.type = "lock_failed" + \/ /\ resp.type = "lock_failed_has_lock" /\ resp.start_ts = client_ts[c].start_ts /\ IF resp.lock_type = "lock_key" /\ ~ resp.lock_ts = client_ts[c].start_ts THEN - /\ client_check_txn_times[c] < MAX_CLIENT_CHECK_TXN_TIMES - /\ client_check_txn_times' = [client_check_txn_times EXCEPT ![c] = @ + 1] /\ SendReqs({[type |-> "check_txn_status", - start_ts |-> client_ts[c].start_ts, - current_ts |-> next_ts, - primary |-> CLIENT_PRIMARY[c], + start_ts |-> resp.lock_ts, + caller_start_ts |-> NoneTs, + primary |-> resp.primary, resolving_pessimistic_lock |-> TRUE]}) - /\ next_ts' = next_ts + 1 - /\ UNCHANGED <> - ELSE - /\ ~ resp.lock_type = "no_lock" - /\ client_check_txn_times[c] < MAX_CLIENT_CHECK_TXN_TIMES - /\ client_check_txn_times' = [client_check_txn_times EXCEPT ![c] = @ + 1] + /\ UNCHANGED <> + ELSE IF ~ resp.lock_ts = client_ts[c].start_ts + THEN /\ SendReqs({[type |-> "check_txn_status", - start_ts |-> client_ts[c].start_ts, - current_ts |-> next_ts, - primary |-> CLIENT_PRIMARY[c], + start_ts |-> resp.lock_ts, + caller_start_ts |-> NoneTs, + primary |-> resp.primary, resolving_pessimistic_lock |-> FALSE]}) - /\ next_ts' = next_ts + 1 - /\ UNCHANGED <> - \/ /\ resp.type = "lock_failed" + /\ UNCHANGED <> + ELSE + /\ UNCHANGED <> + \/ /\ resp.type = "lock_failed_write_conflict" /\ resp.start_ts = client_ts[c].start_ts - /\ resp.latest_commit_ts > client_ts[c].for_update_ts + /\ resp.latest_commit_ts >= client_ts[c].for_update_ts /\ client_ts' = [client_ts EXCEPT ![c].for_update_ts = resp.latest_commit_ts] /\ SendReqs({[type |-> "lock_key", start_ts |-> client_ts'[c].start_ts, primary |-> CLIENT_PRIMARY[c], key |-> resp.key, for_update_ts |-> client_ts'[c].for_update_ts]}) - /\ UNCHANGED <> + /\ UNCHANGED <> ClientPrewritePessimistic(c) == /\ client_state[c] = "locking" /\ client_key[c].locking = {} /\ client_state' = [client_state EXCEPT ![c] = "prewriting"] - /\ client_key' = [client_key EXCEPT ![c].prewriting = CLIENT_KEY[c]] + /\ client_key' = [client_key EXCEPT ![c].prewriting = CLIENT_WRITE_KEY[c]] /\ SendReqs({[type |-> "prewrite_pessimistic", start_ts |-> client_ts[c].start_ts, primary |-> CLIENT_PRIMARY[c], - key |-> k] : k \in CLIENT_KEY[c]}) - /\ UNCHANGED <> + key |-> k] : k \in CLIENT_WRITE_KEY[c]}) + /\ UNCHANGED <> ClientReadFailedCheckTxnStatus(c) == /\ \E resp \in resp_msgs : - /\ resp.type = "get_resp" - /\ resp.met_optimistic_lock = TRUE - /\ client_check_txn_times[c] < MAX_CLIENT_CHECK_TXN_TIMES - /\ client_check_txn_times' = [client_check_txn_times EXCEPT ![c] = @ + 1] + /\ resp.type = "get_failed" /\ SendReqs({[type |-> "check_txn_status", - start_ts |-> client_ts[c].start_ts, - current_ts |-> next_ts, - primary |-> CLIENT_PRIMARY[c], + start_ts |-> resp.lock_ts, + caller_start_ts |-> client_ts[c].start_ts, + primary |-> resp.primary, resolving_pessimistic_lock |-> FALSE]}) - /\ next_ts' = next_ts + 1 - /\ UNCHANGED <> + /\ UNCHANGED <> ClientPrewriteOptimistic(c) == /\ client_state[c] = "init" @@ -314,7 +284,7 @@ ClientPrewriteOptimistic(c) == start_ts |-> client_ts'[c].start_ts, primary |-> CLIENT_PRIMARY[c], key |-> k] : k \in CLIENT_WRITE_KEY[c]}) - /\ UNCHANGED <> + /\ UNCHANGED <> ClientPrewrited(c) == /\ client_state[c] = "prewriting" @@ -324,8 +294,7 @@ ClientPrewrited(c) == /\ resp.start_ts = client_ts[c].start_ts /\ resp.key \in client_key[c].prewriting /\ client_key' = [client_key EXCEPT ![c].prewriting = @ \ {resp.key}] - /\ UNCHANGED <> + /\ UNCHANGED <> ClientCommit(c) == /\ client_state[c] = "prewriting" @@ -337,7 +306,7 @@ ClientCommit(c) == start_ts |-> client_ts'[c].start_ts, primary |-> CLIENT_PRIMARY[c], commit_ts |-> client_ts'[c].commit_ts]}) - /\ UNCHANGED <> + /\ UNCHANGED <> ----------------------------------------------------------------------------- \* Server Actions @@ -421,7 +390,7 @@ ServerLockKey == primary |-> req.primary, min_commit_ts |-> NoneTs, type |-> "lock_key"]}] - /\ IF ~ commit_read = {} + /\ IF ~ commit_read = {} THEN \* Actually there's only one msg to be sent. /\ SendResps({[start_ts |-> start_ts, @@ -440,20 +409,18 @@ ServerLockKey == \/ \E w \in latest_commit : /\ w.ts > req.for_update_ts /\ SendResp([start_ts |-> start_ts, - type |-> "lock_failed", + type |-> "lock_failed_write_conflict", key |-> k, - latest_commit_ts |-> w.ts, - lock_ts |-> NoneTs, - lock_type |-> "no_lock"]) + latest_commit_ts |-> w.ts]) /\ UNCHANGED <> ELSE /\ \E l \in key_lock[k] : ~ l.ts = start_ts /\ SendResps({[start_ts |-> start_ts, - type |-> "lock_failed", + type |-> "lock_failed_has_lock", + primary |-> l.primary, key |-> k, - latest_commit_ts |-> NoneTs, lock_ts |-> l.ts, - lock_type |-> l.type] : l \in key_lock[k]}) + lock_type |-> l.type] : l \in key_lock[k]}) /\ UNCHANGED <> ServerReadKey == @@ -461,10 +428,6 @@ ServerReadKey == /\ req.type = "get" /\ \E c \in CLIENT : /\ client_ts[c].start_ts = req.start_ts - /\ client_key_read_times[c][req.key] < MAX_CLIENT_READ_TIMES - /\ client_key_read_times' = [client_key_read_times - EXCEPT ![c] = [client_key_read_times[c] - EXCEPT ![req.key] = client_key_read_times[c][req.key] + 1]] /\ LET k == req.key start_ts == req.start_ts @@ -477,18 +440,15 @@ ServerReadKey == /\ SendResps({[start_ts |-> start_ts, type |-> "get_resp", key |-> k, - value_ts |-> w.start_ts, - met_optimistic_lock |-> FALSE] : w \in commit_read}) - /\ UNCHANGED <> + value_ts |-> w.start_ts] : w \in commit_read}) + /\ UNCHANGED <> ELSE - /\ SendResp([start_ts |-> start_ts, - type |-> "get_resp", - key |-> k, - value_ts |-> NoneTs, - met_optimistic_lock |-> TRUE]) - /\ UNCHANGED <> + /\ SendResps({[start_ts |-> start_ts, + type |-> "get_failed", + primary |-> req.primary, + key |-> k, + lock_ts |-> l.ts] : l \in key_lock[k]}) + /\ UNCHANGED <> ServerPrewritePessimistic == \E req \in req_msgs : @@ -500,8 +460,11 @@ ServerPrewritePessimistic == \* Pessimistic prewrite is allowed if pressimistic lock is \* acquired, or, there's no lock, and no write record whose \* commit_ts >= start_ts otherwise abort the transaction. - /\ IF \E l \in key_lock[k] : - l.ts = start_ts \/ ~ \E w \in key_write[k] : w.ts >= start_ts + /\ IF + \/ /\ ~ \E w \in key_write[k] : w.ts >= start_ts + /\ ~ \E l \in key_lock[k] : TRUE + \/ \E l \in key_lock[k] : + /\ l.ts = start_ts THEN /\ key_lock' = [key_lock EXCEPT ![k] = {[ts |-> start_ts, primary |-> req.primary, @@ -551,7 +514,9 @@ ServerCommit == /\ SendResp([start_ts |-> start_ts, type |-> "committed"]) /\ UNCHANGED <> ELSE - IF \E l \in key_lock[pk] : l.ts = start_ts + IF \E l \in key_lock[pk] : + /\ l.ts = start_ts + /\ next_ts > l.min_commit_ts THEN \* Commit the key only if the prewrite lock exists. /\ commit(pk, start_ts, req.commit_ts) @@ -582,7 +547,7 @@ ServerCheckTxnStatus == pk == req.primary start_ts == req.start_ts committed == {w \in key_write[pk] : w.start_ts = start_ts /\ w.type = "write"} - current_ts == req.current_ts + caller_start_ts == req.caller_start_ts IN IF \E lock \in key_lock[pk] : lock.ts = start_ts \* Found the matching lock. @@ -597,31 +562,29 @@ ServerCheckTxnStatus == THEN /\ unlock_key(pk) /\ SendResp([type |-> "check_txn_status_resp", - start_ts |-> start_ts, - action |-> "pessimistic_rollbacked"]) + start_ts |-> start_ts, + action |-> "pessimistic_rollbacked"]) /\ UNCHANGED <> ELSE /\ rollback(pk, start_ts) /\ SendReqs({[type |-> "resolve_rollbacked", - start_ts |-> start_ts, - primary |-> pk]}) + start_ts |-> start_ts, + primary |-> pk]}) /\ SendResp([type |-> "check_txn_status_resp", - start_ts |-> start_ts, - action |-> "rollbacked"]) + start_ts |-> start_ts, + action |-> "rollbacked"]) /\ UNCHANGED <> \/ - \* Push min_commit_ts + \* Push min_commit_ts. \* We must ensure that this is not the last chance for this txn \* to be checked, otherwise, we would make a deadlock. - /\ \A c \in CLIENT : - /\ client_ts[c].start_ts = start_ts - /\ client_check_txn_times[c] < MAX_CLIENT_CHECK_TXN_TIMES + /\ \A c \in CLIENT : client_ts[c].start_ts = start_ts /\ \E lock \in key_lock[pk] : - /\ lock.min_commit_ts < current_ts + /\ lock.min_commit_ts < caller_start_ts /\ key_lock' = [key_lock EXCEPT ![pk] = {[ts |-> lock.ts, type |-> lock.type, primary |-> lock.primary, - min_commit_ts |-> current_ts]}] + min_commit_ts |-> caller_start_ts]}] /\ SendResp([type |-> "check_txn_status_resp", start_ts |-> start_ts, action |-> "min_commit_ts_pushed"]) @@ -690,28 +653,25 @@ Init == commit_ts |-> NoneTs, for_update_ts |-> NoneTs, min_commit_ts |-> NoneTs]] - /\ client_key_read_times = [c \in CLIENT |-> [k \in KEY |-> 0]] - /\ client_check_txn_times = [c \in CLIENT |-> 0] /\ key_lock = [k \in KEY |-> {}] /\ key_data = [k \in KEY |-> {}] /\ key_write = [k \in KEY |-> {}] Next == \/ \E c \in OPTIMISTIC_CLIENT : + \/ ClientReadKey(c) \/ ClientReadFailedCheckTxnStatus(c) \/ ClientPrewriteOptimistic(c) \/ ClientPrewrited(c) \/ ClientCommit(c) \/ \E c \in PESSIMISTIC_CLIENT : + \/ ClientReadKey(c) \/ ClientLockKey(c) \/ ClientLockedKey(c) \/ ClientRetryLockKey(c) \/ ClientPrewritePessimistic(c) \/ ClientPrewrited(c) \/ ClientCommit(c) - \/ \E c \in CLIENT : - \E k \in CLIENT_READ_KEY[c] : - /\ ClientReadKey(c, k) \/ ServerReadKey \/ ServerLockKey \/ ServerPrewritePessimistic @@ -750,7 +710,7 @@ CommitConsistency == /\ keyCommitted(CLIENT_PRIMARY[c], resp.start_ts) \* Secondary key must be either committed or locked by the \* start_ts of the transaction. - /\ \A k \in CLIENT_KEY[c] : + /\ \A k \in CLIENT_WRITE_KEY[c] : (~ \E l \in key_lock[k] : l.ts = resp.start_ts) = keyCommitted(k, resp.start_ts) diff --git a/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test1.launch b/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test1.launch deleted file mode 100644 index c15fbc9..0000000 --- a/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test1.launch +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test2.launch b/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test2.launch deleted file mode 100644 index b97a557..0000000 --- a/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test2.launch +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/DistributedTransaction/Test1.cfg b/DistributedTransaction/Test1.cfg index 6fda2fc..9eb4d4d 100644 --- a/DistributedTransaction/Test1.cfg +++ b/DistributedTransaction/Test1.cfg @@ -12,8 +12,6 @@ CONSTANT CLIENT_READ_KEY <- ClientReadKeys CLIENT_WRITE_KEY <- ClientWriteKeys CLIENT_PRIMARY <- ClientPrimary - MAX_CLIENT_READ_TIMES = 3 - MAX_CLIENT_CHECK_TXN_TIMES = 2 INIT Init diff --git a/DistributedTransaction/Test1.tla b/DistributedTransaction/Test1.tla index 63c9f14..1b8f5ea 100644 --- a/DistributedTransaction/Test1.tla +++ b/DistributedTransaction/Test1.tla @@ -7,7 +7,7 @@ CONSTANT c1, c2, c3 Keys == {k1, k2} OptimistiicClient == {c3} PessimisticClient == {c1, c2} -ClientReadKeys == c1 :> {k1, k2} @@ c2 :> {k1} @@ c3 :> {k1, k2} +ClientReadKeys == c1 :> {} @@ c2 :> {} @@ c3 :> {k1, k2} ClientWriteKeys == c1 :> {k1, k2} @@ c2 :> {k1} @@ c3 :> {k1, k2} ClientPrimary == c1 :> k1 @@ c2 :> k1 @@ c3 :> k2 ================================================================================ diff --git a/DistributedTransaction/Test2.cfg b/DistributedTransaction/Test2.cfg index 272e387..623a2f4 100644 --- a/DistributedTransaction/Test2.cfg +++ b/DistributedTransaction/Test2.cfg @@ -13,8 +13,6 @@ CONSTANT CLIENT_READ_KEY <- ClientReadKeys CLIENT_WRITE_KEY <- ClientWriteKeys CLIENT_PRIMARY <- ClientPrimary - MAX_CLIENT_READ_TIMES = 3 - MAX_CLIENT_CHECK_TXN_TIMES = 2 INIT Init diff --git a/DistributedTransaction/Test2.tla b/DistributedTransaction/Test2.tla index 057194f..e31dd23 100644 --- a/DistributedTransaction/Test2.tla +++ b/DistributedTransaction/Test2.tla @@ -7,7 +7,7 @@ CONSTANT c1, c2, c3 Keys == {k1, k2, k3} OptimistiicClient == {c3} PessimisticClient == {c1, c2} -ClientReadKeys == c1 :> {k1, k2, k3} @@ c2 :> {k1, k2} @@ c3 :> {k1, k3} +ClientReadKeys == c1 :> {} @@ c2 :> {} @@ c3 :> {k1, k3} ClientWriteKeys == c1 :> {k1, k2, k3} @@ c2 :> {k1, k2} @@ c3 :> {k1, k3} ClientPrimary == c1 :> k1 @@ c2 :> k1 @@ c3 :> k3 ================================================================================ diff --git a/DistributedTransaction/Test3.cfg b/DistributedTransaction/Test3.cfg new file mode 100644 index 0000000..3ff1959 --- /dev/null +++ b/DistributedTransaction/Test3.cfg @@ -0,0 +1,29 @@ +CONSTANT + k1 = k1 + k2 = k2 + c1 = c1 + c2 = c2 + +CONSTANT + KEY <- Keys + OPTIMISTIC_CLIENT <- OptimistiicClient + PESSIMISTIC_CLIENT <- PessimisticClient + CLIENT_READ_KEY <- ClientReadKeys + CLIENT_WRITE_KEY <- ClientWriteKeys + CLIENT_PRIMARY <- ClientPrimary + +INIT + Init + +NEXT + Next + +INVARIANT + TypeOK + UniqueCommitOrAbort + CommitConsistency + AbortConsistency + WriteConsistency + UniqueLockOrWrite + UniqueWrite + MsgTsConsistency diff --git a/DistributedTransaction/Test3.tla b/DistributedTransaction/Test3.tla new file mode 100644 index 0000000..f9ba94b --- /dev/null +++ b/DistributedTransaction/Test3.tla @@ -0,0 +1,13 @@ +--------------------------------- MODULE Test3 --------------------------------- +EXTENDS DistributedTransaction, TLC + +CONSTANT k1, k2 +CONSTANT c1, c2 + +Keys == {k1, k2} +OptimistiicClient == {c2} +PessimisticClient == {c1} +ClientReadKeys == c1 :> {} @@ c2 :> {k1, k2} +ClientWriteKeys == c1 :> {k1, k2} @@ c2 :> {k1, k2} +ClientPrimary == c1 :> k1 @@ c2 :> k1 +================================================================================ From 6f67db92004d0309c324684fb91129902b4cb5fa Mon Sep 17 00:00:00 2001 From: zhuo1ang Date: Mon, 19 Jul 2021 19:20:20 +0800 Subject: [PATCH 08/30] upd read SI check upd refactor ClientRetryLockKey upd ClientRetryCommit upd max_lock_key_time upd fix bugs upd pessimistic ReadSI check, same as optimistic one --- .../DistributedTransaction.tla | 177 +++++++++++------- .../DistributedTransaction___Test1.launch | 63 +++++++ .../DistributedTransaction___Test2.launch | 61 ++++++ .../DistributedTransaction___Test3.launch | 61 ++++++ .../DistributedTransaction___Test4.launch | 67 +++++++ DistributedTransaction/Test4.cfg | 28 +++ DistributedTransaction/Test4.tla | 13 ++ 7 files changed, 402 insertions(+), 68 deletions(-) create mode 100644 DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test1.launch create mode 100644 DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test2.launch create mode 100644 DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test3.launch create mode 100644 DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test4.launch create mode 100644 DistributedTransaction/Test4.cfg create mode 100644 DistributedTransaction/Test4.tla diff --git a/DistributedTransaction/DistributedTransaction.tla b/DistributedTransaction/DistributedTransaction.tla index 653e1d3..14e7215 100644 --- a/DistributedTransaction/DistributedTransaction.tla +++ b/DistributedTransaction/DistributedTransaction.tla @@ -136,7 +136,7 @@ RespMessages == [start_ts : Ts, type : {"prewrited"}, key : KEY] \* We use the value NoneTs to denotes the key not exists. \* This convention applies for many definations below. - \union [start_ts : Ts, type : {"get_resp"}, key : KEY, value_ts : Ts] + \union [start_ts : Ts, type : {"get_resp"}, key : KEY, value_ts : Ts \union {NoneTs}] \union [start_ts : Ts, type : {"get_failed"}, primary : KEY, key : KEY, lock_ts : Ts] \* Conceptually, acquire a pessimistic lock of a key is equivalent to reading its value, @@ -145,12 +145,13 @@ RespMessages == \* instead of the value. \union [start_ts : Ts, type : {"locked_key"}, key : KEY, value_ts : Ts \union {NoneTs}] \union [start_ts : Ts, type : {"lock_failed_has_lock"}, primary : KEY, key : KEY, lock_ts : Ts, - lock_type : {"no_lock", "lock_key", "prewrite_pessimistic", "prewrite_optimistic"}] + lock_type : {"lock_key", "prewrite_pessimistic", "prewrite_optimistic"}] \union [start_ts : Ts, type : {"lock_failed_write_conflict"}, key : KEY, latest_commit_ts : Ts] \union [start_ts : Ts, type : {"committed", "commit_aborted", "prewrite_aborted", "lock_key_aborted"}] + \union [start_ts : Ts, type : {"commit_failed"}, min_commit_ts : Ts] \union [start_ts : Ts, type : {"check_txn_status_resp"}, action : {"rollbacked", "pessimistic_rollbacked", @@ -222,37 +223,26 @@ ClientLockedKey(c) == ClientRetryLockKey(c) == /\ client_state[c] = "locking" /\ \E resp \in resp_msgs : - \/ /\ resp.type = "lock_failed_has_lock" - /\ resp.start_ts = client_ts[c].start_ts - /\ IF resp.lock_type = "lock_key" /\ ~ resp.lock_ts = client_ts[c].start_ts - THEN - /\ SendReqs({[type |-> "check_txn_status", - start_ts |-> resp.lock_ts, - caller_start_ts |-> NoneTs, - primary |-> resp.primary, - resolving_pessimistic_lock |-> TRUE]}) - /\ UNCHANGED <> - ELSE IF ~ resp.lock_ts = client_ts[c].start_ts - THEN - /\ SendReqs({[type |-> "check_txn_status", - start_ts |-> resp.lock_ts, - caller_start_ts |-> NoneTs, - primary |-> resp.primary, - resolving_pessimistic_lock |-> FALSE]}) - /\ UNCHANGED <> - ELSE - /\ UNCHANGED <> - \/ /\ resp.type = "lock_failed_write_conflict" - /\ resp.start_ts = client_ts[c].start_ts - /\ resp.latest_commit_ts >= client_ts[c].for_update_ts - /\ client_ts' = [client_ts EXCEPT ![c].for_update_ts = resp.latest_commit_ts] - /\ SendReqs({[type |-> "lock_key", - start_ts |-> client_ts'[c].start_ts, - primary |-> CLIENT_PRIMARY[c], - key |-> resp.key, - for_update_ts |-> client_ts'[c].for_update_ts]}) - /\ UNCHANGED <> - + \/ /\ resp.type = "lock_failed_has_lock" + /\ resp.start_ts = client_ts[c].start_ts + /\ resp.lock_ts /= client_ts[c].start_ts + /\ SendReqs({[type |-> "check_txn_status", + start_ts |-> resp.lock_ts, + caller_start_ts |-> NoneTs, + primary |-> resp.primary, + resolving_pessimistic_lock |-> resp.lock_type = "lock_key"]}) + /\ UNCHANGED <> + \/ /\ resp.type = "lock_failed_write_conflict" + /\ resp.start_ts = client_ts[c].start_ts + /\ resp.latest_commit_ts >= client_ts[c].for_update_ts + /\ client_ts' = [client_ts EXCEPT ![c].for_update_ts = resp.latest_commit_ts] + /\ SendReqs({[type |-> "lock_key", + start_ts |-> client_ts'[c].start_ts, + primary |-> CLIENT_PRIMARY[c], + key |-> resp.key, + for_update_ts |-> client_ts'[c].for_update_ts]}) + /\ UNCHANGED <> + ClientPrewritePessimistic(c) == /\ client_state[c] = "locking" /\ client_key[c].locking = {} @@ -307,6 +297,19 @@ ClientCommit(c) == primary |-> CLIENT_PRIMARY[c], commit_ts |-> client_ts'[c].commit_ts]}) /\ UNCHANGED <> + +ClientRetryCommit(c) == + /\ client_state[c] = "commiting" + /\ \E resp \in resp_msgs : + /\ resp.type = "commit_failed" + /\ next_ts > resp.min_commit_ts + /\ client_ts' = [client_ts EXCEPT ![c].commit_ts = next_ts] + /\ next_ts' = next_ts + 1 + /\ SendReqs({[type |-> "commit", + start_ts |-> client_ts'[c].start_ts, + primary |-> CLIENT_PRIMARY[c], + commit_ts |-> client_ts'[c].commit_ts]}) + /\ UNCHANGED <> ----------------------------------------------------------------------------- \* Server Actions @@ -433,19 +436,28 @@ ServerReadKey == start_ts == req.start_ts all_commits == {w \in key_write[k] : w.type = "write"} - commit_read == {w \in all_commits : \A w2 \in all_commits : w2.ts <= w.ts \/ w2.ts >= start_ts} + commit_read == {w \in all_commits : w.ts < start_ts /\ \A w2 \in all_commits : w2.ts <= w.ts \/ w2.ts >= start_ts} IN - /\ IF ~ \E l \in key_lock[k] : l.type = "prewrite_optimistic" + /\ IF \/ key_lock[k] = {} + \/ \E l \in key_lock[k] : l.type = "lock_key" THEN - /\ SendResps({[start_ts |-> start_ts, - type |-> "get_resp", - key |-> k, - value_ts |-> w.start_ts] : w \in commit_read}) - /\ UNCHANGED <> + IF commit_read = {} + THEN + /\ SendResp([start_ts |-> start_ts, + type |-> "get_resp", + key |-> k, + value_ts |-> NoneTs]) + /\ UNCHANGED <> + ELSE + /\ SendResps({[start_ts |-> start_ts, + type |-> "get_resp", + key |-> k, + value_ts |-> w.start_ts] : w \in commit_read}) + /\ UNCHANGED <> ELSE /\ SendResps({[start_ts |-> start_ts, type |-> "get_failed", - primary |-> req.primary, + primary |-> l.primary, key |-> k, lock_ts |-> l.ts] : l \in key_lock[k]}) /\ UNCHANGED <> @@ -516,12 +528,16 @@ ServerCommit == ELSE IF \E l \in key_lock[pk] : /\ l.ts = start_ts - /\ next_ts > l.min_commit_ts THEN - \* Commit the key only if the prewrite lock exists. - /\ commit(pk, start_ts, req.commit_ts) - /\ SendResp([start_ts |-> start_ts, type |-> "committed"]) - /\ UNCHANGED <> + IF \E l \in key_lock[pk] : l.ts = start_ts /\ next_ts > l.min_commit_ts + THEN + \* Commit the key only if the prewrite lock exists. + /\ commit(pk, start_ts, req.commit_ts) + /\ SendResp([start_ts |-> start_ts, type |-> "committed"]) + /\ UNCHANGED <> + ELSE + /\ SendResps({[start_ts |-> start_ts, type |-> "commit_failed", min_commit_ts |-> l.min_commit_ts] : l \in key_lock[pk]}) + /\ UNCHANGED <> ELSE \* Otherwise, abort the transaction. /\ SendResp([start_ts |-> start_ts, type |-> "commit_aborted"]) @@ -576,8 +592,6 @@ ServerCheckTxnStatus == /\ UNCHANGED <> \/ \* Push min_commit_ts. - \* We must ensure that this is not the last chance for this txn - \* to be checked, otherwise, we would make a deadlock. /\ \A c \in CLIENT : client_ts[c].start_ts = start_ts /\ \E lock \in key_lock[pk] : /\ lock.min_commit_ts < caller_start_ts @@ -656,7 +670,7 @@ Init == /\ key_lock = [k \in KEY |-> {}] /\ key_data = [k \in KEY |-> {}] /\ key_write = [k \in KEY |-> {}] - + Next == \/ \E c \in OPTIMISTIC_CLIENT : \/ ClientReadKey(c) @@ -664,6 +678,7 @@ Next == \/ ClientPrewriteOptimistic(c) \/ ClientPrewrited(c) \/ ClientCommit(c) + \/ ClientRetryCommit(c) \/ \E c \in PESSIMISTIC_CLIENT : \/ ClientReadKey(c) \/ ClientLockKey(c) @@ -672,6 +687,7 @@ Next == \/ ClientPrewritePessimistic(c) \/ ClientPrewrited(c) \/ ClientCommit(c) + \/ ClientRetryCommit(c) \/ ServerReadKey \/ ServerLockKey \/ ServerPrewritePessimistic @@ -767,25 +783,50 @@ MsgTsConsistency == req.commit_ts <= next_ts /\ \A resp \in resp_msgs : resp.start_ts <= next_ts -ReadSnapshotIsolation == - /\ \A resp \in resp_msgs : - /\ resp.type = "get_resp" - /\ LET - start_ts == resp.start_ts - key == resp.key - \* As mentioned before, the value is just a timestamp - value == resp.value - met_optimistic_lock == resp.met_optimistic_lock - IN - /\ \E c \in CLIENT: - /\ client_ts[c].start_ts = start_ts - /\ IF client_ts[c].commit_ts \in Ts THEN - /\ \A w \in key_write[key] : - \/ w.type = "rollback" - \/ w.start_ts > start_ts - \/ w.ts < start_ts - ELSE - /\ TRUE +OptimisticReadSnapshotIsolation == + \A resp \in resp_msgs : resp.type = "get_resp" => + /\ LET + start_ts == resp.start_ts + key == resp.key + all_commits_before_start_ts == {w \in key_write[key] : w.type = "write" /\ w.ts <= start_ts} + latest_commit_before_start_ts == + {w \in all_commits_before_start_ts : + \A w2 \in all_commits_before_start_ts : + w.ts >= w2.ts} + IN + IF Cardinality(latest_commit_before_start_ts) = 1 + THEN + \A w \in latest_commit_before_start_ts: w.start_ts = resp.value_ts + ELSE IF Cardinality(latest_commit_before_start_ts) = 0 + THEN + resp.value_ts = NoneTs + ELSE + FALSE + +PessimisticReadSnapshotIsolation == + \A resp \in resp_msgs : resp.type = "key_locked" => + /\ LET + start_ts == resp.start_ts + key == resp.key + all_commits_before_start_ts == {w \in key_write[key] : w.type = "write" /\ w.ts <= start_ts} + latest_commit_before_start_ts == + {w \in all_commits_before_start_ts : + \A w2 \in all_commits_before_start_ts : + w.ts >= w2.ts} + IN + IF Cardinality(latest_commit_before_start_ts) = 1 + THEN + \A w \in latest_commit_before_start_ts: w.start_ts = resp.value_ts + ELSE IF Cardinality(latest_commit_before_start_ts) = 0 + THEN + resp.value_ts = NoneTs + ELSE + FALSE + + +ReadSnapshotIsolation == OptimisticReadSnapshotIsolation /\ PessimisticReadSnapshotIsolation + + \* SnapshotIsolation is implied from the following assumptions (but is not \* necessary) because SnapshotIsolation means that: diff --git a/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test1.launch b/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test1.launch new file mode 100644 index 0000000..f75377e --- /dev/null +++ b/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test1.launch @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test2.launch b/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test2.launch new file mode 100644 index 0000000..986642c --- /dev/null +++ b/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test2.launch @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test3.launch b/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test3.launch new file mode 100644 index 0000000..4727595 --- /dev/null +++ b/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test3.launch @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test4.launch b/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test4.launch new file mode 100644 index 0000000..cf25ce9 --- /dev/null +++ b/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test4.launch @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DistributedTransaction/Test4.cfg b/DistributedTransaction/Test4.cfg new file mode 100644 index 0000000..4af6b93 --- /dev/null +++ b/DistributedTransaction/Test4.cfg @@ -0,0 +1,28 @@ +CONSTANT + k1 = k1 + c1 = c1 + c2 = c2 + +CONSTANT + KEY <- Keys + OPTIMISTIC_CLIENT <- OptimistiicClient + PESSIMISTIC_CLIENT <- PessimisticClient + CLIENT_READ_KEY <- ClientReadKeys + CLIENT_WRITE_KEY <- ClientWriteKeys + CLIENT_PRIMARY <- ClientPrimary + +INIT + Init + +NEXT + Next + +INVARIANT + TypeOK + UniqueCommitOrAbort + CommitConsistency + AbortConsistency + WriteConsistency + UniqueLockOrWrite + UniqueWrite + MsgTsConsistency diff --git a/DistributedTransaction/Test4.tla b/DistributedTransaction/Test4.tla new file mode 100644 index 0000000..7878525 --- /dev/null +++ b/DistributedTransaction/Test4.tla @@ -0,0 +1,13 @@ +--------------------------------- MODULE Test3 --------------------------------- +EXTENDS DistributedTransaction, TLC + +CONSTANT k1 +CONSTANT c1, c2 + +Keys == {k1} +OptimistiicClient == {c2} +PessimisticClient == {c1} +ClientReadKeys == c1 :> {} @@ c2 :> {k1} +ClientWriteKeys == c1 :> {k1} @@ c2 :> {k1} +ClientPrimary == c1 :> k1 @@ c2 :> k1 +================================================================================ From 3a30dad75b74c11201ad5112cc7ec2bd1c064fc9 Mon Sep 17 00:00:00 2001 From: zhuo1ang Date: Thu, 29 Jul 2021 12:23:57 +0800 Subject: [PATCH 09/30] upd remove some resp msgs upd possible msg lost --- .../DistributedTransaction.tla | 247 ++++++++++-------- .../DistributedTransaction___Test1.launch | 6 +- 2 files changed, 143 insertions(+), 110 deletions(-) diff --git a/DistributedTransaction/DistributedTransaction.tla b/DistributedTransaction/DistributedTransaction.tla index 14e7215..b81cebe 100644 --- a/DistributedTransaction/DistributedTransaction.tla +++ b/DistributedTransaction/DistributedTransaction.tla @@ -132,6 +132,7 @@ ReqMessages == \union [start_ts : Ts, caller_start_ts : Ts \union {NoneTs}, primary : KEY, type : {"check_txn_status"}, resolving_pessimistic_lock : BOOLEAN] +(* RespMessages == [start_ts : Ts, type : {"prewrited"}, key : KEY] \* We use the value NoneTs to denotes the key not exists. @@ -158,6 +159,14 @@ RespMessages == "committed", "min_commit_ts_pushed", "lock_not_exist_do_nothing"}] +*) +RespMessages == + [start_ts : Ts, type : {"get_resp"}, key : KEY, value_ts : Ts \union {NoneTs}] + \union [start_ts : Ts, type : {"locked_key"}, key : KEY, value_ts : Ts \union {NoneTs}] + \union [start_ts : Ts, type : {"committed", + "commit_aborted", + "prewrite_aborted", + "lock_key_aborted"}] TypeOK == /\ req_msgs \in SUBSET ReqMessages /\ resp_msgs \in SUBSET RespMessages @@ -211,19 +220,24 @@ ClientLockKey(c) == for_update_ts |-> client_ts'[c].for_update_ts] : k \in CLIENT_WRITE_KEY[c]}) /\ UNCHANGED <> -ClientLockedKey(c) == - /\ client_state[c] = "locking" - /\ \E resp \in resp_msgs : - /\ resp.type = "locked_key" - /\ resp.start_ts = client_ts[c].start_ts - /\ resp.key \in client_key[c].locking - /\ client_key' = [client_key EXCEPT ![c].locking = @ \ {resp.key}] - /\ UNCHANGED <> - -ClientRetryLockKey(c) == - /\ client_state[c] = "locking" - /\ \E resp \in resp_msgs : - \/ /\ resp.type = "lock_failed_has_lock" +ClientLockedKey(resp) == + LET + cli == {c \in CLIENT : client_ts[c].start_ts = resp.start_ts} + IN + /\ \E c \in cli : + /\ client_state[c] = "locking" + /\ resp.type = "locked_key" + /\ resp.start_ts = client_ts[c].start_ts + /\ resp.key \in client_key[c].locking + /\ client_key' = [client_key EXCEPT ![c].locking = @ \ {resp.key}] + +ClientRetryLockKey(resp) == + LET + cli == {c \in CLIENT : client_ts[c].start_ts = resp.start_ts} + IN + \/ /\ \E c \in cli : + /\ client_state[c] = "locking" + /\ resp.type = "lock_failed_has_lock" /\ resp.start_ts = client_ts[c].start_ts /\ resp.lock_ts /= client_ts[c].start_ts /\ SendReqs({[type |-> "check_txn_status", @@ -231,8 +245,8 @@ ClientRetryLockKey(c) == caller_start_ts |-> NoneTs, primary |-> resp.primary, resolving_pessimistic_lock |-> resp.lock_type = "lock_key"]}) - /\ UNCHANGED <> - \/ /\ resp.type = "lock_failed_write_conflict" + \/ /\ \E c \in cli : + /\ resp.type = "lock_failed_write_conflict" /\ resp.start_ts = client_ts[c].start_ts /\ resp.latest_commit_ts >= client_ts[c].for_update_ts /\ client_ts' = [client_ts EXCEPT ![c].for_update_ts = resp.latest_commit_ts] @@ -241,7 +255,6 @@ ClientRetryLockKey(c) == primary |-> CLIENT_PRIMARY[c], key |-> resp.key, for_update_ts |-> client_ts'[c].for_update_ts]}) - /\ UNCHANGED <> ClientPrewritePessimistic(c) == /\ client_state[c] = "locking" @@ -254,15 +267,17 @@ ClientPrewritePessimistic(c) == key |-> k] : k \in CLIENT_WRITE_KEY[c]}) /\ UNCHANGED <> -ClientReadFailedCheckTxnStatus(c) == - /\ \E resp \in resp_msgs : +ClientReadFailedCheckTxnStatus(resp) == + LET + cli == {c \in CLIENT : client_ts[c].start_ts = resp.start_ts} + IN + /\ \E c \in cli : /\ resp.type = "get_failed" /\ SendReqs({[type |-> "check_txn_status", start_ts |-> resp.lock_ts, caller_start_ts |-> client_ts[c].start_ts, primary |-> resp.primary, resolving_pessimistic_lock |-> FALSE]}) - /\ UNCHANGED <> ClientPrewriteOptimistic(c) == /\ client_state[c] = "init" @@ -276,15 +291,17 @@ ClientPrewriteOptimistic(c) == key |-> k] : k \in CLIENT_WRITE_KEY[c]}) /\ UNCHANGED <> -ClientPrewrited(c) == - /\ client_state[c] = "prewriting" - /\ client_key[c].locking = {} - /\ \E resp \in resp_msgs : - /\ resp.type = "prewrited" - /\ resp.start_ts = client_ts[c].start_ts - /\ resp.key \in client_key[c].prewriting - /\ client_key' = [client_key EXCEPT ![c].prewriting = @ \ {resp.key}] - /\ UNCHANGED <> +ClientPrewrited(resp) == + LET + cli == {c \in CLIENT : client_ts[c].start_ts = resp.start_ts} + IN + /\ \E c \in cli : + /\ client_state[c] = "prewriting" + /\ client_key[c].locking = {} + /\ resp.type = "prewrited" + /\ resp.start_ts = client_ts[c].start_ts + /\ resp.key \in client_key[c].prewriting + /\ client_key' = [client_key EXCEPT ![c].prewriting = @ \ {resp.key}] ClientCommit(c) == /\ client_state[c] = "prewriting" @@ -298,9 +315,12 @@ ClientCommit(c) == commit_ts |-> client_ts'[c].commit_ts]}) /\ UNCHANGED <> -ClientRetryCommit(c) == - /\ client_state[c] = "commiting" - /\ \E resp \in resp_msgs : +ClientRetryCommit(resp) == + LET + cli == {c \in CLIENT : client_ts[c].start_ts = resp.start_ts} + IN + /\ \E c \in cli : + /\ client_state[c] = "commiting" /\ resp.type = "commit_failed" /\ next_ts > resp.min_commit_ts /\ client_ts' = [client_ts EXCEPT ![c].commit_ts = next_ts] @@ -309,7 +329,6 @@ ClientRetryCommit(c) == start_ts |-> client_ts'[c].start_ts, primary |-> CLIENT_PRIMARY[c], commit_ts |-> client_ts'[c].commit_ts]}) - /\ UNCHANGED <> ----------------------------------------------------------------------------- \* Server Actions @@ -393,38 +412,50 @@ ServerLockKey == primary |-> req.primary, min_commit_ts |-> NoneTs, type |-> "lock_key"]}] - /\ IF ~ commit_read = {} + /\ IF ~ commit_read = {} THEN - \* Actually there's only one msg to be sent. - /\ SendResps({[start_ts |-> start_ts, - type |-> "locked_key", - key |-> k, - value_ts |-> w2.start_ts] : w2 \in commit_read}) - /\ UNCHANGED <> + LET + commit_record == CHOOSE value \in commit_read : TRUE + value_ts == commit_record.start_ts + IN + /\ \/ /\ ClientLockedKey([start_ts |-> start_ts, + type |-> "locked_key", + key |-> k, + value_ts |-> value_ts]) + /\ UNCHANGED <> + \/ /\ UNCHANGED <> ELSE - /\ SendResp([start_ts |-> start_ts, - type |-> "locked_key", - key |-> k, - value_ts |-> NoneTs]) - /\ UNCHANGED <> + /\ \/ /\ ClientLockedKey([start_ts |-> start_ts, + type |-> "locked_key", + key |-> k, + value_ts |-> NoneTs]) + /\ UNCHANGED <> + \/ /\ UNCHANGED <> \* Otherwise, reject the request and let client to retry \* with new for_update_ts. \/ \E w \in latest_commit : - /\ w.ts > req.for_update_ts - /\ SendResp([start_ts |-> start_ts, - type |-> "lock_failed_write_conflict", - key |-> k, - latest_commit_ts |-> w.ts]) - /\ UNCHANGED <> - ELSE - /\ \E l \in key_lock[k] : ~ l.ts = start_ts - /\ SendResps({[start_ts |-> start_ts, - type |-> "lock_failed_has_lock", - primary |-> l.primary, - key |-> k, - lock_ts |-> l.ts, - lock_type |-> l.type] : l \in key_lock[k]}) - /\ UNCHANGED <> + \/ /\ w.ts > req.for_update_ts + /\ \/ /\ ~ \E w2 \in all_commits : w2.start_ts = req.start_ts + /\ ClientRetryLockKey([start_ts |-> start_ts, + type |-> "lock_failed_write_conflict", + key |-> k, + latest_commit_ts |-> w.ts]) + /\ UNCHANGED <> + \/ /\ UNCHANGED <> + + ELSE + LET + l == CHOOSE lock \in key_lock[k] : TRUE + IN + /\ l.ts /= start_ts + /\ \/ /\ ClientRetryLockKey([start_ts |-> start_ts, + type |-> "lock_failed_has_lock", + primary |-> l.primary, + key |-> k, + lock_ts |-> l.ts, + lock_type |-> l.type]) + /\ UNCHANGED <> + \/ /\ UNCHANGED <> ServerReadKey == \E req \in req_msgs : @@ -455,12 +486,16 @@ ServerReadKey == value_ts |-> w.start_ts] : w \in commit_read}) /\ UNCHANGED <> ELSE - /\ SendResps({[start_ts |-> start_ts, - type |-> "get_failed", - primary |-> l.primary, - key |-> k, - lock_ts |-> l.ts] : l \in key_lock[k]}) - /\ UNCHANGED <> + LET + l == CHOOSE lock \in key_lock[k] : TRUE + IN + \/ /\ ClientReadFailedCheckTxnStatus([start_ts |-> start_ts, + type |-> "get_failed", + primary |-> l.primary, + key |-> k, + lock_ts |-> l.ts]) + /\ UNCHANGED <> + \/ /\ UNCHANGED <> ServerPrewritePessimistic == \E req \in req_msgs : @@ -478,13 +513,14 @@ ServerPrewritePessimistic == \/ \E l \in key_lock[k] : /\ l.ts = start_ts THEN - /\ key_lock' = [key_lock EXCEPT ![k] = {[ts |-> start_ts, - primary |-> req.primary, - type |-> "prewrite_pessimistic", - min_commit_ts |-> NoneTs]}] - /\ key_data' = [key_data EXCEPT ![k] = @ \union {start_ts}] - /\ SendResp([start_ts |-> start_ts, type |-> "prewrited", key |-> k]) - /\ UNCHANGED <> + \/ /\ key_lock' = [key_lock EXCEPT ![k] = {[ts |-> start_ts, + primary |-> req.primary, + type |-> "prewrite_pessimistic", + min_commit_ts |-> NoneTs]}] + /\ key_data' = [key_data EXCEPT ![k] = @ \union {start_ts}] + /\ ClientPrewrited([start_ts |-> start_ts, type |-> "prewrited", key |-> k]) + /\ UNCHANGED <> + \/ /\ UNCHANGED <> ELSE /\ SendResp([start_ts |-> start_ts, type |-> "prewrite_aborted"]) /\ UNCHANGED <> @@ -510,8 +546,9 @@ ServerPrewriteOptimistic == min_commit_ts |-> NoneTs, type |-> "prewrite_optimistic"]}] /\ key_data' = [key_data EXCEPT ![k] = @ \union {start_ts}] - /\ SendResp([start_ts |-> start_ts, type |-> "prewrited", key |-> k]) - /\ UNCHANGED <> + /\ \/ /\ ClientPrewrited([start_ts |-> start_ts, type |-> "prewrited", key |-> k]) + /\ UNCHANGED <> + \/ /\ UNCHANGED <> ServerCommit == \E req \in req_msgs : @@ -536,8 +573,11 @@ ServerCommit == /\ SendResp([start_ts |-> start_ts, type |-> "committed"]) /\ UNCHANGED <> ELSE - /\ SendResps({[start_ts |-> start_ts, type |-> "commit_failed", min_commit_ts |-> l.min_commit_ts] : l \in key_lock[pk]}) - /\ UNCHANGED <> + LET + l == CHOOSE l \in key_lock[pk] : TRUE + IN + /\ SendResps([start_ts |-> start_ts, type |-> "commit_failed", min_commit_ts |-> l.min_commit_ts]) + /\ UNCHANGED <> ELSE \* Otherwise, abort the transaction. /\ SendResp([start_ts |-> start_ts, type |-> "commit_aborted"]) @@ -577,19 +617,19 @@ ServerCheckTxnStatus == /\ req.resolving_pessimistic_lock = TRUE THEN /\ unlock_key(pk) - /\ SendResp([type |-> "check_txn_status_resp", - start_ts |-> start_ts, - action |-> "pessimistic_rollbacked"]) - /\ UNCHANGED <> + \* /\ SendResp([type |-> "check_txn_status_resp", + \* start_ts |-> start_ts, + \* action |-> "pessimistic_rollbacked"]) + /\ UNCHANGED <> ELSE /\ rollback(pk, start_ts) /\ SendReqs({[type |-> "resolve_rollbacked", start_ts |-> start_ts, primary |-> pk]}) - /\ SendResp([type |-> "check_txn_status_resp", - start_ts |-> start_ts, - action |-> "rollbacked"]) - /\ UNCHANGED <> + \* /\ SendResp([type |-> "check_txn_status_resp", + \* start_ts |-> start_ts, + \* action |-> "rollbacked"]) + /\ UNCHANGED <> \/ \* Push min_commit_ts. /\ \A c \in CLIENT : client_ts[c].start_ts = start_ts @@ -599,10 +639,10 @@ ServerCheckTxnStatus == type |-> lock.type, primary |-> lock.primary, min_commit_ts |-> caller_start_ts]}] - /\ SendResp([type |-> "check_txn_status_resp", - start_ts |-> start_ts, - action |-> "min_commit_ts_pushed"]) - /\ UNCHANGED <> + \* /\ SendResp([type |-> "check_txn_status_resp", + \* start_ts |-> start_ts, + \* action |-> "min_commit_ts_pushed"]) + /\ UNCHANGED <> \* Lock not found or start_ts on the lock mismatches. ELSE IF committed /= {} THEN @@ -610,24 +650,24 @@ ServerCheckTxnStatus == start_ts |-> start_ts, primary |-> pk, commit_ts |-> w.ts] : w \in committed}) - /\ SendResp([type |-> "check_txn_status_resp", - start_ts |-> start_ts, - action |-> "committed"]) - /\ UNCHANGED <> + \* /\ SendResp([type |-> "check_txn_status_resp", + \* start_ts |-> start_ts, + \* action |-> "committed"]) + /\ UNCHANGED <> ELSE IF req.resolving_pessimistic_lock = TRUE THEN - /\ SendResp([type |-> "check_txn_status_resp", - start_ts |-> start_ts, - action |-> "lock_not_exist_do_nothing"]) - /\ UNCHANGED <> + \* /\ SendResp([type |-> "check_txn_status_resp", + \* start_ts |-> start_ts, + \* action |-> "lock_not_exist_do_nothing"]) + /\ UNCHANGED <> ELSE /\ rollback(pk, start_ts) /\ SendReqs({[type |-> "resolve_rollbacked", start_ts |-> start_ts, primary |-> pk]}) - /\ SendResp([type |-> "check_txn_status_resp", - start_ts |-> start_ts, - action |-> "rollbacked"]) - /\ UNCHANGED <> + \* /\ SendResp([type |-> "check_txn_status_resp", + \* start_ts |-> start_ts, + \* action |-> "rollbacked"]) + /\ UNCHANGED <> ServerResolveCommitted == \E req \in req_msgs : @@ -674,20 +714,13 @@ Init == Next == \/ \E c \in OPTIMISTIC_CLIENT : \/ ClientReadKey(c) - \/ ClientReadFailedCheckTxnStatus(c) \/ ClientPrewriteOptimistic(c) - \/ ClientPrewrited(c) \/ ClientCommit(c) - \/ ClientRetryCommit(c) \/ \E c \in PESSIMISTIC_CLIENT : \/ ClientReadKey(c) \/ ClientLockKey(c) - \/ ClientLockedKey(c) - \/ ClientRetryLockKey(c) \/ ClientPrewritePessimistic(c) - \/ ClientPrewrited(c) \/ ClientCommit(c) - \/ ClientRetryCommit(c) \/ ServerReadKey \/ ServerLockKey \/ ServerPrewritePessimistic diff --git a/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test1.launch b/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test1.launch index f75377e..e938e9b 100644 --- a/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test1.launch +++ b/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test1.launch @@ -12,11 +12,11 @@ - + - + @@ -33,7 +33,7 @@ - + From 42642e305c895a0d872eafc8d43f7065cd6b9d5d Mon Sep 17 00:00:00 2001 From: zhuo1ang Date: Fri, 13 Aug 2021 11:37:06 +0800 Subject: [PATCH 10/30] apply code review suggestion upd --- .../DistributedTransaction.tla | 465 +++++++++--------- .../DistributedTransaction___Test1.launch | 3 +- .../DistributedTransaction___Test2.launch | 7 +- .../DistributedTransaction___Test3.launch | 3 +- .../DistributedTransaction___Test4.launch | 4 +- 5 files changed, 236 insertions(+), 246 deletions(-) diff --git a/DistributedTransaction/DistributedTransaction.tla b/DistributedTransaction/DistributedTransaction.tla index b81cebe..4de9d33 100644 --- a/DistributedTransaction/DistributedTransaction.tla +++ b/DistributedTransaction/DistributedTransaction.tla @@ -132,34 +132,12 @@ ReqMessages == \union [start_ts : Ts, caller_start_ts : Ts \union {NoneTs}, primary : KEY, type : {"check_txn_status"}, resolving_pessimistic_lock : BOOLEAN] -(* -RespMessages == - [start_ts : Ts, type : {"prewrited"}, key : KEY] - \* We use the value NoneTs to denotes the key not exists. - \* This convention applies for many definations below. - \union [start_ts : Ts, type : {"get_resp"}, key : KEY, value_ts : Ts \union {NoneTs}] - \union [start_ts : Ts, type : {"get_failed"}, primary : KEY, key : KEY, lock_ts : Ts] - - \* Conceptually, acquire a pessimistic lock of a key is equivalent to reading its value, - \* and putting the value in the response can reduce communication. Also, as mentioned - \* above, we don’t care about the actual value here, so a timestamp can be used - \* instead of the value. - \union [start_ts : Ts, type : {"locked_key"}, key : KEY, value_ts : Ts \union {NoneTs}] - \union [start_ts : Ts, type : {"lock_failed_has_lock"}, primary : KEY, key : KEY, lock_ts : Ts, - lock_type : {"lock_key", "prewrite_pessimistic", "prewrite_optimistic"}] - \union [start_ts : Ts, type : {"lock_failed_write_conflict"}, key : KEY, latest_commit_ts : Ts] - \union [start_ts : Ts, type : {"committed", - "commit_aborted", - "prewrite_aborted", - "lock_key_aborted"}] - \union [start_ts : Ts, type : {"commit_failed"}, min_commit_ts : Ts] - \union [start_ts : Ts, type : {"check_txn_status_resp"}, - action : {"rollbacked", - "pessimistic_rollbacked", - "committed", - "min_commit_ts_pushed", - "lock_not_exist_do_nothing"}] -*) +\* The RespMessages defined here is only a subset of the "actual" one in spec. Other resp messages are +\* transmitted(passed) by function arguments. By doing so, we cannot check the retransmission or +\* delay(we do can check the message lost) of these messages. The reason for this is that messages +\* like "lock_key_failed_has_lock" "lock_key_failed_write_conflit" are just too much, which makes the +\* model checking impossible to finish. Considering the delay or retransmit of them is not that serious +\* to the transaction safety, we made the decision to reduce final "distinct state number". RespMessages == [start_ts : Ts, type : {"get_resp"}, key : KEY, value_ts : Ts \union {NoneTs}] \union [start_ts : Ts, type : {"locked_key"}, key : KEY, value_ts : Ts \union {NoneTs}] @@ -167,6 +145,7 @@ RespMessages == "commit_aborted", "prewrite_aborted", "lock_key_aborted"}] + \union [start_ts : Ts, type : {"commit_failed"}, min_commit_ts : Ts] TypeOK == /\ req_msgs \in SUBSET ReqMessages /\ resp_msgs \in SUBSET RespMessages @@ -184,7 +163,6 @@ TypeOK == /\ req_msgs \in SUBSET ReqMessages /\ key_write \in [KEY -> SUBSET ( [ts : Ts, start_ts : Ts, type : {"write"}] \union [ts : Ts, start_ts : Ts, type : {"rollback"}, protected : BOOLEAN])] - \* The reading phase only apply for optimistic transactions /\ client_state \in [CLIENT -> {"init", "locking", "prewriting", "committing"}] /\ client_ts \in [CLIENT -> [start_ts : Ts \union {NoneTs}, commit_ts : Ts \union {NoneTs}, @@ -196,8 +174,7 @@ TypeOK == /\ req_msgs \in SUBSET ReqMessages ----------------------------------------------------------------------------- \* Client Actions -\* Once the get request is sent, it exist permanently in the req_msgs, -\* so we have to limit the read times in server side. +\* Once the get request is sent, it exist permanently in the req_msgs. ClientReadKey(c) == /\ ~ client_state[c] = "init" /\ SendReqs({[type |-> "get", @@ -212,7 +189,7 @@ ClientLockKey(c) == /\ client_ts' = [client_ts EXCEPT ![c].start_ts = next_ts, ![c].for_update_ts = next_ts] /\ next_ts' = next_ts + 1 /\ client_key' = [client_key EXCEPT ![c].locking = CLIENT_WRITE_KEY[c]] - \* Assume we need to acquire pessimistic locks for all keys + \* For pessimistic clients assume we need to acquire pessimistic locks for all keys. /\ SendReqs({[type |-> "lock_key", start_ts |-> client_ts'[c].start_ts, primary |-> CLIENT_PRIMARY[c], @@ -222,9 +199,8 @@ ClientLockKey(c) == ClientLockedKey(resp) == LET - cli == {c \in CLIENT : client_ts[c].start_ts = resp.start_ts} + c == CHOOSE c \in {c \in CLIENT : client_ts[c].start_ts = resp.start_ts} : TRUE IN - /\ \E c \in cli : /\ client_state[c] = "locking" /\ resp.type = "locked_key" /\ resp.start_ts = client_ts[c].start_ts @@ -233,10 +209,9 @@ ClientLockedKey(resp) == ClientRetryLockKey(resp) == LET - cli == {c \in CLIENT : client_ts[c].start_ts = resp.start_ts} + c == CHOOSE c \in {c \in CLIENT : client_ts[c].start_ts = resp.start_ts} : TRUE IN - \/ /\ \E c \in cli : - /\ client_state[c] = "locking" + \/ /\ client_state[c] = "locking" /\ resp.type = "lock_failed_has_lock" /\ resp.start_ts = client_ts[c].start_ts /\ resp.lock_ts /= client_ts[c].start_ts @@ -245,10 +220,9 @@ ClientRetryLockKey(resp) == caller_start_ts |-> NoneTs, primary |-> resp.primary, resolving_pessimistic_lock |-> resp.lock_type = "lock_key"]}) - \/ /\ \E c \in cli : - /\ resp.type = "lock_failed_write_conflict" + \/ /\ resp.type = "lock_failed_write_conflict" /\ resp.start_ts = client_ts[c].start_ts - /\ resp.latest_commit_ts >= client_ts[c].for_update_ts + /\ resp.latest_commit_ts > client_ts[c].for_update_ts /\ client_ts' = [client_ts EXCEPT ![c].for_update_ts = resp.latest_commit_ts] /\ SendReqs({[type |-> "lock_key", start_ts |-> client_ts'[c].start_ts, @@ -269,14 +243,15 @@ ClientPrewritePessimistic(c) == ClientReadFailedCheckTxnStatus(resp) == LET - cli == {c \in CLIENT : client_ts[c].start_ts = resp.start_ts} + c == CHOOSE c \in {c \in CLIENT : client_ts[c].start_ts = resp.start_ts} : TRUE IN - /\ \E c \in cli : /\ resp.type = "get_failed" /\ SendReqs({[type |-> "check_txn_status", start_ts |-> resp.lock_ts, caller_start_ts |-> client_ts[c].start_ts, primary |-> resp.primary, + \* For a read failure, there can't be a pessimistic lock, since + \* when a read mets it, it returns the most recent write record. resolving_pessimistic_lock |-> FALSE]}) ClientPrewriteOptimistic(c) == @@ -293,9 +268,8 @@ ClientPrewriteOptimistic(c) == ClientPrewrited(resp) == LET - cli == {c \in CLIENT : client_ts[c].start_ts = resp.start_ts} + c == CHOOSE c \in {c \in CLIENT : client_ts[c].start_ts = resp.start_ts} : TRUE IN - /\ \E c \in cli : /\ client_state[c] = "prewriting" /\ client_key[c].locking = {} /\ resp.type = "prewrited" @@ -317,9 +291,8 @@ ClientCommit(c) == ClientRetryCommit(resp) == LET - cli == {c \in CLIENT : client_ts[c].start_ts = resp.start_ts} + c == CHOOSE c \in {c \in CLIENT : client_ts[c].start_ts = resp.start_ts} : TRUE IN - /\ \E c \in cli : /\ client_state[c] = "commiting" /\ resp.type = "commit_failed" /\ next_ts > resp.min_commit_ts @@ -384,16 +357,14 @@ ServerLockKey == k == req.key start_ts == req.start_ts IN - \* Pessimistic lock is allowed only if no stale lock exists. If - \* there is one, wait until ClientCheckTxnStatus to clean it up. - IF key_lock[k] = {} THEN - /\ LET - latest_write == {w \in key_write[k] : \A w2 \in key_write[k] : w.ts >= w2.ts} - - all_commits == {w \in key_write[k] : w.type = "write"} - latest_commit == {w \in all_commits : \A w2 \in all_commits : w.ts >= w2.ts} - commit_read == {w \in all_commits : \A w2 \in all_commits : w2.ts <= w.ts \/ w2.ts >= start_ts} - IN + \* Pessimistic lock is allowed only if no stale lock exists. If + \* there is one, wait until ClientCheckTxnStatus to clean it up. + IF key_lock[k] = {} THEN + /\ LET + all_commits == {w \in key_write[k] : w.type = "write"} + latest_commit == {w \in all_commits : \A w2 \in all_commits : w.ts >= w2.ts} + commit_read == {w \in all_commits : \A w2 \in all_commits : w2.ts <= w.ts \/ w2.ts >= start_ts} + IN IF \E w \in key_write[k] : w.start_ts = start_ts /\ w.type = "rollback" THEN \* If corresponding rollback record is found, which @@ -423,6 +394,7 @@ ServerLockKey == key |-> k, value_ts |-> value_ts]) /\ UNCHANGED <> + \* The response message is lost. \/ /\ UNCHANGED <> ELSE /\ \/ /\ ClientLockedKey([start_ts |-> start_ts, @@ -430,32 +402,57 @@ ServerLockKey == key |-> k, value_ts |-> NoneTs]) /\ UNCHANGED <> + \* The response message is lost. \/ /\ UNCHANGED <> \* Otherwise, reject the request and let client to retry \* with new for_update_ts. \/ \E w \in latest_commit : - \/ /\ w.ts > req.for_update_ts - /\ \/ /\ ~ \E w2 \in all_commits : w2.start_ts = req.start_ts - /\ ClientRetryLockKey([start_ts |-> start_ts, - type |-> "lock_failed_write_conflict", - key |-> k, - latest_commit_ts |-> w.ts]) - /\ UNCHANGED <> - \/ /\ UNCHANGED <> - + /\ w.ts > req.for_update_ts + /\ \/ /\ ~ \E w2 \in all_commits : w2.start_ts = req.start_ts + /\ ClientRetryLockKey([start_ts |-> start_ts, + type |-> "lock_failed_write_conflict", + key |-> k, + latest_commit_ts |-> w.ts]) + /\ UNCHANGED <> + \* The response message is lost. + \/ /\ UNCHANGED <> ELSE LET l == CHOOSE lock \in key_lock[k] : TRUE IN - /\ l.ts /= start_ts - /\ \/ /\ ClientRetryLockKey([start_ts |-> start_ts, - type |-> "lock_failed_has_lock", - primary |-> l.primary, - key |-> k, - lock_ts |-> l.ts, - lock_type |-> l.type]) - /\ UNCHANGED <> - \/ /\ UNCHANGED <> + IF l.ts /= start_ts + THEN + \/ /\ ClientRetryLockKey([start_ts |-> start_ts, + type |-> "lock_failed_has_lock", + primary |-> l.primary, + key |-> k, + lock_ts |-> l.ts, + lock_type |-> l.type]) + /\ UNCHANGED <> + \* The response message is lost. + \/ /\ UNCHANGED <> + ELSE + LET + all_commits == {w \in key_write[k] : w.type = "write"} + commit_read == {w \in all_commits : \A w2 \in all_commits : w2.ts <= w.ts \/ w2.ts >= start_ts} + commit_record == CHOOSE value \in commit_read : TRUE + value_ts == commit_record.start_ts + IN + /\ IF ~ commit_read = {} + THEN + /\ \/ /\ ClientLockedKey([start_ts |-> start_ts, + type |-> "locked_key", + key |-> k, + value_ts |-> value_ts]) + /\ UNCHANGED <> + \/ /\ UNCHANGED <> + ELSE + /\ \/ /\ ClientLockedKey([start_ts |-> start_ts, + type |-> "locked_key", + key |-> k, + value_ts |-> NoneTs]) + /\ UNCHANGED <> + \/ /\ UNCHANGED <> ServerReadKey == \E req \in req_msgs : @@ -469,33 +466,33 @@ ServerReadKey == all_commits == {w \in key_write[k] : w.type = "write"} commit_read == {w \in all_commits : w.ts < start_ts /\ \A w2 \in all_commits : w2.ts <= w.ts \/ w2.ts >= start_ts} IN - /\ IF \/ key_lock[k] = {} - \/ \E l \in key_lock[k] : l.type = "lock_key" - THEN - IF commit_read = {} + /\ IF \/ key_lock[k] = {} + \/ \E l \in key_lock[k] : l.type = "lock_key" THEN - /\ SendResp([start_ts |-> start_ts, + IF commit_read = {} + THEN + /\ SendResp([start_ts |-> start_ts, type |-> "get_resp", key |-> k, value_ts |-> NoneTs]) - /\ UNCHANGED <> + /\ UNCHANGED <> + ELSE + /\ SendResps({[start_ts |-> start_ts, + type |-> "get_resp", + key |-> k, + value_ts |-> w.start_ts] : w \in commit_read}) + /\ UNCHANGED <> ELSE - /\ SendResps({[start_ts |-> start_ts, - type |-> "get_resp", - key |-> k, - value_ts |-> w.start_ts] : w \in commit_read}) - /\ UNCHANGED <> - ELSE - LET - l == CHOOSE lock \in key_lock[k] : TRUE - IN - \/ /\ ClientReadFailedCheckTxnStatus([start_ts |-> start_ts, - type |-> "get_failed", - primary |-> l.primary, - key |-> k, - lock_ts |-> l.ts]) - /\ UNCHANGED <> - \/ /\ UNCHANGED <> + LET + l == CHOOSE lock \in key_lock[k] : TRUE + IN + \/ /\ ClientReadFailedCheckTxnStatus([start_ts |-> start_ts, + type |-> "get_failed", + primary |-> l.primary, + key |-> k, + lock_ts |-> l.ts]) + /\ UNCHANGED <> + \/ /\ UNCHANGED <> ServerPrewritePessimistic == \E req \in req_msgs : @@ -504,26 +501,27 @@ ServerPrewritePessimistic == k == req.key start_ts == req.start_ts IN - \* Pessimistic prewrite is allowed if pressimistic lock is - \* acquired, or, there's no lock, and no write record whose - \* commit_ts >= start_ts otherwise abort the transaction. - /\ IF - \/ /\ ~ \E w \in key_write[k] : w.ts >= start_ts - /\ ~ \E l \in key_lock[k] : TRUE - \/ \E l \in key_lock[k] : - /\ l.ts = start_ts - THEN - \/ /\ key_lock' = [key_lock EXCEPT ![k] = {[ts |-> start_ts, - primary |-> req.primary, - type |-> "prewrite_pessimistic", - min_commit_ts |-> NoneTs]}] - /\ key_data' = [key_data EXCEPT ![k] = @ \union {start_ts}] - /\ ClientPrewrited([start_ts |-> start_ts, type |-> "prewrited", key |-> k]) - /\ UNCHANGED <> - \/ /\ UNCHANGED <> - ELSE - /\ SendResp([start_ts |-> start_ts, type |-> "prewrite_aborted"]) - /\ UNCHANGED <> + \* Pessimistic prewrite is allowed if pressimistic lock is + \* acquired, or, there's no lock, and no write record whose + \* commit_ts >= start_ts otherwise abort the transaction. + /\ IF + \/ /\ ~ \E w \in key_write[k] : w.ts >= start_ts + /\ key_lock[k] = {} + \/ \E l \in key_lock[k] : + /\ l.ts = start_ts + /\ l.type = "lock_key" + THEN + \/ /\ key_lock' = [key_lock EXCEPT ![k] = {[ts |-> start_ts, + primary |-> req.primary, + type |-> "prewrite_pessimistic", + min_commit_ts |-> NoneTs]}] + /\ key_data' = [key_data EXCEPT ![k] = @ \union {start_ts}] + /\ ClientPrewrited([start_ts |-> start_ts, type |-> "prewrited", key |-> k]) + /\ UNCHANGED <> + \/ /\ UNCHANGED <> + ELSE + /\ SendResp([start_ts |-> start_ts, type |-> "prewrite_aborted"]) + /\ UNCHANGED <> ServerPrewriteOptimistic == \E req \in req_msgs : @@ -532,11 +530,11 @@ ServerPrewriteOptimistic == k == req.key start_ts == req.start_ts IN - /\ IF \E w \in key_write[k] : w.ts >= start_ts - THEN + /\ IF \E w \in key_write[k] : w.ts >= start_ts + THEN /\ SendResp([start_ts |-> start_ts, type |-> "prewrite_aborted"]) /\ UNCHANGED <> - ELSE + ELSE \* Optimistic prewrite is allowed only if no stale lock exists. If \* there is one, wait until ClientCheckTxnStatus to clean it up. /\ \/ key_lock[k] = {} @@ -557,31 +555,32 @@ ServerCommit == pk == req.primary start_ts == req.start_ts IN - IF \E w \in key_write[pk] : w.start_ts = start_ts /\ w.type = "write" - THEN - \* Key has already been committed. Do nothing. - /\ SendResp([start_ts |-> start_ts, type |-> "committed"]) - /\ UNCHANGED <> - ELSE - IF \E l \in key_lock[pk] : - /\ l.ts = start_ts - THEN - IF \E l \in key_lock[pk] : l.ts = start_ts /\ next_ts > l.min_commit_ts - THEN - \* Commit the key only if the prewrite lock exists. - /\ commit(pk, start_ts, req.commit_ts) - /\ SendResp([start_ts |-> start_ts, type |-> "committed"]) - /\ UNCHANGED <> - ELSE - LET - l == CHOOSE l \in key_lock[pk] : TRUE - IN - /\ SendResps([start_ts |-> start_ts, type |-> "commit_failed", min_commit_ts |-> l.min_commit_ts]) - /\ UNCHANGED <> - ELSE - \* Otherwise, abort the transaction. - /\ SendResp([start_ts |-> start_ts, type |-> "commit_aborted"]) - /\ UNCHANGED <> + IF \E w \in key_write[pk] : w.start_ts = start_ts /\ w.type = "write" + THEN + \* Key has already been committed. Do nothing. + /\ SendResp([start_ts |-> start_ts, type |-> "committed"]) + /\ UNCHANGED <> + ELSE + IF \E l \in key_lock[pk] : + /\ l.ts = start_ts + /\ ~ l.type = "lock_key" + THEN + IF \E l \in key_lock[pk] : l.ts = start_ts /\ next_ts > l.min_commit_ts + THEN + \* Commit the key only if the prewrite lock exists. + /\ commit(pk, start_ts, req.commit_ts) + /\ SendResp([start_ts |-> start_ts, type |-> "committed"]) + /\ UNCHANGED <> + ELSE + LET + l == CHOOSE l \in key_lock[pk] : TRUE + IN + /\ SendResps({[start_ts |-> start_ts, type |-> "commit_failed", min_commit_ts |-> l.min_commit_ts]}) + /\ UNCHANGED <> + ELSE + \* Otherwise, abort the transaction. + /\ SendResp([start_ts |-> start_ts, type |-> "commit_aborted"]) + /\ UNCHANGED <> \* Clean up the stale transaction by checking the status of the primary key. \* @@ -600,48 +599,38 @@ ServerCheckTxnStatus == \E req \in req_msgs : /\ req.type = "check_txn_status" /\ LET - pk == req.primary - start_ts == req.start_ts - committed == {w \in key_write[pk] : w.start_ts = start_ts /\ w.type = "write"} - caller_start_ts == req.caller_start_ts + pk == req.primary + start_ts == req.start_ts + committed == {w \in key_write[pk] : w.start_ts = start_ts /\ w.type = "write"} + caller_start_ts == req.caller_start_ts IN - IF \E lock \in key_lock[pk] : lock.ts = start_ts - \* Found the matching lock. - THEN - \/ - IF - \* Pessimistic lock will be unlocked directly without rollback record. - \E lock \in key_lock[pk] : - /\ lock.ts = start_ts - /\ lock.type = "lock_key" - /\ req.resolving_pessimistic_lock = TRUE - THEN - /\ unlock_key(pk) - \* /\ SendResp([type |-> "check_txn_status_resp", - \* start_ts |-> start_ts, - \* action |-> "pessimistic_rollbacked"]) - /\ UNCHANGED <> - ELSE + IF \E lock \in key_lock[pk] : lock.ts = start_ts + \* Found the matching lock. + THEN + \/ + IF + \* Pessimistic lock will be unlocked directly without rollback record. + \E lock \in key_lock[pk] : + /\ lock.ts = start_ts + /\ lock.type = "lock_key" + /\ req.resolving_pessimistic_lock = TRUE + THEN + /\ unlock_key(pk) + /\ UNCHANGED <> + ELSE /\ rollback(pk, start_ts) /\ SendReqs({[type |-> "resolve_rollbacked", start_ts |-> start_ts, primary |-> pk]}) - \* /\ SendResp([type |-> "check_txn_status_resp", - \* start_ts |-> start_ts, - \* action |-> "rollbacked"]) /\ UNCHANGED <> \/ \* Push min_commit_ts. - /\ \A c \in CLIENT : client_ts[c].start_ts = start_ts /\ \E lock \in key_lock[pk] : /\ lock.min_commit_ts < caller_start_ts /\ key_lock' = [key_lock EXCEPT ![pk] = {[ts |-> lock.ts, - type |-> lock.type, - primary |-> lock.primary, - min_commit_ts |-> caller_start_ts]}] - \* /\ SendResp([type |-> "check_txn_status_resp", - \* start_ts |-> start_ts, - \* action |-> "min_commit_ts_pushed"]) + type |-> lock.type, + primary |-> lock.primary, + min_commit_ts |-> caller_start_ts + 1]}] /\ UNCHANGED <> \* Lock not found or start_ts on the lock mismatches. ELSE @@ -650,23 +639,14 @@ ServerCheckTxnStatus == start_ts |-> start_ts, primary |-> pk, commit_ts |-> w.ts] : w \in committed}) - \* /\ SendResp([type |-> "check_txn_status_resp", - \* start_ts |-> start_ts, - \* action |-> "committed"]) /\ UNCHANGED <> ELSE IF req.resolving_pessimistic_lock = TRUE THEN - \* /\ SendResp([type |-> "check_txn_status_resp", - \* start_ts |-> start_ts, - \* action |-> "lock_not_exist_do_nothing"]) /\ UNCHANGED <> ELSE /\ rollback(pk, start_ts) /\ SendReqs({[type |-> "resolve_rollbacked", start_ts |-> start_ts, primary |-> pk]}) - \* /\ SendResp([type |-> "check_txn_status_resp", - \* start_ts |-> start_ts, - \* action |-> "rollbacked"]) /\ UNCHANGED <> ServerResolveCommitted == @@ -675,12 +655,12 @@ ServerResolveCommitted == /\ LET start_ts == req.start_ts IN - \E k \in KEY: - \E l \in key_lock[k] : - /\ l.primary = req.primary - /\ l.ts = start_ts - /\ commit(k, start_ts, req.commit_ts) - /\ UNCHANGED <> + \E k \in KEY: + \E l \in key_lock[k] : + /\ l.primary = req.primary + /\ l.ts = start_ts + /\ commit(k, start_ts, req.commit_ts) + /\ UNCHANGED <> ServerResolveRollbacked == \E req \in req_msgs : @@ -688,12 +668,12 @@ ServerResolveRollbacked == /\ LET start_ts == req.start_ts IN - \E k \in KEY: - \E l \in key_lock[k] : - /\ l.primary = req.primary - /\ l.ts = start_ts - /\ rollback(k, start_ts) - /\ UNCHANGED <> + \E k \in KEY: + \E l \in key_lock[k] : + /\ l.primary = req.primary + /\ l.ts = start_ts + /\ rollback(k, start_ts) + /\ UNCHANGED <> ----------------------------------------------------------------------------- \* Specification @@ -817,46 +797,61 @@ MsgTsConsistency == /\ \A resp \in resp_msgs : resp.start_ts <= next_ts OptimisticReadSnapshotIsolation == - \A resp \in resp_msgs : resp.type = "get_resp" => - /\ LET - start_ts == resp.start_ts - key == resp.key - all_commits_before_start_ts == {w \in key_write[key] : w.type = "write" /\ w.ts <= start_ts} - latest_commit_before_start_ts == - {w \in all_commits_before_start_ts : - \A w2 \in all_commits_before_start_ts : - w.ts >= w2.ts} - IN - IF Cardinality(latest_commit_before_start_ts) = 1 - THEN - \A w \in latest_commit_before_start_ts: w.start_ts = resp.value_ts - ELSE IF Cardinality(latest_commit_before_start_ts) = 0 - THEN - resp.value_ts = NoneTs - ELSE - FALSE - -PessimisticReadSnapshotIsolation == - \A resp \in resp_msgs : resp.type = "key_locked" => - /\ LET - start_ts == resp.start_ts - key == resp.key - all_commits_before_start_ts == {w \in key_write[key] : w.type = "write" /\ w.ts <= start_ts} - latest_commit_before_start_ts == - {w \in all_commits_before_start_ts : - \A w2 \in all_commits_before_start_ts : - w.ts >= w2.ts} - IN - IF Cardinality(latest_commit_before_start_ts) = 1 - THEN - \A w \in latest_commit_before_start_ts: w.start_ts = resp.value_ts - ELSE IF Cardinality(latest_commit_before_start_ts) = 0 - THEN - resp.value_ts = NoneTs - ELSE - FALSE + \A resp \in resp_msgs : + IF /\ resp.type = "get_resp" + /\ \E resp2 \in resp_msgs : + /\ resp2.type = "committed" + /\ resp2.start_ts = resp.start_ts + THEN + LET + start_ts == resp.start_ts + key == resp.key + all_commits_before_start_ts == {w \in key_write[key] : w.type = "write" /\ w.ts <= start_ts} + latest_commit_before_start_ts == + {w \in all_commits_before_start_ts : + \A w2 \in all_commits_before_start_ts : + w.ts >= w2.ts} + IN + IF Cardinality(latest_commit_before_start_ts) = 1 + THEN + \A w \in latest_commit_before_start_ts: w.start_ts = resp.value_ts + ELSE IF Cardinality(latest_commit_before_start_ts) = 0 + THEN + resp.value_ts = NoneTs + ELSE + FALSE + ELSE + TRUE + +PessimisticReadSnapshotIsolation == + \A resp \in resp_msgs : + IF /\ resp.type = "locked_key" + /\ \E resp2 \in resp_msgs : + /\ resp2.type = "committed" + /\ resp2.start_ts = resp.start_ts + THEN + LET + start_ts == resp.start_ts + client == CHOOSE c \in {c \in CLIENT : client_ts[c] = start_ts} : TRUE + for_update_ts == client_ts[client].for_update_ts + key == resp.key + all_commits_before_for_update_ts == {w \in key_write[key] : w.type = "write" /\ w.ts <= for_update_ts} + latest_commit_before_for_update_ts == + {w \in all_commits_before_for_update_ts : + \A w2 \in all_commits_before_for_update_ts : + w.ts >= w2.ts} + IN + IF Cardinality(latest_commit_before_for_update_ts) = 1 + THEN + \A w \in latest_commit_before_for_update_ts: w.start_ts = resp.value_ts + ELSE IF Cardinality(latest_commit_before_for_update_ts) = 0 + THEN + resp.value_ts = NoneTs + ELSE + FALSE + ELSE + TRUE - ReadSnapshotIsolation == OptimisticReadSnapshotIsolation /\ PessimisticReadSnapshotIsolation diff --git a/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test1.launch b/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test1.launch index e938e9b..a609b01 100644 --- a/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test1.launch +++ b/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test1.launch @@ -12,7 +12,7 @@ - + @@ -33,7 +33,6 @@ - diff --git a/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test2.launch b/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test2.launch index 986642c..33d94a0 100644 --- a/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test2.launch +++ b/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test2.launch @@ -12,11 +12,11 @@ - + - + @@ -33,11 +33,10 @@ - - + diff --git a/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test3.launch b/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test3.launch index 4727595..5f533fb 100644 --- a/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test3.launch +++ b/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test3.launch @@ -12,7 +12,7 @@ - + @@ -33,7 +33,6 @@ - diff --git a/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test4.launch b/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test4.launch index cf25ce9..779d76d 100644 --- a/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test4.launch +++ b/DistributedTransaction/DistributedTransaction.toolbox/DistributedTransaction___Test4.launch @@ -12,7 +12,7 @@ - + @@ -33,8 +33,6 @@ - - From 959dacc552ab33826b2d2e3ad089e92f82deb6ad Mon Sep 17 00:00:00 2001 From: Andy Lok Date: Tue, 17 Aug 2021 17:54:16 +0800 Subject: [PATCH 11/30] restucture distributed transaction spec Signed-off-by: Andy Lok --- .../DistributedTransaction.tla | 811 ++++++++---------- 1 file changed, 374 insertions(+), 437 deletions(-) diff --git a/DistributedTransaction/DistributedTransaction.tla b/DistributedTransaction/DistributedTransaction.tla index 4de9d33..eaf19eb 100644 --- a/DistributedTransaction/DistributedTransaction.tla +++ b/DistributedTransaction/DistributedTransaction.tla @@ -1,5 +1,5 @@ ----------------------- MODULE DistributedTransaction ----------------------- -EXTENDS Integers, FiniteSets +EXTENDS Integers, FiniteSets, TLC \* The set of all keys. CONSTANTS KEY @@ -10,7 +10,7 @@ CLIENT == PESSIMISTIC_CLIENT \union OPTIMISTIC_CLIENT \* Functions that maps a client to keys it wants to read, write. \* representing the involved keys of each client. -\* Note that "read" here stands for optimistic read, and for +\* Note that "read" here stands for optimistic read, and for \* pessimistic client(s), the constants equals an empty set. CONSTANTS CLIENT_READ_KEY, CLIENT_WRITE_KEY CLIENT_KEY == [c \in CLIENT |-> CLIENT_READ_KEY[c] \union CLIENT_WRITE_KEY[c]] @@ -25,37 +25,37 @@ Ts == Nat \ {0} NoneTs == 0 \* The algorithm is easier to understand in terms of the set of msgs of -\* all messages that have ever been sent. A more accurate model would use +\* all messages that have ever been sent. A more accurate model would use \* one or more variables to represent the messages actually in transit, \* and it would include actions representing message loss and duplication \* as well as message receipt. \* \* In the current spec, there is no need to model message loss because we -\* are mainly concerned with the algorithm's safety property. The safety +\* are mainly concerned with the algorithm's safety property. The safety \* part of the spec says only what messages may be received and does not -\* assert that any message actually is received. Thus, there is no +\* assert that any message actually is received. Thus, there is no \* difference between a lost message and one that is never received. VARIABLES req_msgs VARIABLES resp_msgs -\* key_data[k] is the set of multi-version data of the key. Since we +\* key_data[k] is the set of multi-version data of the key. Since we \* don't care about the concrete value of data, a start_ts is sufficient \* to represent one data version. VARIABLES key_data -\* key_lock[k] is the set of lock (zero or one element). A lock is of a -\* record of [ts: start_ts, primary: key, type: lock_type]. If primary -\* equals to k, it is a primary lock, otherwise secondary lock. lock_type +\* key_lock[k] is the set of lock (zero or one element). A lock is of a +\* record of [ts: start_ts, primary: key, type: lock_type]. If primary +\* equals to k, it is a primary lock, otherwise secondary lock. lock_type \* is one of {"prewrite_optimistic", "prewrite_pessimistic", "lock_key"}. \* lock_key denotes the pessimistic lock performed by ServerLockKey \* action, the prewrite_pessimistic denotes percolator optimistic lock \* who is transformed from a lock_key lock by action \* ServerPrewritePessimistic, and prewrite_optimistic denotes the \* classic optimistic lock. -\* +\* \* In TiKV, key_lock has an additional for_update_ts field and the -\* LockType is of four variants: +\* LockType is of four variants: \* {"PUT", "DELETE", "LOCK", "PESSIMISTIC"}. -\* +\* \* In the spec, we abstract them by: \* (1) LockType \in {"PUT", "DELETE", "LOCK"} /\ for_update_ts = 0 <=> \* type = "prewrite_optimistic" @@ -68,10 +68,10 @@ VARIABLES key_data \* TODO: upd min_commit_ts comment. VARIABLES key_lock \* key_write[k] is a sequence of commit or rollback record of the key. -\* It's a record of [ts, start_ts, type, [protected]]. type can be either -\* "write" or "rollback". ts represents the commit_ts of "write" record. -\* Otherwise, ts equals to start_ts on "rollback" record. "rollback" -\* record has an additional protected field. protected signifies the +\* It's a record of [ts, start_ts, type, [protected]]. type can be either +\* "write" or "rollback". ts represents the commit_ts of "write" record. +\* Otherwise, ts equals to start_ts on "rollback" record. "rollback" +\* record has an additional protected field. protected signifies the \* rollback record would not be collapsed. VARIABLES key_write @@ -87,7 +87,7 @@ VARIABLES client_ts VARIABLES client_key \* next_ts is a globally monotonically increasing integer, representing -\* the virtual clock of transactions. In practice, the variable is +\* the virtual clock of transactions. In practice, the variable is \* maintained by PD, the time oracle of a cluster. VARIABLES next_ts @@ -132,42 +132,47 @@ ReqMessages == \union [start_ts : Ts, caller_start_ts : Ts \union {NoneTs}, primary : KEY, type : {"check_txn_status"}, resolving_pessimistic_lock : BOOLEAN] -\* The RespMessages defined here is only a subset of the "actual" one in spec. Other resp messages are -\* transmitted(passed) by function arguments. By doing so, we cannot check the retransmission or +\* The RespMessages defined here is only a subset of the "actual" one in spec. Other resp messages are +\* transmitted(passed) by function arguments. By doing so, we cannot check the retransmission or \* delay(we do can check the message lost) of these messages. The reason for this is that messages -\* like "lock_key_failed_has_lock" "lock_key_failed_write_conflit" are just too much, which makes the -\* model checking impossible to finish. Considering the delay or retransmit of them is not that serious +\* like "lock_key_failed_has_lock" "lock_key_failed_write_conflict" are just too much, which makes the +\* model checking impossible to finish. Considering the delay or retransmit of them is not that serious \* to the transaction safety, we made the decision to reduce final "distinct state number". RespMessages == - [start_ts : Ts, type : {"get_resp"}, key : KEY, value_ts : Ts \union {NoneTs}] - \union [start_ts : Ts, type : {"locked_key"}, key : KEY, value_ts : Ts \union {NoneTs}] + [start_ts : Ts, type : {"read_success"}, key : KEY, value_ts : Ts \union {NoneTs}] \union [start_ts : Ts, type : {"committed", "commit_aborted", "prewrite_aborted", "lock_key_aborted"}] - \union [start_ts : Ts, type : {"commit_failed"}, min_commit_ts : Ts] + +DirectRespMessages == + [start_ts : Ts, type : {"read_failed"}, key : KEY, lock_primary: KEY, lock_ts : Ts] + \union [start_ts : Ts, type : {"lock_key_success"}, key : KEY] + \union [start_ts : Ts, type : {"lock_key_failed_has_lock"}, key : KEY, lock_primary: KEY, lock_ts : Ts] + \union [start_ts : Ts, type : {"lock_key_failed_write_conflict"}, key : KEY, latest_commit_ts : Ts] + \union [start_ts : Ts, type : {"prewrited"}, key : KEY] + \union [start_ts : Ts, type : {"commit_ts_expired"}, min_commit_ts : Ts] TypeOK == /\ req_msgs \in SUBSET ReqMessages /\ resp_msgs \in SUBSET RespMessages /\ key_data \in [KEY -> SUBSET Ts] - /\ key_lock \in [KEY -> SUBSET [ts : Ts, - primary : KEY, + /\ key_lock \in [KEY -> SUBSET [start_ts : Ts, + primary : KEY, \* As defined above, Ts == Nat \ 0, here we use 0 \* to indicates that there's no min_commit_ts limit. min_commit_ts : Ts \union {NoneTs}, type : {"prewrite_optimistic", "prewrite_pessimistic", "lock_key"}]] - \* At most one lock in key_lock[k] - /\ \A k \in KEY: Cardinality(key_lock[k]) <= 1 /\ key_write \in [KEY -> SUBSET ( [ts : Ts, start_ts : Ts, type : {"write"}] \union [ts : Ts, start_ts : Ts, type : {"rollback"}, protected : BOOLEAN])] - /\ client_state \in [CLIENT -> {"init", "locking", "prewriting", "committing"}] + \* At most one lock in key_lock[k] + /\ \A k \in KEY: Cardinality(key_lock[k]) <= 1 + /\ client_state \in [CLIENT -> {"init", "read_finished", "locking", "prewriting", "committing"}] /\ client_ts \in [CLIENT -> [start_ts : Ts \union {NoneTs}, commit_ts : Ts \union {NoneTs}, - for_update_ts : Ts \union {NoneTs}, - min_commit_ts : Ts \union {NoneTs}]] + for_update_ts : Ts \union {NoneTs}]] /\ client_key \in [CLIENT -> [locking: SUBSET KEY, prewriting : SUBSET KEY]] /\ \A c \in CLIENT: client_key[c].locking \intersect client_key[c].prewriting = {} /\ next_ts \in Ts @@ -175,13 +180,16 @@ TypeOK == /\ req_msgs \in SUBSET ReqMessages \* Client Actions \* Once the get request is sent, it exist permanently in the req_msgs. -ClientReadKey(c) == - /\ ~ client_state[c] = "init" +ClientReadOptimistic(c) == + /\ client_state[c] = "init" + /\ client_ts' = [client_ts EXCEPT ![c].start_ts = next_ts] + /\ client_state' = [client_state EXCEPT ![c] = "read_finished"] + /\ next_ts' = next_ts + 1 /\ SendReqs({[type |-> "get", - start_ts |-> client_ts[c].start_ts, + start_ts |-> client_ts'[c].start_ts, primary |-> CLIENT_PRIMARY[c], key |-> k] : k \in CLIENT_READ_KEY[c]}) - /\ UNCHANGED <> + /\ UNCHANGED <> ClientLockKey(c) == /\ client_state[c] = "init" @@ -197,38 +205,46 @@ ClientLockKey(c) == for_update_ts |-> client_ts'[c].for_update_ts] : k \in CLIENT_WRITE_KEY[c]}) /\ UNCHANGED <> -ClientLockedKey(resp) == - LET - c == CHOOSE c \in {c \in CLIENT : client_ts[c].start_ts = resp.start_ts} : TRUE - IN - /\ client_state[c] = "locking" - /\ resp.type = "locked_key" - /\ resp.start_ts = client_ts[c].start_ts - /\ resp.key \in client_key[c].locking - /\ client_key' = [client_key EXCEPT ![c].locking = @ \ {resp.key}] - -ClientRetryLockKey(resp) == - LET - c == CHOOSE c \in {c \in CLIENT : client_ts[c].start_ts = resp.start_ts} : TRUE - IN - \/ /\ client_state[c] = "locking" - /\ resp.type = "lock_failed_has_lock" - /\ resp.start_ts = client_ts[c].start_ts - /\ resp.lock_ts /= client_ts[c].start_ts - /\ SendReqs({[type |-> "check_txn_status", - start_ts |-> resp.lock_ts, - caller_start_ts |-> NoneTs, - primary |-> resp.primary, - resolving_pessimistic_lock |-> resp.lock_type = "lock_key"]}) - \/ /\ resp.type = "lock_failed_write_conflict" - /\ resp.start_ts = client_ts[c].start_ts - /\ resp.latest_commit_ts > client_ts[c].for_update_ts - /\ client_ts' = [client_ts EXCEPT ![c].for_update_ts = resp.latest_commit_ts] - /\ SendReqs({[type |-> "lock_key", - start_ts |-> client_ts'[c].start_ts, - primary |-> CLIENT_PRIMARY[c], - key |-> resp.key, - for_update_ts |-> client_ts'[c].for_update_ts]}) +ClientLockKeySuccess(resp) == + /\ Assert(resp.type = "lock_key_success", "Message Type Error") + /\ \/ UNCHANGED <> + \/ \E c \in CLIENT: + /\ client_ts[c].start_ts = resp.start_ts + /\ client_state[c] = "locking" + /\ resp.key \in client_key[c].locking + /\ client_key' = [client_key EXCEPT ![c].locking = @ \ {resp.key}] + /\ UNCHANGED <> + +ClientRetryLockKeyHasLock(resp) == + /\ Assert(resp.type = "lock_key_failed_has_lock", "Message Type Error") + /\ \/ UNCHANGED <> + \/ \E c \in CLIENT: + /\ client_ts[c].start_ts = resp.start_ts + /\ client_state[c] = "locking" + /\ resp.key \in client_key[c].locking + /\ SendReqs({[type |-> "check_txn_status", + start_ts |-> resp.lock_ts, + caller_start_ts |-> NoneTs, + primary |-> resp.lock_primary, + resolving_pessimistic_lock |-> resp.lock_type = "lock_key"]}) + /\ UNCHANGED <> + +ClientRetryLockKeyWriteConflict(resp) == + /\ Assert(resp.type = "lock_key_failed_write_conflict", "Message Type Error") + /\ \/ UNCHANGED <> + \/ \E c \in CLIENT: + /\ client_ts[c].start_ts = resp.start_ts + /\ client_state[c] = "locking" + /\ resp.key \in client_key[c].locking + /\ resp.latest_commit_ts > client_ts[c].for_update_ts + /\ client_ts' = [client_ts EXCEPT ![c].for_update_ts = next_ts] + /\ next_ts' = next_ts + 1 + /\ SendReqs({[type |-> "lock_key", + start_ts |-> client_ts'[c].start_ts, + primary |-> CLIENT_PRIMARY[c], + key |-> resp.key, + for_update_ts |-> client_ts'[c].for_update_ts]}) + /\ UNCHANGED <> ClientPrewritePessimistic(c) == /\ client_state[c] = "locking" @@ -241,41 +257,39 @@ ClientPrewritePessimistic(c) == key |-> k] : k \in CLIENT_WRITE_KEY[c]}) /\ UNCHANGED <> -ClientReadFailedCheckTxnStatus(resp) == - LET - c == CHOOSE c \in {c \in CLIENT : client_ts[c].start_ts = resp.start_ts} : TRUE - IN - /\ resp.type = "get_failed" - /\ SendReqs({[type |-> "check_txn_status", - start_ts |-> resp.lock_ts, - caller_start_ts |-> client_ts[c].start_ts, - primary |-> resp.primary, - \* For a read failure, there can't be a pessimistic lock, since - \* when a read mets it, it returns the most recent write record. - resolving_pessimistic_lock |-> FALSE]}) +ClientReadOptimisticFailed(resp) == + /\ Assert(resp.type = "read_failed", "Message Type Error") + /\ \/ UNCHANGED <> + \/ \E c \in CLIENT: + /\ client_ts[c].start_ts = resp.start_ts + /\ SendReqs({[type |-> "check_txn_status", + start_ts |-> resp.lock_ts, + caller_start_ts |-> client_ts[c].start_ts, + primary |-> resp.lock_primary, + \* For a read failure, there can't be a pessimistic lock, since + \* when the read simply ignores pessimistic lock. + resolving_pessimistic_lock |-> FALSE]}) + /\ UNCHANGED <> ClientPrewriteOptimistic(c) == - /\ client_state[c] = "init" + /\ client_state[c] = "read_finished" /\ client_state' = [client_state EXCEPT ![c] = "prewriting"] /\ client_key' = [client_key EXCEPT ![c].prewriting = CLIENT_WRITE_KEY[c]] - /\ client_ts' = [client_ts EXCEPT ![c].start_ts = next_ts] - /\ next_ts' = next_ts + 1 /\ SendReqs({[type |-> "prewrite_optimistic", - start_ts |-> client_ts'[c].start_ts, + start_ts |-> client_ts[c].start_ts, primary |-> CLIENT_PRIMARY[c], key |-> k] : k \in CLIENT_WRITE_KEY[c]}) - /\ UNCHANGED <> + /\ UNCHANGED <> ClientPrewrited(resp) == - LET - c == CHOOSE c \in {c \in CLIENT : client_ts[c].start_ts = resp.start_ts} : TRUE - IN - /\ client_state[c] = "prewriting" - /\ client_key[c].locking = {} - /\ resp.type = "prewrited" - /\ resp.start_ts = client_ts[c].start_ts - /\ resp.key \in client_key[c].prewriting - /\ client_key' = [client_key EXCEPT ![c].prewriting = @ \ {resp.key}] + /\ Assert(resp.type = "prewrited", "Message Type Error") + /\ \/ UNCHANGED <> + \/ \E c \in CLIENT: + /\ client_ts[c].start_ts = resp.start_ts + /\ client_state[c] = "prewriting" + /\ resp.key \in client_key[c].prewriting + /\ client_key' = [client_key EXCEPT ![c].prewriting = @ \ {resp.key}] + /\ UNCHANGED <> ClientCommit(c) == /\ client_state[c] = "prewriting" @@ -290,18 +304,19 @@ ClientCommit(c) == /\ UNCHANGED <> ClientRetryCommit(resp) == - LET - c == CHOOSE c \in {c \in CLIENT : client_ts[c].start_ts = resp.start_ts} : TRUE - IN - /\ client_state[c] = "commiting" - /\ resp.type = "commit_failed" - /\ next_ts > resp.min_commit_ts - /\ client_ts' = [client_ts EXCEPT ![c].commit_ts = next_ts] - /\ next_ts' = next_ts + 1 - /\ SendReqs({[type |-> "commit", - start_ts |-> client_ts'[c].start_ts, - primary |-> CLIENT_PRIMARY[c], - commit_ts |-> client_ts'[c].commit_ts]}) + /\ Assert(resp.type = "commit_ts_expired", "Message Type Error") + /\ \/ UNCHANGED <> + \/ \E c \in CLIENT: + /\ client_ts[c].start_ts = resp.start_ts + /\ client_state[c] = "commiting" + /\ client_ts[c].commit_ts < resp.min_commit_ts + /\ client_ts' = [client_ts EXCEPT ![c].commit_ts = next_ts] + /\ next_ts' = next_ts + 1 + /\ SendReqs({[type |-> "commit", + start_ts |-> client_ts'[c].start_ts, + primary |-> CLIENT_PRIMARY[c], + commit_ts |-> client_ts'[c].commit_ts]}) + /\ UNCHANGED <> ----------------------------------------------------------------------------- \* Server Actions @@ -311,7 +326,7 @@ unlock_key(k) == commit(pk, start_ts, commit_ts) == \E l \in key_lock[pk] : - /\ l.ts = start_ts + /\ l.start_ts = start_ts /\ unlock_key(pk) /\ key_write' = [key_write EXCEPT ![pk] = @ \union {[ts |-> commit_ts, type |-> "write", @@ -321,25 +336,25 @@ commit(pk, start_ts, commit_ts) == rollback(k, start_ts) == LET \* Rollback record on the primary key of a pessimistic transaction - \* needs to be protected from being collapsed. If we can't decide + \* needs to be protected from being collapsed. If we can't decide \* whether it suffices that because the lock is missing or mismatched, \* it should also be protected. protected == \/ \E l \in key_lock[k] : - /\ l.ts = start_ts - /\ l.primary = k - /\ l.type \in {"lock_key", "prewrite_pessimistic"} - \/ \E l \in key_lock[k] : l.ts /= start_ts + /\ l.start_ts = start_ts + /\ l.primary = k + /\ l.type \in {"lock_key", "prewrite_pessimistic"} + \/ \E l \in key_lock[k] : l.start_ts /= start_ts \/ key_lock[k] = {} IN \* If a lock exists and has the same ts, unlock it. - /\ IF \E l \in key_lock[k] : l.ts = start_ts + /\ IF \E l \in key_lock[k] : l.start_ts = start_ts THEN unlock_key(k) ELSE UNCHANGED key_lock /\ key_data' = [key_data EXCEPT ![k] = @ \ {start_ts}] /\ IF ~ \E w \in key_write[k]: w.ts = start_ts THEN key_write' = [key_write EXCEPT - ![k] = + ![k] = \* collapse rollback (@ \ {w \in @ : w.type = "rollback" /\ ~ w.protected /\ w.ts < start_ts}) \* write rollback record @@ -350,20 +365,48 @@ rollback(k, start_ts) == ELSE UNCHANGED <> +\* In optimistic transaction, read_ts is start_ts, and, in pessimistic transaction, +\* read_ts is for_update_ts. +find_readable_commit(k, read_ts) == + LET + all_commits_before_read_ts == {w \in key_write[k] : w.type = "write" /\ w.ts <= read_ts} + latest_commit_before_read_ts == + {w \in all_commits_before_read_ts : + \A w2 \in all_commits_before_read_ts : + w.ts >= w2.ts} + IN + latest_commit_before_read_ts + +\* Read successfully. Return `value_ts = NoneTs` if the key has no value. +respond_read_success(k, start_ts, read_ts) == + LET + readable_commit == find_readable_commit(k, read_ts) + IN + \/ /\ readable_commit = {} + /\ SendResp([start_ts |-> start_ts, + type |-> "read_success", + key |-> k, + value_ts |-> NoneTs]) + \/ \E committed_record \in readable_commit: + /\ SendResp([start_ts |-> start_ts, + type |-> "read_success", + key |-> k, + value_ts |-> committed_record.ts]) + ServerLockKey == \E req \in req_msgs : /\ req.type = "lock_key" /\ LET k == req.key start_ts == req.start_ts + for_update_ts == req.for_update_ts IN - \* Pessimistic lock is allowed only if no stale lock exists. If - \* there is one, wait until ClientCheckTxnStatus to clean it up. - IF key_lock[k] = {} THEN + \* Pessimistic lock is allowed only if no stale lock exists. If + \* there is one, wait until ClientCheckTxnStatus to clean it up. + \/ /\ key_lock[k] = {} /\ LET - all_commits == {w \in key_write[k] : w.type = "write"} - latest_commit == {w \in all_commits : \A w2 \in all_commits : w.ts >= w2.ts} - commit_read == {w \in all_commits : \A w2 \in all_commits : w2.ts <= w.ts \/ w2.ts >= start_ts} + all_commits == {w \in key_write[k] : w.type = "write"} + latest_commit == {w \in all_commits : \A w2 \in all_commits : w.ts >= w2.ts} IN IF \E w \in key_write[k] : w.start_ts = start_ts /\ w.type = "rollback" THEN @@ -379,120 +422,65 @@ ServerLockKey == \* a new version is committed after for_update_ts, which \* violates Read Committed guarantee. \/ /\ ~ \E w \in latest_commit : w.ts > req.for_update_ts - /\ key_lock' = [key_lock EXCEPT ![k] = {[ts |-> start_ts, + /\ key_lock' = [key_lock EXCEPT ![k] = {[start_ts |-> start_ts, primary |-> req.primary, min_commit_ts |-> NoneTs, type |-> "lock_key"]}] - /\ IF ~ commit_read = {} - THEN - LET - commit_record == CHOOSE value \in commit_read : TRUE - value_ts == commit_record.start_ts - IN - /\ \/ /\ ClientLockedKey([start_ts |-> start_ts, - type |-> "locked_key", - key |-> k, - value_ts |-> value_ts]) - /\ UNCHANGED <> - \* The response message is lost. - \/ /\ UNCHANGED <> - ELSE - /\ \/ /\ ClientLockedKey([start_ts |-> start_ts, - type |-> "locked_key", - key |-> k, - value_ts |-> NoneTs]) - /\ UNCHANGED <> - \* The response message is lost. - \/ /\ UNCHANGED <> + /\ respond_read_success(k, start_ts, for_update_ts) + /\ ClientLockKeySuccess([start_ts |-> start_ts, + type |-> "lock_key_success", + key |-> k]) + /\ UNCHANGED <> \* Otherwise, reject the request and let client to retry \* with new for_update_ts. \/ \E w \in latest_commit : - /\ w.ts > req.for_update_ts - /\ \/ /\ ~ \E w2 \in all_commits : w2.start_ts = req.start_ts - /\ ClientRetryLockKey([start_ts |-> start_ts, - type |-> "lock_failed_write_conflict", - key |-> k, - latest_commit_ts |-> w.ts]) - /\ UNCHANGED <> - \* The response message is lost. - \/ /\ UNCHANGED <> - ELSE - LET - l == CHOOSE lock \in key_lock[k] : TRUE - IN - IF l.ts /= start_ts + /\ w.ts > req.for_update_ts + /\ ~ \E w2 \in all_commits : w2.start_ts = req.start_ts + /\ ClientRetryLockKeyWriteConflict([start_ts |-> start_ts, + type |-> "lock_key_failed_write_conflict", + key |-> k, + latest_commit_ts |-> w.ts]) + /\ UNCHANGED <> + \* Lock exsits + \/ \E l \in key_lock[k]: + IF l.start_ts = start_ts THEN - \/ /\ ClientRetryLockKey([start_ts |-> start_ts, - type |-> "lock_failed_has_lock", - primary |-> l.primary, - key |-> k, - lock_ts |-> l.ts, - lock_type |-> l.type]) - /\ UNCHANGED <> - \* The response message is lost. - \/ /\ UNCHANGED <> - ELSE - LET - all_commits == {w \in key_write[k] : w.type = "write"} - commit_read == {w \in all_commits : \A w2 \in all_commits : w2.ts <= w.ts \/ w2.ts >= start_ts} - commit_record == CHOOSE value \in commit_read : TRUE - value_ts == commit_record.start_ts - IN - /\ IF ~ commit_read = {} - THEN - /\ \/ /\ ClientLockedKey([start_ts |-> start_ts, - type |-> "locked_key", - key |-> k, - value_ts |-> value_ts]) - /\ UNCHANGED <> - \/ /\ UNCHANGED <> - ELSE - /\ \/ /\ ClientLockedKey([start_ts |-> start_ts, - type |-> "locked_key", - key |-> k, - value_ts |-> NoneTs]) - /\ UNCHANGED <> - \/ /\ UNCHANGED <> + /\ respond_read_success(k, start_ts, for_update_ts) + /\ ClientLockKeySuccess([start_ts |-> start_ts, + type |-> "lock_key_success", + key |-> k]) + /\ UNCHANGED <> + ELSE + /\ ClientRetryLockKeyHasLock([start_ts |-> start_ts, + type |-> "lock_key_failed_has_lock", + key |-> k, + lock_primary |-> l.primary, + lock_ts |-> l.start_ts, + lock_type |-> l.type]) + /\ UNCHANGED <> ServerReadKey == \E req \in req_msgs : /\ req.type = "get" - /\ \E c \in CLIENT : + /\ \E c \in CLIENT : /\ client_ts[c].start_ts = req.start_ts /\ LET k == req.key start_ts == req.start_ts - - all_commits == {w \in key_write[k] : w.type = "write"} - commit_read == {w \in all_commits : w.ts < start_ts /\ \A w2 \in all_commits : w2.ts <= w.ts \/ w2.ts >= start_ts} IN /\ IF \/ key_lock[k] = {} - \/ \E l \in key_lock[k] : l.type = "lock_key" + \/ \E l \in key_lock[k] : l.type = "lock_key" \/ l.start_ts = start_ts THEN - IF commit_read = {} - THEN - /\ SendResp([start_ts |-> start_ts, - type |-> "get_resp", - key |-> k, - value_ts |-> NoneTs]) - /\ UNCHANGED <> - ELSE - /\ SendResps({[start_ts |-> start_ts, - type |-> "get_resp", - key |-> k, - value_ts |-> w.start_ts] : w \in commit_read}) - /\ UNCHANGED <> + /\ respond_read_success(k, start_ts, start_ts) + /\ UNCHANGED <> ELSE - LET - l == CHOOSE lock \in key_lock[k] : TRUE - IN - \/ /\ ClientReadFailedCheckTxnStatus([start_ts |-> start_ts, - type |-> "get_failed", - primary |-> l.primary, - key |-> k, - lock_ts |-> l.ts]) - /\ UNCHANGED <> - \/ /\ UNCHANGED <> + \E l \in key_lock[k]: + /\ ClientReadOptimisticFailed([start_ts |-> start_ts, + type |-> "read_failed", + key |-> k, + lock_primary |-> l.primary, + lock_ts |-> l.start_ts]) + /\ UNCHANGED <> ServerPrewritePessimistic == \E req \in req_msgs : @@ -501,27 +489,28 @@ ServerPrewritePessimistic == k == req.key start_ts == req.start_ts IN - \* Pessimistic prewrite is allowed if pressimistic lock is - \* acquired, or, there's no lock, and no write record whose - \* commit_ts >= start_ts otherwise abort the transaction. - /\ IF - \/ /\ ~ \E w \in key_write[k] : w.ts >= start_ts - /\ key_lock[k] = {} - \/ \E l \in key_lock[k] : - /\ l.ts = start_ts - /\ l.type = "lock_key" - THEN - \/ /\ key_lock' = [key_lock EXCEPT ![k] = {[ts |-> start_ts, - primary |-> req.primary, - type |-> "prewrite_pessimistic", - min_commit_ts |-> NoneTs]}] - /\ key_data' = [key_data EXCEPT ![k] = @ \union {start_ts}] - /\ ClientPrewrited([start_ts |-> start_ts, type |-> "prewrited", key |-> k]) - /\ UNCHANGED <> - \/ /\ UNCHANGED <> - ELSE - /\ SendResp([start_ts |-> start_ts, type |-> "prewrite_aborted"]) - /\ UNCHANGED <> + \* Pessimistic prewrite is allowed if pressimistic lock is + \* acquired, or, there's no lock, and no write record whose + \* acquired, or, there's no lock, and no write record whose + \* acquired, or, there's no lock, and no write record whose + \* commit_ts >= start_ts, otherwise abort the transaction. + IF + \/ /\ ~ \E w \in key_write[k] : w.ts >= start_ts + /\ key_lock[k] = {} + \/ \E l \in key_lock[k] : + /\ l.start_ts = start_ts + /\ l.type = "lock_key" + THEN + /\ key_lock' = [key_lock EXCEPT ![k] = {[start_ts |-> start_ts, + primary |-> req.primary, + type |-> "prewrite_pessimistic", + min_commit_ts |-> NoneTs]}] + /\ key_data' = [key_data EXCEPT ![k] = @ \union {start_ts}] + /\ ClientPrewrited([start_ts |-> start_ts, type |-> "prewrited", key |-> k]) + /\ UNCHANGED <> + ELSE + /\ SendResp([start_ts |-> start_ts, type |-> "prewrite_aborted"]) + /\ UNCHANGED <> ServerPrewriteOptimistic == \E req \in req_msgs : @@ -530,23 +519,22 @@ ServerPrewriteOptimistic == k == req.key start_ts == req.start_ts IN - /\ IF \E w \in key_write[k] : w.ts >= start_ts - THEN - /\ SendResp([start_ts |-> start_ts, type |-> "prewrite_aborted"]) - /\ UNCHANGED <> - ELSE - \* Optimistic prewrite is allowed only if no stale lock exists. If - \* there is one, wait until ClientCheckTxnStatus to clean it up. - /\ \/ key_lock[k] = {} - \/ \E l \in key_lock[k] : l.ts = start_ts - /\ key_lock' = [key_lock EXCEPT ![k] = {[ts |-> start_ts, - primary |-> req.primary, - min_commit_ts |-> NoneTs, - type |-> "prewrite_optimistic"]}] - /\ key_data' = [key_data EXCEPT ![k] = @ \union {start_ts}] - /\ \/ /\ ClientPrewrited([start_ts |-> start_ts, type |-> "prewrited", key |-> k]) - /\ UNCHANGED <> - \/ /\ UNCHANGED <> + IF \E w \in key_write[k] : w.ts >= start_ts + THEN + /\ SendResp([start_ts |-> start_ts, type |-> "prewrite_aborted"]) + /\ UNCHANGED <> + ELSE + \* Optimistic prewrite is allowed only if no stale lock exists. If + \* there is one, wait until ClientCheckTxnStatus to clean it up. + /\ \/ key_lock[k] = {} + \/ \E l \in key_lock[k] : l.start_ts = start_ts + /\ key_lock' = [key_lock EXCEPT ![k] = {[start_ts |-> start_ts, + primary |-> req.primary, + type |-> "prewrite_optimistic", + min_commit_ts |-> NoneTs]}] + /\ key_data' = [key_data EXCEPT ![k] = @ \union {start_ts}] + /\ ClientPrewrited([start_ts |-> start_ts, type |-> "prewrited", key |-> k]) + /\ UNCHANGED <> ServerCommit == \E req \in req_msgs : @@ -555,33 +543,80 @@ ServerCommit == pk == req.primary start_ts == req.start_ts IN - IF \E w \in key_write[pk] : w.start_ts = start_ts /\ w.type = "write" - THEN - \* Key has already been committed. Do nothing. - /\ SendResp([start_ts |-> start_ts, type |-> "committed"]) - /\ UNCHANGED <> - ELSE - IF \E l \in key_lock[pk] : - /\ l.ts = start_ts - /\ ~ l.type = "lock_key" - THEN - IF \E l \in key_lock[pk] : l.ts = start_ts /\ next_ts > l.min_commit_ts - THEN - \* Commit the key only if the prewrite lock exists. - /\ commit(pk, start_ts, req.commit_ts) - /\ SendResp([start_ts |-> start_ts, type |-> "committed"]) - /\ UNCHANGED <> - ELSE - LET - l == CHOOSE l \in key_lock[pk] : TRUE - IN - /\ SendResps({[start_ts |-> start_ts, type |-> "commit_failed", min_commit_ts |-> l.min_commit_ts]}) - /\ UNCHANGED <> - ELSE - \* Otherwise, abort the transaction. + IF \E w \in key_write[pk] : w.start_ts = start_ts /\ w.type = "write" + THEN + \* Key has already been committed. Do nothing. + /\ SendResp([start_ts |-> start_ts, type |-> "committed"]) + /\ UNCHANGED <> + ELSE + \/ \E l \in key_lock[pk]: + IF l.start_ts = start_ts /\ ~ l.type = "lock_key" + THEN + IF req.commit_ts >= l.min_commit_ts + THEN + \* Commit the key only if the prewrite lock exists and commit_ts is greater than + \* the min_commit_ts in the prewrite lock. + /\ commit(pk, start_ts, req.commit_ts) + /\ SendResp([start_ts |-> start_ts, type |-> "committed"]) + /\ UNCHANGED <> + ELSE + /\ ClientRetryCommit([start_ts |-> start_ts, + type |-> "commit_ts_expired", + min_commit_ts |-> l.min_commit_ts]) + /\ UNCHANGED <> + ELSE + /\ SendResp([start_ts |-> start_ts, type |-> "commit_aborted"]) + /\ UNCHANGED <> + \/ /\ key_lock[pk] = {} /\ SendResp([start_ts |-> start_ts, type |-> "commit_aborted"]) /\ UNCHANGED <> +\* Found the matching lock. +check_txn_status_has_lock(lock, caller_start_ts, resolving_pessimistic_lock) == + LET + start_ts == lock.start_ts + pk == lock.primary + IN + \/ IF lock.type = "lock_key" /\ resolving_pessimistic_lock + THEN + \* Pessimistic lock will be unlocked directly without rollback record. + /\ unlock_key(pk) + /\ UNCHANGED <> + ELSE + /\ rollback(pk, start_ts) + /\ SendReqs({[type |-> "resolve_rollbacked", + start_ts |-> start_ts, + primary |-> pk]}) + /\ UNCHANGED <> + \/ + \* Push min_commit_ts. + /\ lock.min_commit_ts <= caller_start_ts + /\ key_lock' = [key_lock EXCEPT ![pk] = {[start_ts |-> lock.start_ts, + type |-> lock.type, + primary |-> lock.primary, + min_commit_ts |-> caller_start_ts + 1]}] + /\ UNCHANGED <> + +\* Lock not found or start_ts on the lock mismatches. +check_txn_status_missing_lock(start_ts, pk, resolving_pessimistic_lock) == + LET + committed_record == {w \in key_write[pk] : w.start_ts = start_ts /\ w.type = "write"} + IN + IF committed_record /= {} THEN + /\ SendReqs({[type |-> "resolve_committed", + start_ts |-> start_ts, + primary |-> pk, + commit_ts |-> w.ts] : w \in committed_record}) + /\ UNCHANGED <> + ELSE IF resolving_pessimistic_lock = TRUE THEN + UNCHANGED <> + ELSE + /\ rollback(pk, start_ts) + /\ SendReqs({[type |-> "resolve_rollbacked", + start_ts |-> start_ts, + primary |-> pk]}) + /\ UNCHANGED <> + \* Clean up the stale transaction by checking the status of the primary key. \* \* In practice, the transaction will be rolled back if TTL on the lock is expired. But @@ -590,7 +625,7 @@ ServerCommit == \* \* Moreover, TiKV will send a response for `TxnStatus` to the client, and then depending \* on the `TxnStatus`, the client will send `resolve_rollback` or `resolve_commit` to the -\* secondary keys to clean up stale locks. In the TLA+ spec, the response to `check_txn_status` +\* secondary keys to clean up stale locks. In the TLA+ spec, the response to `check_txn_status` \* is omitted and TiKV will directly send `resolve_rollback` or `resolve_commit` message to \* secondary keys, because the action of client sending resolve message by proxying the \* `TxnStatus` from TiKV does not change the state of the client, therefore is equal to directly @@ -601,53 +636,15 @@ ServerCheckTxnStatus == /\ LET pk == req.primary start_ts == req.start_ts - committed == {w \in key_write[pk] : w.start_ts = start_ts /\ w.type = "write"} - caller_start_ts == req.caller_start_ts IN - IF \E lock \in key_lock[pk] : lock.ts = start_ts - \* Found the matching lock. - THEN - \/ - IF - \* Pessimistic lock will be unlocked directly without rollback record. - \E lock \in key_lock[pk] : - /\ lock.ts = start_ts - /\ lock.type = "lock_key" - /\ req.resolving_pessimistic_lock = TRUE - THEN - /\ unlock_key(pk) - /\ UNCHANGED <> - ELSE - /\ rollback(pk, start_ts) - /\ SendReqs({[type |-> "resolve_rollbacked", - start_ts |-> start_ts, - primary |-> pk]}) - /\ UNCHANGED <> - \/ - \* Push min_commit_ts. - /\ \E lock \in key_lock[pk] : - /\ lock.min_commit_ts < caller_start_ts - /\ key_lock' = [key_lock EXCEPT ![pk] = {[ts |-> lock.ts, - type |-> lock.type, - primary |-> lock.primary, - min_commit_ts |-> caller_start_ts + 1]}] - /\ UNCHANGED <> - \* Lock not found or start_ts on the lock mismatches. - ELSE - IF committed /= {} THEN - /\ SendReqs({[type |-> "resolve_committed", - start_ts |-> start_ts, - primary |-> pk, - commit_ts |-> w.ts] : w \in committed}) - /\ UNCHANGED <> - ELSE IF req.resolving_pessimistic_lock = TRUE THEN - /\ UNCHANGED <> + \/ \E l \in key_lock[pk]: + IF start_ts = l.start_ts + THEN + check_txn_status_has_lock(l, req.caller_start_ts, req.resolving_pessimistic_lock) ELSE - /\ rollback(pk, start_ts) - /\ SendReqs({[type |-> "resolve_rollbacked", - start_ts |-> start_ts, - primary |-> pk]}) - /\ UNCHANGED <> + check_txn_status_missing_lock(start_ts, pk, req.resolving_pessimistic_lock) + \/ /\ key_lock[pk] = {} + /\ check_txn_status_missing_lock(start_ts, pk, req.resolving_pessimistic_lock) ServerResolveCommitted == \E req \in req_msgs : @@ -655,12 +652,12 @@ ServerResolveCommitted == /\ LET start_ts == req.start_ts IN - \E k \in KEY: - \E l \in key_lock[k] : - /\ l.primary = req.primary - /\ l.ts = start_ts - /\ commit(k, start_ts, req.commit_ts) - /\ UNCHANGED <> + \E k \in KEY: + \E l \in key_lock[k] : + /\ l.primary = req.primary + /\ l.start_ts = start_ts + /\ commit(k, start_ts, req.commit_ts) + /\ UNCHANGED <> ServerResolveRollbacked == \E req \in req_msgs : @@ -668,16 +665,16 @@ ServerResolveRollbacked == /\ LET start_ts == req.start_ts IN - \E k \in KEY: - \E l \in key_lock[k] : - /\ l.primary = req.primary - /\ l.ts = start_ts - /\ rollback(k, start_ts) - /\ UNCHANGED <> + \E k \in KEY: + \E l \in key_lock[k] : + /\ l.primary = req.primary + /\ l.start_ts = start_ts + /\ rollback(k, start_ts) + /\ UNCHANGED <> ----------------------------------------------------------------------------- \* Specification -Init == +Init == /\ next_ts = 1 /\ req_msgs = {} /\ resp_msgs = {} @@ -685,22 +682,20 @@ Init == /\ client_key = [c \in CLIENT |-> [locking |-> {}, prewriting |-> {}]] /\ client_ts = [c \in CLIENT |-> [start_ts |-> NoneTs, commit_ts |-> NoneTs, - for_update_ts |-> NoneTs, - min_commit_ts |-> NoneTs]] + for_update_ts |-> NoneTs]] /\ key_lock = [k \in KEY |-> {}] /\ key_data = [k \in KEY |-> {}] /\ key_write = [k \in KEY |-> {}] - + Next == - \/ \E c \in OPTIMISTIC_CLIENT : - \/ ClientReadKey(c) - \/ ClientPrewriteOptimistic(c) - \/ ClientCommit(c) - \/ \E c \in PESSIMISTIC_CLIENT : - \/ ClientReadKey(c) - \/ ClientLockKey(c) - \/ ClientPrewritePessimistic(c) - \/ ClientCommit(c) + \/ \E c \in OPTIMISTIC_CLIENT: + \/ ClientReadOptimistic(c) + \/ ClientPrewriteOptimistic(c) + \/ ClientCommit(c) + \/ \E c \in PESSIMISTIC_CLIENT: + \/ ClientLockKey(c) + \/ ClientPrewritePessimistic(c) + \/ ClientCommit(c) \/ ServerReadKey \/ ServerLockKey \/ ServerPrewritePessimistic @@ -717,7 +712,7 @@ Spec == Init /\ [][Next]_vars \* Check whether there is a "write" record in key_write[k] corresponding \* to start_ts. keyCommitted(k, start_ts) == - \E w \in key_write[k] : + \E w \in key_write[k] : /\ w.start_ts = start_ts /\ w.type = "write" @@ -740,7 +735,7 @@ CommitConsistency == \* Secondary key must be either committed or locked by the \* start_ts of the transaction. /\ \A k \in CLIENT_WRITE_KEY[c] : - (~ \E l \in key_lock[k] : l.ts = resp.start_ts) = + (~ \E l \in key_lock[k] : l.start_ts = resp.start_ts) = keyCommitted(k, resp.start_ts) \* If a transaction is aborted, all key of that transaction must be not @@ -753,7 +748,7 @@ AbortConsistency == ~ keyCommitted(CLIENT_PRIMARY[c], resp.start_ts) \* For each write, the commit_ts should be strictly greater than the -\* start_ts and have data written into key_data[k]. For each rollback, +\* start_ts and have data written into key_data[k]. For each rollback, \* the commit_ts should equals to the start_ts. WriteConsistency == \A k \in KEY : @@ -770,7 +765,7 @@ UniqueLockOrWrite == \A k \in KEY : \A l \in key_lock[k] : \A w \in key_write[k] : - w.start_ts /= l.ts + w.start_ts /= l.start_ts \* For each key, ecah record in write column should have a unique start_ts. UniqueWrite == @@ -780,14 +775,6 @@ UniqueWrite == ----------------------------------------------------------------------------- \* Snapshot Isolation -\* Asserts that next_ts is monotonically increasing. -NextTsMonotonicity == [][next_ts' >= next_ts]_vars - -\* Asserts that no msg would be deleted once sent. -MsgMonotonicity == - /\ [][\A req \in req_msgs : req \in req_msgs']_vars - /\ [][\A resp \in resp_msgs : resp \in resp_msgs']_vars - \* Asserts that all messages sent should have ts less than next_ts. MsgTsConsistency == /\ \A req \in req_msgs : @@ -797,85 +784,34 @@ MsgTsConsistency == /\ \A resp \in resp_msgs : resp.start_ts <= next_ts OptimisticReadSnapshotIsolation == - \A resp \in resp_msgs : - IF /\ resp.type = "get_resp" - /\ \E resp2 \in resp_msgs : - /\ resp2.type = "committed" - /\ resp2.start_ts = resp.start_ts - THEN - LET - start_ts == resp.start_ts - key == resp.key - all_commits_before_start_ts == {w \in key_write[key] : w.type = "write" /\ w.ts <= start_ts} - latest_commit_before_start_ts == - {w \in all_commits_before_start_ts : - \A w2 \in all_commits_before_start_ts : - w.ts >= w2.ts} - IN - IF Cardinality(latest_commit_before_start_ts) = 1 - THEN - \A w \in latest_commit_before_start_ts: w.start_ts = resp.value_ts - ELSE IF Cardinality(latest_commit_before_start_ts) = 0 - THEN - resp.value_ts = NoneTs - ELSE - FALSE - ELSE - TRUE + \A c \in OPTIMISTIC_CLIENT: + \A resp \in resp_msgs: + resp.start_ts = client_ts[c].start_ts /\ resp.type = "read_success" => + LET + k == resp.key + readable_commit == find_readable_commit(k, resp.start_ts) + IN + \/ /\ readable_commit = {} + /\ resp.value_ts = NoneTs + \/ \E committed_record \in readable_commit: + resp.value_ts = committed_record.ts PessimisticReadSnapshotIsolation == - \A resp \in resp_msgs : - IF /\ resp.type = "locked_key" - /\ \E resp2 \in resp_msgs : - /\ resp2.type = "committed" - /\ resp2.start_ts = resp.start_ts - THEN - LET - start_ts == resp.start_ts - client == CHOOSE c \in {c \in CLIENT : client_ts[c] = start_ts} : TRUE - for_update_ts == client_ts[client].for_update_ts - key == resp.key - all_commits_before_for_update_ts == {w \in key_write[key] : w.type = "write" /\ w.ts <= for_update_ts} - latest_commit_before_for_update_ts == - {w \in all_commits_before_for_update_ts : - \A w2 \in all_commits_before_for_update_ts : - w.ts >= w2.ts} - IN - IF Cardinality(latest_commit_before_for_update_ts) = 1 - THEN - \A w \in latest_commit_before_for_update_ts: w.start_ts = resp.value_ts - ELSE IF Cardinality(latest_commit_before_for_update_ts) = 0 - THEN - resp.value_ts = NoneTs - ELSE - FALSE - ELSE - TRUE - -ReadSnapshotIsolation == OptimisticReadSnapshotIsolation /\ PessimisticReadSnapshotIsolation - - - -\* SnapshotIsolation is implied from the following assumptions (but is not -\* necessary) because SnapshotIsolation means that: -\* (1) Once a transaction is committed, all keys of the transaction should -\* be always readable or have a lock on secondary keys(eventually readable). -\* PROOF BY CommitConsistency, MsgMonotonicity -\* (2) For a given transaction, all transaction that commits after that -\* transaction should have greater commit_ts than the next_ts at the -\* time that the given transaction commits, so as to be able to -\* distinguish the transactions that have committed before and after -\* from all transactions that preserved by (1). -\* PROOF BY NextTsConsistency, MsgTsConsistency -\* (3) All aborted transactions would be always not readable. -\* PROOF BY AbortConsistency, MsgMonotonicity -\* TODO: Explain the ReadSnapshotIsolation -SnapshotIsolation == /\ CommitConsistency - /\ AbortConsistency - /\ NextTsMonotonicity - /\ MsgMonotonicity - /\ MsgTsConsistency - /\ ReadSnapshotIsolation + \A c \in PESSIMISTIC_CLIENT: + \A read_resp \in resp_msgs: + (/\ read_resp.start_ts = client_ts[c].start_ts + /\ read_resp.type = "read_success" + /\ \E commit_resp \in resp_msgs: + commit_resp.start_ts = read_resp.start_ts /\ commit_resp.type = "committed") => + LET + k == read_resp.key + read_ts == client_ts[c].for_update_ts + readable_commit == find_readable_commit(k, read_ts) + IN + \/ /\ readable_commit = {} + /\ read_resp.value_ts = NoneTs + \/ \E committed_record \in readable_commit: + read_resp.value_ts = committed_record.ts ----------------------------------------------------------------------------- THEOREM Safety == Spec => [](/\ TypeOK @@ -885,5 +821,6 @@ THEOREM Safety == /\ WriteConsistency /\ UniqueLockOrWrite /\ UniqueWrite - /\ SnapshotIsolation) + /\ OptimisticReadSnapshotIsolation + /\ PessimisticReadSnapshotIsolation) ============================================================================= From 6ed4051f1d49767a4bb5aa67aac9e96ca26f3e8f Mon Sep 17 00:00:00 2001 From: Andy Lok Date: Tue, 17 Aug 2021 19:12:46 +0800 Subject: [PATCH 12/30] fix comment Signed-off-by: Andy Lok --- DistributedTransaction/DistributedTransaction.tla | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/DistributedTransaction/DistributedTransaction.tla b/DistributedTransaction/DistributedTransaction.tla index eaf19eb..7965875 100644 --- a/DistributedTransaction/DistributedTransaction.tla +++ b/DistributedTransaction/DistributedTransaction.tla @@ -489,11 +489,9 @@ ServerPrewritePessimistic == k == req.key start_ts == req.start_ts IN - \* Pessimistic prewrite is allowed if pressimistic lock is + \* Pessimistic prewrite is only allowed if pressimistic lock is \* acquired, or, there's no lock, and no write record whose - \* acquired, or, there's no lock, and no write record whose - \* acquired, or, there's no lock, and no write record whose - \* commit_ts >= start_ts, otherwise abort the transaction. + \* commit_ts >= start_ts otherwise abort the transaction. IF \/ /\ ~ \E w \in key_write[k] : w.ts >= start_ts /\ key_lock[k] = {} From 3e2ebb46065c95573858597cbdb2d8ec93809b05 Mon Sep 17 00:00:00 2001 From: Andy Lok Date: Tue, 17 Aug 2021 23:21:46 +0800 Subject: [PATCH 13/30] update comment Signed-off-by: Andy Lok --- .../DistributedTransaction.tla | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/DistributedTransaction/DistributedTransaction.tla b/DistributedTransaction/DistributedTransaction.tla index 7965875..44b3bee 100644 --- a/DistributedTransaction/DistributedTransaction.tla +++ b/DistributedTransaction/DistributedTransaction.tla @@ -133,11 +133,12 @@ ReqMessages == resolving_pessimistic_lock : BOOLEAN] \* The RespMessages defined here is only a subset of the "actual" one in spec. Other resp messages are -\* transmitted(passed) by function arguments. By doing so, we cannot check the retransmission or -\* delay(we do can check the message lost) of these messages. The reason for this is that messages -\* like "lock_key_failed_has_lock" "lock_key_failed_write_conflict" are just too much, which makes the -\* model checking impossible to finish. Considering the delay or retransmit of them is not that serious -\* to the transaction safety, we made the decision to reduce final "distinct state number". +\* transmitted(passed) directly to the client by function arguments, defined in DirectRespMessages. By +\* doing so, we cannot simuliate delay and re-transmition but only message losing. The reason why it works +\* is that the response will not be delay and re-transmition in acutal gRPC implementation. +\* +\* The messages defined in RespMessages is used to record the history, in order to check the invariants. +\* (e.g. TiKV server should never has responded a transcation committed while has responded the transaction rollbacked) RespMessages == [start_ts : Ts, type : {"read_success"}, key : KEY, value_ts : Ts \union {NoneTs}] \union [start_ts : Ts, type : {"committed", @@ -617,17 +618,17 @@ check_txn_status_missing_lock(start_ts, pk, resolving_pessimistic_lock) == \* Clean up the stale transaction by checking the status of the primary key. \* -\* In practice, the transaction will be rolled back if TTL on the lock is expired. But -\* because it is hard to model the TTL in TLA+ spec, the TTL is considered constantly -\* expired when the action is taken. +\* In practice, the transaction will be rolled back only if TTL on the lock is expired. But +\* because it is hard to model the TTL in TLA+ spec, the TTL is considered constantly expired +\* when ServerCheckTxnStatus is called. \* -\* Moreover, TiKV will send a response for `TxnStatus` to the client, and then depending -\* on the `TxnStatus`, the client will send `resolve_rollback` or `resolve_commit` to the -\* secondary keys to clean up stale locks. In the TLA+ spec, the response to `check_txn_status` -\* is omitted and TiKV will directly send `resolve_rollback` or `resolve_commit` message to -\* secondary keys, because the action of client sending resolve message by proxying the -\* `TxnStatus` from TiKV does not change the state of the client, therefore is equal to directly -\* sending resolve message by TiKV +\* Moreover, TiKV will send a response `TxnStatus` to the client, and depending on the `TxnStatus` +\* the client will send `resolve_rollback` or `resolve_commit` to the secondary keys to clean up +\* stale locks on secondary keys. In the TLA+ spec, ServerCheckTxnStatus will not respond to the +\* client and instead TiKV will directly send `resolve_rollback` or `resolve_commit` message to +\* the server where the secondary keys are on, because the action of client sending resolve message +\* by proxying the `TxnStatus` from TiKV to other TiKV does not change the state of the client, +\* therefore is equal to directly sending resolve message from TiKV to TiKV directly. ServerCheckTxnStatus == \E req \in req_msgs : /\ req.type = "check_txn_status" @@ -736,7 +737,7 @@ CommitConsistency == (~ \E l \in key_lock[k] : l.start_ts = resp.start_ts) = keyCommitted(k, resp.start_ts) -\* If a transaction is aborted, all key of that transaction must be not +\* If a transaction is aborted, all key of that transaction must not be \* committed. AbortConsistency == \A resp \in resp_msgs : From 0051d7d3e08e4ea0681f33129e8fe98fe2e52e4b Mon Sep 17 00:00:00 2001 From: Andy Lok Date: Tue, 17 Aug 2021 23:30:47 +0800 Subject: [PATCH 14/30] update assume Signed-off-by: Andy Lok --- .../DistributedTransaction.tla | 53 ++++++++++--------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/DistributedTransaction/DistributedTransaction.tla b/DistributedTransaction/DistributedTransaction.tla index 44b3bee..0814ca1 100644 --- a/DistributedTransaction/DistributedTransaction.tla +++ b/DistributedTransaction/DistributedTransaction.tla @@ -8,16 +8,17 @@ CONSTANTS KEY CONSTANTS OPTIMISTIC_CLIENT, PESSIMISTIC_CLIENT CLIENT == PESSIMISTIC_CLIENT \union OPTIMISTIC_CLIENT -\* Functions that maps a client to keys it wants to read, write. -\* representing the involved keys of each client. -\* Note that "read" here stands for optimistic read, and for -\* pessimistic client(s), the constants equals an empty set. CONSTANTS CLIENT_READ_KEY, CLIENT_WRITE_KEY CLIENT_KEY == [c \in CLIENT |-> CLIENT_READ_KEY[c] \union CLIENT_WRITE_KEY[c]] -ASSUME \A c \in CLIENT: CLIENT_KEY[c] \subseteq KEY \* CLIENT_PRIMARY is the primary key of each client. CONSTANTS CLIENT_PRIMARY + +\* Pessimistic clients can't read a key without writing it and commit it, +\* so the CLIENT_READ_KEY for pessimistic clients should be empty. +ASSUME \A c \in PESSIMISTIC_CLIENT: CLIENT_READ_KEY[c] = {} + +ASSUME \A c \in CLIENT: CLIENT_KEY[c] \subseteq KEY ASSUME \A c \in CLIENT: CLIENT_PRIMARY[c] \in CLIENT_KEY[c] \* Timestamp of transactions. @@ -344,11 +345,11 @@ rollback(k, start_ts) == /\ l.start_ts = start_ts /\ l.primary = k /\ l.type \in {"lock_key", "prewrite_pessimistic"} - \/ \E l \in key_lock[k] : l.start_ts /= start_ts + \/ \E l \in key_lock[k]: l.start_ts /= start_ts \/ key_lock[k] = {} IN \* If a lock exists and has the same ts, unlock it. - /\ IF \E l \in key_lock[k] : l.start_ts = start_ts + /\ IF \E l \in key_lock[k]: l.start_ts = start_ts THEN unlock_key(k) ELSE UNCHANGED key_lock /\ key_data' = [key_data EXCEPT ![k] = @ \ {start_ts}] @@ -357,7 +358,7 @@ rollback(k, start_ts) == key_write' = [key_write EXCEPT ![k] = \* collapse rollback - (@ \ {w \in @ : w.type = "rollback" /\ ~ w.protected /\ w.ts < start_ts}) + (@ \ {w \in @: w.type = "rollback" /\ ~ w.protected /\ w.ts < start_ts}) \* write rollback record \union {[ts |-> start_ts, start_ts |-> start_ts, @@ -370,7 +371,7 @@ rollback(k, start_ts) == \* read_ts is for_update_ts. find_readable_commit(k, read_ts) == LET - all_commits_before_read_ts == {w \in key_write[k] : w.type = "write" /\ w.ts <= read_ts} + all_commits_before_read_ts == {w \in key_write[k]: w.type = "write" /\ w.ts <= read_ts} latest_commit_before_read_ts == {w \in all_commits_before_read_ts : \A w2 \in all_commits_before_read_ts : @@ -406,10 +407,10 @@ ServerLockKey == \* there is one, wait until ClientCheckTxnStatus to clean it up. \/ /\ key_lock[k] = {} /\ LET - all_commits == {w \in key_write[k] : w.type = "write"} - latest_commit == {w \in all_commits : \A w2 \in all_commits : w.ts >= w2.ts} + all_commits == {w \in key_write[k]: w.type = "write"} + latest_commit == {w \in all_commits: \A w2 \in all_commits: w.ts >= w2.ts} IN - IF \E w \in key_write[k] : w.start_ts = start_ts /\ w.type = "rollback" + IF \E w \in key_write[k]: w.start_ts = start_ts /\ w.type = "rollback" THEN \* If corresponding rollback record is found, which \* indicates that the transcation is rollbacked, abort the @@ -422,7 +423,7 @@ ServerLockKey == \* Because if the latest record is "write", it means that \* a new version is committed after for_update_ts, which \* violates Read Committed guarantee. - \/ /\ ~ \E w \in latest_commit : w.ts > req.for_update_ts + \/ /\ ~ \E w \in latest_commit: w.ts > req.for_update_ts /\ key_lock' = [key_lock EXCEPT ![k] = {[start_ts |-> start_ts, primary |-> req.primary, min_commit_ts |-> NoneTs, @@ -436,7 +437,7 @@ ServerLockKey == \* with new for_update_ts. \/ \E w \in latest_commit : /\ w.ts > req.for_update_ts - /\ ~ \E w2 \in all_commits : w2.start_ts = req.start_ts + /\ ~ \E w2 \in all_commits: w2.start_ts = req.start_ts /\ ClientRetryLockKeyWriteConflict([start_ts |-> start_ts, type |-> "lock_key_failed_write_conflict", key |-> k, @@ -470,7 +471,7 @@ ServerReadKey == start_ts == req.start_ts IN /\ IF \/ key_lock[k] = {} - \/ \E l \in key_lock[k] : l.type = "lock_key" \/ l.start_ts = start_ts + \/ \E l \in key_lock[k]: l.type = "lock_key" \/ l.start_ts = start_ts THEN /\ respond_read_success(k, start_ts, start_ts) /\ UNCHANGED <> @@ -494,7 +495,7 @@ ServerPrewritePessimistic == \* acquired, or, there's no lock, and no write record whose \* commit_ts >= start_ts otherwise abort the transaction. IF - \/ /\ ~ \E w \in key_write[k] : w.ts >= start_ts + \/ /\ ~ \E w \in key_write[k]: w.ts >= start_ts /\ key_lock[k] = {} \/ \E l \in key_lock[k] : /\ l.start_ts = start_ts @@ -512,13 +513,13 @@ ServerPrewritePessimistic == /\ UNCHANGED <> ServerPrewriteOptimistic == - \E req \in req_msgs : + \E req \in req_msgs: /\ req.type = "prewrite_optimistic" /\ LET k == req.key start_ts == req.start_ts IN - IF \E w \in key_write[k] : w.ts >= start_ts + IF \E w \in key_write[k]: w.ts >= start_ts THEN /\ SendResp([start_ts |-> start_ts, type |-> "prewrite_aborted"]) /\ UNCHANGED <> @@ -526,7 +527,7 @@ ServerPrewriteOptimistic == \* Optimistic prewrite is allowed only if no stale lock exists. If \* there is one, wait until ClientCheckTxnStatus to clean it up. /\ \/ key_lock[k] = {} - \/ \E l \in key_lock[k] : l.start_ts = start_ts + \/ \E l \in key_lock[k]: l.start_ts = start_ts /\ key_lock' = [key_lock EXCEPT ![k] = {[start_ts |-> start_ts, primary |-> req.primary, type |-> "prewrite_optimistic", @@ -536,13 +537,13 @@ ServerPrewriteOptimistic == /\ UNCHANGED <> ServerCommit == - \E req \in req_msgs : + \E req \in req_msgs: /\ req.type = "commit" /\ LET pk == req.primary start_ts == req.start_ts IN - IF \E w \in key_write[pk] : w.start_ts = start_ts /\ w.type = "write" + IF \E w \in key_write[pk]: w.start_ts = start_ts /\ w.type = "write" THEN \* Key has already been committed. Do nothing. /\ SendResp([start_ts |-> start_ts, type |-> "committed"]) @@ -599,13 +600,13 @@ check_txn_status_has_lock(lock, caller_start_ts, resolving_pessimistic_lock) == \* Lock not found or start_ts on the lock mismatches. check_txn_status_missing_lock(start_ts, pk, resolving_pessimistic_lock) == LET - committed_record == {w \in key_write[pk] : w.start_ts = start_ts /\ w.type = "write"} + committed_record == {w \in key_write[pk]: w.start_ts = start_ts /\ w.type = "write"} IN IF committed_record /= {} THEN /\ SendReqs({[type |-> "resolve_committed", start_ts |-> start_ts, primary |-> pk, - commit_ts |-> w.ts] : w \in committed_record}) + commit_ts |-> w.ts]: w \in committed_record}) /\ UNCHANGED <> ELSE IF resolving_pessimistic_lock = TRUE THEN UNCHANGED <> @@ -688,7 +689,7 @@ Init == Next == \/ \E c \in OPTIMISTIC_CLIENT: - \/ ClientReadOptimistic(c) + \/ ClientReadOptimistic(c) \/ ClientPrewriteOptimistic(c) \/ ClientCommit(c) \/ \E c \in PESSIMISTIC_CLIENT: @@ -734,7 +735,7 @@ CommitConsistency == \* Secondary key must be either committed or locked by the \* start_ts of the transaction. /\ \A k \in CLIENT_WRITE_KEY[c] : - (~ \E l \in key_lock[k] : l.start_ts = resp.start_ts) = + (~ \E l \in key_lock[k]: l.start_ts = resp.start_ts) = keyCommitted(k, resp.start_ts) \* If a transaction is aborted, all key of that transaction must not be @@ -780,7 +781,7 @@ MsgTsConsistency == /\ req.start_ts <= next_ts /\ req.type \in {"commit", "resolve_committed"} => req.commit_ts <= next_ts - /\ \A resp \in resp_msgs : resp.start_ts <= next_ts + /\ \A resp \in resp_msgs: resp.start_ts <= next_ts OptimisticReadSnapshotIsolation == \A c \in OPTIMISTIC_CLIENT: From 7ee3808741326264440138a846da3b821132cbf5 Mon Sep 17 00:00:00 2001 From: Andy Lok Date: Tue, 17 Aug 2021 23:41:50 +0800 Subject: [PATCH 15/30] update comment Signed-off-by: Andy Lok --- DistributedTransaction/DistributedTransaction.tla | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/DistributedTransaction/DistributedTransaction.tla b/DistributedTransaction/DistributedTransaction.tla index 0814ca1..ce70515 100644 --- a/DistributedTransaction/DistributedTransaction.tla +++ b/DistributedTransaction/DistributedTransaction.tla @@ -470,6 +470,15 @@ ServerReadKey == k == req.key start_ts == req.start_ts IN + \* If the lock belongs to the transaction sending the request, or + \* if it's a pessimistic lock, return read success. + \* + \* The reason why pessimistic lock will not block the read is that + \* the owner transaction of the pessimistic lock is impossible to + \* commit the key with a commit_ts smaller than req.start_ts because + \* the owner transaction must get commit_ts after all prewrites have + \* completed which is apperently not done since the lock type is + \* not a prewrite lock. /\ IF \/ key_lock[k] = {} \/ \E l \in key_lock[k]: l.type = "lock_key" \/ l.start_ts = start_ts THEN From 04e021d34c2e222868a83d61f64614c95880f057 Mon Sep 17 00:00:00 2001 From: Andy Lok Date: Wed, 18 Aug 2021 12:00:10 +0800 Subject: [PATCH 16/30] enable snapshot checks Signed-off-by: Andy Lok --- DistributedTransaction/DistributedTransaction.tla | 3 ++- DistributedTransaction/Test1.cfg | 2 ++ DistributedTransaction/Test2.cfg | 2 ++ DistributedTransaction/Test3.cfg | 2 ++ DistributedTransaction/Test4.cfg | 2 ++ 5 files changed, 10 insertions(+), 1 deletion(-) diff --git a/DistributedTransaction/DistributedTransaction.tla b/DistributedTransaction/DistributedTransaction.tla index ce70515..ae5c6a0 100644 --- a/DistributedTransaction/DistributedTransaction.tla +++ b/DistributedTransaction/DistributedTransaction.tla @@ -831,5 +831,6 @@ THEOREM Safety == /\ UniqueLockOrWrite /\ UniqueWrite /\ OptimisticReadSnapshotIsolation - /\ PessimisticReadSnapshotIsolation) + /\ PessimisticReadSnapshotIsolation + /\ MsgTsConsistency) ============================================================================= diff --git a/DistributedTransaction/Test1.cfg b/DistributedTransaction/Test1.cfg index 9eb4d4d..9ac3a63 100644 --- a/DistributedTransaction/Test1.cfg +++ b/DistributedTransaction/Test1.cfg @@ -27,4 +27,6 @@ INVARIANT WriteConsistency UniqueLockOrWrite UniqueWrite + OptimisticReadSnapshotIsolation + PessimisticReadSnapshotIsolation MsgTsConsistency diff --git a/DistributedTransaction/Test2.cfg b/DistributedTransaction/Test2.cfg index 623a2f4..0949218 100644 --- a/DistributedTransaction/Test2.cfg +++ b/DistributedTransaction/Test2.cfg @@ -28,4 +28,6 @@ INVARIANT WriteConsistency UniqueLockOrWrite UniqueWrite + OptimisticReadSnapshotIsolation + PessimisticReadSnapshotIsolation MsgTsConsistency diff --git a/DistributedTransaction/Test3.cfg b/DistributedTransaction/Test3.cfg index 3ff1959..9ec8364 100644 --- a/DistributedTransaction/Test3.cfg +++ b/DistributedTransaction/Test3.cfg @@ -26,4 +26,6 @@ INVARIANT WriteConsistency UniqueLockOrWrite UniqueWrite + OptimisticReadSnapshotIsolation + PessimisticReadSnapshotIsolation MsgTsConsistency diff --git a/DistributedTransaction/Test4.cfg b/DistributedTransaction/Test4.cfg index 4af6b93..fff2207 100644 --- a/DistributedTransaction/Test4.cfg +++ b/DistributedTransaction/Test4.cfg @@ -25,4 +25,6 @@ INVARIANT WriteConsistency UniqueLockOrWrite UniqueWrite + OptimisticReadSnapshotIsolation + PessimisticReadSnapshotIsolation MsgTsConsistency From fa38768aa99982f4b09f152bd56e3a94e1ba28a5 Mon Sep 17 00:00:00 2001 From: Andy Lok Date: Thu, 19 Aug 2021 00:18:39 +0800 Subject: [PATCH 17/30] fix pessimistic si check Signed-off-by: Andy Lok --- .../DistributedTransaction.tla | 301 ++++++++++-------- 1 file changed, 170 insertions(+), 131 deletions(-) diff --git a/DistributedTransaction/DistributedTransaction.tla b/DistributedTransaction/DistributedTransaction.tla index ae5c6a0..7117114 100644 --- a/DistributedTransaction/DistributedTransaction.tla +++ b/DistributedTransaction/DistributedTransaction.tla @@ -21,10 +21,6 @@ ASSUME \A c \in PESSIMISTIC_CLIENT: CLIENT_READ_KEY[c] = {} ASSUME \A c \in CLIENT: CLIENT_KEY[c] \subseteq KEY ASSUME \A c \in CLIENT: CLIENT_PRIMARY[c] \in CLIENT_KEY[c] -\* Timestamp of transactions. -Ts == Nat \ {0} -NoneTs == 0 - \* The algorithm is easier to understand in terms of the set of msgs of \* all messages that have ever been sent. A more accurate model would use \* one or more variables to represent the messages actually in transit, @@ -87,13 +83,15 @@ VARIABLES client_ts \* for prewrite. VARIABLES client_key +VARIABLES client_read + \* next_ts is a globally monotonically increasing integer, representing \* the virtual clock of transactions. In practice, the variable is \* maintained by PD, the time oracle of a cluster. VARIABLES next_ts msg_vars == <> -client_vars == <> +client_vars == <> key_vars == <> vars == <> @@ -104,10 +102,17 @@ SendResps(msgs) == resp_msgs' = resp_msgs \union msgs ----------------------------------------------------------------------------- \* Type Definitions +\* Timestamp for transactions. +Ts == Nat \ {0} +NoneTs == 0 + +\* Represents the data version read from TiKV. NoneTs means that the value is empty. +ValueTs == Ts \union {NoneTs} + ReqMessages == [start_ts : Ts, primary : KEY, type : {"lock_key"}, key : KEY, for_update_ts : Ts] - \union [start_ts : Ts, primary : KEY, type : {"get"}, key : KEY] + \union [start_ts : Ts, primary : KEY, type : {"read_optimistic"}, key : KEY] \union [start_ts : Ts, primary : KEY, type : {"prewrite_optimistic"}, key : KEY] \union [start_ts : Ts, primary : KEY, type : {"prewrite_pessimistic"}, key : KEY] \union [start_ts : Ts, primary : KEY, type : {"commit"}, commit_ts : Ts] @@ -134,23 +139,22 @@ ReqMessages == resolving_pessimistic_lock : BOOLEAN] \* The RespMessages defined here is only a subset of the "actual" one in spec. Other resp messages are -\* transmitted(passed) directly to the client by function arguments, defined in DirectRespMessages. By +\* transmitted directly to the client by function arguments, defined in DirectRespMessages. By \* doing so, we cannot simuliate delay and re-transmition but only message losing. The reason why it works \* is that the response will not be delay and re-transmition in acutal gRPC implementation. \* \* The messages defined in RespMessages is used to record the history, in order to check the invariants. \* (e.g. TiKV server should never has responded a transcation committed while has responded the transaction rollbacked) -RespMessages == - [start_ts : Ts, type : {"read_success"}, key : KEY, value_ts : Ts \union {NoneTs}] - \union [start_ts : Ts, type : {"committed", - "commit_aborted", - "prewrite_aborted", - "lock_key_aborted"}] +RespMessages == [start_ts : Ts, type : {"committed", + "commit_aborted", + "lock_key_aborted", + "prewrite_aborted"}] DirectRespMessages == - [start_ts : Ts, type : {"read_failed"}, key : KEY, lock_primary: KEY, lock_ts : Ts] - \union [start_ts : Ts, type : {"lock_key_success"}, key : KEY] - \union [start_ts : Ts, type : {"lock_key_failed_has_lock"}, key : KEY, lock_primary: KEY, lock_ts : Ts] + [start_ts : Ts, type : {"read_optimistic_succeed"}, key : KEY, value_ts : ValueTs] + \union [start_ts : Ts, type : {"lock_key_succeed"}, key : KEY, for_update_ts : Ts, value_ts : ValueTs] + \union [start_ts : Ts, type : {"read_optimistic_failed_has_lock", + "lock_key_failed_has_lock"}, key : KEY, lock_primary: KEY, lock_ts : Ts] \union [start_ts : Ts, type : {"lock_key_failed_write_conflict"}, key : KEY, latest_commit_ts : Ts] \union [start_ts : Ts, type : {"prewrited"}, key : KEY] \union [start_ts : Ts, type : {"commit_ts_expired"}, min_commit_ts : Ts] @@ -161,7 +165,8 @@ TypeOK == /\ req_msgs \in SUBSET ReqMessages /\ key_lock \in [KEY -> SUBSET [start_ts : Ts, primary : KEY, \* As defined above, Ts == Nat \ 0, here we use 0 - \* to indicates that there's no min_commit_ts limit. + \* to indicates that there's no min_commit_ts limit + \* becuase every valid commit ts is larger than 0. min_commit_ts : Ts \union {NoneTs}, type : {"prewrite_optimistic", "prewrite_pessimistic", @@ -171,11 +176,14 @@ TypeOK == /\ req_msgs \in SUBSET ReqMessages \union [ts : Ts, start_ts : Ts, type : {"rollback"}, protected : BOOLEAN])] \* At most one lock in key_lock[k] /\ \A k \in KEY: Cardinality(key_lock[k]) <= 1 - /\ client_state \in [CLIENT -> {"init", "read_finished", "locking", "prewriting", "committing"}] + /\ client_state \in [CLIENT -> {"init", "reading", "locking", "prewriting", "committing"}] /\ client_ts \in [CLIENT -> [start_ts : Ts \union {NoneTs}, commit_ts : Ts \union {NoneTs}, for_update_ts : Ts \union {NoneTs}]] - /\ client_key \in [CLIENT -> [locking: SUBSET KEY, prewriting : SUBSET KEY]] + /\ client_key \in [CLIENT -> [reading : SUBSET KEY, locking : SUBSET KEY, prewriting : SUBSET KEY]] + /\ client_read \in [CLIENT -> [KEY -> + [type : {"not_read_yet"}] + \union [type : {"read_succeed"}, value_ts : ValueTs]]] /\ \A c \in CLIENT: client_key[c].locking \intersect client_key[c].prewriting = {} /\ next_ts \in Ts ----------------------------------------------------------------------------- @@ -184,40 +192,75 @@ TypeOK == /\ req_msgs \in SUBSET ReqMessages \* Once the get request is sent, it exist permanently in the req_msgs. ClientReadOptimistic(c) == /\ client_state[c] = "init" + /\ client_state' = [client_state EXCEPT ![c] = "reading"] /\ client_ts' = [client_ts EXCEPT ![c].start_ts = next_ts] - /\ client_state' = [client_state EXCEPT ![c] = "read_finished"] + /\ client_key' = [client_key EXCEPT ![c].reading = CLIENT_READ_KEY[c]] /\ next_ts' = next_ts + 1 - /\ SendReqs({[type |-> "get", + /\ SendReqs({[type |-> "read_optimistic", start_ts |-> client_ts'[c].start_ts, primary |-> CLIENT_PRIMARY[c], key |-> k] : k \in CLIENT_READ_KEY[c]}) - /\ UNCHANGED <> + /\ UNCHANGED <> + +add_client_read_history(c, k, value_ts) == + client_read' = [client_read EXCEPT ![c][k] = [type |-> "read_succeed", value_ts |-> value_ts]] + +ClientReadOptimisticSucceed(resp) == + /\ Assert(resp.type = "read_optimistic_succeed", "Message Type Error") + /\ \/ UNCHANGED <> + \/ \E c \in CLIENT: + /\ client_ts[c].start_ts = resp.start_ts + /\ client_state[c] = "reading" + /\ resp.key \in client_key[c].reading + /\ client_key' = [client_key EXCEPT ![c].reading = @ \ {resp.key}] + /\ add_client_read_history(c, resp.key, resp.value_ts) + /\ UNCHANGED <> + +ClientReadOptimisticFailedHasLock(resp) == + /\ Assert(resp.type = "read_optimistic_failed_has_lock", "Message Type Error") + /\ \/ UNCHANGED <> + \/ \E c \in CLIENT: + /\ client_ts[c].start_ts = resp.start_ts + /\ SendReqs({[type |-> "check_txn_status", + start_ts |-> resp.lock_ts, + caller_start_ts |-> client_ts[c].start_ts, + primary |-> resp.lock_primary, + \* For a read failure, there can't be a pessimistic lock, since + \* when the read simply ignores pessimistic lock. + resolving_pessimistic_lock |-> FALSE]}) + /\ UNCHANGED <> ClientLockKey(c) == /\ client_state[c] = "init" /\ client_state' = [client_state EXCEPT ![c] = "locking"] /\ client_ts' = [client_ts EXCEPT ![c].start_ts = next_ts, ![c].for_update_ts = next_ts] - /\ next_ts' = next_ts + 1 /\ client_key' = [client_key EXCEPT ![c].locking = CLIENT_WRITE_KEY[c]] + /\ next_ts' = next_ts + 1 \* For pessimistic clients assume we need to acquire pessimistic locks for all keys. /\ SendReqs({[type |-> "lock_key", start_ts |-> client_ts'[c].start_ts, primary |-> CLIENT_PRIMARY[c], key |-> k, for_update_ts |-> client_ts'[c].for_update_ts] : k \in CLIENT_WRITE_KEY[c]}) - /\ UNCHANGED <> + /\ UNCHANGED <> -ClientLockKeySuccess(resp) == - /\ Assert(resp.type = "lock_key_success", "Message Type Error") +ClientLockKeySucceed(resp) == + /\ Assert(resp.type = "lock_key_succeed", "Message Type Error") /\ \/ UNCHANGED <> \/ \E c \in CLIENT: /\ client_ts[c].start_ts = resp.start_ts + \* The client guarantees that it will never recieve a response for + \* a previously sent lock_key request with smaller for_update_ts, which + \* may carry a bad value that breaks the snapshot isolation. This + \* guarantee holds because the previous gRPC connection must have closed. + /\ client_ts[c].for_update_ts = resp.for_update_ts /\ client_state[c] = "locking" /\ resp.key \in client_key[c].locking /\ client_key' = [client_key EXCEPT ![c].locking = @ \ {resp.key}] + /\ add_client_read_history(c, resp.key, resp.value_ts) /\ UNCHANGED <> -ClientRetryLockKeyHasLock(resp) == +ClientLockKeyFailedHasLock(resp) == /\ Assert(resp.type = "lock_key_failed_has_lock", "Message Type Error") /\ \/ UNCHANGED <> \/ \E c \in CLIENT: @@ -231,7 +274,7 @@ ClientRetryLockKeyHasLock(resp) == resolving_pessimistic_lock |-> resp.lock_type = "lock_key"]}) /\ UNCHANGED <> -ClientRetryLockKeyWriteConflict(resp) == +ClientLockKeyFailedWriteConflict(resp) == /\ Assert(resp.type = "lock_key_failed_write_conflict", "Message Type Error") /\ \/ UNCHANGED <> \/ \E c \in CLIENT: @@ -246,42 +289,29 @@ ClientRetryLockKeyWriteConflict(resp) == primary |-> CLIENT_PRIMARY[c], key |-> resp.key, for_update_ts |-> client_ts'[c].for_update_ts]}) - /\ UNCHANGED <> + /\ UNCHANGED <> -ClientPrewritePessimistic(c) == - /\ client_state[c] = "locking" - /\ client_key[c].locking = {} +ClientPrewriteOptimistic(c) == + /\ client_state[c] = "reading" + /\ client_key[c].reading = {} /\ client_state' = [client_state EXCEPT ![c] = "prewriting"] /\ client_key' = [client_key EXCEPT ![c].prewriting = CLIENT_WRITE_KEY[c]] - /\ SendReqs({[type |-> "prewrite_pessimistic", + /\ SendReqs({[type |-> "prewrite_optimistic", start_ts |-> client_ts[c].start_ts, primary |-> CLIENT_PRIMARY[c], key |-> k] : k \in CLIENT_WRITE_KEY[c]}) - /\ UNCHANGED <> + /\ UNCHANGED <> -ClientReadOptimisticFailed(resp) == - /\ Assert(resp.type = "read_failed", "Message Type Error") - /\ \/ UNCHANGED <> - \/ \E c \in CLIENT: - /\ client_ts[c].start_ts = resp.start_ts - /\ SendReqs({[type |-> "check_txn_status", - start_ts |-> resp.lock_ts, - caller_start_ts |-> client_ts[c].start_ts, - primary |-> resp.lock_primary, - \* For a read failure, there can't be a pessimistic lock, since - \* when the read simply ignores pessimistic lock. - resolving_pessimistic_lock |-> FALSE]}) - /\ UNCHANGED <> - -ClientPrewriteOptimistic(c) == - /\ client_state[c] = "read_finished" +ClientPrewritePessimistic(c) == + /\ client_state[c] = "locking" + /\ client_key[c].locking = {} /\ client_state' = [client_state EXCEPT ![c] = "prewriting"] /\ client_key' = [client_key EXCEPT ![c].prewriting = CLIENT_WRITE_KEY[c]] - /\ SendReqs({[type |-> "prewrite_optimistic", + /\ SendReqs({[type |-> "prewrite_pessimistic", start_ts |-> client_ts[c].start_ts, primary |-> CLIENT_PRIMARY[c], key |-> k] : k \in CLIENT_WRITE_KEY[c]}) - /\ UNCHANGED <> + /\ UNCHANGED <> ClientPrewrited(resp) == /\ Assert(resp.type = "prewrited", "Message Type Error") @@ -291,7 +321,7 @@ ClientPrewrited(resp) == /\ client_state[c] = "prewriting" /\ resp.key \in client_key[c].prewriting /\ client_key' = [client_key EXCEPT ![c].prewriting = @ \ {resp.key}] - /\ UNCHANGED <> + /\ UNCHANGED <> ClientCommit(c) == /\ client_state[c] = "prewriting" @@ -303,7 +333,7 @@ ClientCommit(c) == start_ts |-> client_ts'[c].start_ts, primary |-> CLIENT_PRIMARY[c], commit_ts |-> client_ts'[c].commit_ts]}) - /\ UNCHANGED <> + /\ UNCHANGED <> ClientRetryCommit(resp) == /\ Assert(resp.type = "commit_ts_expired", "Message Type Error") @@ -318,7 +348,7 @@ ClientRetryCommit(resp) == start_ts |-> client_ts'[c].start_ts, primary |-> CLIENT_PRIMARY[c], commit_ts |-> client_ts'[c].commit_ts]}) - /\ UNCHANGED <> + /\ UNCHANGED <> ----------------------------------------------------------------------------- \* Server Actions @@ -379,21 +409,72 @@ find_readable_commit(k, read_ts) == IN latest_commit_before_read_ts -\* Read successfully. Return `value_ts = NoneTs` if the key has no value. -respond_read_success(k, start_ts, read_ts) == +\* Optimistic read successfully. Return `value_ts = NoneTs` if the value is empty. +respond_read_succeed(k, start_ts) == LET - readable_commit == find_readable_commit(k, read_ts) + readable_commit == find_readable_commit(k, start_ts) IN \/ /\ readable_commit = {} - /\ SendResp([start_ts |-> start_ts, - type |-> "read_success", - key |-> k, - value_ts |-> NoneTs]) + /\ ClientReadOptimisticSucceed([start_ts |-> start_ts, + type |-> "read_optimistic_succeed", + key |-> k, + value_ts |-> NoneTs]) \/ \E committed_record \in readable_commit: - /\ SendResp([start_ts |-> start_ts, - type |-> "read_success", - key |-> k, - value_ts |-> committed_record.ts]) + /\ ClientReadOptimisticSucceed([start_ts |-> start_ts, + type |-> "read_optimistic_succeed", + key |-> k, + value_ts |-> committed_record.ts]) + +\* Pessimistic lock successfully. Return `value_ts = NoneTs` if the value is empty. +respond_lock_key_succeed(k, start_ts, for_update_ts) == + LET + readable_commit == find_readable_commit(k, for_update_ts) + IN + \/ /\ readable_commit = {} + /\ ClientLockKeySucceed([start_ts |-> start_ts, + type |-> "lock_key_succeed", + key |-> k, + for_update_ts |-> for_update_ts, + value_ts |-> NoneTs]) + \/ \E committed_record \in readable_commit: + /\ ClientLockKeySucceed([start_ts |-> start_ts, + type |-> "lock_key_succeed", + key |-> k, + for_update_ts |-> for_update_ts, + value_ts |-> committed_record.ts]) + +ServerReadKey == + \E req \in req_msgs : + /\ req.type = "read_optimistic" + /\ \E c \in CLIENT : + /\ client_ts[c].start_ts = req.start_ts + /\ LET + k == req.key + start_ts == req.start_ts + IN + \* If the lock belongs to the transaction sending the request, or + \* if it's a pessimistic lock, return read succeed. + \* + \* The reason why pessimistic lock will not block the read is that + \* the owner transaction of the pessimistic lock is impossible to + \* commit the key with a commit_ts smaller than req.start_ts because + \* the owner transaction must get commit_ts after all prewrites have + \* completed which is apperently not done since the lock type is + \* not a prewrite lock. + /\ IF \/ key_lock[k] = {} + \/ \E l \in key_lock[k]: l.type = "lock_key" \/ l.start_ts = start_ts + THEN + /\ respond_read_succeed(k, start_ts) + /\ UNCHANGED <> + ELSE + \E l \in key_lock[k]: + /\ ClientReadOptimisticFailedHasLock( + [start_ts |-> start_ts, + type |-> "read_optimistic_failed_has_lock", + key |-> k, + lock_primary |-> l.primary, + lock_ts |-> l.start_ts]) + /\ UNCHANGED <> ServerLockKey == \E req \in req_msgs : @@ -428,17 +509,14 @@ ServerLockKey == primary |-> req.primary, min_commit_ts |-> NoneTs, type |-> "lock_key"]}] - /\ respond_read_success(k, start_ts, for_update_ts) - /\ ClientLockKeySuccess([start_ts |-> start_ts, - type |-> "lock_key_success", - key |-> k]) - /\ UNCHANGED <> + /\ respond_lock_key_succeed(k, start_ts, for_update_ts) + /\ UNCHANGED <> \* Otherwise, reject the request and let client to retry \* with new for_update_ts. \/ \E w \in latest_commit : /\ w.ts > req.for_update_ts /\ ~ \E w2 \in all_commits: w2.start_ts = req.start_ts - /\ ClientRetryLockKeyWriteConflict([start_ts |-> start_ts, + /\ ClientLockKeyFailedWriteConflict([start_ts |-> start_ts, type |-> "lock_key_failed_write_conflict", key |-> k, latest_commit_ts |-> w.ts]) @@ -447,13 +525,10 @@ ServerLockKey == \/ \E l \in key_lock[k]: IF l.start_ts = start_ts THEN - /\ respond_read_success(k, start_ts, for_update_ts) - /\ ClientLockKeySuccess([start_ts |-> start_ts, - type |-> "lock_key_success", - key |-> k]) - /\ UNCHANGED <> + /\ respond_lock_key_succeed(k, start_ts, for_update_ts) + /\ UNCHANGED <> ELSE - /\ ClientRetryLockKeyHasLock([start_ts |-> start_ts, + /\ ClientLockKeyFailedHasLock([start_ts |-> start_ts, type |-> "lock_key_failed_has_lock", key |-> k, lock_primary |-> l.primary, @@ -461,38 +536,6 @@ ServerLockKey == lock_type |-> l.type]) /\ UNCHANGED <> -ServerReadKey == - \E req \in req_msgs : - /\ req.type = "get" - /\ \E c \in CLIENT : - /\ client_ts[c].start_ts = req.start_ts - /\ LET - k == req.key - start_ts == req.start_ts - IN - \* If the lock belongs to the transaction sending the request, or - \* if it's a pessimistic lock, return read success. - \* - \* The reason why pessimistic lock will not block the read is that - \* the owner transaction of the pessimistic lock is impossible to - \* commit the key with a commit_ts smaller than req.start_ts because - \* the owner transaction must get commit_ts after all prewrites have - \* completed which is apperently not done since the lock type is - \* not a prewrite lock. - /\ IF \/ key_lock[k] = {} - \/ \E l \in key_lock[k]: l.type = "lock_key" \/ l.start_ts = start_ts - THEN - /\ respond_read_success(k, start_ts, start_ts) - /\ UNCHANGED <> - ELSE - \E l \in key_lock[k]: - /\ ClientReadOptimisticFailed([start_ts |-> start_ts, - type |-> "read_failed", - key |-> k, - lock_primary |-> l.primary, - lock_ts |-> l.start_ts]) - /\ UNCHANGED <> - ServerPrewritePessimistic == \E req \in req_msgs : /\ req.type = "prewrite_pessimistic" @@ -688,10 +731,11 @@ Init == /\ req_msgs = {} /\ resp_msgs = {} /\ client_state = [c \in CLIENT |-> "init"] - /\ client_key = [c \in CLIENT |-> [locking |-> {}, prewriting |-> {}]] + /\ client_key = [c \in CLIENT |-> [reading |-> {}, locking |-> {}, prewriting |-> {}]] /\ client_ts = [c \in CLIENT |-> [start_ts |-> NoneTs, commit_ts |-> NoneTs, for_update_ts |-> NoneTs]] + /\ client_read = [c \in CLIENT |-> [k \in KEY |-> [type |-> "not_read_yet"]]] /\ key_lock = [k \in KEY |-> {}] /\ key_data = [k \in KEY |-> {}] /\ key_write = [k \in KEY |-> {}] @@ -794,33 +838,28 @@ MsgTsConsistency == OptimisticReadSnapshotIsolation == \A c \in OPTIMISTIC_CLIENT: - \A resp \in resp_msgs: - resp.start_ts = client_ts[c].start_ts /\ resp.type = "read_success" => + \A k \in CLIENT_READ_KEY[c]: + client_read[c][k].type = "read_succeed" => LET - k == resp.key - readable_commit == find_readable_commit(k, resp.start_ts) + readable_commit == find_readable_commit(k, client_ts[c].start_ts) IN \/ /\ readable_commit = {} - /\ resp.value_ts = NoneTs + /\ client_read[c][k].value_ts = NoneTs \/ \E committed_record \in readable_commit: - resp.value_ts = committed_record.ts + client_read[c][k].value_ts = committed_record.ts PessimisticReadSnapshotIsolation == \A c \in PESSIMISTIC_CLIENT: - \A read_resp \in resp_msgs: - (/\ read_resp.start_ts = client_ts[c].start_ts - /\ read_resp.type = "read_success" - /\ \E commit_resp \in resp_msgs: - commit_resp.start_ts = read_resp.start_ts /\ commit_resp.type = "committed") => - LET - k == read_resp.key - read_ts == client_ts[c].for_update_ts - readable_commit == find_readable_commit(k, read_ts) - IN - \/ /\ readable_commit = {} - /\ read_resp.value_ts = NoneTs - \/ \E committed_record \in readable_commit: - read_resp.value_ts = committed_record.ts + (\E resp \in resp_msgs: resp.start_ts = client_ts[c].start_ts /\ resp.type = "committed") => + \A k \in CLIENT_WRITE_KEY[c]: + client_read[c][k].type = "read_succeed" => + LET + readable_commit == find_readable_commit(k, client_ts[c].for_update_ts) + IN + \/ /\ readable_commit = {} + /\ client_read[c][k].value_ts = NoneTs + \/ \E committed_record \in readable_commit: + client_read[c][k].value_ts = committed_record.ts ----------------------------------------------------------------------------- THEOREM Safety == Spec => [](/\ TypeOK From c04c627269ec2c092d0b8965b8873e11704e6e53 Mon Sep 17 00:00:00 2001 From: Andy Lok Date: Thu, 19 Aug 2021 00:27:10 +0800 Subject: [PATCH 18/30] add comment Signed-off-by: Andy Lok --- DistributedTransaction/DistributedTransaction.tla | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/DistributedTransaction/DistributedTransaction.tla b/DistributedTransaction/DistributedTransaction.tla index 7117114..026e5fb 100644 --- a/DistributedTransaction/DistributedTransaction.tla +++ b/DistributedTransaction/DistributedTransaction.tla @@ -15,6 +15,7 @@ CLIENT_KEY == [c \in CLIENT |-> CLIENT_READ_KEY[c] \union CLIENT_WRITE_KEY[c]] CONSTANTS CLIENT_PRIMARY \* Pessimistic clients can't read a key without writing it and commit it, +\* and pessimistic client read the keys while acquiring pessimistic locks \* so the CLIENT_READ_KEY for pessimistic clients should be empty. ASSUME \A c \in PESSIMISTIC_CLIENT: CLIENT_READ_KEY[c] = {} @@ -77,12 +78,13 @@ VARIABLES client_state \* client_ts[c] is a record of [start_ts, commit_ts, for_update_ts, min_commit_ts]. \* Fields are all initialized to NoneTs. VARIABLES client_ts -\* client_key[c] is a record of [locking: {key}, prewriting: {key}]. -\* Hereby, "locking" denotes the keys whose pessimistic locks -\* haven't been acquired, "prewriting" denotes the keys that are pending -\* for prewrite. +\* client_key[c] is a record of [reading: {key}, locking: {key}, prewriting: {key}]. +\* Hereby, "reading" denotes the keys that the optimistic client is reading, +\* "locking" denotes the keys that the pessimistic client is acquiring for pessimistic +\* locks, and "prewriting" denotes the keys that are waiting for prewrite success. VARIABLES client_key - +\* client_read[c][k] stores the value that the client has read from server (by optimsitic +\* read or pessimistic lock key). This is used for checking snapshot isolation. VARIABLES client_read \* next_ts is a globally monotonically increasing integer, representing From 7fff07d5b25f842d644a0e59f7bdf37b449450ff Mon Sep 17 00:00:00 2001 From: Andy Lok Date: Thu, 19 Aug 2021 03:36:03 +0800 Subject: [PATCH 19/30] add comment Signed-off-by: Andy Lok --- .../DistributedTransaction.tla | 95 +++++++++++-------- 1 file changed, 56 insertions(+), 39 deletions(-) diff --git a/DistributedTransaction/DistributedTransaction.tla b/DistributedTransaction/DistributedTransaction.tla index 026e5fb..d16c670 100644 --- a/DistributedTransaction/DistributedTransaction.tla +++ b/DistributedTransaction/DistributedTransaction.tla @@ -73,8 +73,8 @@ VARIABLES key_lock \* rollback record would not be collapsed. VARIABLES key_write -\* client_state[c] indicates the current transaction stage of client c. -VARIABLES client_state +\* client_stage[c] indicates the current transaction stage of client c. +VARIABLES client_stage \* client_ts[c] is a record of [start_ts, commit_ts, for_update_ts, min_commit_ts]. \* Fields are all initialized to NoneTs. VARIABLES client_ts @@ -83,8 +83,9 @@ VARIABLES client_ts \* "locking" denotes the keys that the pessimistic client is acquiring for pessimistic \* locks, and "prewriting" denotes the keys that are waiting for prewrite success. VARIABLES client_key -\* client_read[c][k] stores the value that the client has read from server (by optimsitic -\* read or pessimistic lock key). This is used for checking snapshot isolation. +\* client_read[c][k] stores the value that the client has read from server for +\* key `k` (by optimsitic read or pessimistic lock key). This is used for checking +\* snapshot isolation. VARIABLES client_read \* next_ts is a globally monotonically increasing integer, representing @@ -93,7 +94,7 @@ VARIABLES client_read VARIABLES next_ts msg_vars == <> -client_vars == <> +client_vars == <> key_vars == <> vars == <> @@ -111,6 +112,7 @@ NoneTs == 0 \* Represents the data version read from TiKV. NoneTs means that the value is empty. ValueTs == Ts \union {NoneTs} +\* The messages that sent from client to the server. ReqMessages == [start_ts : Ts, primary : KEY, type : {"lock_key"}, key : KEY, for_update_ts : Ts] @@ -140,18 +142,24 @@ ReqMessages == \union [start_ts : Ts, caller_start_ts : Ts \union {NoneTs}, primary : KEY, type : {"check_txn_status"}, resolving_pessimistic_lock : BOOLEAN] -\* The RespMessages defined here is only a subset of the "actual" one in spec. Other resp messages are -\* transmitted directly to the client by function arguments, defined in DirectRespMessages. By -\* doing so, we cannot simuliate delay and re-transmition but only message losing. The reason why it works -\* is that the response will not be delay and re-transmition in acutal gRPC implementation. +\* In this spec, the responses from the server to the client are modeled differently from the requests. +\* A response is modeled as a direct call to client handling function which makes the client states and +\* the server states change in atomic. \* -\* The messages defined in RespMessages is used to record the history, in order to check the invariants. -\* (e.g. TiKV server should never has responded a transcation committed while has responded the transaction rollbacked) -RespMessages == [start_ts : Ts, type : {"committed", - "commit_aborted", - "lock_key_aborted", - "prewrite_aborted"}] - +\* This is ok because, different from requests, which may be duplicated, delayed, or lost, the response +\* can only be lost or delayed. Every client handling function will have a branch that the response +\* is simply ignored, in order to simulate losing the response. Still, we can not simulate the delaying +\* situation, but the client should (we thing) be ok with the delayed response since the client only +\* handles the response at the matching stage (e.g. "reading", "locking", "prewriting"), and every +\* client handling function only changes the client state to one key and leaving the state to other keys +\* untouched, which makes the order of receiving the response to different keys is irrelevant. +\* +\* One more draw back is that, a resent request may call the client handling function, which is impossible +\* in real-world because the client must drop the previous connection before retry, in other words, the +\* client is only possible to recieve and handle the response to the request it last sent. We overcome this +\* by checking whether the client is in the right stage to haneld the response. But still, the model will face +\* to some response to the resent resquest for the same client stage. However, the model has just tested +\* more but no less cases comparing to the real-world, and is still passing the check. DirectRespMessages == [start_ts : Ts, type : {"read_optimistic_succeed"}, key : KEY, value_ts : ValueTs] \union [start_ts : Ts, type : {"lock_key_succeed"}, key : KEY, for_update_ts : Ts, value_ts : ValueTs] @@ -161,6 +169,15 @@ DirectRespMessages == \union [start_ts : Ts, type : {"prewrited"}, key : KEY] \union [start_ts : Ts, type : {"commit_ts_expired"}, min_commit_ts : Ts] +\* The responses defined in RespMessages will not be handled by the client in this spec, because they all +\* drive the transaction the an end. The messages defined here are used to record the history, in order to +\* check the invariants later defined in the spec (e.g. TiKV server should never has responded a transcation +\* committed while it has also responded the transaction aborted). +RespMessages == [start_ts : Ts, type : {"committed", + "commit_aborted", + "lock_key_aborted", + "prewrite_aborted"}] + TypeOK == /\ req_msgs \in SUBSET ReqMessages /\ resp_msgs \in SUBSET RespMessages /\ key_data \in [KEY -> SUBSET Ts] @@ -178,7 +195,7 @@ TypeOK == /\ req_msgs \in SUBSET ReqMessages \union [ts : Ts, start_ts : Ts, type : {"rollback"}, protected : BOOLEAN])] \* At most one lock in key_lock[k] /\ \A k \in KEY: Cardinality(key_lock[k]) <= 1 - /\ client_state \in [CLIENT -> {"init", "reading", "locking", "prewriting", "committing"}] + /\ client_stage \in [CLIENT -> {"init", "reading", "locking", "prewriting", "committing"}] /\ client_ts \in [CLIENT -> [start_ts : Ts \union {NoneTs}, commit_ts : Ts \union {NoneTs}, for_update_ts : Ts \union {NoneTs}]] @@ -193,8 +210,8 @@ TypeOK == /\ req_msgs \in SUBSET ReqMessages \* Once the get request is sent, it exist permanently in the req_msgs. ClientReadOptimistic(c) == - /\ client_state[c] = "init" - /\ client_state' = [client_state EXCEPT ![c] = "reading"] + /\ client_stage[c] = "init" + /\ client_stage' = [client_stage EXCEPT ![c] = "reading"] /\ client_ts' = [client_ts EXCEPT ![c].start_ts = next_ts] /\ client_key' = [client_key EXCEPT ![c].reading = CLIENT_READ_KEY[c]] /\ next_ts' = next_ts + 1 @@ -212,11 +229,11 @@ ClientReadOptimisticSucceed(resp) == /\ \/ UNCHANGED <> \/ \E c \in CLIENT: /\ client_ts[c].start_ts = resp.start_ts - /\ client_state[c] = "reading" + /\ client_stage[c] = "reading" /\ resp.key \in client_key[c].reading /\ client_key' = [client_key EXCEPT ![c].reading = @ \ {resp.key}] /\ add_client_read_history(c, resp.key, resp.value_ts) - /\ UNCHANGED <> + /\ UNCHANGED <> ClientReadOptimisticFailedHasLock(resp) == /\ Assert(resp.type = "read_optimistic_failed_has_lock", "Message Type Error") @@ -233,8 +250,8 @@ ClientReadOptimisticFailedHasLock(resp) == /\ UNCHANGED <> ClientLockKey(c) == - /\ client_state[c] = "init" - /\ client_state' = [client_state EXCEPT ![c] = "locking"] + /\ client_stage[c] = "init" + /\ client_stage' = [client_stage EXCEPT ![c] = "locking"] /\ client_ts' = [client_ts EXCEPT ![c].start_ts = next_ts, ![c].for_update_ts = next_ts] /\ client_key' = [client_key EXCEPT ![c].locking = CLIENT_WRITE_KEY[c]] /\ next_ts' = next_ts + 1 @@ -256,18 +273,18 @@ ClientLockKeySucceed(resp) == \* may carry a bad value that breaks the snapshot isolation. This \* guarantee holds because the previous gRPC connection must have closed. /\ client_ts[c].for_update_ts = resp.for_update_ts - /\ client_state[c] = "locking" + /\ client_stage[c] = "locking" /\ resp.key \in client_key[c].locking /\ client_key' = [client_key EXCEPT ![c].locking = @ \ {resp.key}] /\ add_client_read_history(c, resp.key, resp.value_ts) - /\ UNCHANGED <> + /\ UNCHANGED <> ClientLockKeyFailedHasLock(resp) == /\ Assert(resp.type = "lock_key_failed_has_lock", "Message Type Error") /\ \/ UNCHANGED <> \/ \E c \in CLIENT: /\ client_ts[c].start_ts = resp.start_ts - /\ client_state[c] = "locking" + /\ client_stage[c] = "locking" /\ resp.key \in client_key[c].locking /\ SendReqs({[type |-> "check_txn_status", start_ts |-> resp.lock_ts, @@ -281,7 +298,7 @@ ClientLockKeyFailedWriteConflict(resp) == /\ \/ UNCHANGED <> \/ \E c \in CLIENT: /\ client_ts[c].start_ts = resp.start_ts - /\ client_state[c] = "locking" + /\ client_stage[c] = "locking" /\ resp.key \in client_key[c].locking /\ resp.latest_commit_ts > client_ts[c].for_update_ts /\ client_ts' = [client_ts EXCEPT ![c].for_update_ts = next_ts] @@ -291,12 +308,12 @@ ClientLockKeyFailedWriteConflict(resp) == primary |-> CLIENT_PRIMARY[c], key |-> resp.key, for_update_ts |-> client_ts'[c].for_update_ts]}) - /\ UNCHANGED <> + /\ UNCHANGED <> ClientPrewriteOptimistic(c) == - /\ client_state[c] = "reading" + /\ client_stage[c] = "reading" /\ client_key[c].reading = {} - /\ client_state' = [client_state EXCEPT ![c] = "prewriting"] + /\ client_stage' = [client_stage EXCEPT ![c] = "prewriting"] /\ client_key' = [client_key EXCEPT ![c].prewriting = CLIENT_WRITE_KEY[c]] /\ SendReqs({[type |-> "prewrite_optimistic", start_ts |-> client_ts[c].start_ts, @@ -305,9 +322,9 @@ ClientPrewriteOptimistic(c) == /\ UNCHANGED <> ClientPrewritePessimistic(c) == - /\ client_state[c] = "locking" + /\ client_stage[c] = "locking" /\ client_key[c].locking = {} - /\ client_state' = [client_state EXCEPT ![c] = "prewriting"] + /\ client_stage' = [client_stage EXCEPT ![c] = "prewriting"] /\ client_key' = [client_key EXCEPT ![c].prewriting = CLIENT_WRITE_KEY[c]] /\ SendReqs({[type |-> "prewrite_pessimistic", start_ts |-> client_ts[c].start_ts, @@ -320,15 +337,15 @@ ClientPrewrited(resp) == /\ \/ UNCHANGED <> \/ \E c \in CLIENT: /\ client_ts[c].start_ts = resp.start_ts - /\ client_state[c] = "prewriting" + /\ client_stage[c] = "prewriting" /\ resp.key \in client_key[c].prewriting /\ client_key' = [client_key EXCEPT ![c].prewriting = @ \ {resp.key}] - /\ UNCHANGED <> + /\ UNCHANGED <> ClientCommit(c) == - /\ client_state[c] = "prewriting" + /\ client_stage[c] = "prewriting" /\ client_key[c].prewriting = {} - /\ client_state' = [client_state EXCEPT ![c] = "committing"] + /\ client_stage' = [client_stage EXCEPT ![c] = "committing"] /\ client_ts' = [client_ts EXCEPT ![c].commit_ts = next_ts] /\ next_ts' = next_ts + 1 /\ SendReqs({[type |-> "commit", @@ -342,7 +359,7 @@ ClientRetryCommit(resp) == /\ \/ UNCHANGED <> \/ \E c \in CLIENT: /\ client_ts[c].start_ts = resp.start_ts - /\ client_state[c] = "commiting" + /\ client_stage[c] = "commiting" /\ client_ts[c].commit_ts < resp.min_commit_ts /\ client_ts' = [client_ts EXCEPT ![c].commit_ts = next_ts] /\ next_ts' = next_ts + 1 @@ -350,7 +367,7 @@ ClientRetryCommit(resp) == start_ts |-> client_ts'[c].start_ts, primary |-> CLIENT_PRIMARY[c], commit_ts |-> client_ts'[c].commit_ts]}) - /\ UNCHANGED <> + /\ UNCHANGED <> ----------------------------------------------------------------------------- \* Server Actions @@ -732,7 +749,7 @@ Init == /\ next_ts = 1 /\ req_msgs = {} /\ resp_msgs = {} - /\ client_state = [c \in CLIENT |-> "init"] + /\ client_stage = [c \in CLIENT |-> "init"] /\ client_key = [c \in CLIENT |-> [reading |-> {}, locking |-> {}, prewriting |-> {}]] /\ client_ts = [c \in CLIENT |-> [start_ts |-> NoneTs, commit_ts |-> NoneTs, From d21e46ed0c884e995329eb7b66ca6bbf03929590 Mon Sep 17 00:00:00 2001 From: Andy Lok Date: Thu, 19 Aug 2021 03:42:05 +0800 Subject: [PATCH 20/30] add comment Signed-off-by: Andy Lok --- DistributedTransaction/DistributedTransaction.tla | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/DistributedTransaction/DistributedTransaction.tla b/DistributedTransaction/DistributedTransaction.tla index d16c670..88afab8 100644 --- a/DistributedTransaction/DistributedTransaction.tla +++ b/DistributedTransaction/DistributedTransaction.tla @@ -154,12 +154,13 @@ ReqMessages == \* client handling function only changes the client state to one key and leaving the state to other keys \* untouched, which makes the order of receiving the response to different keys is irrelevant. \* -\* One more draw back is that, a resent request may call the client handling function, which is impossible -\* in real-world because the client must drop the previous connection before retry, in other words, the -\* client is only possible to recieve and handle the response to the request it last sent. We overcome this -\* by checking whether the client is in the right stage to haneld the response. But still, the model will face -\* to some response to the resent resquest for the same client stage. However, the model has just tested -\* more but no less cases comparing to the real-world, and is still passing the check. +\* One more draw back is that, a delayed resent request to server may result in a call the client +\* handling function, which is impossible in real-world because the client must drop the previous +\* connection before retry, in other words, the client is only possible to recieve and to handle the +\* response to the request it last sent. We overcome this by checking whether the client is in the right +\* stage to haneld the response. But still, the model will face to some response to the resent resquest +\* for the same client stage. However, the model has just been tested against more but no less cases +\* comparing to the real-world, and is still passing the check. DirectRespMessages == [start_ts : Ts, type : {"read_optimistic_succeed"}, key : KEY, value_ts : ValueTs] \union [start_ts : Ts, type : {"lock_key_succeed"}, key : KEY, for_update_ts : Ts, value_ts : ValueTs] From 37d05aa3b2c5f195b7e78bf1053c220802141ef0 Mon Sep 17 00:00:00 2001 From: Andy Lok Date: Thu, 19 Aug 2021 03:45:19 +0800 Subject: [PATCH 21/30] fix typo Signed-off-by: Andy Lok --- DistributedTransaction/DistributedTransaction.tla | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DistributedTransaction/DistributedTransaction.tla b/DistributedTransaction/DistributedTransaction.tla index 88afab8..0119011 100644 --- a/DistributedTransaction/DistributedTransaction.tla +++ b/DistributedTransaction/DistributedTransaction.tla @@ -158,7 +158,7 @@ ReqMessages == \* handling function, which is impossible in real-world because the client must drop the previous \* connection before retry, in other words, the client is only possible to recieve and to handle the \* response to the request it last sent. We overcome this by checking whether the client is in the right -\* stage to haneld the response. But still, the model will face to some response to the resent resquest +\* stage to handle the response. But still, the model will face to some response to the resent resquest \* for the same client stage. However, the model has just been tested against more but no less cases \* comparing to the real-world, and is still passing the check. DirectRespMessages == From f30aac417799a0d129df47111a8ff32a8c8197b8 Mon Sep 17 00:00:00 2001 From: Andy Lok Date: Thu, 19 Aug 2021 13:51:42 +0800 Subject: [PATCH 22/30] fix dead end in optimistic prewrite Signed-off-by: Andy Lok --- .../DistributedTransaction.tla | 168 +++++++++--------- 1 file changed, 82 insertions(+), 86 deletions(-) diff --git a/DistributedTransaction/DistributedTransaction.tla b/DistributedTransaction/DistributedTransaction.tla index 0119011..3109719 100644 --- a/DistributedTransaction/DistributedTransaction.tla +++ b/DistributedTransaction/DistributedTransaction.tla @@ -67,7 +67,7 @@ VARIABLES key_data VARIABLES key_lock \* key_write[k] is a sequence of commit or rollback record of the key. \* It's a record of [ts, start_ts, type, [protected]]. type can be either -\* "write" or "rollback". ts represents the commit_ts of "write" record. +\* "commit" or "rollback". ts represents the commit_ts of "commit" record. \* Otherwise, ts equals to start_ts on "rollback" record. "rollback" \* record has an additional protected field. protected signifies the \* rollback record would not be collapsed. @@ -164,8 +164,7 @@ ReqMessages == DirectRespMessages == [start_ts : Ts, type : {"read_optimistic_succeed"}, key : KEY, value_ts : ValueTs] \union [start_ts : Ts, type : {"lock_key_succeed"}, key : KEY, for_update_ts : Ts, value_ts : ValueTs] - \union [start_ts : Ts, type : {"read_optimistic_failed_has_lock", - "lock_key_failed_has_lock"}, key : KEY, lock_primary: KEY, lock_ts : Ts] + \union [start_ts : Ts, type : {"key_is_locked"}, key : KEY, lock_primary: KEY, lock_ts : Ts] \union [start_ts : Ts, type : {"lock_key_failed_write_conflict"}, key : KEY, latest_commit_ts : Ts] \union [start_ts : Ts, type : {"prewrited"}, key : KEY] \union [start_ts : Ts, type : {"commit_ts_expired"}, min_commit_ts : Ts] @@ -192,7 +191,7 @@ TypeOK == /\ req_msgs \in SUBSET ReqMessages "prewrite_pessimistic", "lock_key"}]] /\ key_write \in [KEY -> SUBSET ( - [ts : Ts, start_ts : Ts, type : {"write"}] + [ts : Ts, start_ts : Ts, type : {"commit"}] \union [ts : Ts, start_ts : Ts, type : {"rollback"}, protected : BOOLEAN])] \* At most one lock in key_lock[k] /\ \A k \in KEY: Cardinality(key_lock[k]) <= 1 @@ -236,18 +235,18 @@ ClientReadOptimisticSucceed(resp) == /\ add_client_read_history(c, resp.key, resp.value_ts) /\ UNCHANGED <> -ClientReadOptimisticFailedHasLock(resp) == - /\ Assert(resp.type = "read_optimistic_failed_has_lock", "Message Type Error") +ClientResolveLock(resp) == + /\ Assert(resp.type = "key_is_locked", "Message Type Error") /\ \/ UNCHANGED <> \/ \E c \in CLIENT: /\ client_ts[c].start_ts = resp.start_ts - /\ SendReqs({[type |-> "check_txn_status", - start_ts |-> resp.lock_ts, - caller_start_ts |-> client_ts[c].start_ts, - primary |-> resp.lock_primary, - \* For a read failure, there can't be a pessimistic lock, since - \* when the read simply ignores pessimistic lock. - resolving_pessimistic_lock |-> FALSE]}) + /\ client_stage[c] = "locking" + /\ resp.key \in client_key[c].locking + /\ SendReq([type |-> "check_txn_status", + start_ts |-> resp.lock_ts, + caller_start_ts |-> NoneTs, + primary |-> resp.lock_primary, + resolving_pessimistic_lock |-> resp.lock_type = "lock_key"]) /\ UNCHANGED <> ClientLockKey(c) == @@ -280,20 +279,6 @@ ClientLockKeySucceed(resp) == /\ add_client_read_history(c, resp.key, resp.value_ts) /\ UNCHANGED <> -ClientLockKeyFailedHasLock(resp) == - /\ Assert(resp.type = "lock_key_failed_has_lock", "Message Type Error") - /\ \/ UNCHANGED <> - \/ \E c \in CLIENT: - /\ client_ts[c].start_ts = resp.start_ts - /\ client_stage[c] = "locking" - /\ resp.key \in client_key[c].locking - /\ SendReqs({[type |-> "check_txn_status", - start_ts |-> resp.lock_ts, - caller_start_ts |-> NoneTs, - primary |-> resp.lock_primary, - resolving_pessimistic_lock |-> resp.lock_type = "lock_key"]}) - /\ UNCHANGED <> - ClientLockKeyFailedWriteConflict(resp) == /\ Assert(resp.type = "lock_key_failed_write_conflict", "Message Type Error") /\ \/ UNCHANGED <> @@ -381,22 +366,18 @@ commit(pk, start_ts, commit_ts) == /\ l.start_ts = start_ts /\ unlock_key(pk) /\ key_write' = [key_write EXCEPT ![pk] = @ \union {[ts |-> commit_ts, - type |-> "write", + type |-> "commit", start_ts |-> start_ts]}] \* Rollback the transaction that starts at start_ts on key k. rollback(k, start_ts) == LET \* Rollback record on the primary key of a pessimistic transaction - \* needs to be protected from being collapsed. If we can't decide - \* whether it suffices that because the lock is missing or mismatched, - \* it should also be protected. - protected == \/ \E l \in key_lock[k] : - /\ l.start_ts = start_ts - /\ l.primary = k - /\ l.type \in {"lock_key", "prewrite_pessimistic"} - \/ \E l \in key_lock[k]: l.start_ts /= start_ts - \/ key_lock[k] = {} + \* needs to be protected from being collapsed. + protected == \E l \in key_lock[k] : + /\ l.start_ts = start_ts + /\ l.primary = k + /\ l.type \in {"lock_key", "prewrite_pessimistic"} IN \* If a lock exists and has the same ts, unlock it. /\ IF \E l \in key_lock[k]: l.start_ts = start_ts @@ -421,7 +402,7 @@ rollback(k, start_ts) == \* read_ts is for_update_ts. find_readable_commit(k, read_ts) == LET - all_commits_before_read_ts == {w \in key_write[k]: w.type = "write" /\ w.ts <= read_ts} + all_commits_before_read_ts == {w \in key_write[k]: w.type = "commit" /\ w.ts <= read_ts} latest_commit_before_read_ts == {w \in all_commits_before_read_ts : \A w2 \in all_commits_before_read_ts : @@ -488,12 +469,11 @@ ServerReadKey == /\ UNCHANGED <> ELSE \E l \in key_lock[k]: - /\ ClientReadOptimisticFailedHasLock( - [start_ts |-> start_ts, - type |-> "read_optimistic_failed_has_lock", - key |-> k, - lock_primary |-> l.primary, - lock_ts |-> l.start_ts]) + /\ ClientResolveLock([start_ts |-> start_ts, + type |-> "key_is_locked", + key |-> k, + lock_primary |-> l.primary, + lock_ts |-> l.start_ts]) /\ UNCHANGED <> ServerLockKey == @@ -508,7 +488,7 @@ ServerLockKey == \* there is one, wait until ClientCheckTxnStatus to clean it up. \/ /\ key_lock[k] = {} /\ LET - all_commits == {w \in key_write[k]: w.type = "write"} + all_commits == {w \in key_write[k]: w.type = "commit"} latest_commit == {w \in all_commits: \A w2 \in all_commits: w.ts >= w2.ts} IN IF \E w \in key_write[k]: w.start_ts = start_ts /\ w.type = "rollback" @@ -520,8 +500,8 @@ ServerLockKey == /\ UNCHANGED <> ELSE \* Acquire pessimistic lock only if for_update_ts of req - \* is greater or equal to the latest "write" record. - \* Because if the latest record is "write", it means that + \* is greater or equal to the latest "commit" record. + \* Because if the latest record is "commit", it means that \* a new version is committed after for_update_ts, which \* violates Read Committed guarantee. \/ /\ ~ \E w \in latest_commit: w.ts > req.for_update_ts @@ -548,12 +528,52 @@ ServerLockKey == /\ respond_lock_key_succeed(k, start_ts, for_update_ts) /\ UNCHANGED <> ELSE - /\ ClientLockKeyFailedHasLock([start_ts |-> start_ts, - type |-> "lock_key_failed_has_lock", - key |-> k, - lock_primary |-> l.primary, - lock_ts |-> l.start_ts, - lock_type |-> l.type]) + /\ ClientResolveLock([start_ts |-> start_ts, + type |-> "key_is_locked", + key |-> k, + lock_primary |-> l.primary, + lock_ts |-> l.start_ts, + lock_type |-> l.type]) + /\ UNCHANGED <> + +ServerPrewriteOptimistic == + \E req \in req_msgs: + /\ req.type = "prewrite_optimistic" + /\ LET + k == req.key + start_ts == req.start_ts + IN + \/ /\ key_lock[k] = {} + /\ IF \E w \in key_write[k]: w.start_ts = start_ts /\ w.type = "commit" THEN + \* This is a duplicated prewrite request. + UNCHANGED <> + ELSE IF \/ \E w \in key_write[k]: w.start_ts = start_ts /\ w.type = "rollback" + \/ \E w \in key_write[k]: w.ts >= start_ts THEN + \* This transacstion has been rollbacked or has write conflict. + /\ SendResp([start_ts |-> start_ts, type |-> "prewrite_aborted"]) + /\ UNCHANGED <> + ELSE + \* Prewrite the key. + /\ key_lock' = [key_lock EXCEPT ![k] = {[start_ts |-> start_ts, + primary |-> req.primary, + type |-> "prewrite_optimistic", + min_commit_ts |-> NoneTs]}] + /\ key_data' = [key_data EXCEPT ![k] = @ \union {start_ts}] + /\ ClientPrewrited([start_ts |-> start_ts, type |-> "prewrited", key |-> k]) + /\ UNCHANGED <> + \/ \E l \in key_lock[k]: + IF l.start_ts = start_ts + THEN + /\ ClientPrewrited([start_ts |-> start_ts, type |-> "prewrited", key |-> k]) + /\ UNCHANGED <> + ELSE + \* Clean up the stale lock. + /\ ClientResolveLock([start_ts |-> start_ts, + type |-> "key_is_locked", + key |-> k, + lock_primary |-> l.primary, + lock_ts |-> l.start_ts, + lock_type |-> l.type]) /\ UNCHANGED <> ServerPrewritePessimistic == @@ -564,14 +584,14 @@ ServerPrewritePessimistic == start_ts == req.start_ts IN \* Pessimistic prewrite is only allowed if pressimistic lock is - \* acquired, or, there's no lock, and no write record whose - \* commit_ts >= start_ts otherwise abort the transaction. + \* acquired, or, if there's no lock, and no write record whose + \* commit_ts >= start_ts, otherwise abort the transaction. IF - \/ /\ ~ \E w \in key_write[k]: w.ts >= start_ts - /\ key_lock[k] = {} \/ \E l \in key_lock[k] : /\ l.start_ts = start_ts /\ l.type = "lock_key" + \/ /\ key_lock[k] = {} + /\ ~ \E w \in key_write[k]: w.ts >= start_ts THEN /\ key_lock' = [key_lock EXCEPT ![k] = {[start_ts |-> start_ts, primary |-> req.primary, @@ -584,30 +604,6 @@ ServerPrewritePessimistic == /\ SendResp([start_ts |-> start_ts, type |-> "prewrite_aborted"]) /\ UNCHANGED <> -ServerPrewriteOptimistic == - \E req \in req_msgs: - /\ req.type = "prewrite_optimistic" - /\ LET - k == req.key - start_ts == req.start_ts - IN - IF \E w \in key_write[k]: w.ts >= start_ts - THEN - /\ SendResp([start_ts |-> start_ts, type |-> "prewrite_aborted"]) - /\ UNCHANGED <> - ELSE - \* Optimistic prewrite is allowed only if no stale lock exists. If - \* there is one, wait until ClientCheckTxnStatus to clean it up. - /\ \/ key_lock[k] = {} - \/ \E l \in key_lock[k]: l.start_ts = start_ts - /\ key_lock' = [key_lock EXCEPT ![k] = {[start_ts |-> start_ts, - primary |-> req.primary, - type |-> "prewrite_optimistic", - min_commit_ts |-> NoneTs]}] - /\ key_data' = [key_data EXCEPT ![k] = @ \union {start_ts}] - /\ ClientPrewrited([start_ts |-> start_ts, type |-> "prewrited", key |-> k]) - /\ UNCHANGED <> - ServerCommit == \E req \in req_msgs: /\ req.type = "commit" @@ -615,7 +611,7 @@ ServerCommit == pk == req.primary start_ts == req.start_ts IN - IF \E w \in key_write[pk]: w.start_ts = start_ts /\ w.type = "write" + IF \E w \in key_write[pk]: w.start_ts = start_ts /\ w.type = "commit" THEN \* Key has already been committed. Do nothing. /\ SendResp([start_ts |-> start_ts, type |-> "committed"]) @@ -672,7 +668,7 @@ check_txn_status_has_lock(lock, caller_start_ts, resolving_pessimistic_lock) == \* Lock not found or start_ts on the lock mismatches. check_txn_status_missing_lock(start_ts, pk, resolving_pessimistic_lock) == LET - committed_record == {w \in key_write[pk]: w.start_ts = start_ts /\ w.type = "write"} + committed_record == {w \in key_write[pk]: w.start_ts = start_ts /\ w.type = "commit"} IN IF committed_record /= {} THEN /\ SendReqs({[type |-> "resolve_committed", @@ -782,12 +778,12 @@ Spec == Init /\ [][Next]_vars ----------------------------------------------------------------------------- \* Consistency Invariants -\* Check whether there is a "write" record in key_write[k] corresponding +\* Check whether there is a "commit" record in key_write[k] corresponding \* to start_ts. keyCommitted(k, start_ts) == \E w \in key_write[k] : /\ w.start_ts = start_ts - /\ w.type = "write" + /\ w.type = "commit" \* A transaction can't be both committed and aborted. UniqueCommitOrAbort == @@ -826,7 +822,7 @@ AbortConsistency == WriteConsistency == \A k \in KEY : \A w \in key_write[k] : - \/ /\ w.type = "write" + \/ /\ w.type = "commit" /\ w.ts > w.start_ts /\ w.start_ts \in key_data[k] \/ /\ w.type = "rollback" From 53f3141feab217cbe959584725620d47dbb627fb Mon Sep 17 00:00:00 2001 From: Andy Lok Date: Thu, 19 Aug 2021 15:35:35 +0800 Subject: [PATCH 23/30] add test5 Signed-off-by: Andy Lok --- DistributedTransaction/Test5.cfg | 33 ++++++++++++++++++++++++++++++++ DistributedTransaction/Test5.tla | 13 +++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 DistributedTransaction/Test5.cfg create mode 100644 DistributedTransaction/Test5.tla diff --git a/DistributedTransaction/Test5.cfg b/DistributedTransaction/Test5.cfg new file mode 100644 index 0000000..0949218 --- /dev/null +++ b/DistributedTransaction/Test5.cfg @@ -0,0 +1,33 @@ +CONSTANT + k1 = k1 + k2 = k2 + k3 = k3 + c1 = c1 + c2 = c2 + c3 = c3 + +CONSTANT + KEY <- Keys + OPTIMISTIC_CLIENT <- OptimistiicClient + PESSIMISTIC_CLIENT <- PessimisticClient + CLIENT_READ_KEY <- ClientReadKeys + CLIENT_WRITE_KEY <- ClientWriteKeys + CLIENT_PRIMARY <- ClientPrimary + +INIT + Init + +NEXT + Next + +INVARIANT + TypeOK + UniqueCommitOrAbort + CommitConsistency + AbortConsistency + WriteConsistency + UniqueLockOrWrite + UniqueWrite + OptimisticReadSnapshotIsolation + PessimisticReadSnapshotIsolation + MsgTsConsistency diff --git a/DistributedTransaction/Test5.tla b/DistributedTransaction/Test5.tla new file mode 100644 index 0000000..a5e076c --- /dev/null +++ b/DistributedTransaction/Test5.tla @@ -0,0 +1,13 @@ +--------------------------------- MODULE Test5 --------------------------------- +EXTENDS DistributedTransaction, TLC + +CONSTANT k1, k2, k3 +CONSTANT c1, c2, c3 + +Keys == {k1, k2, k3} +OptimistiicClient == {c3} +PessimisticClient == {c1, c2} +ClientReadKeys == c1 :> {} @@ c2 :> {} @@ c3 :> {k1, k3} +ClientWriteKeys == c1 :> {k1, k2, k3} @@ c2 :> {k1, k2} @@ c3 :> {k2, k3} +ClientPrimary == c1 :> k1 @@ c2 :> k1 @@ c3 :> k3 +================================================================================ From 44f333da7761821f5820670edc7c5d41e76c9ed2 Mon Sep 17 00:00:00 2001 From: Zhuoran He Date: Sat, 21 Aug 2021 02:43:07 +0800 Subject: [PATCH 24/30] Add variables key_max_ts --- .../DistributedTransaction.tla | 62 ++++++++++--------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/DistributedTransaction/DistributedTransaction.tla b/DistributedTransaction/DistributedTransaction.tla index 3109719..a056475 100644 --- a/DistributedTransaction/DistributedTransaction.tla +++ b/DistributedTransaction/DistributedTransaction.tla @@ -73,6 +73,9 @@ VARIABLES key_lock \* rollback record would not be collapsed. VARIABLES key_write +\* max_ts of keys, read to keys updates it. +VARIABLES key_max_ts + \* client_stage[c] indicates the current transaction stage of client c. VARIABLES client_stage \* client_ts[c] is a record of [start_ts, commit_ts, for_update_ts, min_commit_ts]. @@ -95,7 +98,7 @@ VARIABLES next_ts msg_vars == <> client_vars == <> -key_vars == <> +key_vars == <> vars == <> SendReq(msg) == req_msgs' = req_msgs \union {msg} @@ -193,6 +196,7 @@ TypeOK == /\ req_msgs \in SUBSET ReqMessages /\ key_write \in [KEY -> SUBSET ( [ts : Ts, start_ts : Ts, type : {"commit"}] \union [ts : Ts, start_ts : Ts, type : {"rollback"}, protected : BOOLEAN])] + /\ key_max_ts \in [KEY -> Ts \union {NoneTs}] \* At most one lock in key_lock[k] /\ \A k \in KEY: Cardinality(key_lock[k]) <= 1 /\ client_stage \in [CLIENT -> {"init", "reading", "locking", "prewriting", "committing"}] @@ -247,7 +251,7 @@ ClientResolveLock(resp) == caller_start_ts |-> NoneTs, primary |-> resp.lock_primary, resolving_pessimistic_lock |-> resp.lock_type = "lock_key"]) - /\ UNCHANGED <> + /\ UNCHANGED <> ClientLockKey(c) == /\ client_stage[c] = "init" @@ -277,7 +281,7 @@ ClientLockKeySucceed(resp) == /\ resp.key \in client_key[c].locking /\ client_key' = [client_key EXCEPT ![c].locking = @ \ {resp.key}] /\ add_client_read_history(c, resp.key, resp.value_ts) - /\ UNCHANGED <> + /\ UNCHANGED <> ClientLockKeyFailedWriteConflict(resp) == /\ Assert(resp.type = "lock_key_failed_write_conflict", "Message Type Error") @@ -294,7 +298,7 @@ ClientLockKeyFailedWriteConflict(resp) == primary |-> CLIENT_PRIMARY[c], key |-> resp.key, for_update_ts |-> client_ts'[c].for_update_ts]}) - /\ UNCHANGED <> + /\ UNCHANGED <> ClientPrewriteOptimistic(c) == /\ client_stage[c] = "reading" @@ -326,7 +330,7 @@ ClientPrewrited(resp) == /\ client_stage[c] = "prewriting" /\ resp.key \in client_key[c].prewriting /\ client_key' = [client_key EXCEPT ![c].prewriting = @ \ {resp.key}] - /\ UNCHANGED <> + /\ UNCHANGED <> ClientCommit(c) == /\ client_stage[c] = "prewriting" @@ -338,7 +342,7 @@ ClientCommit(c) == start_ts |-> client_ts'[c].start_ts, primary |-> CLIENT_PRIMARY[c], commit_ts |-> client_ts'[c].commit_ts]}) - /\ UNCHANGED <> + /\ UNCHANGED <> ClientRetryCommit(resp) == /\ Assert(resp.type = "commit_ts_expired", "Message Type Error") @@ -353,7 +357,7 @@ ClientRetryCommit(resp) == start_ts |-> client_ts'[c].start_ts, primary |-> CLIENT_PRIMARY[c], commit_ts |-> client_ts'[c].commit_ts]}) - /\ UNCHANGED <> + /\ UNCHANGED <> ----------------------------------------------------------------------------- \* Server Actions @@ -415,16 +419,17 @@ respond_read_succeed(k, start_ts) == LET readable_commit == find_readable_commit(k, start_ts) IN - \/ /\ readable_commit = {} - /\ ClientReadOptimisticSucceed([start_ts |-> start_ts, - type |-> "read_optimistic_succeed", - key |-> k, - value_ts |-> NoneTs]) - \/ \E committed_record \in readable_commit: - /\ ClientReadOptimisticSucceed([start_ts |-> start_ts, - type |-> "read_optimistic_succeed", - key |-> k, - value_ts |-> committed_record.ts]) + /\ key_max_ts' = [key_max_ts EXCEPT ![k] = next_ts - 1] + /\ \/ /\ readable_commit = {} + /\ ClientReadOptimisticSucceed([start_ts |-> start_ts, + type |-> "read_optimistic_succeed", + key |-> k, + value_ts |-> NoneTs]) + \/ \E committed_record \in readable_commit: + /\ ClientReadOptimisticSucceed([start_ts |-> start_ts, + type |-> "read_optimistic_succeed", + key |-> k, + value_ts |-> committed_record.ts]) \* Pessimistic lock successfully. Return `value_ts = NoneTs` if the value is empty. respond_lock_key_succeed(k, start_ts, for_update_ts) == @@ -466,7 +471,7 @@ ServerReadKey == \/ \E l \in key_lock[k]: l.type = "lock_key" \/ l.start_ts = start_ts THEN /\ respond_read_succeed(k, start_ts) - /\ UNCHANGED <> + /\ UNCHANGED <> ELSE \E l \in key_lock[k]: /\ ClientResolveLock([start_ts |-> start_ts, @@ -510,7 +515,7 @@ ServerLockKey == min_commit_ts |-> NoneTs, type |-> "lock_key"]}] /\ respond_lock_key_succeed(k, start_ts, for_update_ts) - /\ UNCHANGED <> + /\ UNCHANGED <> \* Otherwise, reject the request and let client to retry \* with new for_update_ts. \/ \E w \in latest_commit : @@ -560,7 +565,7 @@ ServerPrewriteOptimistic == min_commit_ts |-> NoneTs]}] /\ key_data' = [key_data EXCEPT ![k] = @ \union {start_ts}] /\ ClientPrewrited([start_ts |-> start_ts, type |-> "prewrited", key |-> k]) - /\ UNCHANGED <> + /\ UNCHANGED <> \/ \E l \in key_lock[k]: IF l.start_ts = start_ts THEN @@ -599,7 +604,7 @@ ServerPrewritePessimistic == min_commit_ts |-> NoneTs]}] /\ key_data' = [key_data EXCEPT ![k] = @ \union {start_ts}] /\ ClientPrewrited([start_ts |-> start_ts, type |-> "prewrited", key |-> k]) - /\ UNCHANGED <> + /\ UNCHANGED <> ELSE /\ SendResp([start_ts |-> start_ts, type |-> "prewrite_aborted"]) /\ UNCHANGED <> @@ -626,7 +631,7 @@ ServerCommit == \* the min_commit_ts in the prewrite lock. /\ commit(pk, start_ts, req.commit_ts) /\ SendResp([start_ts |-> start_ts, type |-> "committed"]) - /\ UNCHANGED <> + /\ UNCHANGED <> ELSE /\ ClientRetryCommit([start_ts |-> start_ts, type |-> "commit_ts_expired", @@ -649,13 +654,13 @@ check_txn_status_has_lock(lock, caller_start_ts, resolving_pessimistic_lock) == THEN \* Pessimistic lock will be unlocked directly without rollback record. /\ unlock_key(pk) - /\ UNCHANGED <> + /\ UNCHANGED <> ELSE /\ rollback(pk, start_ts) /\ SendReqs({[type |-> "resolve_rollbacked", start_ts |-> start_ts, primary |-> pk]}) - /\ UNCHANGED <> + /\ UNCHANGED <> \/ \* Push min_commit_ts. /\ lock.min_commit_ts <= caller_start_ts @@ -663,7 +668,7 @@ check_txn_status_has_lock(lock, caller_start_ts, resolving_pessimistic_lock) == type |-> lock.type, primary |-> lock.primary, min_commit_ts |-> caller_start_ts + 1]}] - /\ UNCHANGED <> + /\ UNCHANGED <> \* Lock not found or start_ts on the lock mismatches. check_txn_status_missing_lock(start_ts, pk, resolving_pessimistic_lock) == @@ -683,7 +688,7 @@ check_txn_status_missing_lock(start_ts, pk, resolving_pessimistic_lock) == /\ SendReqs({[type |-> "resolve_rollbacked", start_ts |-> start_ts, primary |-> pk]}) - /\ UNCHANGED <> + /\ UNCHANGED <> \* Clean up the stale transaction by checking the status of the primary key. \* @@ -725,7 +730,7 @@ ServerResolveCommitted == /\ l.primary = req.primary /\ l.start_ts = start_ts /\ commit(k, start_ts, req.commit_ts) - /\ UNCHANGED <> + /\ UNCHANGED <> ServerResolveRollbacked == \E req \in req_msgs : @@ -738,7 +743,7 @@ ServerResolveRollbacked == /\ l.primary = req.primary /\ l.start_ts = start_ts /\ rollback(k, start_ts) - /\ UNCHANGED <> + /\ UNCHANGED <> ----------------------------------------------------------------------------- \* Specification @@ -755,6 +760,7 @@ Init == /\ key_lock = [k \in KEY |-> {}] /\ key_data = [k \in KEY |-> {}] /\ key_write = [k \in KEY |-> {}] + /\ key_max_ts = [k \in KEY |-> NoneTs] Next == \/ \E c \in OPTIMISTIC_CLIENT: From 84d57ed8015a47e279daab4b098fad7eeb1f377b Mon Sep 17 00:00:00 2001 From: Zhuoran He Date: Sat, 21 Aug 2021 03:39:09 +0800 Subject: [PATCH 25/30] Update async lock, async prewrite msg --- .../DistributedTransaction.tla | 91 +++++++++++++------ DistributedTransaction/Test1.cfg | 1 + DistributedTransaction/Test1.tla | 1 + 3 files changed, 67 insertions(+), 26 deletions(-) diff --git a/DistributedTransaction/DistributedTransaction.tla b/DistributedTransaction/DistributedTransaction.tla index a056475..b66fe3f 100644 --- a/DistributedTransaction/DistributedTransaction.tla +++ b/DistributedTransaction/DistributedTransaction.tla @@ -8,6 +8,9 @@ CONSTANTS KEY CONSTANTS OPTIMISTIC_CLIENT, PESSIMISTIC_CLIENT CLIENT == PESSIMISTIC_CLIENT \union OPTIMISTIC_CLIENT +CONSTANTS ASYNC_CLIENT +ASSUME ASYNC_CLIENT \in SUBSET CLIENT + CONSTANTS CLIENT_READ_KEY, CLIENT_WRITE_KEY CLIENT_KEY == [c \in CLIENT |-> CLIENT_READ_KEY[c] \union CLIENT_WRITE_KEY[c]] @@ -121,6 +124,8 @@ ReqMessages == for_update_ts : Ts] \union [start_ts : Ts, primary : KEY, type : {"read_optimistic"}, key : KEY] \union [start_ts : Ts, primary : KEY, type : {"prewrite_optimistic"}, key : KEY] + \union [start_ts : Ts, primary : KEY, type : {"async_prewrite_optimistic"}, + key : KEY, all_keys : SUBSET KEY] \union [start_ts : Ts, primary : KEY, type : {"prewrite_pessimistic"}, key : KEY] \union [start_ts : Ts, primary : KEY, type : {"commit"}, commit_ts : Ts] \union [start_ts : Ts, primary : KEY, type : {"resolve_rollbacked"}] @@ -184,15 +189,20 @@ RespMessages == [start_ts : Ts, type : {"committed", TypeOK == /\ req_msgs \in SUBSET ReqMessages /\ resp_msgs \in SUBSET RespMessages /\ key_data \in [KEY -> SUBSET Ts] - /\ key_lock \in [KEY -> SUBSET [start_ts : Ts, - primary : KEY, - \* As defined above, Ts == Nat \ 0, here we use 0 - \* to indicates that there's no min_commit_ts limit - \* becuase every valid commit ts is larger than 0. - min_commit_ts : Ts \union {NoneTs}, - type : {"prewrite_optimistic", - "prewrite_pessimistic", - "lock_key"}]] + /\ key_lock \in [KEY -> SUBSET ([start_ts : Ts, + primary : KEY, + \* As defined above, Ts == Nat \ 0, here we use 0 + \* to indicates that there's no min_commit_ts limit + \* becuase every valid commit ts is larger than 0. + min_commit_ts : Ts \union {NoneTs}, + type : {"prewrite_optimistic", + "prewrite_pessimistic", + "lock_key"}] + \union [start_ts : Ts, + primary : KEY, + min_commit_ts : Ts \union {NoneTs}, + type : {"async"}, + all_keys : SUBSET KEY])] /\ key_write \in [KEY -> SUBSET ( [ts : Ts, start_ts : Ts, type : {"commit"}] \union [ts : Ts, start_ts : Ts, type : {"rollback"}, protected : BOOLEAN])] @@ -305,22 +315,40 @@ ClientPrewriteOptimistic(c) == /\ client_key[c].reading = {} /\ client_stage' = [client_stage EXCEPT ![c] = "prewriting"] /\ client_key' = [client_key EXCEPT ![c].prewriting = CLIENT_WRITE_KEY[c]] - /\ SendReqs({[type |-> "prewrite_optimistic", - start_ts |-> client_ts[c].start_ts, - primary |-> CLIENT_PRIMARY[c], - key |-> k] : k \in CLIENT_WRITE_KEY[c]}) - /\ UNCHANGED <> + /\ IF c \in ASYNC_CLIENT + THEN + /\ SendReqs({[type |-> "async_prewrite_optimistic", + start_ts |-> client_ts[c].start_ts, + primary |-> CLIENT_PRIMARY[c], + key |-> k, + all_keys |-> CLIENT_WRITE_KEY[c]] : k \in CLIENT_WRITE_KEY[c]}) + /\ UNCHANGED <> + ELSE + /\ SendReqs({[type |-> "prewrite_optimistic", + start_ts |-> client_ts[c].start_ts, + primary |-> CLIENT_PRIMARY[c], + key |-> k] : k \in CLIENT_WRITE_KEY[c]}) + /\ UNCHANGED <> ClientPrewritePessimistic(c) == /\ client_stage[c] = "locking" /\ client_key[c].locking = {} /\ client_stage' = [client_stage EXCEPT ![c] = "prewriting"] /\ client_key' = [client_key EXCEPT ![c].prewriting = CLIENT_WRITE_KEY[c]] - /\ SendReqs({[type |-> "prewrite_pessimistic", - start_ts |-> client_ts[c].start_ts, - primary |-> CLIENT_PRIMARY[c], - key |-> k] : k \in CLIENT_WRITE_KEY[c]}) - /\ UNCHANGED <> + /\ IF c \in ASYNC_CLIENT + THEN + /\ SendReqs({[type |-> "prewrite_pessimistic", + start_ts |-> client_ts[c].start_ts, + primary |-> CLIENT_PRIMARY[c], + key |-> k, + all_keys |-> CLIENT_WRITE_KEY[c]] : k \in CLIENT_WRITE_KEY[c]}) + /\ UNCHANGED <> + ELSE + /\ SendReqs({[type |-> "prewrite_pessimistic", + start_ts |-> client_ts[c].start_ts, + primary |-> CLIENT_PRIMARY[c], + key |-> k] : k \in CLIENT_WRITE_KEY[c]}) + /\ UNCHANGED <> ClientPrewrited(resp) == /\ Assert(resp.type = "prewrited", "Message Type Error") @@ -336,13 +364,24 @@ ClientCommit(c) == /\ client_stage[c] = "prewriting" /\ client_key[c].prewriting = {} /\ client_stage' = [client_stage EXCEPT ![c] = "committing"] - /\ client_ts' = [client_ts EXCEPT ![c].commit_ts = next_ts] - /\ next_ts' = next_ts + 1 - /\ SendReqs({[type |-> "commit", - start_ts |-> client_ts'[c].start_ts, - primary |-> CLIENT_PRIMARY[c], - commit_ts |-> client_ts'[c].commit_ts]}) - /\ UNCHANGED <> + /\ IF c \in ASYNC_CLIENT + THEN + \* TODO: update the async version + /\ client_ts' = [client_ts EXCEPT ![c].commit_ts = next_ts] + /\ next_ts' = next_ts + 1 + /\ SendReqs({[type |-> "commit", + start_ts |-> client_ts'[c].start_ts, + primary |-> CLIENT_PRIMARY[c], + commit_ts |-> client_ts'[c].commit_ts]}) + /\ UNCHANGED <> + ELSE + /\ client_ts' = [client_ts EXCEPT ![c].commit_ts = next_ts] + /\ next_ts' = next_ts + 1 + /\ SendReqs({[type |-> "commit", + start_ts |-> client_ts'[c].start_ts, + primary |-> CLIENT_PRIMARY[c], + commit_ts |-> client_ts'[c].commit_ts]}) + /\ UNCHANGED <> ClientRetryCommit(resp) == /\ Assert(resp.type = "commit_ts_expired", "Message Type Error") diff --git a/DistributedTransaction/Test1.cfg b/DistributedTransaction/Test1.cfg index 9ac3a63..21920d7 100644 --- a/DistributedTransaction/Test1.cfg +++ b/DistributedTransaction/Test1.cfg @@ -12,6 +12,7 @@ CONSTANT CLIENT_READ_KEY <- ClientReadKeys CLIENT_WRITE_KEY <- ClientWriteKeys CLIENT_PRIMARY <- ClientPrimary + ASYNC_CLIENT <- AsyncClient INIT Init diff --git a/DistributedTransaction/Test1.tla b/DistributedTransaction/Test1.tla index 1b8f5ea..a9b4465 100644 --- a/DistributedTransaction/Test1.tla +++ b/DistributedTransaction/Test1.tla @@ -10,4 +10,5 @@ PessimisticClient == {c1, c2} ClientReadKeys == c1 :> {} @@ c2 :> {} @@ c3 :> {k1, k2} ClientWriteKeys == c1 :> {k1, k2} @@ c2 :> {k1} @@ c3 :> {k1, k2} ClientPrimary == c1 :> k1 @@ c2 :> k1 @@ c3 :> k2 +AsyncClient == {} ================================================================================ From bb0f92bb3297047546ccd95563c5c5ba3d916228 Mon Sep 17 00:00:00 2001 From: Zhuoran He Date: Sun, 22 Aug 2021 00:15:12 +0800 Subject: [PATCH 26/30] Update async prewrited, async client commit --- .../DistributedTransaction.tla | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/DistributedTransaction/DistributedTransaction.tla b/DistributedTransaction/DistributedTransaction.tla index b66fe3f..d9f7016 100644 --- a/DistributedTransaction/DistributedTransaction.tla +++ b/DistributedTransaction/DistributedTransaction.tla @@ -175,6 +175,7 @@ DirectRespMessages == \union [start_ts : Ts, type : {"key_is_locked"}, key : KEY, lock_primary: KEY, lock_ts : Ts] \union [start_ts : Ts, type : {"lock_key_failed_write_conflict"}, key : KEY, latest_commit_ts : Ts] \union [start_ts : Ts, type : {"prewrited"}, key : KEY] + \union [start_ts : Ts, type : {"async_prewrited"}, key : KEY, max_ts : Ts \union {NoneTs}] \union [start_ts : Ts, type : {"commit_ts_expired"}, min_commit_ts : Ts] \* The responses defined in RespMessages will not be handled by the client in this spec, because they all @@ -220,6 +221,11 @@ TypeOK == /\ req_msgs \in SUBSET ReqMessages /\ \A c \in CLIENT: client_key[c].locking \intersect client_key[c].prewriting = {} /\ next_ts \in Ts ----------------------------------------------------------------------------- +\* Util functions +Max(S) == CHOOSE x \in S: + \A y \in S: + y <= x +----------------------------------------------------------------------------- \* Client Actions \* Once the get request is sent, it exist permanently in the req_msgs. @@ -317,12 +323,15 @@ ClientPrewriteOptimistic(c) == /\ client_key' = [client_key EXCEPT ![c].prewriting = CLIENT_WRITE_KEY[c]] /\ IF c \in ASYNC_CLIENT THEN + \* Async commit, get a min_commit_ts from pd. + /\ client_ts' = [client_ts EXCEPT ![c].min_commit_ts = next_ts] + /\ next_ts' = next_ts + 1 /\ SendReqs({[type |-> "async_prewrite_optimistic", start_ts |-> client_ts[c].start_ts, primary |-> CLIENT_PRIMARY[c], key |-> k, all_keys |-> CLIENT_WRITE_KEY[c]] : k \in CLIENT_WRITE_KEY[c]}) - /\ UNCHANGED <> + /\ UNCHANGED <> ELSE /\ SendReqs({[type |-> "prewrite_optimistic", start_ts |-> client_ts[c].start_ts, @@ -351,14 +360,20 @@ ClientPrewritePessimistic(c) == /\ UNCHANGED <> ClientPrewrited(resp) == - /\ Assert(resp.type = "prewrited", "Message Type Error") + /\ Assert(resp.type \in {"prewrited", "aysnc_prewrited"}, "Message Type Error") /\ \/ UNCHANGED <> \/ \E c \in CLIENT: /\ client_ts[c].start_ts = resp.start_ts /\ client_stage[c] = "prewriting" /\ resp.key \in client_key[c].prewriting /\ client_key' = [client_key EXCEPT ![c].prewriting = @ \ {resp.key}] - /\ UNCHANGED <> + /\ IF c \in ASYNC_CLIENT + THEN + /\ client_ts' = [client_ts EXCEPT ![c].min_commit_ts = + Max({client_ts[c].min_commit_ts, resp.max_ts + 1})] + /\ UNCHANGED <> + ELSE + /\ UNCHANGED <> ClientCommit(c) == /\ client_stage[c] = "prewriting" @@ -366,9 +381,7 @@ ClientCommit(c) == /\ client_stage' = [client_stage EXCEPT ![c] = "committing"] /\ IF c \in ASYNC_CLIENT THEN - \* TODO: update the async version - /\ client_ts' = [client_ts EXCEPT ![c].commit_ts = next_ts] - /\ next_ts' = next_ts + 1 + /\ client_ts' = [client_ts EXCEPT ![c].commit_ts = client_ts[c].min_commit_ts] /\ SendReqs({[type |-> "commit", start_ts |-> client_ts'[c].start_ts, primary |-> CLIENT_PRIMARY[c], From b79a5a5bd5e960c0600276ad30a100735377942b Mon Sep 17 00:00:00 2001 From: Zhuoran He Date: Sun, 22 Aug 2021 01:53:33 +0800 Subject: [PATCH 27/30] Update async commit, overlap record, invarint check --- .../DistributedTransaction.tla | 70 +++++++++++++------ DistributedTransaction/Test1.tla | 2 +- 2 files changed, 48 insertions(+), 24 deletions(-) diff --git a/DistributedTransaction/DistributedTransaction.tla b/DistributedTransaction/DistributedTransaction.tla index d9f7016..ecc9644 100644 --- a/DistributedTransaction/DistributedTransaction.tla +++ b/DistributedTransaction/DistributedTransaction.tla @@ -127,6 +127,8 @@ ReqMessages == \union [start_ts : Ts, primary : KEY, type : {"async_prewrite_optimistic"}, key : KEY, all_keys : SUBSET KEY] \union [start_ts : Ts, primary : KEY, type : {"prewrite_pessimistic"}, key : KEY] + \union [start_ts : Ts, primary : KEY, type : {"async_prewrite_pessimistic"}, + key : KEY, all_keys : SUBSET KEY] \union [start_ts : Ts, primary : KEY, type : {"commit"}, commit_ts : Ts] \union [start_ts : Ts, primary : KEY, type : {"resolve_rollbacked"}] \union [start_ts : Ts, primary : KEY, type : {"resolve_committed"}, commit_ts : Ts] @@ -179,7 +181,7 @@ DirectRespMessages == \union [start_ts : Ts, type : {"commit_ts_expired"}, min_commit_ts : Ts] \* The responses defined in RespMessages will not be handled by the client in this spec, because they all -\* drive the transaction the an end. The messages defined here are used to record the history, in order to +\* drive the transaction to an end. The messages defined here are used to record the history, in order to \* check the invariants later defined in the spec (e.g. TiKV server should never has responded a transcation \* committed while it has also responded the transaction aborted). RespMessages == [start_ts : Ts, type : {"committed", @@ -206,10 +208,13 @@ TypeOK == /\ req_msgs \in SUBSET ReqMessages all_keys : SUBSET KEY])] /\ key_write \in [KEY -> SUBSET ( [ts : Ts, start_ts : Ts, type : {"commit"}] - \union [ts : Ts, start_ts : Ts, type : {"rollback"}, protected : BOOLEAN])] + \union [ts : Ts, start_ts : Ts, type : {"rollback"}, protected : BOOLEAN] + \union [start_ts : Ts, commit_ts : Ts, type : {"overlapped"}])] /\ key_max_ts \in [KEY -> Ts \union {NoneTs}] \* At most one lock in key_lock[k] /\ \A k \in KEY: Cardinality(key_lock[k]) <= 1 + \* The final stage of a txn: "committed" or "aborted" is now defined here, + \* as described above, we use the var `RespMessages` to represent them. /\ client_stage \in [CLIENT -> {"init", "reading", "locking", "prewriting", "committing"}] /\ client_ts \in [CLIENT -> [start_ts : Ts \union {NoneTs}, commit_ts : Ts \union {NoneTs}, @@ -346,7 +351,7 @@ ClientPrewritePessimistic(c) == /\ client_key' = [client_key EXCEPT ![c].prewriting = CLIENT_WRITE_KEY[c]] /\ IF c \in ASYNC_CLIENT THEN - /\ SendReqs({[type |-> "prewrite_pessimistic", + /\ SendReqs({[type |-> "async_prewrite_pessimistic", start_ts |-> client_ts[c].start_ts, primary |-> CLIENT_PRIMARY[c], key |-> k, @@ -442,15 +447,18 @@ rollback(k, start_ts) == /\ key_data' = [key_data EXCEPT ![k] = @ \ {start_ts}] /\ IF ~ \E w \in key_write[k]: w.ts = start_ts THEN - key_write' = [key_write EXCEPT - ![k] = - \* collapse rollback - (@ \ {w \in @: w.type = "rollback" /\ ~ w.protected /\ w.ts < start_ts}) - \* write rollback record - \union {[ts |-> start_ts, - start_ts |-> start_ts, - type |-> "rollback", - protected |-> protected]}] + key_write' = [key_write EXCEPT + ![k] = + \* collapse rollback + (@ \ {w \in @: w.type = "rollback" /\ ~ w.protected /\ w.ts < start_ts}) + \* write rollback record + \union {[ts |-> start_ts, + start_ts |-> start_ts, + type |-> "rollback", + protected |-> protected]}] + ELSE IF \E w \in key_write[k]: w.type = "commit" /\ w.ts = start_ts + THEN + key_write' = [key_write EXCEPT ![k] = [start_ts |-> key_write[k].start_ts, commit_ts |-> start_ts, type |-> "overlapped"]] ELSE UNCHANGED <> @@ -846,7 +854,9 @@ keyCommitted(k, start_ts) == \* A transaction can't be both committed and aborted. UniqueCommitOrAbort == \A resp, resp2 \in resp_msgs : - (resp.type = "committed") /\ (resp2.type = "commit_aborted") => + (resp.type = "committed") /\ (resp2.type \in {"commit_aborted", + "lock_key_aborted", + "prewrite_aborted"}) => resp.start_ts /= resp2.start_ts \* If a transaction is committed, the primary key must be committed and @@ -855,21 +865,32 @@ UniqueCommitOrAbort == CommitConsistency == \A resp \in resp_msgs : (resp.type = "committed") => - \E c \in CLIENT : - /\ client_ts[c].start_ts = resp.start_ts - \* Primary key must be committed - /\ keyCommitted(CLIENT_PRIMARY[c], resp.start_ts) - \* Secondary key must be either committed or locked by the - \* start_ts of the transaction. - /\ \A k \in CLIENT_WRITE_KEY[c] : - (~ \E l \in key_lock[k]: l.start_ts = resp.start_ts) = - keyCommitted(k, resp.start_ts) + \/ \E c \in CLIENT: + /\ client_ts[c].start_ts = resp.start_ts + \* Primary key must be committed + /\ keyCommitted(CLIENT_PRIMARY[c], resp.start_ts) + \* Secondary key must be either committed or locked by the + \* start_ts of the transaction. + /\ \A k \in CLIENT_WRITE_KEY[c]: + (~ \E l \in key_lock[k]: l.start_ts = resp.start_ts) = + keyCommitted(k, resp.start_ts) + \/ \E c \in ASYNC_CLIENT: + /\ client_ts[c].start_ts = resp.start_ts + \* For every key in an async txn, either it's committed, + \* or it's prewrited and the corresponding lock exists. + /\ \A k \in CLIENT_WRITE_KEY[c]: + \/ keyCommitted(k, resp.start_ts) + \/ \E l \in key_lock[k]: + /\ l.type \in {"async_prewrite_optimistic", "async_prewrite_pessimistic"} + /\ l.ts = resp.start_ts \* If a transaction is aborted, all key of that transaction must not be \* committed. AbortConsistency == \A resp \in resp_msgs : - (resp.type = "commit_aborted") => + (resp.type \in {"commit_aborted", + "lock_key_aborted", + "prewrite_aborted"}) => \A c \in CLIENT : (client_ts[c].start_ts = resp.start_ts) => ~ keyCommitted(CLIENT_PRIMARY[c], resp.start_ts) @@ -885,6 +906,9 @@ WriteConsistency == /\ w.start_ts \in key_data[k] \/ /\ w.type = "rollback" /\ w.ts = w.start_ts + \/ /\ w.type = "overlapped" + /\ w.commit_ts > w.start_ts + /\ w.start_ts \in key_data[k] \* When the lock exists, there can't be a corresponding commit record, \* vice versa. diff --git a/DistributedTransaction/Test1.tla b/DistributedTransaction/Test1.tla index a9b4465..6e00730 100644 --- a/DistributedTransaction/Test1.tla +++ b/DistributedTransaction/Test1.tla @@ -10,5 +10,5 @@ PessimisticClient == {c1, c2} ClientReadKeys == c1 :> {} @@ c2 :> {} @@ c3 :> {k1, k2} ClientWriteKeys == c1 :> {k1, k2} @@ c2 :> {k1} @@ c3 :> {k1, k2} ClientPrimary == c1 :> k1 @@ c2 :> k1 @@ c3 :> k2 -AsyncClient == {} +AsyncClient == {c1} ================================================================================ From bfeb6dc8c92ba4580a0518558c162ad1970ac28f Mon Sep 17 00:00:00 2001 From: Zhuoran He Date: Sun, 22 Aug 2021 02:41:48 +0800 Subject: [PATCH 28/30] Fix a previous bug, fix check --- .../DistributedTransaction.tla | 55 ++++++++++++------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/DistributedTransaction/DistributedTransaction.tla b/DistributedTransaction/DistributedTransaction.tla index ecc9644..6213efb 100644 --- a/DistributedTransaction/DistributedTransaction.tla +++ b/DistributedTransaction/DistributedTransaction.tla @@ -372,6 +372,7 @@ ClientPrewrited(resp) == /\ client_stage[c] = "prewriting" /\ resp.key \in client_key[c].prewriting /\ client_key' = [client_key EXCEPT ![c].prewriting = @ \ {resp.key}] + /\ ~ \E resp2 \in resp_msgs: resp2.type = "prewrite_aborted" /\ resp2.start_ts = client_ts[c].start_ts /\ IF c \in ASYNC_CLIENT THEN /\ client_ts' = [client_ts EXCEPT ![c].min_commit_ts = @@ -652,7 +653,7 @@ ServerPrewritePessimistic == \* acquired, or, if there's no lock, and no write record whose \* commit_ts >= start_ts, otherwise abort the transaction. IF - \/ \E l \in key_lock[k] : + \/ \E l \in key_lock[k]: /\ l.start_ts = start_ts /\ l.type = "lock_key" \/ /\ key_lock[k] = {} @@ -665,6 +666,15 @@ ServerPrewritePessimistic == /\ key_data' = [key_data EXCEPT ![k] = @ \union {start_ts}] /\ ClientPrewrited([start_ts |-> start_ts, type |-> "prewrited", key |-> k]) /\ UNCHANGED <> + \* duplicated prewrite message + ELSE IF \/ \E l \in key_lock[k]: + /\ l.type \in {"async_prewrite_pessimistic", "prewrite_pessimistic"} + /\ l.start_ts = start_ts + \/ \E w \in key_write[k]: + /\ w.type \in {"commit", "overlapped"} + /\ w.start_ts = start_ts + THEN + /\ UNCHANGED <> ELSE /\ SendResp([start_ts |-> start_ts, type |-> "prewrite_aborted"]) /\ UNCHANGED <> @@ -863,26 +873,29 @@ UniqueCommitOrAbort == \* the secondary keys of the same transaction must be either committed \* or locked. CommitConsistency == - \A resp \in resp_msgs : - (resp.type = "committed") => - \/ \E c \in CLIENT: - /\ client_ts[c].start_ts = resp.start_ts - \* Primary key must be committed - /\ keyCommitted(CLIENT_PRIMARY[c], resp.start_ts) - \* Secondary key must be either committed or locked by the - \* start_ts of the transaction. - /\ \A k \in CLIENT_WRITE_KEY[c]: - (~ \E l \in key_lock[k]: l.start_ts = resp.start_ts) = - keyCommitted(k, resp.start_ts) - \/ \E c \in ASYNC_CLIENT: - /\ client_ts[c].start_ts = resp.start_ts - \* For every key in an async txn, either it's committed, - \* or it's prewrited and the corresponding lock exists. - /\ \A k \in CLIENT_WRITE_KEY[c]: - \/ keyCommitted(k, resp.start_ts) - \/ \E l \in key_lock[k]: - /\ l.type \in {"async_prewrite_optimistic", "async_prewrite_pessimistic"} - /\ l.ts = resp.start_ts + /\ \A resp \in resp_msgs: + (resp.type = "committed") => + \/ \E c \in CLIENT: + /\ client_ts[c].start_ts = resp.start_ts + \* Primary key must be committed + /\ keyCommitted(CLIENT_PRIMARY[c], resp.start_ts) + \* Secondary key must be either committed or locked by the + \* start_ts of the transaction. + /\ \A k \in CLIENT_WRITE_KEY[c]: + (~ \E l \in key_lock[k]: l.start_ts = resp.start_ts) = + keyCommitted(k, resp.start_ts) + \* For async clients, once every key is prewrited, the txn is considered + \* commited, so consistency check starts at this time. + /\ \A c \in ASYNC_CLIENT: + (/\ client_stage[c] \in {"prewriting", "committing"} + /\ client_key[c].prewriting = {}) => + \* For every key in an async txn, either it's committed, + \* or it's prewrited and the corresponding lock exists. + /\ \A k \in CLIENT_WRITE_KEY[c]: + \/ keyCommitted(k, client_ts[c].start_ts) + \/ \E l \in key_lock[k]: + /\ l.type \in {"async_prewrite_optimistic", "async_prewrite_pessimistic"} + /\ l.ts = client_ts[c].start_ts \* If a transaction is aborted, all key of that transaction must not be \* committed. From 33a1d26bfea878a18d62d4a2ed870a9c2601920b Mon Sep 17 00:00:00 2001 From: Zhuoran He Date: Sun, 22 Aug 2021 17:14:20 +0800 Subject: [PATCH 29/30] Fix many bugs --- .../DistributedTransaction.tla | 169 +++++++++++++----- DistributedTransaction/Test1.tla | 2 +- 2 files changed, 126 insertions(+), 45 deletions(-) diff --git a/DistributedTransaction/DistributedTransaction.tla b/DistributedTransaction/DistributedTransaction.tla index 6213efb..7677605 100644 --- a/DistributedTransaction/DistributedTransaction.tla +++ b/DistributedTransaction/DistributedTransaction.tla @@ -187,7 +187,8 @@ DirectRespMessages == RespMessages == [start_ts : Ts, type : {"committed", "commit_aborted", "lock_key_aborted", - "prewrite_aborted"}] + "prewrite_aborted", + "check_txn_status_aborted"}] TypeOK == /\ req_msgs \in SUBSET ReqMessages /\ resp_msgs \in SUBSET RespMessages @@ -204,7 +205,7 @@ TypeOK == /\ req_msgs \in SUBSET ReqMessages \union [start_ts : Ts, primary : KEY, min_commit_ts : Ts \union {NoneTs}, - type : {"async"}, + type : {"async_optimistic", "async_pessimistic"}, all_keys : SUBSET KEY])] /\ key_write \in [KEY -> SUBSET ( [ts : Ts, start_ts : Ts, type : {"commit"}] @@ -218,7 +219,8 @@ TypeOK == /\ req_msgs \in SUBSET ReqMessages /\ client_stage \in [CLIENT -> {"init", "reading", "locking", "prewriting", "committing"}] /\ client_ts \in [CLIENT -> [start_ts : Ts \union {NoneTs}, commit_ts : Ts \union {NoneTs}, - for_update_ts : Ts \union {NoneTs}]] + for_update_ts : Ts \union {NoneTs}, + min_commit_ts : Ts \union {NoneTs}]] /\ client_key \in [CLIENT -> [reading : SUBSET KEY, locking : SUBSET KEY, prewriting : SUBSET KEY]] /\ client_read \in [CLIENT -> [KEY -> [type : {"not_read_yet"}] @@ -365,7 +367,7 @@ ClientPrewritePessimistic(c) == /\ UNCHANGED <> ClientPrewrited(resp) == - /\ Assert(resp.type \in {"prewrited", "aysnc_prewrited"}, "Message Type Error") + /\ Assert(resp.type \in {"prewrited", "async_prewrited"}, "Message Type Error") /\ \/ UNCHANGED <> \/ \E c \in CLIENT: /\ client_ts[c].start_ts = resp.start_ts @@ -387,12 +389,14 @@ ClientCommit(c) == /\ client_stage' = [client_stage EXCEPT ![c] = "committing"] /\ IF c \in ASYNC_CLIENT THEN - /\ client_ts' = [client_ts EXCEPT ![c].commit_ts = client_ts[c].min_commit_ts] + /\ client_ts' = [client_ts EXCEPT ![c].commit_ts = Max({client_ts[c].min_commit_ts, + client_ts[c].start_ts + 1, + client_ts[c].for_update_ts + 1})] /\ SendReqs({[type |-> "commit", start_ts |-> client_ts'[c].start_ts, primary |-> CLIENT_PRIMARY[c], commit_ts |-> client_ts'[c].commit_ts]}) - /\ UNCHANGED <> + /\ UNCHANGED <> ELSE /\ client_ts' = [client_ts EXCEPT ![c].commit_ts = next_ts] /\ next_ts' = next_ts + 1 @@ -604,7 +608,7 @@ ServerLockKey == ServerPrewriteOptimistic == \E req \in req_msgs: - /\ req.type = "prewrite_optimistic" + /\ req.type \in {"prewrite_optimistic", "async_prewrite_optimistic"} /\ LET k == req.key start_ts == req.start_ts @@ -620,18 +624,35 @@ ServerPrewriteOptimistic == /\ UNCHANGED <> ELSE \* Prewrite the key. - /\ key_lock' = [key_lock EXCEPT ![k] = {[start_ts |-> start_ts, - primary |-> req.primary, - type |-> "prewrite_optimistic", - min_commit_ts |-> NoneTs]}] /\ key_data' = [key_data EXCEPT ![k] = @ \union {start_ts}] - /\ ClientPrewrited([start_ts |-> start_ts, type |-> "prewrited", key |-> k]) - /\ UNCHANGED <> + /\ IF req.type = "async_prewrite_optimistic" + THEN + /\ key_lock' = [key_lock EXCEPT ![k] = {[start_ts |-> start_ts, + primary |-> req.primary, + type |-> "async_optimistic", + min_commit_ts |-> NoneTs, + all_keys |-> req.all_keys]}] + /\ ClientPrewrited([start_ts |-> start_ts, type |-> "async_prewrited", + key |-> k, max_ts |-> key_max_ts[k]]) + /\ UNCHANGED <> + ELSE + /\ key_lock' = [key_lock EXCEPT ![k] = {[start_ts |-> start_ts, + primary |-> req.primary, + type |-> "prewrite_optimistic", + min_commit_ts |-> NoneTs]}] + /\ ClientPrewrited([start_ts |-> start_ts, type |-> "prewrited", key |-> k]) + /\ UNCHANGED <> \/ \E l \in key_lock[k]: IF l.start_ts = start_ts THEN - /\ ClientPrewrited([start_ts |-> start_ts, type |-> "prewrited", key |-> k]) - /\ UNCHANGED <> + /\ IF req.type = "async_prewrite_optimistic" + THEN + /\ ClientPrewrited([start_ts |-> start_ts, type |-> "async_prewrited", + key |-> k, max_ts |-> key_max_ts[k]]) + /\ UNCHANGED <> + ELSE + /\ ClientPrewrited([start_ts |-> start_ts, type |-> "prewrited", key |-> k]) + /\ UNCHANGED <> ELSE \* Clean up the stale lock. /\ ClientResolveLock([start_ts |-> start_ts, @@ -644,7 +665,7 @@ ServerPrewriteOptimistic == ServerPrewritePessimistic == \E req \in req_msgs : - /\ req.type = "prewrite_pessimistic" + /\ req.type \in {"prewrite_pessimistic", "async_prewrite_pessimistic"} /\ LET k == req.key start_ts == req.start_ts @@ -659,16 +680,27 @@ ServerPrewritePessimistic == \/ /\ key_lock[k] = {} /\ ~ \E w \in key_write[k]: w.ts >= start_ts THEN - /\ key_lock' = [key_lock EXCEPT ![k] = {[start_ts |-> start_ts, - primary |-> req.primary, - type |-> "prewrite_pessimistic", - min_commit_ts |-> NoneTs]}] /\ key_data' = [key_data EXCEPT ![k] = @ \union {start_ts}] - /\ ClientPrewrited([start_ts |-> start_ts, type |-> "prewrited", key |-> k]) - /\ UNCHANGED <> + /\ IF req.type = "async_prewrite_pessimistic" + THEN + /\ key_lock' = [key_lock EXCEPT ![k] = {[start_ts |-> start_ts, + primary |-> req.primary, + type |-> "async_pessimistic", + min_commit_ts |-> NoneTs, + all_keys |-> req.all_keys]}] + /\ ClientPrewrited([start_ts |-> start_ts, type |-> "async_prewrited", + key |-> k, max_ts |-> key_max_ts[k]]) + /\ UNCHANGED <> + ELSE + /\ key_lock' = [key_lock EXCEPT ![k] = {[start_ts |-> start_ts, + primary |-> req.primary, + type |-> "prewrite_pessimistic", + min_commit_ts |-> NoneTs]}] + /\ ClientPrewrited([start_ts |-> start_ts, type |-> "prewrited", key |-> k]) + /\ UNCHANGED <> \* duplicated prewrite message ELSE IF \/ \E l \in key_lock[k]: - /\ l.type \in {"async_prewrite_pessimistic", "prewrite_pessimistic"} + /\ l.type \in {"async_pessimistic", "prewrite_pessimistic"} /\ l.start_ts = start_ts \/ \E w \in key_write[k]: /\ w.type \in {"commit", "overlapped"} @@ -714,6 +746,13 @@ ServerCommit == /\ SendResp([start_ts |-> start_ts, type |-> "commit_aborted"]) /\ UNCHANGED <> +\* Check whether there is a "commit" record in key_write[k] corresponding +\* to start_ts. +keyCommitted(k, start_ts) == + \E w \in key_write[k] : + /\ w.start_ts = start_ts + /\ w.type = "commit" + \* Found the matching lock. check_txn_status_has_lock(lock, caller_start_ts, resolving_pessimistic_lock) == LET @@ -726,19 +765,63 @@ check_txn_status_has_lock(lock, caller_start_ts, resolving_pessimistic_lock) == /\ unlock_key(pk) /\ UNCHANGED <> ELSE - /\ rollback(pk, start_ts) - /\ SendReqs({[type |-> "resolve_rollbacked", - start_ts |-> start_ts, - primary |-> pk]}) - /\ UNCHANGED <> + /\ IF lock.type \in {"async_optimistic", "async_pessimistic"} + THEN + \* For async commit transactions, if every key is either locked or + \* committed, we must *not* rollback it. + LET + all_keys == (CHOOSE pk_lock \in key_lock[pk] : TRUE).all_keys + IN + IF \A k \in all_keys: + \/ \E l2 \in key_lock[k]: l2.type = lock.type /\ l2.start_ts = start_ts + \/ keyCommitted(k, start_ts) + THEN + \* A little bit inconsistent with real world TiKV. + LET + all_async_locked_key == {k \in all_keys : + (\E l2 \in key_lock[k]: + l2.type = lock.type /\ l2.start_ts = start_ts)} + all_min_commit_ts == {(CHOOSE l2 \in key_lock[k] : TRUE).min_commit_ts : k \in all_async_locked_key} + commit_ts == Max(all_min_commit_ts \union {start_ts}) + 1 + IN + /\ commit(pk, start_ts, commit_ts) + /\ SendReqs({[type |-> "resolve_committed", + start_ts |-> start_ts, + primary |-> pk, + commit_ts |-> commit_ts]}) + /\ UNCHANGED <> + ELSE + /\ rollback(pk, start_ts) + /\ SendReqs({[type |-> "resolve_rollbacked", + start_ts |-> start_ts, + primary |-> pk]}) + /\ SendResp([type |-> "check_txn_status_aborted", + start_ts |-> start_ts]) + /\ UNCHANGED <> + ELSE + /\ rollback(pk, start_ts) + /\ SendReqs({[type |-> "resolve_rollbacked", + start_ts |-> start_ts, + primary |-> pk]}) + /\ UNCHANGED <> \/ \* Push min_commit_ts. /\ lock.min_commit_ts <= caller_start_ts - /\ key_lock' = [key_lock EXCEPT ![pk] = {[start_ts |-> lock.start_ts, - type |-> lock.type, - primary |-> lock.primary, - min_commit_ts |-> caller_start_ts + 1]}] - /\ UNCHANGED <> + /\ IF lock.type \in {"async_optimistic", "async_pessimistic"} + THEN + /\ key_lock' = [key_lock EXCEPT ![pk] = {[start_ts |-> lock.start_ts, + type |-> lock.type, + primary |-> lock.primary, + min_commit_ts |-> caller_start_ts + 1, + all_keys |-> lock.all_keys]}] + /\ UNCHANGED <> + ELSE + /\ key_lock' = [key_lock EXCEPT ![pk] = {[start_ts |-> lock.start_ts, + type |-> lock.type, + primary |-> lock.primary, + min_commit_ts |-> caller_start_ts + 1]}] + /\ UNCHANGED <> + \* Lock not found or start_ts on the lock mismatches. check_txn_status_missing_lock(start_ts, pk, resolving_pessimistic_lock) == @@ -825,7 +908,8 @@ Init == /\ client_key = [c \in CLIENT |-> [reading |-> {}, locking |-> {}, prewriting |-> {}]] /\ client_ts = [c \in CLIENT |-> [start_ts |-> NoneTs, commit_ts |-> NoneTs, - for_update_ts |-> NoneTs]] + for_update_ts |-> NoneTs, + min_commit_ts |-> NoneTs]] /\ client_read = [c \in CLIENT |-> [k \in KEY |-> [type |-> "not_read_yet"]]] /\ key_lock = [k \in KEY |-> {}] /\ key_data = [k \in KEY |-> {}] @@ -854,13 +938,6 @@ Spec == Init /\ [][Next]_vars ----------------------------------------------------------------------------- \* Consistency Invariants -\* Check whether there is a "commit" record in key_write[k] corresponding -\* to start_ts. -keyCommitted(k, start_ts) == - \E w \in key_write[k] : - /\ w.start_ts = start_ts - /\ w.type = "commit" - \* A transaction can't be both committed and aborted. UniqueCommitOrAbort == \A resp, resp2 \in resp_msgs : @@ -888,14 +965,18 @@ CommitConsistency == \* commited, so consistency check starts at this time. /\ \A c \in ASYNC_CLIENT: (/\ client_stage[c] \in {"prewriting", "committing"} - /\ client_key[c].prewriting = {}) => + /\ client_key[c].prewriting = {} + /\ ~ \E resp \in resp_msgs: + /\ resp.type \in {"prewrite_aborted", "lock_key_aborted", + "check_txn_status_aborted", "commit_aborted"} + /\ resp.start_ts = client_ts[c].start_ts) => \* For every key in an async txn, either it's committed, \* or it's prewrited and the corresponding lock exists. /\ \A k \in CLIENT_WRITE_KEY[c]: \/ keyCommitted(k, client_ts[c].start_ts) \/ \E l \in key_lock[k]: - /\ l.type \in {"async_prewrite_optimistic", "async_prewrite_pessimistic"} - /\ l.ts = client_ts[c].start_ts + /\ l.type \in {"async_optimistic", "async_pessimistic"} + /\ l.start_ts = client_ts[c].start_ts \* If a transaction is aborted, all key of that transaction must not be \* committed. diff --git a/DistributedTransaction/Test1.tla b/DistributedTransaction/Test1.tla index 6e00730..a3e2fb9 100644 --- a/DistributedTransaction/Test1.tla +++ b/DistributedTransaction/Test1.tla @@ -10,5 +10,5 @@ PessimisticClient == {c1, c2} ClientReadKeys == c1 :> {} @@ c2 :> {} @@ c3 :> {k1, k2} ClientWriteKeys == c1 :> {k1, k2} @@ c2 :> {k1} @@ c3 :> {k1, k2} ClientPrimary == c1 :> k1 @@ c2 :> k1 @@ c3 :> k2 -AsyncClient == {c1} +AsyncClient == {c1, c2, c3} ================================================================================ From aaa06315c0447b47c22525683ca40be02fdea8b6 Mon Sep 17 00:00:00 2001 From: Zhuoran He Date: Sun, 22 Aug 2021 18:53:52 +0800 Subject: [PATCH 30/30] Fix many bugs --- DistributedTransaction/DistributedTransaction.tla | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/DistributedTransaction/DistributedTransaction.tla b/DistributedTransaction/DistributedTransaction.tla index 7677605..d8f3620 100644 --- a/DistributedTransaction/DistributedTransaction.tla +++ b/DistributedTransaction/DistributedTransaction.tla @@ -463,7 +463,9 @@ rollback(k, start_ts) == protected |-> protected]}] ELSE IF \E w \in key_write[k]: w.type = "commit" /\ w.ts = start_ts THEN - key_write' = [key_write EXCEPT ![k] = [start_ts |-> key_write[k].start_ts, commit_ts |-> start_ts, type |-> "overlapped"]] + key_write' = [key_write EXCEPT ![k] = (@ \ {w \in @: w.type = "commit" /\ w.ts = start_ts}) + \union [start_ts |-> key_write[k].start_ts, + commit_ts |-> start_ts, type |-> "overlapped"]] ELSE UNCHANGED <> @@ -782,8 +784,14 @@ check_txn_status_has_lock(lock, caller_start_ts, resolving_pessimistic_lock) == (\E l2 \in key_lock[k]: l2.type = lock.type /\ l2.start_ts = start_ts)} all_min_commit_ts == {(CHOOSE l2 \in key_lock[k] : TRUE).min_commit_ts : k \in all_async_locked_key} - commit_ts == Max(all_min_commit_ts \union {start_ts}) + 1 + + client == CHOOSE c \in CLIENT: client_ts[c].start_ts = start_ts + all_key_max_ts == {key_max_ts[k] : k \in CLIENT_KEY[client]} + commit_ts == Max(all_min_commit_ts + \union all_key_max_ts + \union {start_ts}) + 1 IN + /\ next_ts > commit_ts /\ commit(pk, start_ts, commit_ts) /\ SendReqs({[type |-> "resolve_committed", start_ts |-> start_ts, @@ -984,7 +992,8 @@ AbortConsistency == \A resp \in resp_msgs : (resp.type \in {"commit_aborted", "lock_key_aborted", - "prewrite_aborted"}) => + "prewrite_aborted", + "check_txn_status_aborted"}) => \A c \in CLIENT : (client_ts[c].start_ts = resp.start_ts) => ~ keyCommitted(CLIENT_PRIMARY[c], resp.start_ts)