From cd30c06ca7a3cf5a9a90509c585441777cda5f0f Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 8 Oct 2019 15:07:55 +0200 Subject: [PATCH] doc: Add pipeToSelf in interaction-patterns.md, #27877 (#27912) --- .../akka/typed/InteractionPatternsTest.java | 152 ++++++++++++++++++ .../akka/typed/InteractionPatternsSpec.scala | 65 ++++++++ .../paradox/typed/images/pipe-to-self.png | Bin 0 -> 38969 bytes .../paradox/typed/interaction-patterns.md | 34 ++++ 4 files changed, 251 insertions(+) create mode 100644 akka-docs/src/main/paradox/typed/images/pipe-to-self.png diff --git a/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/InteractionPatternsTest.java b/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/InteractionPatternsTest.java index 96e6aa8154..b2b589cb60 100644 --- a/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/InteractionPatternsTest.java +++ b/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/InteractionPatternsTest.java @@ -4,6 +4,7 @@ package jdocs.akka.typed; +import akka.Done; import akka.actor.testkit.typed.javadsl.LogCapturing; import akka.actor.testkit.typed.javadsl.TestKitJunitResource; import akka.actor.typed.ActorRef; @@ -19,10 +20,12 @@ import org.scalatest.junit.JUnitSuite; import java.net.URI; import java.time.Duration; import java.util.*; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.TimeUnit; import static jdocs.akka.typed.InteractionPatternsTest.Samples.*; +import static org.junit.Assert.assertEquals; public class InteractionPatternsTest extends JUnitSuite { @@ -665,6 +668,130 @@ public class InteractionPatternsTest extends JUnitSuite { } } + interface PipeToSelfSample { + // #pipeToSelf + public interface CustomerDataAccess { + CompletionStage update(Customer customer); + } + + public class Customer { + public final String id; + public final long version; + public final String name; + public final String address; + + public Customer(String id, long version, String name, String address) { + this.id = id; + this.version = version; + this.name = name; + this.address = address; + } + } + + public class CustomerRepository extends AbstractBehavior { + + private static final int MAX_OPERATIONS_IN_PROGRESS = 10; + + interface Command {} + + public static class Update implements Command { + public final Customer customer; + public final ActorRef replyTo; + + public Update(Customer customer, ActorRef replyTo) { + this.customer = customer; + this.replyTo = replyTo; + } + } + + interface OperationResult {} + + public static class UpdateSuccess implements OperationResult { + public final String id; + + public UpdateSuccess(String id) { + this.id = id; + } + } + + public static class UpdateFailure implements OperationResult { + public final String id; + public final String reason; + + public UpdateFailure(String id, String reason) { + this.id = id; + this.reason = reason; + } + } + + private static class WrappedUpdateResult implements Command { + public final OperationResult result; + public final ActorRef replyTo; + + private WrappedUpdateResult(OperationResult result, ActorRef replyTo) { + this.result = result; + this.replyTo = replyTo; + } + } + + public static Behavior create(CustomerDataAccess dataAccess) { + return Behaviors.setup(context -> new CustomerRepository(context, dataAccess)); + } + + private final CustomerDataAccess dataAccess; + private int operationsInProgress = 0; + + private CustomerRepository(ActorContext context, CustomerDataAccess dataAccess) { + super(context); + this.dataAccess = dataAccess; + } + + @Override + public Receive createReceive() { + return newReceiveBuilder() + .onMessage(Update.class, this::onUpdate) + .onMessage(WrappedUpdateResult.class, this::onUpdateResult) + .build(); + } + + private Behavior onUpdate(Update command) { + if (operationsInProgress == MAX_OPERATIONS_IN_PROGRESS) { + command.replyTo.tell( + new UpdateFailure( + command.customer.id, + "Max " + MAX_OPERATIONS_IN_PROGRESS + " concurrent operations supported")); + } else { + // increase operationsInProgress counter + operationsInProgress++; + CompletionStage futureResult = dataAccess.update(command.customer); + getContext() + .pipeToSelf( + futureResult, + (ok, exc) -> { + if (exc == null) + return new WrappedUpdateResult( + new UpdateSuccess(command.customer.id), command.replyTo); + else + return new WrappedUpdateResult( + new UpdateFailure(command.customer.id, exc.getMessage()), + command.replyTo); + }); + } + return this; + } + + private Behavior onUpdateResult(WrappedUpdateResult wrapped) { + // decrease operationsInProgress counter + operationsInProgress--; + // send result to original requestor + wrapped.replyTo.tell(wrapped.result); + return this; + } + } + // #pipeToSelf + + } + @ClassRule public static final TestKitJunitResource testKit = new TestKitJunitResource(); @Rule public final LogCapturing logCapturing = new LogCapturing(); @@ -700,4 +827,29 @@ public class InteractionPatternsTest extends JUnitSuite { probe.expectNoMessage(); probe.expectMessage(Duration.ofSeconds(2), new Buncher.Batch(Arrays.asList(msgOne, msgTwo))); } + + @Test + public void testPipeToSelf() { + + PipeToSelfSample.CustomerDataAccess dataAccess = + new PipeToSelfSample.CustomerDataAccess() { + @Override + public CompletionStage update(PipeToSelfSample.Customer customer) { + return CompletableFuture.completedFuture(Done.getInstance()); + } + }; + + ActorRef repository = + testKit.spawn(PipeToSelfSample.CustomerRepository.create(dataAccess)); + TestProbe probe = + testKit.createTestProbe(PipeToSelfSample.CustomerRepository.OperationResult.class); + + repository.tell( + new PipeToSelfSample.CustomerRepository.Update( + new PipeToSelfSample.Customer("123", 1L, "Alice", "Fairy tail road 7"), + probe.getRef())); + assertEquals( + "123", + probe.expectMessageClass(PipeToSelfSample.CustomerRepository.UpdateSuccess.class).id); + } } diff --git a/akka-actor-typed-tests/src/test/scala/docs/akka/typed/InteractionPatternsSpec.scala b/akka-actor-typed-tests/src/test/scala/docs/akka/typed/InteractionPatternsSpec.scala index 4bbf3af4db..aefa85ae8e 100644 --- a/akka-actor-typed-tests/src/test/scala/docs/akka/typed/InteractionPatternsSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/docs/akka/typed/InteractionPatternsSpec.scala @@ -11,6 +11,7 @@ import scala.concurrent.duration._ import scala.util.Failure import scala.util.Success +import akka.Done import akka.NotUsed import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit import akka.actor.testkit.typed.scaladsl.LogCapturing @@ -427,4 +428,68 @@ class InteractionPatternsSpec extends ScalaTestWithActorTestKit with WordSpecLik result.futureValue shouldEqual CookieFabric.Cookies(5) } + + "contain a sample for pipeToSelf" in { + //#pipeToSelf + + trait CustomerDataAccess { + def update(value: Customer): Future[Done] + } + + final case class Customer(id: String, version: Long, name: String, address: String) + + object CustomerRepository { + sealed trait Command + + final case class Update(value: Customer, replyTo: ActorRef[UpdateResult]) extends Command + sealed trait UpdateResult + final case class UpdateSuccess(id: String) extends UpdateResult + final case class UpdateFailure(id: String, reason: String) extends UpdateResult + private final case class WrappedUpdateResult(result: UpdateResult, replyTo: ActorRef[UpdateResult]) + extends Command + + private val MaxOperationsInProgress = 10 + + def apply(dataAccess: CustomerDataAccess): Behavior[Command] = { + next(dataAccess, operationsInProgress = 0) + } + + private def next(dataAccess: CustomerDataAccess, operationsInProgress: Int): Behavior[Command] = { + Behaviors.receive { (context, command) => + command match { + case Update(value, replyTo) => + if (operationsInProgress == MaxOperationsInProgress) { + replyTo ! UpdateFailure(value.id, s"Max $MaxOperationsInProgress concurrent operations supported") + Behaviors.same + } else { + val futureResult = dataAccess.update(value) + context.pipeToSelf(futureResult) { + // map the Future value to a message, handled by this actor + case Success(_) => WrappedUpdateResult(UpdateSuccess(value.id), replyTo) + case Failure(e) => WrappedUpdateResult(UpdateFailure(value.id, e.getMessage), replyTo) + } + // increase operationsInProgress counter + next(dataAccess, operationsInProgress + 1) + } + + case WrappedUpdateResult(result, replyTo) => + // send result to original requestor + replyTo ! result + // decrease operationsInProgress counter + next(dataAccess, operationsInProgress - 1) + } + } + } + } + //#pipeToSelf + + val dataAccess = new CustomerDataAccess { + override def update(value: Customer): Future[Done] = Future.successful(Done) + } + + val repository = spawn(CustomerRepository(dataAccess)) + val probe = createTestProbe[CustomerRepository.UpdateResult]() + repository ! CustomerRepository.Update(Customer("123", 1L, "Alice", "Fairy tail road 7"), probe.ref) + probe.expectMessage(CustomerRepository.UpdateSuccess("123")) + } } diff --git a/akka-docs/src/main/paradox/typed/images/pipe-to-self.png b/akka-docs/src/main/paradox/typed/images/pipe-to-self.png new file mode 100644 index 0000000000000000000000000000000000000000..bdf0eef91c987f6c7e8f1c80e1db1d2e0bde9231 GIT binary patch literal 38969 zcmeAS@N?(olHy`uVBq!ia0y~yU|PYz!0?!Zje&u|bH}q=3=E8kna<7u+0O3H`8oMT z!3BxQsVQ=_KUup5*+Nl`(JnnMX}?j(D{)Br~m3WuCGmY)V7M^FK|e z7M=OhBgE>Sps`e|;+g8}ml_h160?jA8V<=U`c&V z1_s8+%#etZ2wxwoFbx5m+O@q>*W`v>l<2HTIw4Z=^Gj87Nw-=7FXt#Bv$C=6)QswftllyTAW;zSx}Oh zpQivaH!&%{w8U0P31pE13_#pjl9`6EDX$pnt>pY%eUOa4p`L+0+-#8XAWx@@u`B>577m4gvx! zEQ~Np!AGEjcXFABF+vEUptV6Dq*4G?^bi-L;)=vl>m`Ow4GIbz986HkDd4FjvY`$F z9NH``3)-%}op21U2%^?;YC6PxxC7P*2sBBBZFiPZMi>ZE%Mv;d?n0xe>V-@cV~b#`**E)ANkV89@-;a1L;4{yJ(T(qdm zFQ$N}SHdobVN2Ak8}b|;_BZ8MoVH|bY;0{1xFUCN{iRJsznP#l?cV&}fn!P=5`S$z2FRXr8@zcZCbK#pcKBvx~U+Txywdeg5>)goCYr zBwd`_d4g)wSYp=QR;~H*G5yQqUA$Sh12?B%VhsLXd|O}J*4=?4i{IeSpVRp-I@9A8 zUAq>xJ<2Tap4X3t2Xm?(6_y;!R1eC@`Ck?6ko>rCOzeFb@+9XWADbA$i^>|KNc32{937gq4T)x<<+%M>gDR&qSl7(`Y>mHKv`MX z`lh~*U(S}>EqFC6s#H#UemF0{&ADjI2dgH8%uqZQee^bfxsq zqq`d~UA|Nmuv+F-XM;e9zY9~u{~wq2W1f`E?|l67*z0vmdcK@vFMcy4u;fhO=9ioG z_bt1ceeL2#_w#%UuHDnUbmPX2-Rd0=ZaHpilYRd`>|Npn< zvMU_v_4WPoN;-e_H2*mU)9R`}^X5IdQsG(5XWx_eM|AF-tlS^ zckK6a?%!*>ez+}Ixbft7znj8=93J+Pf!jCV7N0z2hR5{S61~{kpRKF}dp?_RlPkJC)&XL2w9hFv?*B5eP~H+tK~ zojYTG-syR=B6H7Cxrzh-)@fbOz2WlOJZIV6>TkQY?LG0_-{VD1bL+|bm)*CmTQ|?{ z&lBU+MK_Nge^wB1;YFZwNcF|Mc&6ipGs>RH7*0M`@YCzvgnd!p=2o3<$&|U}u_gbL zop5ZZi0Jf9JO5Pws(Z?R<<=!HWjCJSeY=(0_)Di6MrUN@E#nujcbmOEZ}HsS--G0K zNA*ZqHr-E3H_Ur>-0trmZLi7f#UVvSLTh98w$--UFA_Rdpke*x|FdxW&xa1Zxl*E1S#qka;g$Xwx5+c}#)N}!f{eU&_LZcDU9!l1?66OEcUiDi_V2=cKAVJ9tBb#8 z6&z=~bGrOqyiWz&g*L^SwaP#Low58ApdTEYy?W}r`k$qxPu3jfTgdtN*oxE#o0H{mfKSVm8H{}8r6gt zkN-F;K7Gon2JJ7F9nU{}(RH10jo*IVVxcn43!jcfzu(E4`f^{~`}X}BzryD*N8O!t z$w(_<{?9<=w#zHkbqiik|6+LTJ@1|T1G$WUMQ^#z+46kp^?N~oy>ti=Z(N*MX?y6k)Yrln?E9bX<2{~m#+~8T4MXLr*Tft%)0lD^)C-*c z+jJe5jrdt0ciu2wo`3(N?G|-xKNg&KySw;@)xpr3-oM&mYZv<4|L^MhQO6kHv;S39 zpWPz&JbtHuud$pTr1{NQ%y;g7cE{iTf6&Aj8-;xwuZnC!eC!;b*>JvEcenb*y6XRq z3UW~nat;pD3hoB0`7fHiZf_8)li~$-yGd@1uNwpPtls-P)_Ry&{iXi=hV`k>qccN8 zw<>WNr0UNoH}Oj9I3X<6jPSM2W$uC3_jUH3{>mFX zd2a2JXY$1&lb^}QHYRH@EPDRh{q?E?DUP$(KDLlJ5bUsNUe&wQUpXtSpS=)yDAn-0 z`0Z5j;Haoow->%R(6IGt)?&A7Hcb)I@6FeV&gHMM%HQ{Lxs2xPw@nRPj;G`j>oYU- zwj@+qm)jW}JZJgH!1ad19h;|@m&RYLpI>qAYzf=^=qsJ+T<00@$ZSa{aGb69g2U7? z_WY7xp=up&7xydg%u)Upb(5r5IKb-`wr=X+YL4;{X-rR=8b^Bue{yY5uw$*<3{|dX@ud}x1{WZQ*e<5F)o5QFZBC}s3409&9GL*&^p8UyD&a7+k%(85)L#>-*Jy+76KfkAb*}Pgm_@m*mzu7mZe_Q|E ze$Bgy_xkMQV_5#G{y0?{9_v+DIG6Dt|G($;PdY7+Nqp$|Zuci*Yc5;*?w5TP{VVRS zRQ2jRIJ;5q#rM0%w~KAu{;=Y~!{efnk~S+E{2p#U#k|aK;j?FAt}<~?<+H1@R%v_v zpTNw&py`jzSLW+=#uxsmsjBXpm3igC+L*t}zd8R#zjgkrE^FU^tXFdFy(Rm8J&XP| z$^L!)kIqZW4%Qx=Ui0m!`I@b|LH2i8Klg{d-1cGHuTQV@SFd6JpUA}XsJC=aZsLdI z=2mmI-}@Wz*Wp1#{qwix)pMDaJUmzZ|JyhrClj-qcDw0n=-&(B~SX%Kf^4C?Pr>A=y_6S>WiRsJxzDe6u^fl^Tja?|q-+BA( z9KQemn)lA+&h38lclQ6D)?Hos@Wij~h42cz^xctMx%Z*2si2e@{L4X0`fIZ}Vftt3QrTj#(eA z4;Htj%gthp`F%-$`cIV=5+aJtT%YfQ)Ee*u>(ku*ODWxj;k9KB=(1>ho(m-~GC)yYAIRbH%gr$HdlFSUx@S zbIs8+1yeoWNxV*&RX=;J8}>y`Kd3-)YuK_OW<;Mfl|d=H(wx*KE49gz0eUYx9fA z{;Fp7D)5kAMSdVLQ$MN=yOB{}?{~T*NPx_?hx%DSCf0ax~i4ZBY z7pwneS-bxcd+N-Rxhf}EzYFS~XkNV5VoO{U7q4dNmu>rgxJfTaY?*V?MffJO!i9g0 z9dczS9Iu@Fzs2W~AESJRwW*_Rm+ynLG!7n}iVDRFhx*T}r8RoD`U=cYaA;vS%seWs z{mN?6*8CR=XIN(GW%$<>9=@ulE8f9X)M58&NAP0y%_kn_aJ9)VuX_=AEN%1U>TbI* zdo`ElCJpJQ^*(bP{2Z_4rRuKsd*8CHi6c=qfA)(1+oJPKGs3lEjGpy=-x!<6+Ek;yAtU#`2!N*oM>86HF zg~@I!QyfE@Tq=TKD+7_?%Sc zbvpZ>TTMtj5WFDq_yU&ile!}x=ZWpu_{aMBfrk52JrX!P>^1)`xjDJXPUz?2kB>5H z+ycaYuiNvbrf9=&;a_Tt7wm6j6Y*l-Q{p^()mq~h&L0}X0)#`pBo|!&B(mdk%-O#y zTDR8ETlOk&4XbN>bFW=KQA9B)ezi_X4x&PxH23H$CoV_l@2UIOH6Zcb)ut(7^O!b0{V7m6?c>&c9cLLYXgGdw zzVPHqgj|$G?c4KvKFlhde%pvc_m8EcM{m&!#kqFdE4?X6}79dyVa% z@5%lbdH3&%ojE5^K4|s>R!8m*F%N?$OCI-^Dlgx~V=|%ryzC7UK}jy(KEY56XOdgaS3Mt%uXLy5Ia{ zWw&F<@4WLiAsnmxC)k#G*n6Ie63qR(JdvfKv>~CxQ#OKYmJNNNy(emF6BE~PIw>n5N`|ADr^z{DP3$tVIzli=?(a^~~ zfjw$+aJXWe!KC1v_G$^1zY0aU*+r|&%Km(`c{cO;+{fqk>@2=>Vx=8hGGh{lf`s}O ziJh%-;a}`(n)xgGuJX!0Zi%$-O|G)?<8Wl{V)Wu>|B%6W@FoAj*Kn2jl;r*7i0B7?I{E>&E0S2Eu;F`PVi>eHRI*NS;Pr>)M) zx^wD)xiHh)o-5+TgPs|?R>bS zLHP&I+(Y?C+#0nx?k!RLwsGXa!viikE^(mFUxi6mDe<*D_;9qiW;XA3!$16*BJ1*&MIT698 z@BS;&H1GQ9$eA0?a68f_&+VN zD_Ho-%j9PJGV#Xs91MI*D~vk=uE`~qO4hzB`0OC|JL-dT!{kr*g+XB|yYfRbKb@*kQ=jk=FXVw7cb}EeRpt8?6u~Zk#mow7ntu~GIQo*hoasnzRgt+*m;$> zE=#jH`m9>$%Koe4TZppIjqP71`_1V&pJDQ4a-)sx)2DvibKj+(>0=ah$qQsT_3n`! z=MBm9Y#!HLRsxz=_+Q_UtK};zyxg?!O0d8L7G33Emsie-I+aoA6dA!HxR_Cut7GZA z*&F@)*(1zeuikJscvoqUW8&6!=1FHwu3GRLv`UC7Z@3m)_i^W`sZ7^DbjSafPj|XG zU)Oi@jj8$a;+3K7Wq%I7Nmy->$GT4Z{AMNgkMbSrjk{;+vrhW>W8JjY1!b)h68|TB zJA5JOirLHD+ONIxhveV?Ftz>q=d{)*fys;yn2&$B>=*aIytQS$nbM)aZ0XNMbJHZ^ zQ+xU4=Im}(U9s;KZ;JIM28S=E<)uCb`LFi9+V$km=GcD=D=zQ*yUt$w(#}cN7r7

hfCwWtKQ(y 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 E0A?x14*&oF literal 0 HcmV?d00001 diff --git a/akka-docs/src/main/paradox/typed/interaction-patterns.md b/akka-docs/src/main/paradox/typed/interaction-patterns.md index 16e53f5cfc..f36d257f94 100644 --- a/akka-docs/src/main/paradox/typed/interaction-patterns.md +++ b/akka-docs/src/main/paradox/typed/interaction-patterns.md @@ -216,6 +216,40 @@ Java * There can only be a single response to one `ask` (see @ref:[per session child Actor](#per-session-child-actor)) * When `ask` times out, the receiving actor does not know and may still process it to completion, or even start processing it after the fact +## Send Future result to self + +When using an API that returns a @scala[`Future`]@java[`CompletionStage`] from an actor it's common that you would +like to use the value of the in the actor when the @scala[`Future`]@java[`CompletionStage`] is completed. For +this purpose the `ActorContext` provides a `pipeToSelf` method. + +**Example:** + +![pipe-to-self.png](./images/pipe-to-self.png) + +An actor, `CustomerRepository`, is invoking a method on `CustomerDataAccess` that returns a @scala[`Future`]@java[`CompletionStage`]. + +Scala +: @@snip [InteractionPatternsSpec.scala](/akka-actor-typed-tests/src/test/scala/docs/akka/typed/InteractionPatternsSpec.scala) { #pipeToSelf } + +Java +: @@snip [InteractionPatternsTest.java](/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/InteractionPatternsTest.java) { #pipeToSelf } + +It could be tempting to just use @scala[`onComplete on the Future`]@java[`a callback on the CompletionStage`], but +that introduces the risk of accessing internal state of the actor that is not thread-safe from an external thread. +For example, the `numberOfPendingOperations` counter in above example can't be accessed from such callback. +Therefore it is better to map the result to a message and perform further processing when receiving that message. + +**Useful when:** + + * Accessing APIs that are returning @scala[`Future`]@java[`CompletionStage`] from an actor, such as a database or + an external service + * The actor needs to continue processing when the @scala[`Future`]@java[`CompletionStage`] has completed + * Keep context from the original request and use that when the @scala[`Future`]@java[`CompletionStage`] has completed, + for example an `replyTo` actor reference + +**Problems:** + + * Boilerplate of adding wrapper messages for the results ## Per session child Actor