From b29bd66369d917eed88ddb4126beb9cbc18ec341 Mon Sep 17 00:00:00 2001 From: swarajsaaj Date: Thu, 1 Oct 2020 21:09:39 +0530 Subject: [PATCH] #1510 Improvments done in Circuit Breaker --- circuit-breaker/README.md | 206 ++++++++++++++---- circuit-breaker/etc/circuit-breaker.urm.png | Bin 44822 -> 78626 bytes circuit-breaker/etc/circuit-breaker.urm.puml | 52 +++-- .../java/com/iluwatar/circuitbreaker/App.java | 76 +++++-- .../circuitbreaker/CircuitBreaker.java | 118 ++-------- .../circuitbreaker/DefaultCircuitBreaker.java | 153 +++++++++++++ ...Service.java => DelayedRemoteService.java} | 22 +- .../circuitbreaker/MonitoringService.java | 39 +++- .../circuitbreaker/QuickRemoteService.java} | 16 +- .../circuitbreaker/RemoteService.java | 34 +++ .../RemoteServiceException.java | 11 + .../com/iluwatar/circuitbreaker/AppTest.java | 129 +++++++++++ ...st.java => DefaultCircuitBreakerTest.java} | 42 ++-- .../DelayedRemoteServiceTest.java | 59 +++++ .../circuitbreaker/MonitoringServiceTest.java | 41 +++- pom.xml | 8 +- 16 files changed, 754 insertions(+), 252 deletions(-) create mode 100644 circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DefaultCircuitBreaker.java rename circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/{DelayedService.java => DelayedRemoteService.java} (81%) rename circuit-breaker/src/{test/java/com/iluwatar/circuitbreaker/DelayedServiceTest.java => main/java/com/iluwatar/circuitbreaker/QuickRemoteService.java} (77%) create mode 100644 circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/RemoteService.java create mode 100644 circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/RemoteServiceException.java create mode 100644 circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/AppTest.java rename circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/{CircuitBreakerTest.java => DefaultCircuitBreakerTest.java} (70%) create mode 100644 circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/DelayedRemoteServiceTest.java diff --git a/circuit-breaker/README.md b/circuit-breaker/README.md index 8d2205f2b..f8c9aca69 100644 --- a/circuit-breaker/README.md +++ b/circuit-breaker/README.md @@ -52,40 +52,105 @@ In terms of code, the end user application is: ```java public class App { - + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); - + + /** + * Program entry point. + * + * @param args command line args + */ public static void main(String[] args) { - var obj = new MonitoringService(); - var circuitBreaker = new CircuitBreaker(3000, 1, 2000 * 1000 * 1000); + var serverStartTime = System.nanoTime(); - while (true) { - LOGGER.info(obj.localResourceResponse()); - LOGGER.info(obj.remoteResourceResponse(circuitBreaker, serverStartTime)); - LOGGER.info(circuitBreaker.getState()); - try { - Thread.sleep(5 * 1000); - } catch (InterruptedException e) { - LOGGER.error(e.getMessage()); - } + + var delayedService = new DelayedRemoteService(serverStartTime, 5); + //Set the circuit Breaker parameters + var delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000, 2, + 2000 * 1000 * 1000); + + var quickService = new QuickRemoteService(); + //Set the circuit Breaker parameters + var quickServiceCircuitBreaker = new DefaultCircuitBreaker(quickService, 3000, 2, + 2000 * 1000 * 1000); + + //Create an object of monitoring service which makes both local and remote calls + var monitoringService = new MonitoringService(delayedServiceCircuitBreaker, + quickServiceCircuitBreaker); + + //Fetch response from local resource + LOGGER.info(monitoringService.localResourceResponse()); + + //Fetch response from delayed service 2 times, to meet the failure threshold + LOGGER.info(monitoringService.delayedServiceResponse()); + LOGGER.info(monitoringService.delayedServiceResponse()); + + //Fetch current state of delayed service circuit breaker after crossing failure threshold limit + //which is OPEN now + LOGGER.info(delayedServiceCircuitBreaker.getState()); + + //Meanwhile, the delayed service is down, fetch response from the healthy quick service + LOGGER.info(monitoringService.quickServiceResponse()); + LOGGER.info(quickServiceCircuitBreaker.getState()); + + //Wait for the delayed service to become responsive + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); } + //Check the state of delayed circuit breaker, should be HALF_OPEN + LOGGER.info(delayedServiceCircuitBreaker.getState()); + + //Fetch response from delayed service, which should be healthy by now + LOGGER.info(monitoringService.delayedServiceResponse()); + //As successful response is fetched, it should be CLOSED again. + LOGGER.info(delayedServiceCircuitBreaker.getState()); } } ``` The monitoring service: -``` java +```java public class MonitoringService { + private final CircuitBreaker delayedService; + + private final CircuitBreaker quickService; + + public MonitoringService(CircuitBreaker delayedService, CircuitBreaker quickService) { + this.delayedService = delayedService; + this.quickService = quickService; + } + + //Assumption: Local service won't fail, no need to wrap it in a circuit breaker logic public String localResourceResponse() { return "Local Service is working"; } - public String remoteResourceResponse(CircuitBreaker circuitBreaker, long serverStartTime) { + /** + * Fetch response from the delayed service (with some simulated startup time). + * + * @return response string + */ + public String delayedServiceResponse() { try { - return circuitBreaker.call("delayedService", serverStartTime); - } catch (Exception e) { + return this.delayedService.attemptRequest(); + } catch (RemoteServiceException e) { + return e.getMessage(); + } + } + + /** + * Fetches response from a healthy service without any failure. + * + * @return response string + */ + public String quickServiceResponse() { + try { + return this.quickService.attemptRequest(); + } catch (RemoteServiceException e) { return e.getMessage(); } } @@ -95,76 +160,129 @@ As it can be seen, it does the call to get local resources directly, but it wrap remote (costly) service in a circuit breaker object, which prevents faults as follows: ```java -public class CircuitBreaker { +public class DefaultCircuitBreaker implements CircuitBreaker { + private final long timeout; private final long retryTimePeriod; + private final RemoteService service; long lastFailureTime; int failureCount; private final int failureThreshold; private State state; private final long futureTime = 1000 * 1000 * 1000 * 1000; - CircuitBreaker(long timeout, int failureThreshold, long retryTimePeriod) { + /** + * Constructor to create an instance of Circuit Breaker. + * + * @param timeout Timeout for the API request. Not necessary for this simple example + * @param failureThreshold Number of failures we receive from the depended service before changing + * state to 'OPEN' + * @param retryTimePeriod Time period after which a new request is made to remote service for + * status check. + */ + DefaultCircuitBreaker(RemoteService serviceToCall, long timeout, int failureThreshold, + long retryTimePeriod) { + this.service = serviceToCall; + // We start in a closed state hoping that everything is fine this.state = State.CLOSED; this.failureThreshold = failureThreshold; + // Timeout for the API request. + // Used to break the calls made to remote resource if it exceeds the limit this.timeout = timeout; this.retryTimePeriod = retryTimePeriod; + //An absurd amount of time in future which basically indicates the last failure never happened this.lastFailureTime = System.nanoTime() + futureTime; this.failureCount = 0; } - - private void reset() { + + //Reset everything to defaults + @Override + public void recordSuccess() { this.failureCount = 0; - this.lastFailureTime = System.nanoTime() + futureTime; + this.lastFailureTime = System.nanoTime() + futureTime; this.state = State.CLOSED; } - private void recordFailure() { + @Override + public void recordFailure() { failureCount = failureCount + 1; this.lastFailureTime = System.nanoTime(); } - - protected void setState() { - if (failureCount > failureThreshold) { + + //Evaluate the current state based on failureThreshold, failureCount and lastFailureTime. + protected void evaluateState() { + if (failureCount >= failureThreshold) { //Then something is wrong with remote service if ((System.nanoTime() - lastFailureTime) > retryTimePeriod) { + //We have waited long enough and should try checking if service is up state = State.HALF_OPEN; } else { + //Service would still probably be down state = State.OPEN; } } else { + //Everything is working fine state = State.CLOSED; } } - + + @Override public String getState() { + evaluateState(); return state.name(); } - - public void setStateForBypass(State state) { + + /** + * Break the circuit beforehand if it is known service is down Or connect the circuit manually if + * service comes online before expected. + * + * @param state State at which circuit is in + */ + @Override + public void setState(State state) { this.state = state; + switch (state) { + case OPEN: + this.failureCount = failureThreshold; + this.lastFailureTime = System.nanoTime(); + break; + case HALF_OPEN: + this.failureCount = failureThreshold; + this.lastFailureTime = System.nanoTime() - retryTimePeriod; + break; + default: + this.failureCount = 0; + } } - - public String call(String serviceToCall, long serverStartTime) throws Exception { - setState(); + + /** + * Executes service call. + * + * @return Value from the remote resource, stale response or a custom exception + */ + @Override + public String attemptRequest() throws RemoteServiceException { + evaluateState(); if (state == State.OPEN) { + // return cached response if no the circuit is in OPEN state return "This is stale response from API"; } else { - if (serviceToCall.equals("delayedService")) { - var delayedService = new DelayedService(20); - var response = delayedService.response(serverStartTime); - if (response.split(" ")[3].equals("working")) { - reset(); - return response; - } else { - recordFailure(); - throw new Exception("Remote service not responding"); - } - } else { - throw new Exception("Unknown Service Name"); + // Make the API request if the circuit is not OPEN + try { + //In a real application, this would be run in a thread and the timeout + //parameter of the circuit breaker would be utilized to know if service + //is working. Here, we simulate that based on server response itself + var response = service.call(); + // Yay!! the API responded fine. Let's reset everything. + recordSuccess(); + return response; + } catch (RemoteServiceException ex) { + recordFailure(); + throw ex; } } } } + ``` How does the above pattern prevent failures? Let's understand via this finite state machine diff --git a/circuit-breaker/etc/circuit-breaker.urm.png b/circuit-breaker/etc/circuit-breaker.urm.png index 9278ce216665a5088e3f9b90ad310087e0dd9108..cfa7214ba299aed397545b69b7efb3b23b9b60ec 100644 GIT binary patch literal 78626 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 Tt0PBtoDvh363Tq|%>DlXOs-Q` literal 44822 zcmbTebySpH8#g+Nf{K8GFd_&l(lCffmqAE(2?I)Z4;|Jh-67p2-3`*+-3;AGH=I4_ z^FHtUoj<;_&RVX;nqjzS-}}De7uVKZT2dJ2)`MFR2n0vut$-{9f-wYvTQ?&wT1}mnCe)5u-4Ijq-p<1-`d)oi;2nH_=Bl6!o(P+WoB~s z87CmpR$CeEU;75 zdzX0J9>Tjd!a{fGsC`9%mi`t;*N(%@>6g#SD@d7seSb+M(vnaRF3iL_$=G6(qN44E z9g&v!$R(8!+A`s+XV%tM)Sg3Ro=XpzCg*wHn`%zu-5vBDdqUq{FhuH>Cv{jOWB(HV z9I-4<@Dl@kSyo>AiBX}4(y0SwUR^R4#KqmRT#6Ij)i(__#S&C?_D^yTM5s62`0Z29 z8++Z7(%`Z0wr+%4zD@Torj~A$LC(k8a;+NQmk79q82C8xt#zLwZAQs4TqFc_6_I}` zfDFGT(Mt8)e?>E@k7u1Xoh$Q$?=u}Euevh-^(~LK!wu4M#+4#X`l@`ZH;)HO?_D1y zXVw*-kiV6sbF<)@4PIPktbDIIk@DMwCu%Q}@FtXghtCFnWMb^*E-OmJK2tF{ zQ9O9|13BAMgE+Gw6L;xYm|ew~>zMXP!qoFEdQWV+ViW$jT7qc}2enbT zttGTc2VL-Gtrgall^CEPCRnTMu$+l}cZI+`u6QivvFboz>fl%8pzDXkuCvm50Z|C# zBSb{tmAsw$TGUM^+@bRe3QGFVk0P9>4DMF`9CAs|4N+7T3+|R6B+MM1c=rb1L-w5^ zcEDRV8vJYS0o7iR}ka*vv8 z%j|cOi-ulbUS35(7tT>iN_`_QhwuDQ%Oo3l`AvC0Ef;H{OzNl8XTmq~zE)IJ2zft9 zS}**wBt?Y{HIB0Zmn*M`x_7$xH)m(97CRrVL;w6XeZ{8 zH*ad!^Ix3qctV*BS^jLEijX-Y7jd2z5n>sxHwT+F9%}subnR=t4EScl=v0^BL za(9V^78Du^z80}Qy4M}|#)GQ(9SXo!ft!=>TpP)^ z*Y%z|sC9O7TAD4fm>q7?i*XL&D>9u34;jjWi?x}WnzEaY$NEa~pS^GOz4oWf( z^XbE_+4e|#J3FWC7HY?X4IT1>&8aRU8L^=VL$yQj%MszilhcjzP30D1V&YwYmCED2 z)jms2%_oeZbPA%g!!UT6Kvjs276FO18Httqk;=~7bj8eL5Tu3lbItnXE2e45GzkdG zlTT0`U7=gFbaX4HM?1cJ$25%md;N0ZW&_@FQ}U9W8%MQBY!ML=>-uH(u3W9AbJz=E zAxHPo9gsqSvnlh`(RQB+>6zq;iNW&mZrq54=bDVM9j|}?_x`O>8m?!ooJykho0SLs zBUQ&M^b6i>W|Qr*&GJ3MdBSTHR;Y*EHrnKs6%~;i+&qFZ%?6zF0%h-~3=>x+SYb}J zUvI7@G&&FdRPf}3sg?YWFsq6~f*k78$ommUB@vHY=!nQSHtb(@(@s*~rV-(7D;P!Un8C{>ZSm;Vl7RL%3v&CN9~myF_Ss2(tB2LY*a>!IoK zTZJ7SwK7L{E)ftZ`j2U7+>Uf3bUZp9F{IW8VU{is6wKn zqB>;NUC$85OEWXIe9xhL+RbvsG&qToJX&90WcRTwsZbhH?=Jlh$?JtDjS zP18=n!b#_;&XSNf^Y7t16;oDX5Y5YS;@9`UXZKh%82Cwe(yW)*T{Azm!lpa$Q-R07 zZBE?*g@i_0x@4|Tlpipu>YOJH-A*3K*Xz6D?dJ9?@!n-Q&B)*>`(3@k&m;V8yV;+j z`r`bg46#~dHg!I9mclYtV6f9C#qWa@sw%B5tZ zxDd)Ss%DjfdwaVP5B_(rNi!PR(kJ@3)CR{}bvee~W&e_DvOK5CyKT z7k++s65b-Q@W|$j2qUpC_Q%Z9vhsA=6tmUO3WwBJ2hv0PzQA}mI5?WlIdhetICWyg z1fMHOwl9ws8p*Xquo@NFZE1#lI%?2xIht>wj?wl-1Sn_linzcdQb(R7IMYnYQY9ef zPhQCdZ3yM^Fx+?)yoN$Cn@|4&6;z`58Wq>l-Me=?BG_#ah)nr3*^~YCP$rFY5au~% z5*w7QQv8!kS`8kAQjr{w(WOazZ)G51<3A+{eMfh+B`Ws@Rd=mSNAh(3NemqTOgO{y z^7GTOF|n|;WN%%9mk>hG#y@T!9Jn5$zBAKazXT?2w_v1}FDs8%(qS+d=sorS!~t!I zdGi&-;qMPCS=p8Q52%t}qI;*d1OPXYy_8I zB;C`Jfvv}+>h9$w{wa1Qc4MNvysWJ1Xd!|{x82Rn?Gbl$P*7sWz*tQ zA*W8yT91N^0sGmXUr0(1-y#Cb$n*KZ9?hcHd46`1pO@F{w#dd{=>q3~(-m=C2ed1P z35u(0)ToM1crMLJyOsX5t*tHXdS7z>Xz-Q$7AOrN7*|Z*bqM6;`O$ODyJ!^Y*U(V0 zl_(MzA0Lm8kKcO9z}fE2%YhXfEslR;yj(C5)E2cmnCa=`Gg<9=ktUZ? z5SI8E8w&z4s!=*D-ZuJ2G}CP)alw! z!6kubXH}^f*r= z-+TG;L%SG`yNt;g@2_1xwLOqh9zNV7OD zLM3P2XQATv;CAgx?zGvG_dMs-%0p!0UY9DQC%bEGjSXv?LmBRiRgH{|dGehfR6Anf zuBn$=BFhlzlusExgpoTmAEm?ujBF3*en}R;sR(`k{-x63``+@+s?1b14vXET8|8h{)5YGu&(~_&IBT4S@la?v7aD?OW?+8@ zm%C1r`wPvkR5Z*{vCYGM$IHXs^sJL*V%4U$CaDod8JUZ$1{3Ry$DR$Axa8_@1mRVY zbS_CYsEb2kPZm9!p6&IqrqKd}=b8c<&r7D9@~y^8UNPj4Bk3CtYO~cu9A~{3FOIr| zmCMSqxNY+cj`oIP&i01BXgJ+fs&?^?;@%8%pq|CPv|2Mgt1RC{u6gkC?>O2*Q#i|i%rk$NLHw;2Al=QG7+egcul7=kA`!&i__PX zl=2^0JWI9w=XS(m?VUb!;AwX8`x-u9tU>OS&j!=kKjhrLE2cDrI6VxxMcB$5D8Zo0 zW-y#HS#eEhee{Yk5qBnsnI1}udzVmFUm9L|ynrh1MFQ3i?z(+Rf0%L6%yZ*xBk6!b3LlEESfW)1$MM`?O`SNO8s(==plF zYZtcbZqGtrviI?<@9so-XnA?L2dn}FBe@GlprhtSZA*7kJ%iKI+^t;9@NsRg-H_`n1`rdr#mQq$C66u`~^$X6CX}51Idv?r91S2&o zC`yZ=4sxHp1;t55~ZP9ghz-Wzzyek1bl-+i|or+CmT3hO&439&;VkYH2~QUz2B4 z_<7IQzt-h#NlB2wcnRAX=KCX7g#QpZ4@auufT)1EIsfy%AdU~U(oETTE-t|Uw;9k0 zuh$x&zLVPwWvVhhe_pSTC^7%_@!!X6aXvj*zi=ktC{a`%v@=oKsWzkPxY1jJ7yO6_ z8}jc5dn~9OHz_azlmrad_gwLHU0+m7H2tz9Y%MJ*TdJtxTbu|hf49fJvmp)q&qe0TedbK^uJ#K9r`1zTGfdyI0k038ELyZm>v_Zl46fJ5>ge&toON}9k zg;UeM&zrod99|02kNDXCCcqEw;yk>ATaxEPp4=?61>aPk@>0d^bid*KV!)V719Vih zM$>$Zz962;id2*2s7mo-oPyYl zsw#-rdXW_I|mLMeaY}Of= zqv|?r&*6vOBq|`eek!z0b#x@@v|)m#M6@&a={uQamHiF@^Tds#Z*xU2ARoW{ZP}qG~@~PANn2espTsFq*K?xy@B;CT~*fZi@Fiutn~ZuGBcW(j1@`n@{EVSNXs`Gk`+B#NneM- zbX*QU6c2kvafiMzRf||zA1_@`ljEillobPwjK8`S6f!`eX_NH2nntMnveZQ6pkf-8 zCmUm$2q#ZVg$Iz>uaG}oLS+J(Eh_rt!g+N+Wj8dW;kLl)pf@XMN9w$lSj9~l9*u&i3d@JKi-%WbPqszqI0rzH(w-Uko3 z;^F1G9{otE2sV#X3(cAGf%KD{`tiw1#q!^$p_mZ!XMbUUSh@M0+P#*Fdxh9Bd}oxd z7j&JSo$^Y)Wa}#N_E1T-a7ME!vX)I6mMD(9mG(PMaA=i@s@=hcZ{9o!dtPf0t;GCW zLsY&dz&*6p0gh7{HmD;e9LV(hzx8<#fii}99G|n7Gr>B=P%s{_rFGv5tIoMT@z9Jc zWx+92Fuk#QLpO%@k6xunI<$nu? zok?do<&PqF>UsYAMpnzrd#pUGB5UAwmfNk#606bt)fXq5wzf;%tS5w4q z>axhT_t!e-7Z(o!P`BbS*9^Ba6~lmV-2W5$p@Q(kWh>#;in2+jLyEhiL+#*3Pxsfg z-h8{}Pi)eks`$1U8y`wW$l5hDG&WT&Dj?vN#ZUG8`Cwr1!Ef^fYq~&6_3CI&drNcg z2UP`%o5u7NnE-5#2LrgyP~+iIaLJy=1B@mVd(%jPV@Nh4ih0zpayLJ z+x7pyl#lrvFdklKH~3% zoT_dcEzGaC_PI|%iP!BR`5B~D=3FD!SiO@>*;oKQG)wg=R`&Uo*u$%TgL@Q7ZZa+^ z&`xxJSbPW5kz%f@@2PyKuWvC|Lj|J9{V3kLm{JL*hO5Q_Y4}sIR@+u!?1j(|jb<& z3l24KyK(V>&6ZLWZe%2#>XFSmaa^>@-r}bRR7r7p6~DOzI)mb zX{@?5=0Vs-O`Tm~O^ay%v1ko1xoZf86_Fp*UZJV_`*?j8YEz_KTc)Z#^7B~+1`>oF zL-Z$+fqgn{W5vRg<%CWe)RF@E+17B57}A-I4To8wkg#6KXs-lIAz!ll4<9x%Rw?%v znV=@h`CDa|w8EYzspJ}~YHC6EHqNB>8ST$_%FT1n%u$eg8h=Z|cT1Fkfe{H?#@^o0 zO>Od)!v~c$(Q3xXTl2$81t7mG+7A!iB%?<4=`@$h%&XcpD9n-zHmgoJogot-+V$ok}8ww=LdZX81o)(qV z#l&5MKmx&E3MNHVYs9s2L0;B%h$$87d1{sXK0hnZfOCt9FV5{;f1&+3>KbBg_#N#% zPqiBg5C|z?#i^4vip8#Sk3FS;?`!oE^s4tZ$` zpp=K`TKKsR01+7&5H}_YB0?poDNB-8)8N{Ibz2CIwT8r0{^IN@=Dsig#$6T~&MWzK zwT%E$0+BB!=yU+<{@k33JzY(^rF%TsE2B59Ae{Th)+D9IUv7sFIN^|#08-w zV0*-!jIAVZw%&t4d90|H$b5^0KRaC+pb+hJoK`JJjfr}QICB7UIcDC%DKgX&%BR)X zCV!RFfSo-wm_DV~Qh&)U?u}z>Tfxor&(*I$?#a`IRKE&biDEP92hG6UeN!=e;|;A| z7nMS)w2#Y8wX&eqVuPy?QVjHNJ}phgkbeFFWBZTX0x*9{+##@f&_W$HERs>Ku6ZJF z9uTR`3C`+l07yoZETNr5oslCA>2D?AeO1n4_F-%v6pY7+CM!=880H(jGtl%G6L{aY zwo1ozY&J46d=FJ_mAZmwf`-7-G9s1|Sh3o*m~%^EDBzgAxTmM1)1&XSHPcW{+Kvol z&XkTL6-(4H%@YfCaVs@XxGnipsidh%*m|Y^o2xv9Es9|t0C%iw@;YtH%d5i@Mk44H z_!YBRqIjqskDSEYU!%W^1G!Lp_fCRc97$456_=TlgzbIPDL$L+SsX{BpGb z;)IwLmk^!dRp4&}xnW+isN@z+OU@PhKSB|ZNgj9uy~|~PZ3MkG0+7MtlIr&JqVM#V zZkXnQlsr5&p15tu|rVz(Lx$*)%yuWWUHI#ZS=b7NjWtEqt8l!A~=&hr3QyPKZ|x0CPZ z(a>Z-vC5hrbEh#Jp@D7ms z)K;j19Q&Qq2dqv8KpJ#Pl1*~#(}F-KzrVIY-EjNh{8Z+_8hs0EWr@}5LM|^wqeIy1evIq2uUtw%(BVq4+|6Cb*ZcJKGt8;SVFMYiyMt8>E;BUT%YP zx)2eii3$0rMM1o~PJes4;krl==1HqedHv+Hg)QUZ*28yoCiPnnnSJs2j*nF}WnvQ_ z6Z@e6Tlm%Ecl&ss5cBuU=cl4yoag=EGyR)k;W*-h3HSAoZ0X5?jzX6m zOu3LQyGM2YKZe730Z)5M^h3&UaJQ{>lqPxc`%QvxRcHI2nq1BN0au7&V8nzJrzO@* zL&X|W9q0^Lqg0dBdC6X%#rZ@Ua+iI}KZT!|M0A;w}E1mo9c}tKiiG_nuLt+R3vw&5;rHNTwdoIHY2k&_*Gl%H`R^To@`fj z@$|vl8)<)z1akAs4u7vg`SPeZ!2Yp+={ye`6!5fn!z1cDxQ^E(Q`Yljad}N zj8lyl$rBLVC4%P*H!cMb*2Zhw+${IoUa9}lL%>?OG;ffd>uI;tYrtc1dewyOiD|oP zOckdJgCdSC$_vo1wY9a#FnjiKR2M&WAVs!(OS%xw)bHOn88B>7nX2r`Y~6dj&vHg` zwPb?uiP*95gmnU-i%4^8Y@nK} z=e%byxk~)A)T-(B{qEpJS0KbSdD%BCcouBO>x<#t?ZbBv_yX(BciF!%k<^n(Jvo z!)iAF$!a!M3#r^pXOwMJ`hxmlp%HOg==Z^NrL)0It;Npuo!{o==BSSWR!R{W>(nvE zy^PC>RAe%iY|Z!LZ-2klgWTCp`>K+!p5&vN8Qyh% zw&_d8+uhd})hTs8_&DJ?C)KP^AEx1qPamH2e@L*(%wQ(Aeu|8ie!}m4a)`&>fLBvh z0eI8M7NFm(Kf*R`!$0fTdI+TcB;RNnqItidU$75MKp$<~@a)}<8T2iVTTM1YXqZ&v z$0BCW%G?`%-i6x%e$eukl)ozJHPX__=P*wv@Egu*<_%b+Ot}pt|CaNKjfdCmI3%Mg zk)J~B^$ANL8EBMRUXOme@Tm)%n*H>2hZk5_Z>e1RQ_rDH$@dB4=Sq@&`PCaEwuZGb zhi`{eI!>j_QGcY&!vxqqm}xVuw9QmLgW@sg3B!8rfR)VC9oZMQ_Gp&w9O}~8WBIxk ze>=ST!bLf1N?%A6w3vz4{R+d@s6Z;56hv@jaZ|CrOw2Lq8GbWh34@W<9C91AdhK>$ zqK~N87Z&m?=jGqw;yh%w)|a{j48Lv7)vF>!8-7mJi152D>xul~5;UCoeM|MR8drA{ zYye-F1~fqkUA;t~e}q!;Q=52XpC#U}t;H19j5pdn+*Hquuy`x_E;NTiRc+Lan<#sk z$M=T~s=%~hRZBD1Pyni)t-c4c7!V0rM(Z=mU^)elkaBB-^i;J&w8*_WWNtxQXWae? zBb~;7&~V`AV@95%IW4P2pAWQ}oPb&}kZA2LebV#_l1s7ibJyHKAh2aD)eeETaWEmC z*8}EbQjk!MwYrVBbO$8jl-(hQKcCSJ`ujyX{~xjdkK{j$Vigb?g+fpFh=1 zQ24t=9bJt76QcO6+GK2M+qMvuCRtCVr?x0#RC^e_!lS>R6;#5YPy+(RhJP52>3B2n zr(YO7%MLZSwN1)K6P@Kg|0KVY@7RFTJw`Xjgew)&rGb@;s4H8ZP{Rq|j+d64R`s?o zTw8T-fDdUSZkFSx+y0fYO%4Ki4bwnqbzoo21xf(*Mf+`k-|PGB{Q-4p(sAQJryiLv zS7f|MOe7TbVSMbH3v{Bg`itLnrlLKJ-xV`tlj1ij%~`c$erWYD3ba_D4CyV;PhHsA zp+AS(($_9dT*s8-=K!|N_4zutmY;!gw%Ypq93ppRKR5UTZJ2wRJa&s}My#&i8kEbu(Mdh#$JX%?5n@0RD>8*Yaxroil@HBf zQqiJzXOoCu zW~iCy$2eJF0OQoc!(#`y_?>6um&7|g$ZXXE+<~h1HOkSoB&Yx7Gti1pXg<8zGNLB~ zji&!mo#^tWdb0Tyo1wg?%k}3NXOoVBQI=0oa^z9GjxXBy0aIQnlys2NTOq1Vmu3`0hg-qfIc@UH6Mzgenva`20vS%M)~5bCq#k+}>XwGw6&Q8gYJw z={;xtDsEM|iyz%b+}$ z&sCU%E#7p(FoLb)4508NG5+11t^-q?fHBj7pQz%g4IXDMeW$~1kS1ghKB`>IbpC!L1bVe^fBd6vEitgKsJ-~;MGSwH-)tbR`wGS4Og*oSCaa7 z(%NztpMN3*l^RGlK+Cwq$hR;`KqcGxG}k_kP0qQUmJ(A1-R5L4Rs&+e@bl7jDZbLh zT)s1F7FWY5n#vorGVu6E%`RHKz?x~tO7y*8c${|P8RLT}z`}V&c6n)B!K~=GIhKYi z5ZrHncXhQByQ{(Lp6X6*HoT2S<9s81s-Mf{bc2dyVuwwX={3j?nj6DyDg;~+4xFo< z?Im-rm3Adc<}LP|^nT!piF#lwHQfk(($z!DpWo(Z; zy4CjnPY}tx^h-LoUQ1j@e8!)GKec%(H17WOO?t>$##kWQFZwpO_63_=z)L$oN|}eq zP{`W)M??g+BV~s3Cosc7(+i@QF7k?+k7)&ZLya4;U1!`~#UnYyqwopx{jzY^CTM`y zIhpez6PUisnZR+BDsRx6XcLFGQZFPR0 zCembH5|VGMfCiHg8P3>yjhF$g?`S^*+D1pXOaJ|Mfx1JZ;5L8vPCJKC|jsGf7E9 z?q_kM1O=7ADWRP=m9DL@p`K2C+sT(+ZXQ7^>Z1|)!r*WqT{tMRn5RfzhM${z<#L{@C~CdnUj`qS$%&?>eV)D;*cED4MKL2A;s9DVW5R+`_y zAs=fmGc*7YvLj9dIVY2-J49?(TOPW*XZ6fhJ8o8qKQ+5wYLc+~Bry?j6O!>|Zq4cr zv{SuK(s+u@{vxZ1H#bgtw$|Q76PW%+L!D(-)a9?fgr`VRZ(EmQ>tO@d@Wia6O5%^+ z)KG$BtjoicQe*_X~CfFw972T+oXxoty+xBu(mh zK*dY6L9Jxx<*@}gPUh>`SGzy;3mG`IDO z$$PUtFLxDSNWRQ{iLPN_hx|S~W+<|KPFVYSyWaYKiS_U1QxN9h2FSr37#*hA=rb%j)?$b7$=bj_s;jHM_gt@XQ*a(AupJ z(5eZ3y@{_O2#bGk1yUb+*VC($PuL12Ak=t_`|zE;T?yxW#eqL1n9=-Jk=zHyC0Qw* z4pHs89V-U-J8QLsU)bDW<1{H**h(AxLLwu#W|qYza^ta6kR1_=L#Ze+L`US2Xp-nb z@(cu$(O6nrs8)tyC$nYMn>>+;S#cga21`((qo=VCFj zqU$b|_atMZR-h09$Hhkf>*I*S=Drv&9LC(o47WkjP%?kT-2nA?Its5h@>kke)*tr{SgJhO&4oOeNG=EQKeG}qwt7Sx~ z7tR4(Vls5!u7#+py$prKrv6V~xrJe8aRq+$V>}@Ce+JpmC_UXV@Z;S(b=z;_UkoeR zR41uo{rBSMhBslA-R{p-|6Nj%Gr%IK|@|$rrV>u~B9;)N3^K9Jq0K#Q62SCLzWw z7!b%D0U8cZltX_kZ{Pc} zT3)71$MWA+moecULQM?{!MPL$fRFaNHlQF~mL~VUN{o z8tw`1NiKna$*0>3V&=ZMxFI zhba%J1LW!3&MM8#S}EXBk(VS0<9Yeu&cYO(zlq13 zds;OqTe}$3;p#=s+8*!Yr1 z%X;3%b=&{5Zh9by<7`ml^I+0vvcx~>7xIhR6V4NlN^I(?la1Nt<~v*lVDPdr!+g0H zFPG)%zm7FuFXuR*VX8c%KPVO7%B&fiVw%Y); z;PEE~XJH1|O(PRbKT6cIsgnQQ@n_aCo&A-X&&!=o_L(QJVOyy0ED@~f)QUCTl~wdb zY9+^9uq^D4V88VcP}mb)km|9Qs{0Z7FG1lGAPk|2s$Psqr4uJB7gq)lnC4oBmA+tM zUB_e-lhUvSlP5s!Xb;;t{)Urp;zFrdLtr*}`jn6Fv)2i5(q9liT_V!A@Q?wj%dV&C z;cvclMG1oHtFhgn=7E}lw(9@6fBl^LdxGW#+OaJJXt^L7xDUNQ72do%1Vbj_*&G4c z_u_6b=V~@=8;izaCXgg)e&V);fq1hS$s>#6E>4no@2*-5yJNPrLbcL3oj^}DfAf!c zhk)DH-GayaasH=RgNYH3sjJfZ04xFb2V(x>>6a>JO5$IFU5v>zpA#nXbgpp9Aij0+ zjlBcHm};?4kJ6lV1OD(kAq`r*&bO$>&;hZA#wKAff^l)Y`pQC(;)Tiia*8&X8Jmk{ z(vZZ#dDxQ$XA$LbWJ?2E2E4y)+~Sl7{d-@S7Dsu|j-4jP_D909YrxcN8Noukr9PL4 zAB4eDDJUl91pqEoi%MxUsqN)_sAM%VsKQU5c4r_VBqPX|-pM4D_%v38wB0(|z?SG)l>gp=5 z)dD@3R29KTX#M6S)B{hqJJxX@i1cIPaWVmTNN^`Xc0$`w9QJP3r1R=X{U z`y)nUl31Et@-?jMh@3FxSO9CH$XJ&jyjlt+!%y`M&nUZH5IHhQ?D zLubAd?m>iNw(tVH9m-)KIU>%_N=5A`LD%Hv|U(Me%yb#Eb zI6ubF4G8WZ>TOnTH1rFo@U$Wf*U*7R_f#*2qTIa5e4!m?njj}PRA4qG%WOvlKj5@I zDKnp-yQg4uY4(a9$T&bC&%XREe#9?kpJ_j}7@7~6>7|62^Pn$N^1C;Qych9*Sh(wY z6MDUf7%OTzd737CgRpXQ#NXxiI0|QNBhN1 zu$p+$U=J{PLCgHY`kF0jMYJO}%s)QuwKb~Dw-+2AApTntJ|+DF(G+b_Pc(~wE8|y) zz>n_Ko?0n0Z&|Q!ELb&-%d zrK2++Xx_Fu>DG8h#PUq}#X$r+f1J@&e;UQa1f2iGIQ@sWzp9ZD=0dGhjAY_Z+x2%2 z>+ggB1$g}CbA9CXR#3QxvST_zx?-j_H#O!vql}qJayb6S3H<^ zONB@Cc%Jv>`QII?h_VJlN+S`{2#wtuc!{qs`-s{AD9v!lC%+eZv+jW*(}GXh zRVsB}t%)mO3_WS`g<*@&L(d~&X|?|sb*KTIZai|bFF?-%D%lqFL|5(_v`Vw#o+YY` z=~!AG=Jdx};Na%&Qvw>X-YPE1pyT?eitUCr@a1-PmjxQ8k&pj!V^u+dhle{|TKtFa zg{G>afaoq%rDBVqY(nOD*WI>k4+1c;h%^@r7Z)QZ2WwX@p;n zK;;t}BD5e;V$P!pD%F1NCk)Mx6&9VtSi?#IupZ4LY>FbJ`G3}V4)De|yv>A@c)a+3$iv=TEFCW zx*=94m#3<+O$5Q@mE98~LoiHZMpl<9-r;rc`0*!V+J?IT>ky(l?yssPT)=bdEeWaI z7IX>R@p7`C!Zn*(ycHJs2}#*49JU#b#V&VHUP{m9WWCd%PpGuKoQ%Ip5IpQwXQPP7 ziadk9vc;}Va5Qk@Ayyf2NgGINGPF6V^H~T#jHU2Y(X$U?4g9uY#8pWhr%i_nxIod6%-XuddR#s^m)r~U^ z1eEDUz=I$i?;0670*1>1t#ty}p8-Ur zBG4T47{Gs9F#ZIsXB|P#ds5Pyjf0~mPEZHxFFd}z5)k^fyE+s$-UTS*OflQ57wjy` ziDLKoT+i*T{{f10L^&5bU>3?tjr&ZUkOi_z!Fd9FzQ02nK%>y`FC2GN=Vv>6uYIZ- zkYmlL%En&hSet_&(W;M@eTODNsZdqr>UUb_h5Tu*P&bYgUV9g7TN z^G8kwhsG`)F(AHCZ>#Jj*v;BX7!?~P{#YN*P5p*r)bm=ry_P!0$t$IAPZOM$31v#z zm^e`fi496zb_7kM|H~Kb@P7f&$>lU$F?MbpNo1*WgK>8C`5Isl`l#c*b~**R zbAZ1>B!{riBPmO*bTb+RdXN<15irFid`OV^Q$3B#Y4Vkv3o%Rl=*4F@Bj~A;RRcfU z)#Zfm?jitC>F%rmF8g6-_*FxZXx{`k^em|$`!6DzYnFwJ2w%9V|JQ4ORh{hWIs3iJ z-^(WpZU?pCzaP*~619z~n+*pJ#YwA(P6;5JOl9WLZu+LWpx)6uTC-#`XdYNri(OJ* zo$sJ`d#Fj)QX;Pjgw%##$e2^+1aR&srHj}m4GcghOOPm}hg1Ux2Wa4jT3knKZcd1m zjWrzQ)-$YP5$pN;%<*RDDdC)9MQVoq%vbt5+7_uIgyCt#x5_@8o!F|B+l2h+MBy|` ziRlY&5SCmD)_WW)mqWPzzMT)O#ir->X-Ga)k+zV9un$nJwKKwxALr?#fj`Ur4^`^i z8}>W*k^kJQ@3|8^`1(O@uf#BKrEY!xOvP_tf&)kr;F{>42}D8Tk>&HbK;Tu*c-=!h zi7yQU<+GNDN5~^Pn;mBp{DkduMfCsfv!89`^nNg~4aSpw!UOW&wTIDLj>+#8s(U*X zw3skb`9ssm7QGvj@I8TNc{yy)Z9bzLKXv+J3=?A&9Pa_m=6?qD4W<^|SYeTsqSIhJ zjU9~5jQq&!{k^S(6NOM{wEH)5*G% zu#~q|XfBS_0&kIk%!PxC%gZHe%+~GwoMG20Svk-9d-#{4&-N9kMI{;EjQi>?k}pBu*YHc0v4w}=Z_ zLqeq(H&?*+M|R>eI|>Ge?XQP-X9`)b4TrNPg99EOH7*M{@3G6ql5U)+*fP;NkjdB* z7>C&WJBY$*phYRTyun^2qxnM>ht1bhIc<(6J-5_5+_L{@@h{Nfv`*9y-DX#GbL5C| zRz0Ju0!9e1eV?_ajXbd*>bQs=VjL9VAobWHjwAW!sq^r2ET54MNXXym=(q-IiFWxJ z5nspD=$kenAvJK!=mE8cQ?9S3dFOV%2%2eIF;U^F#8mfu*c0GA@0u2kaZ_ zr;aOrfX~1y9Ry@p6=vUfwaRxD;Ya8dD3aN@J%~sM;dtpjggs%ARu6O|z`dl-f5^-x z?LB;5gmB?LPaEO3UQ7bVj6ZV4lw@tc_YHetz`>0*%$=Uv*C(YK($VazAY^%AAJO5tFvW&LSpdQ z1YJ<%pd>gAvj66yGuekWB}=wE{E>!JkJ2S_df4?e&5G`+r4J3C-1!O>waErHm(e! zct!DtaoVUtqqjV(67%W!a0l%O1%9Kjb>R>UE|temF`@pM_QZrQSpm7k5axrC^=EH5;% zMtfwcwOzqetU$a`Ax+#ctH)se>!!Yua=lHqw0thy2161vIu4IkX1S-pKlpN>Rb(Fa8k4^*v!w| ze@~5mgf_bAGqKYvOH3wTp;T9J?mmAeW~v&kH1Ej+>iraW3h|**EvnL)Q`hha4*-hL&AEWeFhHw;b9%71AoNb z(36}8k*mInifk%acY)bBConMp+gL#?d10C#PeX>d8{#(fsFsftlRZGvZai>G%k?NouI)^K7d=0C;i8+hl8x*p_eJcnZXEy({PI5J+}Q z=%*)~BK9xk>ov-H+~%CU=@!f)I;NX3xRc86&WXucRk68>x!YduN8!1AZ%ylH6|!Ky zY~z>=OHsg*6%)5gi(_GlLbf*|Tq%k5|J)IJ4w6s&9v2}*`)hQ!9NfDbDGFZ^ClJUA zSVv0-xCp$;o8eODOX86t$~^CWal9YMMGsOqucnk_6mWOuw_%1VJajR3BLE2!!(tj* zS-HG9t@f!?r35|Tj=SI^9hR#Lfp>26L*Lu+20mqmGR;Q;Vx%}%n4CPVu9QR{w->UwhCN992(Apr0|6#=%ddZ7v`b}9YB2f>{_A8uA6LcHNBa<^kM zel0G3O0`)^4z)VoD*#argUCAzAa%|}8-s8&a;+9+v&!<6YdFZ>S2#|QEpuQC=0kzy z+qjT?#K$lmo17P#N^io`pQr|PT0Vdv3J5vUf{|rP`*7JgA%Oj zPh6`>sMKHyA`#g6SP}iEs2GugXiF6h+uF{~LHk0DlUg}>P_n@DJ^AOHWlyfWYiPh; zRfp-X(Bu}Wzsc4{ZfX{0C08{YkO5|<3AGW$>)hqnV{ zMDF{dcG1{8>={mx#N%QE1r?5OQ2cFtW3>dCwa)m5hpQLYC2ykUWuyfwMT|DBucduN`8aCJ0ePKDp>IfE;ojR zSE;p(>f|GGY0`V5BH`%%c%LW9&bzjVd%`-og2niLsuQRXP#!v7@x3x`y;LjgmX+-v z{3)HN|3=&(U^m-fMHZWXe&4B{!Ov;txtB4D&*ep?<9A!h#CostP2nBIFmNHz97gV z*b!p*xSha~KW0&6aUUNyud?C`Wen8p1aJI6kgUh+m-H>%&TPdLf}~OUKkvVmpaaac z|2Wh84(aI})ZqO&QJj5xR7+9%Eg0XANjz9b zz=nX#9v<=o>#v`B*9z|PZ5mbddV5(RTE!^pWAw_$TG85_FE{zGFbzGGp8fl1*XVOoKO8L52?%s)kS~{3uZ~lJ8625iu6*SMAoIbF`6pa$# z6#Lr2Z)0_|y6h*JTvT7u%;}7keP%Wt$pG#?$g%+VW^+cvL;FTu9N z0^bp@5IHLlqg#`Oj6N~Qx4Xf3l@2lIE$?A9`O$n(s<(E$&j~>_66DcmqKlycb$!Wf z=nuEEGM3}huf1I8K6tU4mZ7W{+$B008N`ouCswpVv-{i~SM&!Dz z{on!^F<`O}x>d9~pH&O5Xic2i?QVRiAU8Ey!rw{*cUW?N+HA=??V$!Fo->0Wl=89& ze34ZSAf*<*6}%!>2+jmP)Du=S46*crd|6Vuy&+CIEt9Wik$w|~Ka!C9$TlcoD>R9` zC*Ra!1GKH2`xhpfAzt)qLPPr)L+U;4;Y(`@w!~2qL%hEaNQ3QRIX#Y_Z?Q z%UC(8+tkM_4jAhpabSxpUDvxnkOFXu&MUJbjOdZ?o~!n+93bu?UA%wPcnv;rt+l4neNV9 z6i8j9Zoo}y?O4mLN$Yz7OuSGeVf1mhOqI^J?*~OrQap!`A{n=DCMzeHRo|X@ES{G! zgl}s(Tok3S%=j5K&@hi=NAdr17J2~Ol2UgYB0ff=B0Od63Rj_)S|aRMUJ4HVuhS!R zTNmu8wm>)%=z$M~>XRRd_P(BlwRnc0vhvdV-Nx@%!|1F-dB>SqLM ze$?d?whky;xV5bUHuM|Wbxfw3(j=_QdzerpNGoIsJN)WBWn~JAL218Dhr2S9@JC=j z$#>SCpW;jD%=%w$_l^l9~hW;TE z+te(xN3;J|-UOqc#bfO1(`3#Vyzj&Ja!`_a-kp^-*T)!sE5C`Yt>s(EU(o+NSRY}% zH{RI*HMKGUKU%vPDBEchoh8z#$|hmgT{PKf{0D8kokD}rgO;R2B=-SXUqVXc=eO2x zuuz$@N}tAff-eSEl$o>?{Ohe6op|U7)X}Rk++{UtB1JRF_>y%bg4jGC^nAm43Ug~!Ck4r3%_iuBPZzre2Ud(U0? z((zXiwfY770R1UOI;CoHhE#1j_O!J6)LvGPs~udg&SzNG>Md@LDzk*f$G7VOZ>$BQ z)tnZ4wa2ToGBv98v8tyj)`-U8;tU2w_~Qa;8HkfU7vwMVm2uq3bnRuO4fdZEv5;1$ zD5ll6kw{-YAffV(i0+F%!`JA;)=t6p5gr%wmh0?`WI`UySxk52=$reIp_ZVWFZ-sm zZqAQISVO&ptzvg%?89yQ;gEaHAUSzkf-p(t!&nkpkszqTiToJU>j4vXRX6cwr+Q3H zK$$mmfg_8K$kSx|+A)M#z~Q8K;N4x0s142ffUQyqtj$*sH2VaL)L#Bz$HH#hDI~=U z7OOx-y6yK!4^{a(Dj_Z^OQte_(V$8Kwlz?V3+OV5_y%yEkZ{P_RCUI%Cv*E9DuP#_ zc0fI0O&){D+Y(@Ce<0JR9(|kBO_|}KQ1*r6^3!a;K(wo0#&KtZU*dpbZ36%$vw-jU zoaQghbw~krDp>p=z=H7$yjZ}E$vDZ$q0XWpCML!&%vxCJW=J9tY+9ab^dCw7YR*2v z{N%k0C?pjNM^zGrj+I$TCJTKBnU;}K^GHO*Ho?E+9>|0Kh#P$xP8-YQX!Y%D)5kHE z*ZT!x033Ah=yrOMewQpZsA>ijev{}b3N|(ik9s~QP1_!B>8{GOm!fP(U0^exMo0rn z7MmpCxB!9$0LyM}R_4_a2`2M=z`}TsU?3VQK-?KWY0l#<85a%e*YTfwy4Cqoj{^q2 zmQlb8K8K~F(9@In?v=1#;2^M&zY8-=@^6St(rvs9xV)8}+7m#TwKGs z_VV>_bs0b%aD-Y%b)EjD{mYVKR4q4~%>627^VP)KejIKRd`ld*SoK{1J9zh#t7&EK zFNO}_jN1|XOKK|ej&53qpb`)Ss3uayMjf{&L|&^%5UChW1Ri=lt-3|M2AfZ5&!sQTCzApd0j1K{{oJoEZ*xk|~)OW$~FZAe|x%o*8t~NolF(h?iFMqbBi|w`NN4QN2Jq9j-_$_8IiPT8Mgdm{Syt>5rja48 zWbmq4wbiS`a@Ubu{7mCIc2H_68Ts%r)-S%3tYB^P)pFZqWe@ty><=d z{(s(Ki)-e6{bogBWe>6 z5f-1=j&727*byBMqC+zgT7C|B?ALxw{iVUsgVTpVP6}j&;X%+xU&d zwbt63`h`pi4QCsm0-_LhZ`1r(yZeA%BVw_ejn{(loUg_ASeO1yc)2&(Q{+aIrX#){#y=4pT;+4)XW zso0T3HF=Yjq$uW7z(ogm|!viFK*1<4YwlQz6{ZoV%5BZ+N`=N?5f zv0uUs!uLoh4rCtqK@RzTNkXh-n#7`c0h_8lf>oG;q?8W>$m%ddk)J)Cc=!tX{^`eP z2`0i#^`wu!9uKi?YS!set4-~@=waP`_iFbYiQ>}IDk=<|vfS>uHZ=KIJctAoR6UlK z*^u%rrQvGl{`c24hPS4hBGb~CRZvgT-o8~lI;d(1hY%SbP`lK51mQCqy?OhVKN}tY zR{*wAJdD|1GX{3NvneWqJZpcAANlYZg4J}3AsDNZM(+38_^6yTF^e7cKC%-TsfjHl zMoTyvD=w-c)z&z|9~YBCP43ei%X1O6)eQAQ4n!Odj^(c^0P?``XsR zmds1KNCbdA6I|}mK))<5!b81jXgUH!<0m^&q+*6v78kYk^o(}4`XlA}z|*x5)4WcX zt>?xk4(3Kp9UYC6=iK)pTraHf0LQ!2u915SR-SC%;dB$Lr9Gv3DeZfHE_ZBXVyur? z0M^keya-XxDO0r#=Kh|Z^4$L{yt%7M&iScV)GHJK#F|ZBz(OPT`u6y8Ir&h0F!%*9 zA>aasqRX;*VcFBanW)ft@EchhT{Fp1$zpu)d|2S3oResDtJdw z&iN%rg8<{v0(l-fu@?%~#jApnZqRg`1ahpdvjHkV~Eju7JwcJN|`}BBFwk25@55SpAN=lNVIy(bRXe_@I z3pn9dCw!w<*k}+{QX=Onc*w6zdO5LoPa#}S-%Onz^Ja+>F;CW18{64ECtd&a3E6KB zoiK&;x!2y}wC(=dT{G+!BgzSVI4P;h&!0cXidb4|T&wq1R7fT`yf7#wQUGj0n&(ak z3>8Io2o$v-?>n+gr^hFC#rl6QEsf@Kw6U?F7aI-fm&cc5n{^P~p2`(KPVD_Vh#0j% zf3@+-()6;<{%jM z8xa4#>>ULSX3olr`Od~dT`jHnk00li^krSuWA75@P+snzj1vkjRo+i2xQ>&PLlEH; zaI7-NT3bCt2lCNVbl<&uC){)-7A~%+6mSnEO19L@G#Q-BAfm~J@l1>Hiudf+WOqwY ziM?9?*|rH?sZ|K1Wou{W>f9&@T&{=+9E0D$;C~()seTbSY2)G)KQggb$mRdeyaC}l zKT0DZ;F!}2!l+GoIh;Au*qF}6&Am2OmpOyM&;9qPRxx>$*4SjZH+r(|zaj*29x!NNG6gr3`Z(-ePFGPyggOM&4-$ke1 z_{5(X^C3Twa23w`x0I~rCBfwLhKE06bu+$rhRna8loW^!fa#E1x{!|Vu zW1-1H>_#Hn$=dVjDo3LBuMCpXd{&sh0t*T@wcKN86w@UDBa!#yQ!k9xr?LROM?nNl zv*<7Cf+;;2?p~Vhf*n5|?$+E;IViU-*xR%FLLL+th^A-A$;0Dy+#Q9G*_1NPXNU7& z0|;M)eNPG8Gj6HvFqj$$%kVce&pG! zi)|%Wwlw>c7OyT6=se`RoBNxNMS}ihq3P38iu-G;kUYgAz56Xi731U4J(MRXnVD*; zrFP{G3?FJze~g1_2tL#$7GgG%R0S&in9nzeL$xl?pPwW=3zzStH#2YP85_Co=FzU{ z!t&~UdVuAgVSgDoV&EXt};>FGY(RL{xW`qCu) ztEy6r2hsKxdOnN6)og7@&6n^v0fucq?EO^T1$ z^T?|6IhT*w9d7ZU`l}xA=hIM%N=cy;@lL2o^Tp~aCUAAc^&N^@8Ee$Kb&pr$**96@ z?dO9W)q&Nym^WY4{1e^mchJcLpM@j$Usc+%?Xr21DlP%M&>`}CBW2yQlI@2{VW z@BvJSr3cCqOC2u>=?kmj^;&X}k&d>~=HqLPVgFKLW7swV8K1_?wA`a{&n=xK{DHE) z{RLu#i_Kjr!?D{3EDp2r#l9w4PY_SaSIU>v)C|!vZ>$tl=jScElr^C)3aS22WYfa- zE1zZ#b2|?pmmq7~V*J(rv$z6t8Yc3YXnwe zVt5^oA`qq~J?M^S?BpdLUJ^Q4u_-=xkNW zCn{`?yGtK}G2DsbS=EXS`0;3I&1}~v;-(O~Jecdt5ID8Du>9b^rsskhisKmO%EgF9 z;0z2@{iznV#)vYe(%R~}a7M;b`{e<6cVfQqr-gb2mZ?+=zm!l||E}UUwdZ1GjIz(Q zau3iP6>JYGpDyMDOW{U#`d@8VdhRTD3Ye1Iqv)Fq`#Gn!VeF7s1Nr6xTIQ2HqNu=t zy+tl4J;ZHK8;&C@%L4db0UO&;?HPyTO!FNvvF)853pZ#(gE@#SeU6B*Sske(x=#l->r@iT{yL7j4JUfmm)_J$|I z0QV-l)Xpgu$iz%cm*02_1%_mz@8!ZlASZ+URE8t(rFp*8;D*06py>U0v8E`(kP9tf zb|DQ(lh3gxp{|hx5VryI?km zKRiAZr2fEcPPEi&P9u!W{GJ1E}Bwnko18Yac{YUyTD7 zjH87KabVm zkf743<6eyg4Gfu<%T>Lqt*WVS>fzOH6oNaEg$k|J9AD=Uy&OvddN7R7mY5aZ;7iPS zwJt~3z*unIG`>&o?DSu4hU0J=6xidDo5de-uZy4xpP^Q$#Idx?VNM8d@MtFf`Mx3>@oI?#PY zeyfvn{ZVy-SOhIFa+)zzSy))oqCihBsAy_Pj@|j$DQFnO!Oq^Fs}`XMn2Rah1>AMc ztjhYFPaa;Jg;Q|gcIpqe=OrX0M9T%TV~=Kb9k4fVsGW6rn4cGIaIS%jywV<5iPLiG*5C^y z=1%62z0NUkl`SVP?&1PCo(cMKJg%bGtHH7h3JUJzMnvG^=9`aKC?pF7l$DjiVEkdC z*bg7VI&a|0SK6&c`TDlRavGU`umTbS2nwpJgG3|(f64op-AV^!d_WsTmz{+kU|NBN z1C-cOv-RHhqZ!@_y?%X{25`(Z4h?#j2b>ZAn`_f;&gW6=(iOa z8M#A~yX7KAsUW_p*?1d0&iz6>C0q-JeQhKn+y2~C!yBrS{9TUFT3e+RrJ@PuJ-O6Ja8F$qN=M{5o#Yv++ zTtSz2Hc6=N<|ZI;@k(xR(C!6iKR~h@T*Q30I|tMjxp#Y6!LrY%ecnmKUx&Y%+Ptpb zdL8jrKUBhoUu)HUWyU!YlMcsx$5FN4vlaK1qusJX$YEI*FGYxjM`VzCy`KZCnEI?w zTg$9-OwTF4(6XYZ-KDx1;=)}pm9OdnY{A_+3zEs5?K5s)pnx<0{Ourt?}&r%tQ5L@ z5!^7?CT!e~<9-ODr$C_*KZV}(oIUsf;SAsujfYl5?Vr>+0vO&R72 zLJN1{gNt+azJ?D)iMG)mbvvX;yS!uke45rjPRai9!&-UM#z=O=5BXn$Cqg63{85R2 z`IZLhq6RS{Ubt{G#bOpFdyr2|wH;#5uMBL`>W2tp`kFMF2)q@VYSAi1*>8wUwUC?4 ziLe(yEbl#5o_a7W5lt6z(!KNzv}25kdF&KFKR1Vig@ugHiFj@E4cRg)5ar_tP2E@! z%m$677F^>ZBVU{Eo}9SAwSh@+?V1W=MBXv5c)dxkZ>zt`q|hsznnw4Mh!|@$^F3+q z=$@U{kHt=Ah)UjFu19H;b!do5Xl`x>r_m{Uer9H7ZVu&NA7l81b><8U_oS)bXI_kF z*BLWE>oarN%s=^#x=2MMcQ=g4g3!TrS!oJeTTlnW#r0+pTp!3s^5@S}Qd2d`tppv0 z5R-d}6ATOUho^3&lMbize62~>j@RNj_!?5mvDnXj-%0;d%+8EVTG<0-9u<@z)v^#- zhw{l)#B92;!E!bJkYy9xcy~(nyadl_yRCd`h9-FFxzj!NCHAewX=+Qk%K+%LYkoH% z>gA7=QM!(1;(3dA%b!r*8(T_=!fIeVJNr-uQAWF5YQYa+sqaCclU#))bp?|hzaw~V z6Yc*r7Lq+V_Q%V)(LdRjuQ zQ{cgeInRvS+R=2;pC9yOE0G%;XM_HvNOz8~ancI6eg+VLUJkf}^Vt$lZeI9O*QBu! z^LZodFj0_kvjYkB8CwKQjinf~w!vO&+Km<61e3Mxa0A|Q=l&9n=1CBsZBnmwvpC!` z1RiatW-pnxv0T%&P#P%iBu$C{E{M|o@@?E-L)FI@xDKX1gC~m875U&R^Axvb)xZmA zJU++TFRzvDt0bREYXb5qKE1E+x{S$js4rE`k%@hS|FoJ=sqTFXy|MB@!sx3^HfS;)Ps1u zQHx$4u-|)od#$Xj3_4poI%p^;FeJGYaDZ5NOK+$b^v?Z$JT$^ls1*eBR^!r*OaW6$ zNonc3Hi_sF>$}He<;nyE%U{Ur0bF+&Z-$mOAn@_<(~K{`;mMG>S!YP&O^k@|!?H-D z%Ym05Dk74=PQ}RhB`p^r2pZ&c5rXT^7rj7-8dvsit;hM_#o=9@sQZt|%)+-LwCsY2 z)?+_E*!;~m(&&HJ_1Nh?UhkLcUzuNyK}bzGtFzUwxF{R2;aY;I3UI>>`>3JY%Uq5j z{ojo%@fd%OSM1hX3DbzZ~xC-ByZ`YObxO--!i%b^#K`^O0&yw5DyKCxA zX_7SCs(Gub9j8ukF>JRn>}f7!_jUng>@d^0Ufc_5-rL?6vVtja8geYztae<^W{p~P zI6Iacd+z4)k?1Tehf0^A?cPcmRCLpDZ;hqCao*#c(IMQ|_wIQpiyWgI-xF11cc0I? zaYF-X5~%OqO;5UwwuRX&4}8Gqay@dk)a~Hl z(7q9ME#H1(CLvPy6J+sOuE3a*3W_OFX=da45@Sn?#(T;36mpIK8a z(1YesaaK345Kb+qf*79llSv1@I~ zjGP_*cTh%A<&HW{@WC^;Z{uq0@#3|6vq_TzIJ5566s!&t{d95T-5Uu_-|C+T!zY_K zl)xaRgVZv#zML}{56-A~n4E5&NDa?VS`T#2qv+>gD3ZKOZ|6DfdTCkY0)fgo*jgGk z#1Yavm#pJziG$!C6cy2Q?5(NkEFdnno<}M1tFG3t*sy6`lRxX^bWf&eZyio=P8QmA z-n?G@pz=Xhg^*&-S0T01qU^TZ?8MD@H^03tBfvw||GRFST>Q~izI(M46a?>k70WnW z%qPTMj#sajl-S0`JXXw&f=n1lcVjJ4D!DKHVvtL#YFlq{KX-Q6S-8HtJX&nf^PHGi zUZ4nLN%8OLX7AwY=;Xu++wS^7qM3{tbuWeJOAVmY<~pv*%5i^OZ$$x4W^)tGulUVX z;h!ewLczJmfeTW7qTom}=xXG@UM5`Hu}4fkL`WEEf2kkBuo)c^k^>aAX02Ors>RX3 zcr{QRHTLUqY!+`~!@>mZ>{7Cr+7QXc)e*kdOst4{7|yq}=UpN_-OWIa)mrJ+zR_s; znfV!HbZFHNHmjW>AWv>QkW(k?AS1)kAAhL}PtwnOUof8y>#v_|HZh1U<>g5_@ALTg zoCNeLfmr&lr^b_Yb+k051cRVMcp=EGkxAf77997cR`;a`^>w2U4>LKsIh?;KReY-KGpC+ z%NO{4{9nmA?X4j1sRL10II=wW%HX-6pf*|odg#62<&8wKwzBILDnA0Hq6bGuA6ZAL zorUf0w886_L9^C&6cwojDJX|+&aL>uY-1e$amvy5R5~C%QG4(GYL2UO1+;Q!uV3-W zCl&_ivYBb?mi6U~uH~@tmAGjvV@WOx4KC3FEiVQKaqyvUiKgdg8bf#_)X=8JT(i*< zQl)LsQ4ye(KVUmqWCcp5r1XIzjXN9fNfS$2B+=>v*L}jsGJQU??kl6nJ1DVE*;s<>M}D(p;EeUJ2 zv`;I2I5D)omEKzQ{Q)6lRC49px59+T#?G3vsQ@wi6k#bXmcG^z(HI!!>HM65We^sC zh~T)LG6yD^u8xG~s#UHGJ!sGjyVpvo84~EL0@B5~&d21hFMKuzTmYN=W?9KRL z_cd^rTTXX3e|9xBUEa!vg_?jeWx9uGr95x2n zgf(7aqr?5!>QB#s52RA$LI7|?dU_+E>r_UDzEsW|v&ouY07BE-TcuWt*3zP_j!Kv3 z?eufFs6bsCpjgn*>_~+7X3auTcM*1zWS|;y|Lf03JH_u0<|dA7R3VZR?L+Yo1Kn{e zN*ghb=PjKB06dXhiKEv>cFrkag9hO-W`HxU-}wp~E9I3i z(&&(KQ$tS57HG+-qWo*5*4vU);9zqir>5+7-nX)YU#Viw^FVqirrf!0o1htL zO5N9DiJGWqPB?72@Lk4vv^s2o$iYHsC@%SPXw^RXIv0NB;ZW_|lLh*%4P$rK6ze+T zoVZpN>qS)HGbFKhtZ)(mGJ5o-JNMJ`i>dQo#_*+b29`S98dE9daxlO(fkl5Ng z@GdSk1pph}lBX)5NyqQ~Ng^^$+}3=s9&^(;{IQ2joa@T+oxC@L90DW_UIYhn9ErBc zZ>^+xd0Po%A!ir;F+L1L7_lm&p;gg4YU1!@8}L*JpUZqFHX&h0<1D3&I?f=MHCgM> zP#M4_J?1FZpRCbr3zv|PM4OI_Y3C(L1yZ#|ul7(cE<%aGtw>s{l_lJe?ZT5>{c~^x z1)>(W#*us@&T#DvBSjnfYtG#q8|B!J_vYi@v6!iY+QxNSjS(E|RDZQ#6T5ovauGO+ zOi6)wJ;Hy(Fv>S!jHp4xJMeoyNpx>`2zMpXe+kcgr5+Ib`Prd35+4|(dXXLuCy8J} zrE^hPS^pHyR}UEQ7$pE5^B8id7Pxiyn`nS{n^FC*d8@kNNCLjdA9)(1pfJXL<7vRw zrt|uJ1$)~$P@r95zb*p3eH#L@fuNT>=94Glvk*vhx&(pqp2-TpZE#uXc&4U0Iysf; zpsH(IZe{}eloOnDa80eOo*$vl0z2)~lhSzs#a1AV`=uvfAOcs7!E55ElPwdibWVBM z46#Pxt(_C&u(=qMGeVwSc@<3Q7RXw!Bjwn@eIoY4mcNn`5h!VR6T{DrgvYokih%mY z=DOJ3v_G37!U!ZgOE|m?3YHi1kMiP+`hc*e?_I2V>>qjMFWLL)x0^=*0V~3UFNukd zH__AMFdb38zpHZB{%s3LCLhvS^0+uy4Ny-4T`BFk3fMXPREd?VLBIuo0Ig>|j{K>(b|+`Yvny&TfC6q* z+4~q|`am3}Ov@qiDO3puU$$&-A9XMo2D(k{P6w%hPNpq!og^w+g|B`C1G~Ll9JCE; z4wJ5#kE?9aitTk267ot2{(=HJNssmS`#!5gfv68ogs>vs>ct`BeP#CDho~Rx>!Wm@ zJ_YfuRzH*R5h#w`>XFsP@3oVoDXh-iHttrR_0Cv8$P~3L24?Vrg@qQ$}>CQ zM}RCsiqYSjS|4jnTV7GY9}c6_7!i9=CHaIZ#o`Ir!L*Abiu_&akNWE{OnbI3g2$9G zI;hnJM?t_3267m5EfwR>@eV<6rwH-(8z3?>^Y3eT`+Uc;rT?}XyVDM*VzVq8yW^HY zGOj&6*g_z%wk=VqT0Y#IJOE%UU{(Pzko_80<h-M^@@EGmd+&LP1GyLbxHJ?7m}h8c&6q*-YH%FLwSz23%(jBuLAKNX zg7HyLmj&c9chR+A;Q(|8Aih7iPf@@G^8QRfp1$K`-7ARF|G>v%R`Fui1aX)HZ#yb+ zwC*J$wAPOltQyy2Svz!z*6+piW-+W{aOZvRib~dOjnP6WqI^``y>CZ0#h@Okucv_L zb~gfj6YQGA1q{u#kCfEn3Z+gK8#};V>x|xCq10bXHfYgHPErH}a2#wyJ(Mdv(T0Yj z8D7g=`bJAQbond*dkEAOB-y|8yTc|gU|Kob7&$r70p1JnCz{t^inh*y;Ad2HbdM^v zzF5iP%#6k6+Z&}CASQW!qK{ikj=}#=;6l(k>cd7DLX%sJ{X8k`Fgrx99Z~_^U;9Ad z=%FQZzrX)2P3Mk)nws;oZwCh$;V=-U0!$4fp*#S63eaJ|CQwt1{~3O`&6^TjZ9b_4 zf_+8&n|Kg>MReq^%@66_1ucup%Y{u$a=wJrRNK?(B0xz1Z`gwWZ~gj?ymzep%h&}b zb_Pf;;L>4Lg7y-aCd*6xH$&%CCiw;QrDE7VO@098c&}T+hKBskNl8kGY46;pfPh2& zZ_-i#AzSYXbm%{!Bc>F*%Mj81CeupWb%~MH+L?uiWET=(Ne~4bclLNUjtYR|Ly4k> zu1{1Ofg19h&PxzDL@HY}nFd#YhT5n{j4~4|PusTS$4t5fUlCA}KKGX!MJX_?yJDWX zpRwyr|Ac~i_cRNs^2jqQdM7-)$ig>jp^b{^879hklcu)b}9AtREw)U!;&=Y9~m)%!-6usQY}#GmfkXz=7ojb_D@m& zn&ugkYGJB4pHE?CH<(8Uus@PO#bPD2OjL6>=A)E+HyysW@okfZDJG3-NVD1$LkSA=AU8NNkF^e2dZ_Mle<1Vrvm`>7hxZ>Gc{{WN#!hr zM76b-#>(AJxlNr(!=RY1^LpP3Zw>53rMWyrWo5sm0*HPBDy}z>i;LRJL#=Rg#N=A#g(nEK?e>nTSb* zbo9n21KYKuVRzHAnn<@ zyT@stx3OW+Ne2+sz`*^g!Ts|FD~+%)QeRG&D~{RDjkVfU>WB9U$6SF$N|~~el?jY% zZ!nMDbBoY*5CO8DKXP>52Uuy`VL9i8Rq@Q&>IiUtv;L(8-yu|N&-N%;aIrY)l#y?DB4iG}H;BBwix=2m9y|nrp(&ILJMP~* z5f6>O^a`eb66fjsRiirTNPthp0c4Rx;0iMT50e)doq>eh3C{b0SUEskfszi;_zo~U zeXUNRwGhK8#By8g)%5#qyQEAERbmHL71QHVFo49pzo(nM!@$vP6&S!VAgYx7&Q-22 z%5iwGUccUamI_6PcSOHFOvIUmZYo0fdG9*9wm66OXjg50ig5MZ-@D zBIm%^NF5s2nH?o+LZTK?7uPaW(ZHC$!Bkmo51jFnQRnm&leMw(TmU`p@vk%hR&37; zh3&mH%jE%d51|8ct^RB!pTehVk`m)nv;9ANd&laN*&AO`I_^-sTN$bw7*PBbRa8Wh zO}17KER}SY;cP5YLjjY>0hB+*8F3mkG;8S&{@Ck;G;~@#9d&h`jj1vwE0l^9YmC|D zM*ZTT&LA{VMWW?SF2@>`NPKb*JMMP?%#yj2FJ1Je}2}{fssbk5F*U z3WPNZ$wg%|Xef^+J2)Z{s8qf8H~;G)mVqKmd;b*N47B zpl5IJI0T0WV~7^)u8m`=M~Ap}2g7dOAd_X4mpu&~dwaXGiG{=UNO@@jgd`yi^RuL{ z_aggac+UkytB?0w`-?NV!#)y(j?wORfUAB!=?l>L2<^D;&0z@oS3Ap|g2+9<;}(pj zWNDsJ$J9>B(*eciGh8J0kCOfjBW5!BpgW*xV#|)Vy+jsS+*-ZiZ*&#J*Coe zr8pI3!veY36vj837#QeYHPPcaRApDhvq(9>PL?wo)r&IFKT% zwQlCUX6}DA(G0zx`VP;YYJGZJ&mTvEf=+brLvcZY9<;b!3a-vM<(4)2R%T4b87rRAFJPRifI!c|k zzMG|x)I=;Q=J|JMti%m=^;~_??&KRcKy+lNT-Hp0`B308)m2P&RQDfjMnhBqvI1p@ z*pP&@DCo7kkJFe!sKW5x>BCCw2zK4Kr;uLa0WvuyQqFx4MDH>Rt;DH@@bE^!g2ga` zk%BkGii-HnE>^ZHV`ud);ghk{W@ahuUTm`?hK6oYXRMyz)%n^d{9a&xm3F6F5L;An^=D<+<;o zXjz}h97FsVy$A8ZRV-%l?m2Gs>68wo(w2-R|0VST4{W09WHj3@D4`khRR(m1!-qww zlkr*`@@e)F88GYAHA*nDsaj^eeSKF%MX7U}=$EulTOye0?V_&!;36jMj^!eiYVmvl z0m?qUO=(Tfak$bqB01W80TG8O6_X?wp25OgYX}*6y3bsd;p~?$lA@liFO;oq5)v2> zvhm*xA=Dp4mKU_d!h?k%*FNg9SqYl)J7mRD`gM=W_=H}kIfFU~ga!x2-tkHup^@F2 zz`=JDX<-GecHk;3NVWG7wp5o`VZ%b425BHyE(II^AA3*k|JEi*rHUOb6_k;wYkR}q zQR3@oP!$1$UAORV;G|H~nVQBX4hAj0oA5h9{jnDcLO+4P?ef`Tm1xc7;s?&6%{S!p zb;A>tE?Aj1p>OZ~*N`iX;hzJy{#)^6=$D7+a-hKUFI{mL>+`Ozl!y|@o&aAb!cN4^WBPhb(Kwv*M9b5Gt2Z=e zTmycKw0q9*VX8g%u4trkoC15wN}Iag4%XE9u9{YQd1#$O1F=|(qK#b?!SYNm23mmL?;FjQXJqM6Mko^Gl z0jok|oxpF1(LZ^4H9x^ZsyXJ+H;MEADDT&*1-WdVWwdDEfKc$%yZ1S1 z61+VUPT|S$Tf@h>d7t3E+6HyyoxSI3_n(2@@_r@_o&M88O*r2ga9GR@ZHKI1H6|P% z7_?7&IvZK{y1zPGQVJV0_H}0FT_ z5xa*|K0}YoMJR=~d3|}~wX%k)>}gI2V*Cvk+j*zh-qKTeUKcHt8BuzB%~xCMyw^IU zo=w|gmS+;#H!e-Ye?LBACtdHFi^_GYyUB=7m;8ALqiLtHl*;?l=^S3+wT`v0H4$N- zlQ=Plkw1xe{`;-ErVx94)ulmhHpP8enbC)U%A&vF!;{3-Zm51idXSj^c<56!pDLJ7 zfbA;k&kPgYxCH&rrDu=1iX0ki-VPqj-Hk3houxc8#t1%V(QGt_`43apwV=wWji9xh|z(esE3)HAog_$mh%eRg8r?zypSh3pq##TWH|n;wo0(H zPygci`hJT`B`tPrp!YP!cy2l<=#P4PtHf;14vM{X)^$t{okPBD{&1#9?o@zSxWT|l zu5Pi{sw;_&)qY(^&lDlMRY#Kb`xC#otl7EEH>bL<(WDw5%jmP~pYl3}O`v?FHt2z& zDCV}IWly`mnUsJ9VG_&_DaQI98@(K71h3%xY+D!X>548~$+RfEUFxU(wpne=Y@jGKjBq~jr4|VuJvNLkzs`06#Xv|q)Ej)GmnwmJdG{nycacOGC$8&^lg3GwkF8# zwh#ZqwuF=m7cML@gJQ?b)e%g{^l3BR^i-ieu6$7^&EB?$@ZuwcTT|=Ro#l?E5?!gV z<^qzck~vA&6?PdmDca>Dak4yu6;!@-OkE{)n{eINo_HY>h6B_<;2N}Hw3AK3-twb{1P6cnur4#D--vrqa=QVs-`hQZPm^AH|e z78aI>Uv_fBeXqRYg0t^`j9HEti=`EG|p{$q!vJ zyQTt}{YpXwtnTLXsi~LK=hDT65IW6*QZ&2H=8l0(zzjreB+zsDRo@Y1>a+lls-lZj zK)4~Q5b~Se@(tC@iG9C4*)Ho4)iHA}*Gmv1L$3bdN4M?OdFL#hCE~`; zd&|1jBC{Ie{^NBtr@&U9Vv7fY%=ilkF2f3UR`z#8HRFozo~^O$rK+{7;Zvgte~~&3 zSefpGmz)C}Q3Rx!3U$<1K78$~YB4c`mc%zRj^sJ6w&+XOrCvN#(z!OL&0`x{WJX*m z7V374J{_F=iy;V-5(dp_hu!unhPVP7CDtoFkjYZnhX0K}bqScK|(W;fVkBfkc zeSpP^Z*QVCL;oF(NYDu2wO=B6dQ46Cb{G0t?vDAs~Vut-2y%gCYs4c<@3@N z?#?fgANR;_(ktnX=Tg&EF?TNCSUjGVD{l(@-*@@iwzQzrJw_H5O-S zw{*F$TzR%aAy7|)=n$L9b4M1+Pr3ZAC55YCgG_d<#O6a;d{R=(^r@dWW5bo3r4|Gb zhwWT$r}X*9^}2S;+OMs%eCrgbPb_g_HHuZ|F(}0PDT^{umxKA|6{}BjwTWp=%7RjL zFNLb<=I=||4~hbq)r<9Kxp|4D>Xw5lp#q3iW{6hA>SrRs2%Bhr*?H;@O@pyVDYK35 zHT7P8N+xcVsbFiJDQB!E%jRgm_P)};JLY2fbbWIWBLj?Br%Q3Tn=q9QhBc<6NIM@^ zc%$jf0elwJ4!g{3?%-O8aa_$;Q|V^7=5tjdIuZGMk~S1Uv)UwKLokRluoL(<9MxbY zG`@YCFlMw+8~lkw$I1R>ve5WcC$ayy@|+OU^NBz+_4Tjjnm3ot znqogq+)5x)r_Tpyei5uw9)_3Kh^_7g5238-pfNUu)HL}A2;RSvs(NK)%-V9{rT8wbY&Y#p^n2lGR?2Ha)1_X~%%Zz%xVeX7*FDdTV zT3*mK*mRPsOk%e}%?jB%6Z6_;-}{R!rN4*LY4_y9h~g8B?$~#kvH;CeSKjO8xh2@j zP&*fmNe%x;k39IzTaL9=R!qZ7=cVI;LGA;GE9VDX7uOJ+0yXJ^IhohOVg$J&G0z#S zeW(kBaV7TWumk9G%?U#It#KS2+0Z-f-$09&3$ElSxf5o zV+5QrtNCOb7UZqNrojzw3wYRXQ0{qB(~%RJXb> zo`RiPD(_k3m3T>m7FF$lzhf#7)w9uzf80>@Jhi*!dbPLwCI_>`Ha|HDS2{tu z+Oa=gALFl%BWOtVc)r-p6I@Lmx+GBOoA{ue%mWlU8jZcM2Qn5Bx#7x6@vod7 zmr2od87tHRn_+b%ZdnC9ZEJ6@t_fPbI~pV763xKjgI*Ymdd55+NN+IfS|<4N^9l)J z41f`hOH(ti#qZW78M-PvJ92@-=b8QSpbX~tA*Jg5N z;Pc0vv$rTC37ulkxdog9z#9OjUhqkVM`)z3_A!aNynOYFL0}XdQCah6TV&q65%Etr z!s^DD0p6r%2@r&xHDL1+0}%{x!r)qBo~;U@3^B{g9)p#eS=2tb^F_u z%}K5T(1AaFGO7{hq}>bg!asmF!|1+Rhljx8s}Mgko7Z&hS1 zsa9iFvWM-|bw961n8w zn)T$0)bKuei*~qf!(3&E@&dCRt1f@aw-v*xi}&e-Nm@S2$sU@MaF-L`^npBmYxrk> z29NWP4>_CoFsTW@`Um@^;rnFzgwyzKpkFWS#1ipDgwim5J2HE5Be~}%dR+!k_$+U| z@^I+D80SqFI%F*RH9f$hfkYkoC(oYslYmHdiWro+bdm+Imt2I}G_nVqKgE7JRebRT zqZ2Zg?4;;_JrfXeenahyP3sFPE;+@4wl}Zp!8J~AfGnkskAvXywuwn2$~-#t8~7_K zFqo&zRqo4@H=k4mHRC#jk2ZSosp-6OJed&kA`tW+^JMZmf3l#{8HZ=>0h_c^Manlo zU3yXA2oH?!gY$amC>3@@5y79-QWT2Aam89^T>{@Q$OE2RIDe1)!V-F=di8U;vLO6v zR#gT9BR-zsS%;!KQ3cHse|GZW@bJQt8u`$Uc99lHdXJ42RZDhKe~AWNR#OM}2r-4{ zg7oCFhK~FOo)5jpSl^ne4mr;Z(lI;`8mZ0Gpu1_NR`%&p)+(LzahgK-pf`Z^DoXP9 z_BMypP1I{)mqt@5a`5K>%X;)8j`Jv(^hnb%F37rkVJ7JF_A6G0zo6E^`MZ|M`dT26 z@W#mKT_J{x>nJ4jp4+SMkg?KPj~}y8PITh8O7VQi6X=yxdu#Remt36MRpP;sfq{W6 zEYrN&m!maAe~2p_HH#QR_zvs*F9!&D-RPd7oEs2j>#@n*PigBwnxV^~WJ>qM9;FL( zOKGB?ZrfU(62__F_k=@YT zoG!HplZeEk{&MpcCR)!A-`Rl?V$i}JvR7aEp?S#|6vY(Dw!@$>d~c3L_8Bt2kUJzuii}HB(39PeWVRY9A$3b?Qc{}4MN@d-R^XFbUtLNIV!!z#jr~8EEH_kPzK{8GqhmZ5_ULxHE!F z%iE7kirv|VEVm2}3zMnB0Wnpl+((f?aqmd$qicRASky@E2wOLZqkTJU zn+PA3namF?-t}PwhutqII2{iUFOX>|__O47KwHTNO4Ta;1&D{trZrTqFPh diff --git a/circuit-breaker/etc/circuit-breaker.urm.puml b/circuit-breaker/etc/circuit-breaker.urm.puml index 214719002..8128c539a 100644 --- a/circuit-breaker/etc/circuit-breaker.urm.puml +++ b/circuit-breaker/etc/circuit-breaker.urm.puml @@ -5,32 +5,51 @@ package com.iluwatar.circuitbreaker { + App() + main(args : String[]) {static} } - class CircuitBreaker { + interface CircuitBreaker { + + attemptRequest() : String {abstract} + + getState() : String {abstract} + + recordFailure() {abstract} + + recordSuccess() {abstract} + + setState(State) {abstract} + } + class DefaultCircuitBreaker { ~ failureCount : int - failureThreshold : int - futureTime : long ~ lastFailureTime : long - retryTimePeriod : long + - service : RemoteService - state : State - timeout : long - ~ CircuitBreaker(timeout : long, failureThreshold : int, retryTimePeriod : long) - + call(serviceToCall : String, serverStartTime : long) : String + ~ DefaultCircuitBreaker(serviceToCall : RemoteService, timeout : long, failureThreshold : int, retryTimePeriod : long) + + attemptRequest() : String + # evaluateState() + getState() : String - - recordFailure() - - reset() - # setState() - + setStateForBypass(state : State) + + recordFailure() + + recordSuccess() + + setState(state : State) } - class DelayedService { + class DelayedRemoteService { - delay : int - + DelayedService() - + DelayedService(delay : int) - + response(serverStartTime : long) : String + - serverStartTime : long + + DelayedRemoteService() + + DelayedRemoteService(serverStartTime : long, delay : int) + + call() : String } class MonitoringService { - + MonitoringService() + - delayedService : CircuitBreaker + - quickService : CircuitBreaker + + MonitoringService(delayedService : CircuitBreaker, quickService : CircuitBreaker) + + delayedServiceResponse() : String + localResourceResponse() : String - + remoteResourceResponse(circuitBreaker : CircuitBreaker, serverStartTime : long) : String + + quickServiceResponse() : String + } + class QuickRemoteService { + + QuickRemoteService() + + call() : String + } + interface RemoteService { + + call() : String {abstract} } enum State { + CLOSED {static} @@ -40,5 +59,10 @@ package com.iluwatar.circuitbreaker { + values() : State[] {static} } } -CircuitBreaker --> "-state" State +DefaultCircuitBreaker --> "-state" State +MonitoringService --> "-delayedService" CircuitBreaker +DefaultCircuitBreaker --> "-service" RemoteService +DefaultCircuitBreaker ..|> CircuitBreaker +DelayedRemoteService ..|> RemoteService +QuickRemoteService ..|> RemoteService @enduml \ No newline at end of file diff --git a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/App.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/App.java index c3465d801..0a88e4e5a 100644 --- a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/App.java +++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/App.java @@ -36,17 +36,18 @@ import org.slf4j.LoggerFactory; * operational again, so that we can use it *

*

- * In this example, the circuit breaker pattern is demonstrated by using two services: {@link - * MonitoringService} and {@link DelayedService}. The monitoring service is responsible for calling - * two services: a local service and a remote service {@link DelayedService} , and by using the - * circuit breaker construction we ensure that if the call to remote service is going to fail, we - * are going to save our resources and not make the function call at all, by wrapping our call to - * the remote service in the circuit breaker object. + * In this example, the circuit breaker pattern is demonstrated by using three services: {@link + * DelayedRemoteService}, {@link QuickRemoteService} and {@link MonitoringService}. The monitoring + * service is responsible for calling three services: a local service, a quick remove service + * {@link QuickRemoteService} and a delayed remote service {@link DelayedRemoteService} , and by + * using the circuit breaker construction we ensure that if the call to remote service is going to + * fail, we are going to save our resources and not make the function call at all, by wrapping our + * call to the remote services in the {@link DefaultCircuitBreaker} implementation object. *

*

- * This works as follows: The {@link CircuitBreaker} object can be in one of three states: - * Open, Closed and Half-Open, which represents the real world circuits. If the - * state is closed (initial), we assume everything is alright and perform the function call. + * This works as follows: The {@link DefaultCircuitBreaker} object can be in one of three states: + * Open, Closed and Half-Open, which represents the real world circuits. If + * the state is closed (initial), we assume everything is alright and perform the function call. * However, every time the call fails, we note it and once it crosses a threshold, we set the state * to Open, preventing any further calls to the remote server. Then, after a certain retry period * (during which we expect thee service to recover), we make another call to the remote server and @@ -63,22 +64,51 @@ public class App { * * @param args command line args */ - @SuppressWarnings("squid:S2189") public static void main(String[] args) { - //Create an object of monitoring service which makes both local and remote calls - var obj = new MonitoringService(); - //Set the circuit Breaker parameters - var circuitBreaker = new CircuitBreaker(3000, 1, 2000 * 1000 * 1000); + var serverStartTime = System.nanoTime(); - while (true) { - LOGGER.info(obj.localResourceResponse()); - LOGGER.info(obj.remoteResourceResponse(circuitBreaker, serverStartTime)); - LOGGER.info(circuitBreaker.getState()); - try { - Thread.sleep(5 * 1000); - } catch (InterruptedException e) { - LOGGER.error(e.getMessage()); - } + + var delayedService = new DelayedRemoteService(serverStartTime, 5); + //Set the circuit Breaker parameters + var delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000, 2, + 2000 * 1000 * 1000); + + var quickService = new QuickRemoteService(); + //Set the circuit Breaker parameters + var quickServiceCircuitBreaker = new DefaultCircuitBreaker(quickService, 3000, 2, + 2000 * 1000 * 1000); + + //Create an object of monitoring service which makes both local and remote calls + var monitoringService = new MonitoringService(delayedServiceCircuitBreaker, + quickServiceCircuitBreaker); + + //Fetch response from local resource + LOGGER.info(monitoringService.localResourceResponse()); + + //Fetch response from delayed service 2 times, to meet the failure threshold + LOGGER.info(monitoringService.delayedServiceResponse()); + LOGGER.info(monitoringService.delayedServiceResponse()); + + //Fetch current state of delayed service circuit breaker after crossing failure threshold limit + //which is OPEN now + LOGGER.info(delayedServiceCircuitBreaker.getState()); + + //Meanwhile, the delayed service is down, fetch response from the healthy quick service + LOGGER.info(monitoringService.quickServiceResponse()); + LOGGER.info(quickServiceCircuitBreaker.getState()); + + //Wait for the delayed service to become responsive + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); } + //Check the state of delayed circuit breaker, should be HALF_OPEN + LOGGER.info(delayedServiceCircuitBreaker.getState()); + + //Fetch response from delayed service, which should be healthy by now + LOGGER.info(monitoringService.delayedServiceResponse()); + //As successful response is fetched, it should be CLOSED again. + LOGGER.info(delayedServiceCircuitBreaker.getState()); } } diff --git a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/CircuitBreaker.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/CircuitBreaker.java index 18268b1ce..b05e72104 100644 --- a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/CircuitBreaker.java +++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/CircuitBreaker.java @@ -24,114 +24,22 @@ package com.iluwatar.circuitbreaker; /** - * The circuit breaker class with all configurations. + * The Circuit breaker interface. */ -public class CircuitBreaker { - private final long timeout; - private final long retryTimePeriod; - long lastFailureTime; - int failureCount; - private final int failureThreshold; - private State state; - private final long futureTime = 1000 * 1000 * 1000 * 1000; +public interface CircuitBreaker { - /** - * Constructor to create an instance of Circuit Breaker. - * - * @param timeout Timeout for the API request. Not necessary for this simple example - * @param failureThreshold Number of failures we receive from the depended service before changing - * state to 'OPEN' - * @param retryTimePeriod Time period after which a new request is made to remote service for - * status check. - */ - CircuitBreaker(long timeout, int failureThreshold, long retryTimePeriod) { - // We start in a closed state hoping that everything is fine - this.state = State.CLOSED; - this.failureThreshold = failureThreshold; - // Timeout for the API request. - // Used to break the calls made to remote resource if it exceeds the limit - this.timeout = timeout; - this.retryTimePeriod = retryTimePeriod; - //An absurd amount of time in future which basically indicates the last failure never happened - this.lastFailureTime = System.nanoTime() + futureTime; - this.failureCount = 0; - } + //Success response. Reset everything to defaults + void recordSuccess(); - //Reset everything to defaults - private void reset() { - this.failureCount = 0; - this.lastFailureTime = System.nanoTime() + futureTime; - this.state = State.CLOSED; - } + //Failure response. Handle accordingly and change state if required. + void recordFailure(); - private void recordFailure() { - failureCount = failureCount + 1; - this.lastFailureTime = System.nanoTime(); - } + //Get the current state of circuit breaker + String getState(); - protected void setState() { - if (failureCount > failureThreshold) { //Then something is wrong with remote service - if ((System.nanoTime() - lastFailureTime) > retryTimePeriod) { - //We have waited long enough and should try checking if service is up - state = State.HALF_OPEN; - } else { - //Service would still probably be down - state = State.OPEN; - } - } else { - //Everything is working fine - state = State.CLOSED; - } - } + //Set the specific state manually. + void setState(State state); - public String getState() { - return state.name(); - } - - /** - * Break the circuit beforehand if it is known service is down Or connect the circuit manually if - * service comes online before expected. - * - * @param state State at which circuit is in - */ - public void setStateForBypass(State state) { - this.state = state; - } - - /** - * Executes service call. - * - * @param serviceToCall The name of the service in String. Can be changed to data URLs in case - * of web applications - * @param serverStartTime Time at which actual server was started which makes calls to this - * service - * @return Value from the remote resource, stale response or a custom exception - */ - public String call(String serviceToCall, long serverStartTime) throws Exception { - setState(); - if (state == State.OPEN) { - // return cached response if no the circuit is in OPEN state - return "This is stale response from API"; - } else { - // Make the API request if the circuit is not OPEN - if (serviceToCall.equals("delayedService")) { - var delayedService = new DelayedService(20); - var response = delayedService.response(serverStartTime); - //In a real application, this would be run in a thread and the timeout - //parameter of the circuit breaker would be utilized to know if service - //is working. Here, we simulate that based on server response itself - if (response.split(" ")[3].equals("working")) { - // Yay!! the API responded fine. Let's reset everything. - reset(); - return response; - } else { - // Uh-oh!! the call still failed. Let's update that in our records. - recordFailure(); - throw new Exception("Remote service not responding"); - } - } else { - throw new Exception("Unknown Service Name"); - } - } - } -} \ No newline at end of file + //Attempt to fetch response from the remote service. + String attemptRequest() throws RemoteServiceException; +} diff --git a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DefaultCircuitBreaker.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DefaultCircuitBreaker.java new file mode 100644 index 000000000..1d48c142a --- /dev/null +++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DefaultCircuitBreaker.java @@ -0,0 +1,153 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.circuitbreaker; + +/** + * The delay based Circuit breaker implementation that works in a + * CLOSED->OPEN-(retry_time_period)->HALF_OPEN->CLOSED flow with some retry time period for failed + * services and a failure threshold for service to open + * circuit. + */ +public class DefaultCircuitBreaker implements CircuitBreaker { + + private final long timeout; + private final long retryTimePeriod; + private final RemoteService service; + long lastFailureTime; + int failureCount; + private final int failureThreshold; + private State state; + private final long futureTime = 1000 * 1000 * 1000 * 1000; + + /** + * Constructor to create an instance of Circuit Breaker. + * + * @param timeout Timeout for the API request. Not necessary for this simple example + * @param failureThreshold Number of failures we receive from the depended service before changing + * state to 'OPEN' + * @param retryTimePeriod Time period after which a new request is made to remote service for + * status check. + */ + DefaultCircuitBreaker(RemoteService serviceToCall, long timeout, int failureThreshold, + long retryTimePeriod) { + this.service = serviceToCall; + // We start in a closed state hoping that everything is fine + this.state = State.CLOSED; + this.failureThreshold = failureThreshold; + // Timeout for the API request. + // Used to break the calls made to remote resource if it exceeds the limit + this.timeout = timeout; + this.retryTimePeriod = retryTimePeriod; + //An absurd amount of time in future which basically indicates the last failure never happened + this.lastFailureTime = System.nanoTime() + futureTime; + this.failureCount = 0; + } + + //Reset everything to defaults + @Override + public void recordSuccess() { + this.failureCount = 0; + this.lastFailureTime = System.nanoTime() + futureTime; + this.state = State.CLOSED; + } + + @Override + public void recordFailure() { + failureCount = failureCount + 1; + this.lastFailureTime = System.nanoTime(); + } + + //Evaluate the current state based on failureThreshold, failureCount and lastFailureTime. + protected void evaluateState() { + if (failureCount >= failureThreshold) { //Then something is wrong with remote service + if ((System.nanoTime() - lastFailureTime) > retryTimePeriod) { + //We have waited long enough and should try checking if service is up + state = State.HALF_OPEN; + } else { + //Service would still probably be down + state = State.OPEN; + } + } else { + //Everything is working fine + state = State.CLOSED; + } + } + + @Override + public String getState() { + evaluateState(); + return state.name(); + } + + /** + * Break the circuit beforehand if it is known service is down Or connect the circuit manually if + * service comes online before expected. + * + * @param state State at which circuit is in + */ + @Override + public void setState(State state) { + this.state = state; + switch (state) { + case OPEN: + this.failureCount = failureThreshold; + this.lastFailureTime = System.nanoTime(); + break; + case HALF_OPEN: + this.failureCount = failureThreshold; + this.lastFailureTime = System.nanoTime() - retryTimePeriod; + break; + default: + this.failureCount = 0; + } + } + + /** + * Executes service call. + * + * @return Value from the remote resource, stale response or a custom exception + */ + @Override + public String attemptRequest() throws RemoteServiceException { + evaluateState(); + if (state == State.OPEN) { + // return cached response if no the circuit is in OPEN state + return "This is stale response from API"; + } else { + // Make the API request if the circuit is not OPEN + try { + //In a real application, this would be run in a thread and the timeout + //parameter of the circuit breaker would be utilized to know if service + //is working. Here, we simulate that based on server response itself + var response = service.call(); + // Yay!! the API responded fine. Let's reset everything. + recordSuccess(); + return response; + } catch (RemoteServiceException ex) { + recordFailure(); + throw ex; + } + } + } +} diff --git a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DelayedService.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DelayedRemoteService.java similarity index 81% rename from circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DelayedService.java rename to circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DelayedRemoteService.java index 13861923b..f4add64fc 100644 --- a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DelayedService.java +++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DelayedRemoteService.java @@ -27,7 +27,9 @@ package com.iluwatar.circuitbreaker; * This simulates the remote service It responds only after a certain timeout period (default set to * 20 seconds). */ -public class DelayedService { +public class DelayedRemoteService implements RemoteService { + + private final long serverStartTime; private final int delay; /** @@ -35,22 +37,23 @@ public class DelayedService { * * @param delay the delay after which service would behave properly, in seconds */ - public DelayedService(int delay) { + public DelayedRemoteService(long serverStartTime, int delay) { + this.serverStartTime = serverStartTime; this.delay = delay; } - public DelayedService() { - this.delay = 60; + public DelayedRemoteService() { + this.serverStartTime = System.nanoTime(); + this.delay = 20; } /** * Responds based on delay, current time and server start time if the service is down / working. * - * @param serverStartTime Time at which actual server was started which makes calls to this - * service * @return The state of the service */ - public String response(long serverStartTime) { + @Override + public String call() throws RemoteServiceException { var currentTime = System.nanoTime(); //Since currentTime and serverStartTime are both in nanoseconds, we convert it to //seconds by diving by 10e9 and ensure floating point division by multiplying it @@ -58,9 +61,8 @@ public class DelayedService { //send the reply if ((currentTime - serverStartTime) * 1.0 / (1000 * 1000 * 1000) < delay) { //Can use Thread.sleep() here to block and simulate a hung server - return "Delayed service is down"; - } else { - return "Delayed service is working"; + throw new RemoteServiceException("Delayed service is down"); } + return "Delayed service is working"; } } diff --git a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/MonitoringService.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/MonitoringService.java index e91367175..3fb8d8396 100644 --- a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/MonitoringService.java +++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/MonitoringService.java @@ -24,28 +24,47 @@ package com.iluwatar.circuitbreaker; /** - * The service class which makes local and remote calls Uses {@link CircuitBreaker} object to ensure - * remote calls don't use up resources. + * The service class which makes local and remote calls Uses {@link DefaultCircuitBreaker} object to + * ensure remote calls don't use up resources. */ public class MonitoringService { + private final CircuitBreaker delayedService; + + private final CircuitBreaker quickService; + + public MonitoringService(CircuitBreaker delayedService, CircuitBreaker quickService) { + this.delayedService = delayedService; + this.quickService = quickService; + } + //Assumption: Local service won't fail, no need to wrap it in a circuit breaker logic public String localResourceResponse() { return "Local Service is working"; } /** - * Try to get result from remote server. + * Fetch response from the delayed service (with some simulated startup time). * - * @param circuitBreaker The circuitBreaker object with all parameters - * @param serverStartTime Time at which actual server was started which makes calls to this - * service - * @return result from the remote response or exception raised by it. + * @return response string */ - public String remoteResourceResponse(CircuitBreaker circuitBreaker, long serverStartTime) { + public String delayedServiceResponse() { try { - return circuitBreaker.call("delayedService", serverStartTime); - } catch (Exception e) { + return this.delayedService.attemptRequest(); + } catch (RemoteServiceException e) { + return e.getMessage(); + } + } + + /** + * Fetches response from a healthy service without any failure. + * + * @return response string + */ + public String quickServiceResponse() { + try { + return this.quickService.attemptRequest(); + } catch (RemoteServiceException e) { return e.getMessage(); } } diff --git a/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/DelayedServiceTest.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/QuickRemoteService.java similarity index 77% rename from circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/DelayedServiceTest.java rename to circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/QuickRemoteService.java index af747f794..1b73bc0e3 100644 --- a/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/DelayedServiceTest.java +++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/QuickRemoteService.java @@ -23,19 +23,13 @@ package com.iluwatar.circuitbreaker; -import static org.junit.jupiter.api.Assertions.assertEquals; - -import org.junit.jupiter.api.Test; - /** - * Monitoring Service test + * A quick response remote service, that responds healthy without any delay or failure. */ -public class DelayedServiceTest { +public class QuickRemoteService implements RemoteService { - //Improves code coverage - @Test - public void testDefaultConstructor() { - var obj = new DelayedService(); - assertEquals(obj.response(System.nanoTime()), "Delayed service is down"); + @Override + public String call() throws RemoteServiceException { + return "Quick Service is working"; } } diff --git a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/RemoteService.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/RemoteService.java new file mode 100644 index 000000000..3c2fd9c78 --- /dev/null +++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/RemoteService.java @@ -0,0 +1,34 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.circuitbreaker; + +/** + * The Remote service interface, used by {@link CircuitBreaker} for fetching response from remote + * services. + */ +public interface RemoteService { + + //Fetch response from remote service. + String call() throws RemoteServiceException; +} diff --git a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/RemoteServiceException.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/RemoteServiceException.java new file mode 100644 index 000000000..f384f5a41 --- /dev/null +++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/RemoteServiceException.java @@ -0,0 +1,11 @@ +package com.iluwatar.circuitbreaker; + +/** + * Exception thrown when {@link RemoteService} does not respond successfully. + */ +public class RemoteServiceException extends Exception { + + public RemoteServiceException(String message) { + super(message); + } +} diff --git a/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/AppTest.java b/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/AppTest.java new file mode 100644 index 000000000..3b4041103 --- /dev/null +++ b/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/AppTest.java @@ -0,0 +1,129 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.circuitbreaker; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * App Test showing usage of circuit breaker. + */ +public class AppTest { + + //Startup delay for delayed service (in seconds) + private static final int STARTUP_DELAY = 4; + + //Number of failed requests for circuit breaker to open + private static final int FAILURE_THRESHOLD = 1; + + //Time period in seconds for circuit breaker to retry service + private static final int RETRY_PERIOD = 2; + + private MonitoringService monitoringService; + + private CircuitBreaker delayedServiceCircuitBreaker; + + private CircuitBreaker quickServiceCircuitBreaker; + + /** + * Setup the circuit breakers and services, where {@link DelayedRemoteService} will be start with + * a delay of 4 seconds and a {@link QuickRemoteService} responding healthy. Both services are + * wrapped in a {@link DefaultCircuitBreaker} implementation with failure threshold of 1 failure + * and retry time period of 2 seconds. + */ + @BeforeEach + public void setupCircuitBreakers() { + var delayedService = new DelayedRemoteService(System.nanoTime(), STARTUP_DELAY); + //Set the circuit Breaker parameters + delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000, + FAILURE_THRESHOLD, + RETRY_PERIOD * 1000 * 1000 * 1000); + + var quickService = new QuickRemoteService(); + //Set the circuit Breaker parameters + quickServiceCircuitBreaker = new DefaultCircuitBreaker(quickService, 3000, FAILURE_THRESHOLD, + RETRY_PERIOD * 1000 * 1000 * 1000); + + monitoringService = new MonitoringService(delayedServiceCircuitBreaker, + quickServiceCircuitBreaker); + + } + + @Test + public void testFailure_OpenStateTransition() { + //Calling delayed service, which will be unhealthy till 4 seconds + assertEquals("Delayed service is down", monitoringService.delayedServiceResponse()); + //As failure threshold is "1", the circuit breaker is changed to OPEN + assertEquals("OPEN", delayedServiceCircuitBreaker.getState()); + //As circuit state is OPEN, we expect a quick fallback response from circuit breaker. + assertEquals("This is stale response from API", monitoringService.delayedServiceResponse()); + + //Meanwhile, the quick service is responding and the circuit state is CLOSED + assertEquals("Quick Service is working", monitoringService.quickServiceResponse()); + assertEquals("CLOSED", quickServiceCircuitBreaker.getState()); + + } + + @Test + public void testFailure_HalfOpenStateTransition() { + //Calling delayed service, which will be unhealthy till 4 seconds + assertEquals("Delayed service is down", monitoringService.delayedServiceResponse()); + //As failure threshold is "1", the circuit breaker is changed to OPEN + assertEquals("OPEN", delayedServiceCircuitBreaker.getState()); + + //Waiting for recovery period of 2 seconds for circuit breaker to retry service. + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + //After 2 seconds, the circuit breaker should move to "HALF_OPEN" state and retry fetching response from service again + assertEquals("HALF_OPEN", delayedServiceCircuitBreaker.getState()); + + } + + @Test + public void testRecovery_ClosedStateTransition() { + //Calling delayed service, which will be unhealthy till 4 seconds + assertEquals("Delayed service is down", monitoringService.delayedServiceResponse()); + //As failure threshold is "1", the circuit breaker is changed to OPEN + assertEquals("OPEN", delayedServiceCircuitBreaker.getState()); + + //Waiting for 4 seconds, which is enough for DelayedService to become healthy and respond successfully. + try { + Thread.sleep(4000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + //As retry period is 2 seconds (<4 seconds of wait), hence the circuit breaker should be back in HALF_OPEN state. + assertEquals("HALF_OPEN", delayedServiceCircuitBreaker.getState()); + //Check the success response from delayed service. + assertEquals("Delayed service is working", monitoringService.delayedServiceResponse()); + //As the response is success, the state should be CLOSED + assertEquals("CLOSED", delayedServiceCircuitBreaker.getState()); + } + +} diff --git a/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/CircuitBreakerTest.java b/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/DefaultCircuitBreakerTest.java similarity index 70% rename from circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/CircuitBreakerTest.java rename to circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/DefaultCircuitBreakerTest.java index 98b59a6ae..4d300b36f 100644 --- a/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/CircuitBreakerTest.java +++ b/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/DefaultCircuitBreakerTest.java @@ -25,56 +25,60 @@ package com.iluwatar.circuitbreaker; import static org.junit.jupiter.api.Assertions.assertEquals; +import java.rmi.Remote; import org.junit.jupiter.api.Test; /** * Circuit Breaker test */ -public class CircuitBreakerTest { +public class DefaultCircuitBreakerTest { //long timeout, int failureThreshold, long retryTimePeriod @Test - public void testSetState() { - var circuitBreaker = new CircuitBreaker(1, 1, 100); + public void testEvaluateState() { + var circuitBreaker = new DefaultCircuitBreaker(null, 1, 1, 100); //Right now, failureCountfailureThreshold, and lastFailureTime is nearly equal to current time, //state should be half-open assertEquals(circuitBreaker.getState(), "HALF_OPEN"); //Since failureCount>failureThreshold, and lastFailureTime is much lesser current time, //state should be open circuitBreaker.lastFailureTime = System.nanoTime() - 1000 * 1000 * 1000 * 1000; - circuitBreaker.setState(); + circuitBreaker.evaluateState(); assertEquals(circuitBreaker.getState(), "OPEN"); //Now set it back again to closed to test idempotency circuitBreaker.failureCount = 0; - circuitBreaker.setState(); + circuitBreaker.evaluateState(); assertEquals(circuitBreaker.getState(), "CLOSED"); } @Test public void testSetStateForBypass() { - var circuitBreaker = new CircuitBreaker(1, 1, 100); + var circuitBreaker = new DefaultCircuitBreaker(null, 1, 1, 2000 * 1000 * 1000); //Right now, failureCount { + var obj = new DelayedRemoteService(); + obj.call(); + }); + } + + /** + * Testing server started in past (2 seconds ago) and with a simulated delay of 1 second. + * + * @throws RemoteServiceException + */ + @Test + public void testParameterizedConstructor() throws RemoteServiceException { + var obj = new DelayedRemoteService(System.nanoTime()-2000*1000*1000,1); + assertEquals("Delayed service is working",obj.call()); + } +} diff --git a/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/MonitoringServiceTest.java b/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/MonitoringServiceTest.java index 71ede3e1c..f6b802b96 100644 --- a/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/MonitoringServiceTest.java +++ b/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/MonitoringServiceTest.java @@ -35,28 +35,45 @@ public class MonitoringServiceTest { //long timeout, int failureThreshold, long retryTimePeriod @Test public void testLocalResponse() { - var monitoringService = new MonitoringService(); + var monitoringService = new MonitoringService(null,null); var response = monitoringService.localResourceResponse(); assertEquals(response, "Local Service is working"); } @Test - public void testRemoteResponse() { - var monitoringService = new MonitoringService(); - var circuitBreaker = new CircuitBreaker(1, 1, 100); + public void testDelayedRemoteResponseSuccess() { + var delayedService = new DelayedRemoteService(System.nanoTime()-2*1000*1000*1000, 2); + var delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000, + 1, + 2 * 1000 * 1000 * 1000); + + var monitoringService = new MonitoringService(delayedServiceCircuitBreaker,null); //Set time in past to make the server work - var serverStartTime = System.nanoTime() / 10; - var response = monitoringService.remoteResourceResponse(circuitBreaker, serverStartTime); + var response = monitoringService.delayedServiceResponse(); assertEquals(response, "Delayed service is working"); } @Test - public void testRemoteResponse2() { - var monitoringService = new MonitoringService(); - var circuitBreaker = new CircuitBreaker(1, 1, 100); + public void testDelayedRemoteResponseFailure() { + var delayedService = new DelayedRemoteService(System.nanoTime(), 2); + var delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000, + 1, + 2 * 1000 * 1000 * 1000); + var monitoringService = new MonitoringService(delayedServiceCircuitBreaker,null); //Set time as current time as initially server fails - var serverStartTime = System.nanoTime(); - var response = monitoringService.remoteResourceResponse(circuitBreaker, serverStartTime); - assertEquals(response, "Remote service not responding"); + var response = monitoringService.delayedServiceResponse(); + assertEquals(response, "Delayed service is down"); + } + + @Test + public void testQuickRemoteServiceResponse() { + var delayedService = new QuickRemoteService(); + var delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000, + 1, + 2 * 1000 * 1000 * 1000); + var monitoringService = new MonitoringService(delayedServiceCircuitBreaker,null); + //Set time as current time as initially server fails + var response = monitoringService.delayedServiceResponse(); + assertEquals(response, "Quick Service is working"); } } diff --git a/pom.xml b/pom.xml index 102668588..2e1f904c3 100644 --- a/pom.xml +++ b/pom.xml @@ -63,7 +63,7 @@ Java Design Patterns - abstract-factory + circuit-breaker - role-object +