From 37b46131da31e45e8005e0c6490861ce354a6dfe Mon Sep 17 00:00:00 2001 From: Stephen Asbury Date: Fri, 17 May 2019 11:23:44 -0700 Subject: [PATCH] Added minimal doc for nats streaming based on training slides --- SUMMARY.md | 12 ++++- developer/resources/ring_buffer.png | Bin 0 -> 25282 bytes developer/resources/start_positions.png | Bin 0 -> 23794 bytes developer/streaming/README.md | 62 ++++++++++++++++++++++++ developer/streaming/acks.md | 25 ++++++++++ developer/streaming/connecting.md | 15 ++++++ developer/streaming/durables.md | 7 +++ developer/streaming/publishing.md | 15 ++++++ developer/streaming/queues.md | 11 +++++ developer/streaming/receiving.md | 46 ++++++++++++++++++ 10 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 developer/resources/ring_buffer.png create mode 100644 developer/resources/start_positions.png create mode 100644 developer/streaming/README.md create mode 100644 developer/streaming/acks.md create mode 100644 developer/streaming/connecting.md create mode 100644 developer/streaming/durables.md create mode 100644 developer/streaming/publishing.md create mode 100644 developer/streaming/queues.md create mode 100644 developer/streaming/receiving.md diff --git a/SUMMARY.md b/SUMMARY.md index 2dce020..e95df79 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -45,7 +45,7 @@ ## Developing With NATS -* [Intro](developer/README.md) +* [Introduction](developer/README.md) * [Concepts](developer/concepts/intro.md) * [Subject-Based Messaging](developer/concepts/subjects.md) @@ -106,6 +106,16 @@ * [Explore NATS Queueing](developer/tutorials/queues.md) * [Advanced Connect and Custom Dialer in Go](developer/tutorials/custom_dialer.md) +## Developing With NATS Streaming + +* [Introduction](developer/streaming/README.md) +* [Connecting to NATS Streaming](developer/streaming/connecting.md) +* [Publishing to a Channel](developer/streaming/publishing.md) +* [Receiving Messages from a Channel](developer/streaming/receiving.md) +* [Durable Subscriptions](developer/streaming/durables.md) +* [Queue Subscriptions](developer/streaming/queues.md) +* [Acknowledgements](developer/streaming/acks.md) + ## NATS Protocol * [Protocol Demo](nats_protocol/nats-protocol-demo.md) * [Client Protocol](nats_protocol/nats-protocol.md) diff --git a/developer/resources/ring_buffer.png b/developer/resources/ring_buffer.png new file mode 100644 index 0000000000000000000000000000000000000000..44fd040b25ddf13f687e7c25894389d95f03c4c6 GIT binary patch literal 25282 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≥$dv-+S)PMQ#Lb%~YGZ^k;`>6WJZpYqJipx2hwrGO*yOnpyzLI8-v z5cEOLWV%S6Q*D)H6n)9K*Gx+j=aokoO;Iab72FlvC@C)0z64RIB_s3aae4iR*8!;IB4b#RoU9|@E8D#h zdvGZQv};*lmaf2N8X;~)VYw8L6{L0jVPEO+xknqtViz4blLgi30b+m$)o6Ap3EiBs zWuTRcM==zB0yIT4rE22WIdB=Ydv^gd3N+}5^Un?FgX;a|8iwX6i~;)$Jl~^a7y9Bw zX;fvhL<2KU4x#Xtx>>H`JT~jo2ku(2OIVuFLdk}jH#+-d-vPps%2x+9OF?BIZze;h z4Qu?K%>cCj&#!xfqaV+=)lJ=W{vWNf4`Nfq0ol9Lny`Rtls7WUe6%!`0Q(vvIzYf^Cr~43;}x79%ZUk7x`$rO6*{LZMvx{gu%!!5RPYS5081=`Bc&vo;9&+1W`bsg4^j0r z+LE=@nO_eT&N3jq7I$2(L{vEguuqsY%C4x3rNG)sFDqjV!w2il&R-J6zqCchfvW_4 z1h<>~%$Fb5R83}Mfv$=61OaWxt0v2kCmpu2b5J;hCXC|)K}Lox`AK^WSx1~3$Y_gb zGM{uJe}7n;$<%G^3YZ@;19qfieK}Vz%P)&nen)vBex3ET^ znJjw4QP*mG{{mO>OCD1KD*=~?&k_Wr|2!~3D@3Y_1E*eG+1Y4BdOHD3dUZu>q}6`+ zuQ7kU)irECdn-N{j5^U=*jQAA$QRKSI&Us0xieimxhUB|k!}@)=q>&AEtM@*!!Ur1 z*8ufvZhI!K?g+OgHkMXb0};!UW1y^(e9n&D*QnYma>GBE&~ma95jc-Yq1x^ga7`P~ z)v%z&J~wQJY!U0wvsePUb}VD!e6anyfX3Y3;_v2qVlOh>cbS=43ofEj{})KMhk+sh z)$A(9g`>?qt(fcQinQ)Ev$$}AS}_svkJ8vteqd`*x)^X^B3i2SN~-)zT|{1^L^aid z-Ov^RA5{igd-Q>~oyDvBJ{sE zMz2=qH|+XQCLKb4aM!(B!zU2sR@c4xSs8@hQtJIol9;3i<$m;`D`qj2s|^5z(Z1oM zm%EF4%T(Rx8*;ohStLNXtndH!+t2f{PwThy_8F~~a;BKaiS7HqRzEJQ+H7#Y3h!(A z>bKICuW|~iEaz3SwyZF7Z!u7c$z+}6B70ei!-r~*Y#^!N&upf_-UfWCLtd5k7lgJ@ad0<|_3Xr4V(g4xceg}7oO+khg{ zs+O@Xx&s~*6vdm@{|lgM-UCP{{$k`7=$%(p$GFGFY2E0H^x;tF-cCxN^ZQ9)wQ*pV5}Zi>-9r3&Z1V}_;3(S;s^OG+TNxqti2CA zO)Hjsy&+`N@2U3<=sD4vZYcTD_KY+ztw`nj!T?375$8GA&pZyt%r74dx3qysx2o^T zGVe3?iQDfGrv<@ja`pJXl;MzSctQ7JPGJ%hx8a(J6-0GFH!58JSAP7I&CR<^9OF6* z`8I^H6nS9BmY60^h@>k1tsZB~hL%Ro%i>d5zu@4Nwr zg$Q{j6%POUR9>rlD!%H=k}DeEAG2VeA`DP2#$sD|@igq9 zDCWFY2auIfxpKx*uu;;J+V3m5w>|FC*nqq7l?!y0oD}rtBvm-SDfrDn0BF*owVDo$ zXUM+MYADOu>nk!k(wfbHzzh*@?(eU~cT04=et&DF%a=G&0m92#Mb`#$jSfgvVq@Hb zqvGaPDc}XyqT>Z|(-oc5 zF);yommIr*fDR|j>nk)tJ@XRhReA*sK;oLu0($5QYB4pKy(DsfCl)3EwAsP3S`k0e zy7}0x$BZkZDxRN7hP;h2Hem<#uc7qRq@s}n{R1f_v&)b zs{oBmM9tv9-}%l3zc=4SF{MR}yef+qoVT){>-O=pFP08f(&RT8w#$C0LRI7QTmAkK zQh@kp=S=iF{BVoTZ%E~xrN5G6fy>;OG}PtnK__bK`B4?HF{E&@mSdcc!7_B0qTFQ- z1Kfq%Qu#0-pk?pLTz9lqW7<1_Y*E4_Bmp=sS%zsvt4iY^{Iu zg4^I0Bm@Q$G{GgfTX1)m5F~?z1PQ?j?j8sZ!CfcVpuxF^=Y5|a-}nFixK*c4)zqo# zBi+4v_u6aiNta$hW_vtu2P`oRU#J<{y~U{ThWkj6j*nm8UiZSMh1sF(X69F*-iS|T zm}7!2$`7nLW-+H0Fhp>Oq`9}ezf)J?c9qvrDt)%fAps=9Y8*=l3&#gnP+t;n_Kj0O z!Tl9-3_+Ad6mI;?wi^cfIx%~iBeqC@UmQ&@P1D6*Q=jGf#;hC$SN!-eyPA!o)4)A8VL|xH2tFu~BJ^Y&QncCU>CqfFrXK zOs1zXa;B27cJkj};X~olJa9iws1RMH7(`R#ffd_${v_+geOQ-jqBqlu9%xICk2cv$ z8W!mofFVaymX`q`W|J|%Mn~hIQIr3xm`x-mA|Xn|XJJ;ngYbfJC|5Hqwv z-4uHxx{-tV5v?lFYxyO8%L9*MPXTJo{0miB|IyD%1*j{{R&saD*9nn-A_^X(DT5Fa zN)sguqkU!V7Y;!746vbWp=D7%GeD+FPeO$Ng3QJqrER5mg4CpW%O_wgR1_c%)JZ{^ zl)yrr&Hg+-vhdr4Snu=Bpu@g7jn&uBY@l2cTtuo2F=Z=WU%%+k05|$WJldT2r}E5Z z7+|g6R&&+b9#!=i{j!K7WiIXUk=uJBl$B~TKr`!eDPWN>-3mwiFAH3tnAEror;8vJs{MTg{_I2$;VS%bTdi0O}W@D3fOwb>B zrKd|oFGmWhNfd!iF3gva+oH#s19%C)hnMi{HRO_ckaA6j)qd?nFeuC?%lyNFb*=-7 znM{6$$0SiDgDu(!V1Wu(1M~#pBQPDB@ZO0t4ktWR{~fMsDS;Npfc_ny!X3S)9yduk z-1hfOLBA>36kcpUETKHy<|3`Q?rc?Jbq~;)^8lUIIPX6<=%Sfpd<<-*Zya*4vVwX5 zcz!)NMrw4XA9c$AV6QR+9D|o;qMKv|H2|tcV6Oxmy7j5z7x_4J%pC$A2(FOy#nE~c@ zdGw?E>t|)6m@yyspB>tKr3g~PwuZ{>aY^GLvq@Rg;&H!-@25sR;<|A zK#_7={=4@KMs*I0q|Ae=Ix+2q=mKxfG$`guP*KZT(|?*T1y&>N7k)K<(_-1q%cX^V z`BZ+eZu3gJ)gRI>54yg-7VcWIzX)jT;Px0Co+DbhXb#4j3`J>jUkz-=^8&PVW>e4H zuFv;TG^oBDj7cT-wUgu80{(X?FwspjwxWnc_DMgC(_G;xGqT4Yv4O7~z} zSrx1)rJ8V|PGGOX6C2%jGdZLTL%f)cdPE;fG+ZHob#Fxpw1>UAi*(v5bR~hZhA?C( z^D^bA?Z3#bz(2Zcld<(#+rT|&jJ z&oAmrtroXe4NNQMpuLWbE;P4|`~Bv&+xfPA*Q(a>p<8qT#qL}89bH`H^E0w=Ia69a zkIt&yog^;|HP0-RTI?=CS2|o+bd+=!mJ=7bWDAl#dU@-^{k&+NdZi#sE`1VB=efo! zU5?O~rD`t~rA+FPf%bkm_)c?g9 zcL6J*J3gxJWrS_y+=|_ff`;#WF2ix6bwRA5)qD3P13ma+K4ly(TN#!Pp0JHAw%8~?F-;jLOP3!u)~vycgj)N(I8Pe^RcQn8?Xrr@ z%3!pleV7;7@;9|hOG{ulPugQ0MY z7!x5bqCi#EE~R*rvpEQ+c@n&Y*U-%g!zzhv>~wBK@m5>kjrN`Bv(#dBHGnaSin_gL zG=_hOr@QeNDM%+IAY9SF+8)4Wi-3=7X99dWyXs`br3uu6J{(FPjG$Zzubqg)vMZ-a zh`76vjQbe+-c0^VWjkmou@~7EcjxFA*LELZ_S5)@+uYm_+xO&M?aQJR>`mmue`QZG z4EZ##OuZ2BhX4m?S!S%y%gL7-*3T?m(O(KYVS*f=uK{sWx!h9s#2Q{N;Kfb>;@d9( z$tYJOV%9yElBB|NX2t7=sL$O#d<2D5SE$c=xyVM3jW@g{2B`eEN$18QTP$^|JVT^L z^_#oV>D^a+Ps;Cb?XGxhv0GlPcQBMGs(jWU#tWmj%}$EMaAGA4cw&3nLYWQSpPXpA zw9qZfa4Or*#oTZ_T0zoo$r;2=|B)I{%{L=1!bHU%nha^g90&#M)D})>J-cJ1$FZ0y zH0B_OIeSl=FDX9?^YT^)Tv$;F22?Pa)E|N9jEw3ZKU5$Bg zlmg8@Ted6@{ z7r(Y(&DY_0+E0?7b-=?)a`)w30X%1u#rMH^>Dz*28rD|><>Tn_7sNV^D4W}KAU9bg zT{M7N^lt!A4OyzRvwCSKCn(`r)l5=;`zK{b&P*ekooMCW1l@5Uwq?cGj4^pguk$c~ z`WkxHWK09=XRvwtgqulTG_rlQQXUh*qD8BOBHU&Hg?w5Gy_s|bh6bAO34ODcE5Y7! zMBy*tp~sbUx*D&4?xKGeeupChsR@|OhtJ>Cd&c}q*n@0y%EzuZ%j1Pd5czA`*8 z%O}F9qUE!i#xi>wnlJv^6D$hAlP^Y$d5X#rSJ#E>p1UuvWW<(m*mTK3x)_oNZw+?PDRUDD_iytI2bw4K4>2?NrN`e!*wS?^2I1;uvK6oNOai zdC2PhC#j3qGwkJj#`}G#plv6aVfK$H^35e#c;HJ!xTNsK30|)=GhZ`{4D!NKENE09 z;EXC3qRqtxtBFe(bP<|%U?bcEmCH0c^!+@@KQKgOC-08hb|xUGH^8Fo?h`w2Zb_eY z5gf!){cP!&{w;Zx?^+546u?Y8NIjf)_p?gX0cOuvIZqd|1V7WdRbpud0X!tTw7c!c z^yLZ^wG+a2Vv&eli~1=7Rs?=uKAQL;>U55&j}If&jL20Row%rgkgM)cP#svPZ@kjU z^ABM_{drawYt6bXXiwrL3rXGDdslLQslPW{!fltW;V=AdQ;^?}n9nLCaC;rYSBrOD z7WP^(n??YaO2b^AlPYDnElk*Tft)tWp+XE2yFHIzsgay_r|x2~sjLlic-+1!ej~-T ze|Q)GhAD}8vyQAW1-r;Dyv-l?c?w_>b#x-(@OptTu7xC_;)eJFa_Kner#r9ntG><_ zKr&JHyfyP6fta~+LB~0JQHXx}E{OYCR$(!#KUjc#x^v|`5ix0N_2ai?$652)x5@64x` zg~S!KM5loZc#DR8@Xwc5Ms9C8U*!B(4@xishV_#b^=BuB)gQHNIk_*E8uIu!u1`+Z zn<_kitARnA^kBUjM*NvWx>VMP0`TA@-Fu|v0Adu;3~ z-TH{xKUxhPu5-W&(zk6Uqx^4`g0-`!T=D2&;dSgi0w`p}b8*Rgo-!8Wz!nat(uk%X zdtse_n6-BlBSs;Wqr%dbr&IJYZ!2B^ZJDxj{B8#o(S5%(;UqnrgtivnnDTMzaqiD= zQ=7Z>3qzk)DHgC8f=G{Z*U3)PWz)fY5=28croVIugi;%Eo*zre_V&$xW?28R#PY`) z?FqMJ(2JOP@$dq3R}BAZ{MBu6C;oQXo9cq{4P3h9#5UB)RldnhlA;<6$BqRK%n3*; zvmmVuKu^aPh0i?=0(KATh+}1@uCnP8QjBG}N=mNmOr%=o&cDhZcNTKmOThhvPdgfcTXFD?2iX{WXqt z>;drLo?3vQnI72B3)1!(qMYm(eC=OB ztX~w|v*W)UBxEJB&tvC#-!K|p6uFtLy^nHSbNV`)-V)w>AlhurOX!@HZi^(M{ZRO- z+`!PlktykR&$t#$z!B44rK(3u-Z-@_vJ} zW!}b6Md2#dC~*3E+UC{jzd8ADbDnSY;v9g2B#^XzY_`v3mU78-d1fE!wf65Xz7 z=WsAgGyOSDxkOL<>CM>9*Skb5W`PBGk$sOML@oTgnTT<~m1T)cK>e|PgYPR-$_N%{5jLx_*a zjwjqvnc4A+_lX@eck*`J*8_taISa-!E2r7!B&GQ+YI7_Yl`X8jaT!_|YHNgCda zEepP@%4-+d&DQ;UkN@=p!bwHGkjF($&f#+M5nPgFMfMZ!F%N+J3oCl)ytn7V^X#%d(Fr1*dBj3x@NtP-%@<4ATwF;r;7`f6Z> zut}~Zs$NOb_D2a;iOAheL8UrOIWU{RIh;{qG$q*8ry{4f;T9^)bCV)=Dx+vMzA<7* z-jCtWvvvQ6!{2cLJ@z($a**a{`|b`TBd}SIaWVup&Lv9cCIraLVTE|YGTaX}EydBJ z@2j^Pq+boQ1ct9USe^4jA>|?(a@L#jpjyKZq$_r#)W%jl{!}nUemFtv53_bQtDYx# zt4>&9dG;%Zg#oWSw!V*DG?+tyeZs^H$OrSiLTvZa=x}>q(#z&54`oyha!yb-AeHT& znBrJ}-Y$Xu@ea``QheLH#k+1)v=df=@Gm0!BYq4d6hz!bFpy|+l@KrsRlJbH%&p**V=sDadgUn zA3#86)b{R9BqJm!g=Q|CKDmA z33g=-0PW#F*ZTgcs1-e-Mag8h?? z9!3{czw=NcIp#8dY%cg^GS&3b`uICrQ>D9&Rfy>%zmB5tjv8z9*|FpXBtkNn0U(Yg4K zjOz_u8{MeT$zoDdzt354qd;+#yhRD3lI;HEGdVG9N_UQ4shqoiE9e2aJvC#F=owt= zI`STcgg7IrPK-FUH8RnscqNPt&aum&Rb;&T@SMv`;Um+qlFAhBMGKCghIfQzUaIMs+dlV-lpq_Ia!~v7&qVlVE}%+I>ETm?f1M@1 zOQhV2bQFsLHrtj0x`&B8Ubiq2Ygp-Qfrs@R4J?N#6ve6Aqx0 zU^3OZNQ(L(OkMcTPicQ=to4lTLr6^&!7{sR%ur%bEzUS;+|HVfLD77! z1Q{yD-*|(J;I~hU&6L&t1Y6v^w%a!mCa}WHDqck&#BOeE@A>s99t&-7Sau89 zj;F1^AifmLIkE9aZ7J3Wx`s`=UWm~alQKRH8zWDQot`cL7@Qzx~K6-J~PH|ATzkT2VVG{Op$jpmB zLEL}zdIhe9Uw2d>N4Fx|>=nCdtIRC3FJ6D{j}q#EB*^aN(P5S*;-4$+6}ranN5~;Y zZXVK(z@e|qxFg%b5Ip>T_Vl3ZZg;D4%IZzwe*AAY4nE7l@Yvxna!-7){nnsDY770I z#LArxgjH5SE;~dYt#V-Fk~)!%$j7P|)knU*R3Z1yW8UR#IsAmJzaddNUZwpgD^WQd zt>2C_6c0i5Hi$MSSUt@JF8|w}^GwnC-!iG{=_pO>Y=^Sia)yCUp5bz2`$Qbgadf@B z_z|G*yHtH5G<$PWR=uuG*qt@a@zy%hR6ylf3+fTM^sP5o zAkC5lc_~$NrajJ0^JM3zx~ndG&A6)#gpAEg%8GhBlXhVn6FHasw>S@l$v1(Dm^bOe z>}fF2dV2h&OHS1z)7i+yWKb4=4D;E$rH@0}UNvLUW+K&$6O87WVaO|OCGZ!r+a#6$ zmcq10AFwxu8!u0&o3d`m_LE@wa2WRb#x)m5iX9lg(K!W85rP>~3bprD{or6fg_prD|4fd4TF@W35zun`{c1KLSR z;v-b~DA7LfS<_NQ!&&2#ET6I6XJ$haJ0nwOx6k&#C@3g?H$LFwXH#cGu-j)FTPHp@ z0rG!F@ByD+Z?lkt{~6+JEkLgENdYWk=V%J%V&-IKB^N{jgTefcCT4s}qT>H~9QaLu z+``$}o{xp)%aIh>-3*(0UKm_{f32&nU&>#<^~?+ zf4$2m?`UZXto%B^ARGTbBmb{^|5=Bh<#qA@gP4DJ`p;cptAa@UEdRqcK_uBu8cirD z2$Ynlu!aXNAu_DuS2=}D=bkXtA%YR$Z~1O&pG5`%sB{JPO=qK=jaua zFrQJy)g37+E$2(J*a&bv+QD$<%8g{(n=I!^3R;MUyrXzaEVfTcndASbb?oVBiN4vZ z!Rw**U`1KA?egxisYEWl@5NJo^?uHMnm0vU0z?V<|Mkjx3thoiqJV{IPw7V;Hwbgr z`6J&rThbR+S`NbE{!MmV4%T0x4x4>S;a-)fEBZZ|KrkuV9hQbKt-0{WXz_l_Xl4Xr z#AvA33q=wVaX}bu0^eZRuF=ef=eLf49)vK5_T3mkf)5i6joBaO>s&d}5DP*6CP3(j z+>KTo{09+(M*PF_r$7mWNd)}rxX0G5_#-7uvG(q-9ECc>mW`m)>U?61I+d;bP!=N!Ra;cVEH@ert zR#BrBz9l`YcgX+A1Z$HOm&r(pL|n*)?kB8^PFiyc*xLl<-6!8O`ufm{$-*!!4{v-# z52I;JW)8$UkLBzc2oKf~BFCjG$JkXe<4D`PBQs&9j3Yl?dxR40{f_Jq)4D}UPszRs zh(moWeaL?GMC`Z@0ulYcN^-LNn`|+W3hs)K6p%RU4DkMU2p= zuGdTm^E$5j&uQ1UUz8??wtBmqLd!s3d~J0|;eOPAk6sUM{$@=59wWof zv{6{-b+@!!&;&b5SheyVEFv>r(Hs%EcprXTl6^;aSRyMFISD-S&1OPPvXu>8)LuJ?{0bT9E_OSxQUSwg&hpQkkjN9w9PB-A@*Os}6QTDJT{ zEX~uagta4q^cXNrQ|iEA@x8-n{AhxHMD+6qoUPsHMDS$GSGp~pemihmK!mU4d^mWC zY%+-Hc{NA}kr(;mck~O=Ii!0{02X5o_Aa%xnnimtX4S_}l7}s>LPRj&QKJ6``aty( z53T^!*fqMt+{f;A>qlwX_4n0G!kWfO1kTHle0wq17Ai*D>2{r;G zGkp9a`~ty+BD0Xc!j3NNuFOOqqr=c0a!Q*KLd%{1V}I-spz*7{-Z(;$Z)O$z>}kMb zn@SIhT}Hobb^n7X)L`C7$Ky_LK_VJ)-4d-YOqsS%x%>&81C>|WjrSV~1=&WqZ-2d= z6%TLX6=kN=tR@z49v@E*pI$3(~^%f5bQtgKI!Vk_yL zzLYg}9?pfeBpPKb$2CE4YUam0msT3(cA}?ld(zK=PcV^7V7ARg{{C=3JnxC)F?7Ug zwEt1YI%=@E3%96N{UkeAlaqt=f0dL{!4@<|CbHl73EMJk^2fh+>!YvkLIBjpu|Gm6 zh9DhPf$Wh7PRxo+8fIYO|I2c-qC>lyqKO7SzYdB{_w_U!H-OzAC5ihY^{+Ydh5bos z&w=?uWF%+riMD$|^-=KTBkYQ0|D-25JL7+>W^}tc>@b||ifuK6xh6&Vdvw!Exf&v+NYLiXIf9BKll0&iS2h?Of)V?@avs`(qXPZo}aqQ9^nupR4 zg(Rks5kGYRZhZmahjFFcg=HX0L0g<6&LjS?RFbFK4k4q!3eBs1z_f>gW_!y5>2BxV z*31y6$Y6|m2yT=jgO>w3e73GVuxF@^YcHS9BdoCx3f*nR`Co=gp(A5Fe{H?UNZTrl*@ zg^o2Flr!>=yTY@=lmg1K0St3hq7^)BJ4&tC(WdB9 z`sH6fUbx7huM_uAp-ahJ$rDHr-KXtQdB;5KMsK4}=spUvl{<`6GI9KA;qig0A|!82 zhsmb+mo;?IY{5~GS>bMl(Di}e+O-p*4C3R?lSm0y3HT(+5!P%GlaAc!czSH&Mn|&0 zv1krQkT;VCQ|gyQePSa7m(Pr|MDD%R})GU!~Ekffg|FX|As}85M?cDz{WqMM4tap zOJKND5XdI~eq&4E64aL-eN8v#m@**$7#ppU%j{ijOU+FA8PWV3o*-MoU1NSIm<8I5 zZ^yw)`r`%WFZ%68{!d(M9BXtA73_8eM5kOQ1IV#u>3y?{%I>ccp%Bon989p2WJg!K zXihnslu%TImyg}_encp9NF$+r%Kj9kU#0&p`jBkW$Ok@Vv8vZpji&cXxF4rqGujwj z4Hk%2`HhS_`5}6d0lY24Rbxhkn0V67g2LvWzq-rykI4IqApVJPlsc5viu;6aIZ*B^ zDN_y?Q;JNURYOU&;sl&jgi>Q>-~;+agM)l$5O`86pwOJ3h4gZ8(M`E*q_phLZ~!tn4RaULZ&) zxEJuZb};f?3CLRs-?)D(S<-}ryKhLDc3Z{;B9TNk4t^Q0XL)0Zk$OJ-u%n1#BPgfG zxZ<01K9ZZQeD{^#$M{0S%`Yg=kq$fsLH= z9zFy9jcz(K=k~M0Bgy1;sjofo+F!V#LoQ9wT{aWKm48Kua#VnHE^O z#ni77V?qh(O2a@F_$&=EGabak{wLm?Pk=(L{#+XQtq@C+I<_q+)ALm7A0uB!1?jR` zD#_j0hxPBs@;u~Wdxzw3BFaak7Yl_rAN=}M$+vHB#j{#kbaBl0NVu%t^(DUv=y?FF z_#kGMp<10)k%k{^-Fj>>B3!R2=c`&2@c_}qH+5)iS}N!W4w)~1Ba{jTYF;gKRvN5D z(vVcDyQUmenQ3zJNV(0B6bB6OP$|MVtF~CezFHuZ*6=C0u{XOa#{F>If9yFsdi!Kr z=2)iCD#lWqDJ@A0Fd31YFOyk_`Y)OA-va0eXJ5?{)da&ofm~^T4c!prJtrgBNKs-| znQ{NifJk5~$0ZR+XyQ*PmUC0tx z?Wd;HTTBwTlhi?*f8Dr?X-ksdLu;m7UNsx@fx8rk_5IJk2|lDr{=Kc^A~{ z0wt!7V@Tat#o}r@fX%?EJ^W1-VTKMBn%}@XJWhv~V`=x2_+dSSRPUw-SKqVQpu>Ng z$eLy3B`#LE<7MF8quF1)OO7-h745k1!3#JuqEweu=qT`}iN#^r3jOgd9|_;v{(Ro` zdEPy^S@n6rtjTyGMqBaGYIbu_CLxj_`zf1~>jVuw+;@L15-`;GYr~4b^Dh zC0YJ;x=68{*t~bx>htB!NbQ)=i|6%+(`F}Vvt?U9zh`q#crGv2X*bC?6aI_uHF#FO zp!nez*+sH2D5Y}_e^Y^-`4!mCZ3DyG5fk@0juxfrZ|dq}!O4PoW?7Ge93#$Gul3HL z3gM63^zf>+(NGap!~M_sL7J@l{D9Q$LWc_; ze@f*}hYe`K>AhPW_s>Wqn}e@O`O1DfHDm}Ikv>oT_v)%YP>K%@q2BTF6ZDBnHVW*x z)(U9zk6Q3gn|Qb+3%su+@Dsh?33mnd^J2Y2%h*qa$xaqqzJ4)RS64?d+*$y4K|8;t z0dEe`ZkQE+3BjG=?#VT*`=qKk;fdH@-yF^p5LvjBS{~c!bJp=DBy4(a?qxepS*`)D zk2DL;iv=e&+8l;-TCU^>deA=u!@G?iDplJ3Fn%!UE(XGjkVSRN+p8jx`!LIem8`Hs zDfpER;dH*z7^~WRthZWA9wI1A-dVOJX~UxDI&7Ev@Ho!kFphTP&9Z*hFk2oO8A&Xj z_KkGQiA}%#rkdVoCRxmLPWRkVBOfZ5Lk}V#*y^}Fn5h~6W@;n~N_+V3+{oeqW1a*7 zcI&H|#3)a^eAd|p8#LN1b&X@Hfh=-LP~?ehS#hycD!ZvP(W=mk_2fnahI{b3( zNT^%qZ;F?E^u~9fqRiw8i>r%wEE#G^fYt^v_%3M686cy~a&8%EcQH0SuXXTZY)9ko z7`demQ$!e`k^Ee8s2ICle-y)OpK=hQ2|3d;HqPbty1&xvUJ6W18e;qU^#jV`l%&Df z!AxnB!@GeY;oUVqm^Uc~!(zMCc}%6!y1|br(RKV^?yq_jQU1OS{EBP(NAVy7)o_k* zzopsMY`*$5v191R4e_=&TR*Z+GR#mo@pH&uzpwJi)n=}mfb5z3b!q>lffD(JDn6vf z;G1n-iQzHk*MYQ-cW`_hk5g>v_PfJ}>v1&%W7*cTV!0y6!1G=g&3R&cJj-xn4!VYR z_4?r{u?xXY4sprX(>v2KeXck=arhhV{_Zd?YMu}YJDh0)LLc?sL}1mB-l8r!>$Dw? zbFXs`MbYv;KhR zN2PpoF+O(qZ{=IO7F@wur~Bi--`-u}(-55sBg;1i%Irk)Ur3^2wfr@7sUg7=Ve8kW zlHm^tjBwD~@7S9QCo8kTQ?k??>u4d&UP5U$=^wsVcX;Bv@?w|@?}ungd;G9CU7eAQ zM2%-v)eraRY`fw9T~qUCabcmsI?K)WqVHu%_Hp&Iy>jxgf*OI@hiOa~#!%mhPd7;9 zU(bEzJJ-C<&-+NxO0Ud;V5|Fmw-XA5C*M_Rh;vvw!V`M%MYJ0A3HGsg1YUN!=f%L+ zkhjE*q~dmTD6J8s=Fa032zr$f@0l(*BW!VFa2u96r&)=dIu4kxh-8HYY=dnbeH27c zd)03vLumwR5n=oA+*ym^QjJ4`S@JnPrL<2^zO=F946tbefkXklX0$Bq zxR@s!_3uahKj9tol7|g8a;`^yWH!RV;biPmhxq+Bg7 zUFmqS&klmR9i}kx#^Swh)zgOh0x7`+W&2JYE?9baXu6A}k zxRh4*yRm73kGcDN37wSD| zKjr+HpL*|9Cq|Av##x)5gxcY@C58+TTd$vLYgEc;ZC5FCCAU7}8Cc?vi6Wo46IC$X z*KWnp@c?w3_ zWy4MSoopb}FLQbCS=>fo0Wi^9Sc!<0bs z4=!M&0d7~?;Yr=RVr zln>eaE`*YddGh_kvaeMvbDyp&e|i{Fv6gvQne_YVs|!TZqxk{m-Vd1g{CsueLUaW z-tGz}my~VEG3_!YG;gNMc}I_DRa6IoM^{J8-jvLb@_mz_-u0>2nyFew#?Et7G3$s2 zHU=#=l~PXFtyIHLa|tMSrF!}s%BSw1eLl1aX2$h^b6qMySlQg2;M1P^V@ftHIqt!` zGex?evOE5QeRqUazY~;Q%9C*>I0f9HgDFol%AxjVvy%06Xzq9$rmX!JP+x0GN)D`ed!b65a;~w~ ziD!9qQ27gHzj&5iopa5XoADQP66R7Ns?Ns03)UT?S!#r<&lamYC?TQ}rBWmsbHXN3 zi%rMR$ERd)&P5?tHk4lHzeBtpeD%A29(w8=jb+^Kz7QOB)J1=KxH%<_99uSd+Yuj) z%f@>^R_LWf)rNe9?&+U6(nT+z$d>8hsH{G(-`R_W}PvFhD)xiNLTbPc&_##YWhum!G%mg~!^#WDLZGlCFG`0A2W%kU3W-$84} zI1JCT35c}1tes8IWX34!J~->Ll3d+U3*DLYHL>>Zf^)SEB6{3b(}x`6#prtb6VNrL zoA;2JidAuwZ*lNev6QD+wGzu%$LBVEOmMZv-y2d_m(hGOOfsXRX5n7vVDqMr*IGbI z2Q07(zDm9`?D(MNHLCod4<>TAp;szhA3U|}e%omqwVA&;`P2<_BkrvJ%qT!qA=OR^ zX7R|Ch)UR2Jq>&$a>R;oQ_NgRwoH5oUG3F+921Iqakte|w7uosN$lecBT_bD#Z>U~ zrE!6xy{2j5S``^zA z*>wlWHL*cr-pTv$Pt)LboN9t=2$QrV6xy(R#-iK9c*A;Y<+iMpgy5Owo4SYJUj!Sp zNNSuZqj8~Y_-M#G9Ya0!t_7MRgp3;|;hl|NknYVqEk}?+FxuikLNqkazWxp4jPkTN zgNSgPHe9dwcloBa7S3o{QpWIn9jw88oychWe6{oE3jb{H#kZ@FF500^^2nravn8Jl zqIZRcqtKWE^C6Jcqk|F?TN!q_$Xl*9>QcXn9}r(1=mI{2r=qj5u~mrzKlrstl@<#?u*LMov}UIdc%g4v(Y9MKb zqsMcTW&M=_rN$;wQHh47PSRc+jK20wpNh&_*C_4Llvuk!hZ5Rda^3G5v~oo%!2pC0 zvBu7r3Mo)K@{G}j1hnqZT2oYnXbFhsx5NV9ISc7geFupt+i@<1-|;JkL6InNWR3CR zZ8urQsg~d$7-2)-1tP&alTGSd4;Ru(sU^WZf1>QRJ&GA;lqcsL-d-Qpxd21u5`7$6 zhZAjp7rzVR&ta!UoKJy@xU^;+-4!MYcFzeu0nKtN!G zMSKT=YD*+-Ut0+&I)vvKwO|{<%xtSDFJDoFs6{9VgE)t=15bN@n#$szSjM$1SwPog zzzoTP;IN|Y9t_j$0@l6Aw13y>l?mkSFWyR0l0QJE7D54_>^uDsV#(~>oKWzaZ)nSN zi9Ff-j4+5Q{=RtTnk6_a4fr&HQr3&H<88X{tH1b=C*MAR%$(J8G=`_Zkgj2$pel^^ zt_|kq{r86CNKF+(;_3Lj@xGNR$EECyadnf41#fCd;b=qyo+&mdJh>L3Wvgr)=-<#J zgawrj3nWS2)hy5_xa?FqPX85xu$oev!^jLiu$t`TZq1bRib2U_ODM*>KXflA1ik zC(px?vdBHgODs{XnkKraYv`Q6u-abw&LSyYDfU|ejNPeIc8ZPZ7lJlrwVIfsgfhf_ zbyR!Ywd$h%+maXJcjTbpAguTTkq zRO3tDn$sG4YST4s{PBM>)pryx)3$C} z+^hJHP_k+Dn-gB#!6RL<0t9oevT+Uh7s)~dLoVJn04^xL!k;IB4UwD)+wQxZd0MFmdTcZ7JSrq)E=Izw4QL> z_oz4tHw+TK!3~Z_#EBf#35@Oa7yC(M9u1pFeV`w{Ie-d&S_R;ouNpg*+ul~2SS2OK zIT;${z4(hy?L{trr{nvQS#F>U_(;ucLbir2l%JB6OCu9NMN&f+s%(4xhlNRBG_O9< z3Gw~*m!SaDoZ1wLHc_oV$tP14RltET46XicaMD@$tJ)8mkArIUuI~_{ zuA5W-a3*`?_bvD|olD)A*a@XMO;ajtgqf|Dn5*{X@5E(M-5p>&7}9BQ`wpUEo+F5P!{9y2uWm z8&v5=S#m_6&XLCW5BS3qsk_QzSA)5MYz;GHyOi6bp5UCkB`lWnl1hE$>}Zgg#cpYP zcCpdIv1;x^>CV=Qu7=*F2V1`q0;7dJ%i9lU_G|T3$5!!3P4Q43b&ZOD8B7~=A{6Hu z;sXx03k(5ALH|>qG5VH9_T4%B!R9Bt#6<=M6#S*W^r-UB-W59Q_lAEdqgkMvWrYsU z-Sm3!u8r?{O>ZxNB$XM>Tzkjyya%~0TTEa|iCYIP2mCu{`;o^)us8K)vC2Rt1y)B6 zjE&*!bI&Y{u1rr=iXDAg=fzWZx`zwF85uVz%Su7nnSheo)Zh1jxbnIDm1hYO3@(TVk z?Ub^aZ`qcE3Py!sP^qtK(ba!tZs2nD60Ms_sNaQ&}mZeLY}V z0Rw4K8_lw0HI0f2`}98_8YNgq23<~3~V1@WkWGWS>XXnE2Jt5!JV zD0}iGkSb8wfKV0XfD-;ZX84Ha(9faH!SoqO%xOdzkCdo@ufcxg2nIuH$p{76GtGIS zCU~{5fbh#eO(05LeR@2jOjMPRoZivOIM_6rca?jIv=K_QCJF{>V+tw0van6DSRM4* z5}d>F+ZD1WNw*GVb}F)CU%RezjasUn(%9j$$$=^p;H-iI43aX1PZxAw*(-(x$PGdE z6IL`1OK*7MEDDT_fOBMAc>wBIJaHDoY4{k%GQSPOTAR`}`4YYXeOT(CTTd(G zj5n#KXn%D!S#>M{XgD!AmqO{sY}Gqhq5e$X90=g;qal^BCm{~4L1jBeY&*D_!Ms9x zk#E0~u5r<>rAIPTA_En@Xbxyk!I>cJ4%eBvIsGIjN_fT;Aa(5n4#Yw66b7;g#B?Hh@l%}wynweZEaym=wqP3T_Wmg}iszg0+7|)^VxNXv zCpj{`wF6EW#w5NPVtvQ=IDgX1?r9i*oo5RSil+jL=&}pPSzBo9qX9OLLybG+)yD`o zROd`stP1`dq?hgiXrh;c1ULS=9q)4cu}gA)oKHU)r=Xe$DWz5ZYne(yp9Rd2JF?2|=T7D>b^$#?JI zgvz;_@9K83LJVKKIG~mwC(uF6N1Ls_oIl9`yM(HquiT?%OJNviZaj&bxT?}vP(*}S zBn8nsICpH*_;h^qD#TBRtf!c35>!j|E&WpZ?zzUWfE=Q?Q?hCnI7!RM$iVo;WdH}h zpG=>b)4cz2F>!Zh2Aftca-%jkSzFWyf7#+UBB?jFbXQeYCF>q_LV8aX3%WpW-!9T_g*$=ix{5XbrMU{Cbqd z;z9|OF^~;7Y34!-6`1DPD(5B0%R>=uqNzzV#_!W4hiWZ zzN!RN&ZiiJ?540)=B2!Fetr8)5;&=M|stQjgzdUIv=PeM>JF@n?0@%PB5tk zOaQ@?TmwT7>$;=jW(Sb)(|rR@L-E!I%l++&S(8mJ6E+n<3pTXzi8LB6A>F51fQiAm z2gQNoGt>EvF|SJ3-U=IGK1}qG03jZ+OqwZ1X5ivwzzH9)W&Nk|zyQ%tlzbv6qam6q ztcg*P04RdjPt;C#jpY&PO)!!msw6=r+{X{(XCsxahaOob5i5SzL}m+Fp4YrP13Cl% z@B)wJrRkT%CG~uk$$la1rj(p@a~MqT%bx zax28s19CK>4MY-4& zR^_`h$%n)ecB(8xAmto#BIdH17B!h%URi0KyBJP_2884g@#^niJ;I`>KF<)Ens!CD zsNc+YSaTwD=)e*At*J!qoV!s&`iM&orCz%ikK}>KGV5ffyo^_nTSZ!V=H1N3o0_%` z*lGBO#PUaAA!eb=s;$tYEI@?Ru|h@3S1*Z4ZrrS}yomUPQ8?fCG~<2t z9-c8kV&wmKo!~HiH}=CC&~65*!!Jox0s=lsHoWj94a2c0;=5mKY&G(q3~ly z1M&*@_9|rsyd7$1^EPXm^kN2JD^Ca!uP*=eSN-aTqoLUW~MCzjjQdDt}_9t*9kH76R2Xp6G*X zG@Qc$9971lVS9sS)(eqb66sRy;;nNd-2)5Qcw$<#My4pNf0FE+*y|NZWRvg%mL38=Z#X?%AB)kt)%D44F#gcrO%?j6kd4SAK!RG`WrbH=DbdN}WQY+m z!w@b4DnZw1a5Qb>s;u4*>WQ4)ba#GuEo@f>ZMHna&c`ojzNvV%U9Vj_Ij+p8^DOzKfpD~bjINTNsoezk44fYZ139ZlE7s^1$bJ9eKqiBXF{$uruf)4`t}S>z!F{2fUy*}lc!e` z)dMiH3`ruQpH7AX1#Pzf?P>+01T00eG?O%QE}+K)aTvbi@GPNm4&Fma3?OM2Yd!?W zDg82Ba}jfrH8G(OsrY-h2{M5hwv|6$wC@mNR_&uXI2y6o`MM!n1m#rqF8(LroH5o}Hu>KSJUjPeF0 z@cMu*$}`Ag>OMJ<>2v9CD*2$JSs*?z-=0;k<&#mjg51C zR#V+fY!RDLCfC!I*HcB*O``c{^N@33^Wo+i6Yuz;pMD$+{K(q5bDUMr5-hh?U5wt@ z*>T5KjYtL>$Lcen)X0e75p>RI$6jCwcKH2@-w~B52Fc9Ha(VWGMPS5|Vks5TVRA^YInO;|03&-^@`Y~QM?cdj3Vwknd zsMlOSVAhk@2s|(fRtTxBy5Y^G-DZG8b-L3?q%4%AbPTc+SMV4~Yg?yeL z?}6lj=#@cnp%57E%G)AQ%LZzE0y9EY7ak(L17iH;Q{t2d9HmCmvYp%wJ)hc}wuPc1 zmH1bA(UI{DUZ7TGVbY75b8L zW(j7o(1Yds(Z$>EM~2BuNj9U+;FmCLy|$Y}m-R^;rYDgGu|wPlJ&gdW=jk0Lgcx{0Nj)Oadnq zOpG^&U(uNHPe|1O`>Q(cXC{!DL$IemhfByB)WfIy_roaJbl zgzQ`3@ouw^RnbMDm=ussC&V>g$}wE0X*osDPahsT1*anD}pH7y;ZTw zS7Vca1K^iR&GAxy4zF9o(-w@%QM`AgLFF_CKa&-HyB;r+%yrQMZ@W1`kE=vY0(~vW ze6xm%F?=&F`LD`2O5v-D#Cu*E`WiTaGBy;yWNi@>c**D8ny@~z_8i7WB z0O|4PL3P47Q;GamHe0v;e}0EG!Q}o%HaGPdd@IuGr0oj$Wlu7V1)4 zF2uP3Pk2JC@HNk$L~MS$Nu;DD@|s($;emi|!;oGGEgnkiM$$*sEHL0teD%s1&o|wW zg<|M*pnvLgC!-#53tu28AI$$o zIzm#D^xlBcBExY|kqi*BC5*F&miLE^f3NFFnh49bU6-&ShC!A|*yTjXDu=!fw$)%= zYAHYj$U*EsIE$@i4$OiD_FM&^$Kn%pRXm5nrMql+ja=B<3sHmuxH+`6vzwh_C$at-OU_?nX*q=40#fU{+Nt=P>=8T$l@ops5=?78gz&k0lV?dA3&~%VYk-8-QiRGTM28%!P8ajq^BzN>Tvr+X$jY>!%NI zqrM8%h6yh7cjT!pzq_UuIDd#!o-II{U|p)&KvMmBeK4aN`k|_!!QCUu%SV|q+;jrlai8~m(>;zI!+}@GdN)vc0tHU)%V;W#$){krmW0x1|%J=|O6PqgQ>s6CK z(1Il^twKzAv;JIs^nFN_Mo5TY=h2OqoU_$(CDQ+jQK#O%oC2x)Bfv8axvYjV_txLy zggc}QZMB}P#_~#~$wa8YODd!!SDRA8fR9A(G<%WU8$E&0@gNRAs}5jBLo^Zgv>yGy zjiuG5J3{mab1h+;=aZy^IFUD10BScn+E@7{{{vy~-D9gfDUr4`P6QBazJtZ7VZN;+ z85h4hG}errGE}rG5Vv-z_-ioh+ngK^Y(|ZLw8fHn0zXVtD@|KE;opZI1)oAX3X)h7 zYe1+DZF=!U0hMOVFR94CPz)`SI3Nx`=AJBg^*3`as)_*haL2UJoAi`sFDBl!>tE55 z`&8$u<(yPNb8`65On(hO=0pxeOIXzA@a(OYk>?|`$=5?lKOl4nZq4U?6Iy?zffk=p zA$pRtX{(NjT#0H4W_1g?^$SQkW_V#FnXkjn(zsRY2pgP{Fkr)^#j~z2#A0JH#= z19pJ=i-c99>zm!mBvD79N!H!b!5?JmuCxD?{%pu)M}>a>guxLnJ(b(@TC^Zxd85wk=mDSW4+(Vff1*vZ{ufD zv#@V)@(Yp|EKsM&=p2wOcN+3Rzy$lLfY$BZ=*lzon_NH3mk5oOQ2?%+SX(>)&&mpk zceTQ#b#j#UI!?J2*9z5Jdixnj@bdFzTynm z=};gzw*s}av|JcHldHl1F>=w9efc-rb7+aGNjWd@^dkSEF}iH1^KuG-Vy5ujb~n%J zM?Uv|zF}r)Wbes0R4bsLR)Vz~OdqF>ST;;3%EzxI17M=0cE{$VRU;H@%{1)TF;U-O zUM%eUYYa&E-B+FltfBMol$Zc3cd^<*B3>*TZfriCcloNn138BeWxKo_7D z!{+Fwoc0o&m|)GB6h+aB4r>2_o6!*_8NAleMshPT!vY&P;-}~{wjJQ0?=C=>QY0d{f3MTd^2Ot)SW6Yr zT*o)8Xcz3rHCD-C8|(6bzUf@|>z83EBiADF14+;mVgx?N4*l)bPrVk8PcI*uOWpzC zhV+l$nyvF-uMaBTM2erM*IQXRQkvqeew3l;>|6-I0Dz0nQr z9VkE!1Jp?Fp@2$=-tOi|j8o&&w|PF?7n80{Ub!M5B9A=hVTGkd{?j_M9GL_%byq(+ zI;zV}`xc0>=CB|vbM&6o%`jc6zZ*${KSBYmBt`5h>Trblr?C#S5fTEEse3KGU)=sM zh-;<+K`_vWd+x^mj8>8u@ajDcEGvSL$Dl)!=TG9BBLJRVH>@1ISbG`KHtYh z;pHMad;2iS8on|>t2F}DE+tCc`M%^;ihNR%_>CFOY}0ID_wjDmILRtNo3}{I_Mfi0 zpzDj<7()K8jc1iy#)AXzniLvQ%yZF4ANrqysuTbk#cF0KgZnptbI|IUVVt#8lf}w< zdjcrSe7xidK#q(;n`l`iVmy8?(Dm5^AQFHM1-~}So*orLK@om<{aXMq^haIw7XsL7 zK?`UQ-(Ytr)~iw3|Lq&@RB>I~qW|e|O$l5Td3tmQQX>Tikei_(mNa9qj*f$AQM)hT zEZ&HA5hnlR9oD*wgr)?h#)O#pWG|^A`!t;UuQmY)Agc;CLOypfyNBG_@U-|R_prb# zh-KsKRzd0qSBBhL_NGSh`CdclE>uCVY`{FD)t!(`+<>Cz8s0l z;$J`-1(=#yETG1E9Z@#u7ra1U^;Y`*PPh=hMT7^ZCsKUH<@boFvso&^a8YvgEyBcg zfFvZEc8BfG1yw%kzGr$}m~Xo*gzA)kJqkl^JZjIHG}~eZ)nbY@epRDF-+p%DmTr~+ zQIY#PZ0BptJ$q7qu?mYZVK)WxDjo58*FmIJ@K}3{*BoS*S5;Lt_hK1B+%(JYThHNo zy5gk;lq#RS)?EF?bfq97V>#1Q=sLGpT-6 z{bNy-@V+zAX;0L{-;dJY-gIk)z1eII?QKB!j}QUfVML30ojrKHtu({`TC|%8CBh)= zYdkEiBC;Ldel(Ii&*+8Ac}L3H+qbFlW*-$k0Pc%WxUe_@#o^&${{p}RoZL8fR#vl@8mHVbV_0wC4qTI;zJkw){b z{@;cl2QjBIC0?IJZ%0=V@YZK(XI+KQeYCMuGNq4y<)qX-(l(RbAC0AZ)1n$!p_5n# z{(_`gN2>{gw6AC5JZNZ9OZB4W^!ORE5C_-@ce2B?B`=~S9zjJNtB5irXt`&ld}{il zke_ekBq_N&-1MSIzZ9yL^z(3E{P z^rSu*qjdP7;l~L3K;HN)}Su` zi6=GlL)SYmXMT=Q`go`3rOztX9SMw<^F0{;{K@R6@PNh-1;Q2I_`^!0McSpFjXFG*@N%T-TC;dw@{ApQ_5GMV?&el9yG)2UIFcmL}tDq8MszWh1wqAe zPxZzUDGScQ>le444hwDkGDLBFW)H*Bz^KQ(Ryb~oUJ)k6>U4^ODxOu#0|wf7CWci#n^b@wYfLD$QA%9P+xmsUU|b9s$m#%_Z@E9B_vpb)eRE0u`k3YZ*@0KmGKp=(p!+~oI0 z=>))zuw5RUf9wE9!<(p zX>;G&X(U*d+M08{H{_9UB%`3 z6=4A;5`X-+Eu8YhUP;kYH82VkkdK;q%~>ygD(VhA;sU_vi>*IMF!uOX0@VpQ=G(gc@(eR9iaL8NDRtn<%w@LxbZ zPbx+S6T5`ngiYoZZP!zd@6otCDpRzS@&Q(6i5>@jo?4VOvh+h5v4W*j zil&gff$C&I3)}Z)|2g{sHNe{bK*IuDrg)QlpD9LoQfX!i{w6p$a!q?~Y^i71#LUI4 zbAC`FKkFR5noJl8w0Z=W)Tb;YEUy!EF7BH4J-Dr1cs<_Q%dIt>|F=6~N;mw+XIVDB zIc~17n~s>OruRZVDIxmjNDQ1|?fu;Apb#sJ>O@V_Im4-z25zJpX=l!{ItFXIq7<`krYsNj85CL~Zmq+ynfSJ>~Wj>;h;JeXkunm^` zVmo(cxLbp_l!N$Qmeai>Sht3X_wVssqbp7p^#K#0v#$;77@eO5s>bcpsg?<4zj;un z180?{g;3Y}v>c;k6B$7>?bDI|zslZ*U$Hh}%r%7%FJQcv#z31gG)uT*&(AmxA?)o% z1f;7O`A&Ey%LmG^?9i|7gyeiE8s5m|aUp9mF$5XcYP}peoDq~2@iF0}XePlgxBIg9 zU`ir87t5<-m41y_opgj!PkOn1MYe7pa>^ZICXed&w^|$fMZj1Mwv3Of2XERdyIYFS zQhIGPyD~TMeosJ(0M@K>MSN^{a-AW21NZx`uX&%PXHHJem%?mTqSG_$BZ(j7&Q4BF z4BYe-$GY>hAGQ8cwlJju=6yd56F2XLDvV_1r|HAe4ZqFr8u2gVrlzRXsNZZ1-z9wm zbem7WajZ7RcHxsI#(Qc$aZ8R0lHVZ?Wvw6wlr`Gdx)<}LTHO1zQ@inttmQ@4^iA7k zj;Z&>@6wO;<(q%^MI}l)bXo(_a=6P>S7b(O=^Kh#Ru?+~p$k8b>!@p7A(9U~dgxP@ew3Ow&O7%WJ7a3SE2YModbyX5P0Qy3n4rCij8O#+U~AN2nA zZ?@Cag|5vmoU$j?ZL$QPAwBL$%t-TkYG1WXecB4&%NrIy`N}0dC+io%RdJ84`2k=x zcQ~InkG{(NB7b?j%!)AnI&lR~zt!@iPF6kDHs1=|W_A*Fyf+_HHvTMC9d8x3^IOx) zLrY9|W|0wGsV3I9B=E^GqF1@Od2YRLY;_=YTMZU1|0mG(_wV1q#3~J>%|={(`EsIt zKIJ-}4;|u7Mo-(7tRLzJUXMNzmAO{S{l573gz2~H=f1Na<^ry;m8Si~cX%odhgU*vo%y*z8PtN(K zKY-mn@Kb)WO(QPg4ZN?e?hj=lWWJ?0Y_jQ>g4fqdq8v8l4pGvGuwj`8)PU}eT=;>( zo<35%H8Q5Z2gp0rFh(G;neOQkRn5}7udz56|1nTZ3ff<1o`@*OR!G5tDX(7YPZUK#et?J}B&SGq$HmYQ)58x^d3)SpC zg5FhAd92s9bIX^-nkE@^-W=I2$Ivilsb42}x@>wpyHfuv{TC!Zd42WVjt}B|L^56{ zCzWoiOx{4*+U#hJbR1>-S+Iltz{wTDqy43)vpp^Fz7a^iC%pu)%VaCH2It+Itkfz<$~dsqMonrd4i!J32b5B9mGzh&ZRtqsqwR zhupQ4b}*Umgj58P2!BWeY!kI9?o>|8*SP}kw!ELE4fYqpn2$G1N@` z!<4IPo$C#kfAvE!G#T2gLlcMti3wfOymsyz@=147>jz#1vSVa;z~t(al|f@AO(;$i zn*<1KwEMXEE4#>@(Pv&@%4(d!n$!(|K(S8CR$;u4#q>=YYST^H@tG;zYu+pG9Fn0) zHJLoA|B)*@-BK4G2%39eP?+{?0OO&|w5DX2ul<;f#_xL)A2vt=>*wmhJEP~TR_J_H z{Jxnrr2dS=pmj~m!N`h4iI;CV6Y0pNwTQ2U<_e4OlBS{n>&l^lJ^%!KCw0K5c=K z@2HZy)iV$_g?dZbE9zL0i_%~AkBkSD0TyMx#@>Rr6X!8$TIK0qGTa_S4PHGj~59Wg5a?UzGz$N zP4h+_tGa#PkMRN)er1lvwkEUU0nH9~LXKJ^Mjv(ktga3&;*HZY5rAH4{|oaMg?u~Idg)P@p7rtwd{>{LT*lE(P!(|1*vh}I3Z!w;G6 z@qYDl>b7{X6|Uv1l|(UIQ|q@vP?Ht1b~fABJE_5~qQh9w%g*{j`{g(daWbZ{{FdyI6#z$rY;9{kb^A~{e($}R|>pS|Ih3GP~?WnH|zrCw5g@+a6a|$Ip#hHgQ^U&3{le6>0P_mpY zAd|)HSRgcgIMZ*zi!EO7r0pY3jXJ7FeICwW=j3+uS;kW|5p%o9ewMP8r9I;Lml3+# z5ql`om|#A)nV(Vn5<9=Un_G5p2NkydLJZQ2*#3jU%YwI8Q1pd=*hLxslKsuM{8O`M z3a?eB4{1h~ESY>_Y3Y}@@Mm>pW3trE>u{JPXe#7cU#W{g)SrbWa;8?of~I!MEIl*n zn{X$4R?6iEJv!zDL&1U_-hv&%56mNyUEA7;lXsrddL{;kw4UDF2dwd%mCC_ko*yfN zh$TAdGfnO4e805JhhX$tW?Ayy9oz#@aNs$k(@9aG7o;_pvmFf|o2MJ!mavYznEFGI z9rP@>#Way~Ea0`pN!QhB^95-YA&2%$5F-~N)r#^L9MI~qhjoRmypDLXetmdP_3atT zbmFh9tp}ePxB6C-!X%>K6aeb z6EJIhrvDGvg#elmG&5H;`}IlM*zIvvYSEs9Am2rt^<_qg{=oRwtIuDkn8-CioNR12 z<=6i*_6sziSFL`pDR=jr7kw{cX8OC8@Gd9>X|EuF8T@arB#s4dyHd2(yE5&rVQA5h z*G9EPw~75({}W`VZ38M29;l8;lvyY#`I_r_!B|KmPMus4hNe0p!99jIqV9NBM3|E~ zMPi9?w6QtpZM_HEM@k5x8ffGh&!gJ$GtMqAU1HlRfe5%PmIxFGokDk=$Rnk&(>0+T zM;h2$SZOExK)(u_H>yUV@V4!Yy_v#4*<)d3H%XvZc0~RkV9h&l^{p4?!*F$ViWr83 zZ$XpG)BI(bvLFlJl6BS&fBqeNLaA#)b^9m@5oiZ@T9kQw$U{d2RS}^S@me;UgXsg^ zog$1{aN!!ys*@UJ7XVoS;C)yh4GGlDAE~~jI`o8gIkOT)9C(YJ2)?8q|62}k zdcQn+ZBQbP>X)~(G2QTdm@YGPi!I-mMEFg1(lzi=SWRea#~~MTD!P@y&gR|*^-5tH zujg2&IdN+alCCFM(gC3>U)p0u(fIA>dyTLS0BQr?pSC3kYR5&SlthVaN&Mk?8!NUH zErS*L?EZw{0nwCKajsWJmS)1^_d3Zna*-dz309t`NNr{xj3HN4_X_%7}&{Mc}jm|3igGKZ>&DBMn{ zC#8g8rXAe`hg-zXWulvq+tE!K8xC>vn&?V+gQjdS1tkA<)4N#rN#M0p!~R|Oz<<$z zHEOQD<&9pT!rrCvf94TxhXAXX`fu>blRk)sjj4G<&10=x;!4Z~)Em;r>5w^2FC#3n z8r1{xeIuZ@)?K@nK+b9=gEXnf918KK=fVLESu&+n#gS5irv_eBL9UGse-jHfpBv~H z&`X+{(VQ_Cf`tYgm$VLG$ulVAM*zqkAZxi{FqUtnniK|LVWcOVGCP1j8n#r_NW_OJKSwmA*3>$X921>)F~p<1Ch(zgZ>po2Ro#qOTtNZl`}hyjkP(?pB(Niq^u9#O$*zX+B1<>q7Z> zI~_Zo9a-vR+;F&Ss2yT|fd{=45h-#5JGv_ziIN!#ouQJXtiFNZjQqc$fG-?ZrM26E zc3Br4>7d(K9ArPAkub5n-7Q$FaHP1>?jc3$%m1hm(>*BIl`y}S*_%5~dqfqx&biz- zhLdK+u@|OjdNmMLGmhO*_3c;_!bTw7kT(Mqs|G1~pvWe$-RSS-!Q2VbF%W|+ zf#$*r$tOj>NeoV@o~ESV)>Fn+#K7yyXEKB)?4#ilJh|)8HQ^R1NJ^PmOsF&zthGcg z-(X3u>aFex#S`1o3Gb8?LpR_79rq@po=Y~JNr+&9|DU9>>HAUAkXJK6rsW+w$chfO9I3(Y>Q`J_uiMGj(ft(pi4FYgA@%~u zAEbmp6m}wbqg}&-sM-A?OHwo&jBt{rh^8C<^z3{JsxJtDgoN^UeDkBF}E6 zlx-=%zH-f~S{9?uCY5|(mcp{*N%=}fS@WxYu*4(m%o~RWx^)?$Me}BtkCp?H-d2d*J_ia)Q`pKjl<`1wX{;&6^e=nFcVOP}TL&k~( Sometimes the maintainers will refer to NATS as "nats core" and streaming as "stan" or "streaming." + +Messages to the streaming service are opaque byte arrays, just as they are with NATS. However, the streaming server protocol uses protocol buffers to wrap these byte arrays. So if you listen to the NATS traffic the messages will appear as protocol buffers, while the actual data sent and received will simply be byte arrays. + +NATS streaming uses the concept of a channel to represent an ordered collection of messages. Clients send to and receive from channels instead of subjects. The subjects used by the streaming libraries and server are managed internally. Channels do not currently support wildcard. Channels aren’t raw subjects. Streaming isn’t raw NATS. The streaming libraries hide some of the differences. + +Think of channels as a ring buffer. Messages are added until the configured limit is reached. Old messages are removed to make room for new ones. Old messages can expire, based on configuration. Subscriptions don’t affect channel content. Subscriptions are like a cursor on the ring buffer. + +![ring buffer](../resources/ring_buffer.png) + +Positions in the channel are specified in multiple ways: + +* Sequence number - counting from 1 +* Time +* Time delta (converted to time on client) + +New subscriptions can also specify last received to indicate they only want new messages. Sequence numbers are persistent, when message #1 goes away the oldest message is message #2. Trying to go to a position before the oldest message will be moved to the oldest message. + +![starting positions](../resources/start_positions.png) + +## Subscription Types + +NATS Streaming supports several types of subscriptions: + +* Regular +* Durable +* Queue +* Durable/Queue + +Regular subscriptions pick the location of their channel position on creation and it is stored while the subscriber is active. Durable subscriptions store their position in the streaming server. Queue subscriptions share a channel position. Durable/Queue subscriptions share a channel position stored in the server. All subscriptions can be configured with a starting position, but only new durable subscriptions and new regular subscriptions respect the request. + +All subscriptions define their position on creation. Regular subscriptions lose their position if the application crashes, the app disconnects or they unsubscribe. Durable subscriptions maintain their position through disconnect, subscriber close, but not through unsubscribe. The position on reconnect comes from the server not the options in both cases. Queue subscriptions share a position. Regular queue subscriptions lose their position on the last disconnect/unsubscribe. Durable queue subscriptions maintain their position through disconnect, but not through the last unsubscribe. Positions provided in options are ignored after the position is set. + +## Acknowledgements + +In order to implement at least once delivery NATS streaming uses ACK messages for publishers and subscribers. Each message sent from the streaming server to the client must be acknowledged or it will be re-delivered. Developers must switch their mind set. The same message can arrive more than once. Messages should be idempotent. The client libraries can help with ACKs. Subscriptions can use manual or automatic ACKs. Manual ACKs are safer, since the program controls when they happen. An ACK wait setting is used to define the timeout before an ACK is considered missing. + +> Ack wait = 10s means that the server won’t redeliver for at least 10s + +Using ACKs for each message sent can be a performance hit - round trip per message. NATS streaming allows subscriptions to set a max in flight value. Max in flight determines how many unacknowledged messages can be sent to the client. Ack Wait is used to decide when the ACK for a message has failed and it needs to be redelivered. New and redelivered messages are sent upon availability, in order. + +Messages are sent in order, when they are available: + +* Max inflight = 2 +* Send msg 1 and msg 2 +* ACK 2 +* Message 3 arrives at the server +* Send message 3 (since it is available) +* When Ack wait expires, msg 1 is available +* Send msg 1 (1 and 3 are in flight) + +The streaming server sends available messages in order, but 1 isn’t available until its Ack wait expires. If max in flight = 1 then only 1 message is on the wire at a time, it will be re-sent until it is acknowledged. Re-delivered messages will not come out of order in this situation. + +Setting max in flight to a number greater than 1 requires some thought and foresight to deal with redelivery scenarios. + +Max in flight is a per-subscription setting. In the case of queue subscribers, each client can set the value. Normally, each client will use the same value but this is not a requirement. + +NATS streaming uses acknowledgements on the sending side as well as the subscribing side. The streaming server acknowledges messages it receives and has persisted. A maximum in flight setting is used for publishers. No more than max in flight can be on their way to the server at one time. The library may provide various mechanisms to handle publisher ACKs. **The application must manage redelivery to the server**. diff --git a/developer/streaming/acks.md b/developer/streaming/acks.md new file mode 100644 index 0000000..72eeb94 --- /dev/null +++ b/developer/streaming/acks.md @@ -0,0 +1,25 @@ +# Acknowledgements + +Subscribers can use auto-ack or manual-ack. Auto-ack is the default for most clients and is sent by the library when the message callback returns. Manual ack provides more control. The subscription options provide flags to: + +* Set manual acks to true +* Set the ack wait used by the server for messages to this subscription + +The ack wait is the time the server will wait before resending a message. + +```go +sub, err := sc.Subscribe("foo", + func(m *stan.Msg) { + m.Ack() + }, stan.SetManualAckMode(), stan.AckWait(aw)) +``` + +## Max In Flight + +Subscribers can set max in flight to rate limit incoming messages. The server will send at most “max in flight” messages before receiving an acknowledgement. Setting max in flight to 1 insures every message is processed in order. + +```go +sc.Subscribe("foo", func(m *stan.Msg) {...}, + stan.SetManualAckMode(), + stan.MaxInflight(25)) +``` diff --git a/developer/streaming/connecting.md b/developer/streaming/connecting.md new file mode 100644 index 0000000..20e83c2 --- /dev/null +++ b/developer/streaming/connecting.md @@ -0,0 +1,15 @@ +# Connecting to NATS Streaming + +NATS Streaming is a service on top of NATS. To connect to the service you first connect to NATS and then use the client library to communicate with the server over your NATS connection. Most of the libraries provide a convenience mechanism for connecting in a single step. These convenience methods will take some NATS options, like the server, and perform the NATS connection first, then then run the protocol to connect to the streaming server. + +Connecting to a streaming server requires a cluster id, defined by the server configuration, and a client ID defined by the client. + +```go +sc, err := stan.Connect(clusterID, clientID, stan.NatsURL(“nats://localhost:4222”)) +``` + +Sometimes you may want to provide NATS settings that aren't available in the streaming libraries connect method. Or, you may want to reuse a NATS connection instead of creating a new one. In this case the libraries generally provide a way to connect to streaming with an existing NATS connection: + +```go +sc, err := stan.Connect(clusterID, clientID, stan.NatsConn(nc)) +``` \ No newline at end of file diff --git a/developer/streaming/durables.md b/developer/streaming/durables.md new file mode 100644 index 0000000..e10023b --- /dev/null +++ b/developer/streaming/durables.md @@ -0,0 +1,7 @@ +# Durable Subscriptions + +Regular subscriptions remember their position while the client is connected. If the client disconnects the position is lost. Durable subscriptions remember their position even if the client is disconnected. Durable subscriptions identify themselves with a name. Connect and disconnect won’t affect the durable subscriptions position in the channel. Unsubscribe will clear the durable subscription. + +```go +sc.Subscribe("foo", func(m *stan.Msg) {...}, stan.DurableName("my-durable")) +``` diff --git a/developer/streaming/publishing.md b/developer/streaming/publishing.md new file mode 100644 index 0000000..b9eaa28 --- /dev/null +++ b/developer/streaming/publishing.md @@ -0,0 +1,15 @@ +# Publishing to a Channel + +The streaming client library can provide a method for publishing synchronously. .These publish methods block until the ACK is returned by the server. An error or exception is used to indicate a timeout or other error. + +```go +err := sc.Publish("foo", []byte("Hello World")) +``` + +Streaming libraries can also provide a way to publish asynchronously. An ACK callback of some kind is required. The library will publish the message and notify the callback on ACK or timeout. The global id associated with the message being sent is returned from publish so that the application can identify it on callback. + +```go +ackHandler := func(ackedNuid string, err error){ ... } + +nuid, err := sc.PublishAsync("foo", []byte("Hello World"), ackHandler) +``` \ No newline at end of file diff --git a/developer/streaming/queues.md b/developer/streaming/queues.md new file mode 100644 index 0000000..0cad323 --- /dev/null +++ b/developer/streaming/queues.md @@ -0,0 +1,11 @@ +# Queue Subscriptions + +Queue subscriptions are created like other subscriptions with the addition of a queue name. All subscriptions, across clients, share the queue based on this unique name. Other subscriptions can receive messages independently of the queue groups. Unsubscribe removes a client from a group, the last unsubscribe kills the group. Max in flight is per subscription. + +```go +qsub1, _ := sc.QueueSubscribe(channelID, + queueName, func(m *stan.Msg) {...}) + +qsub2, _ := sc.QueueSubscribe(channelID, + queueName, func(m *stan.Msg) {...}) +``` \ No newline at end of file diff --git a/developer/streaming/receiving.md b/developer/streaming/receiving.md new file mode 100644 index 0000000..0811e06 --- /dev/null +++ b/developer/streaming/receiving.md @@ -0,0 +1,46 @@ +# Receiving Messages from a Channel + +Clients subscribe to channels by name. Wildcards are not supported. Receiving messages is similar to core NATS. Messages in streaming use protocol buffers and will have a bit more structure than NATS opaque messages. Client messages are still presented and accepted as raw/opaque binary data. The use of protocol buffers is transparent. + +Subscriptions come in several forms: + +* Regular +* Durable +* Queue +* Queue/Durable + +Subscriptions set their starting position on creation using position or time. For example, in Go you can start at: + +* The last message received + +```go +sub, err := sc.Subscribe("foo", + func(m *stan.Msg) {...}, + stan.StartWithLastReceived()) +``` + +* The beginning of the channel + +```go +sub, err := sc.Subscribe("foo", + func(m *stan.Msg) {...}, + stan.DeliverAllAvailable()) +``` + +* A specific message, indexing starts at 1 + +```go +sub, err := sc.Subscribe("foo", + func(m *stan.Msg) {...}, + stan.StartAtSequence(22)) +``` + +* A specific time the message arrived in the channel + +```go +var startTime time.Time +... +sub, err := sc.Subscribe("foo", + func(m *stan.Msg) {...}, + stan.StartAtTime(startTime)) +``` \ No newline at end of file