Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

=con #17670 Fix potential ReceivePipeline MatchError #17678

Merged
merged 1 commit into from
Aug 20, 2015
Merged

=con #17670 Fix potential ReceivePipeline MatchError #17678

merged 1 commit into from
Aug 20, 2015

Conversation

jeremystone
Copy link
Contributor

Patch that should hopefully fix #17670

@akka-ci
Copy link

akka-ci commented Jun 6, 2015

Can one of the repo owners verify this patch?

aroundCache = Some((receive, zipped))
zipped
}
super.aroundReceive(around, msg)
around(msg)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pretty sure we must call super.aroundReceive somewhere, is this change safe?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed. Have re-jigged to use super.aroundReceive as the zero for the fold to pick up the unhandled msg case

@drewhk
Copy link
Member

drewhk commented Jun 16, 2015

OK TO TEST

@akka-ci akka-ci added validating PR is currently being validated by Jenkins tested PR that was successfully built and tested by Jenkins and removed validating PR is currently being validated by Jenkins labels Jun 16, 2015
@akka-ci
Copy link

akka-ci commented Jun 16, 2015

Test PASSed.
Refer to this link for build results (access rights to CI server needed):
https://jenkins.akka.io/job/pr-validator-per-commit-jenkins/2895/

@@ -38,10 +38,13 @@ trait ReceivePipeline extends Actor {
val around = aroundCache match {
case Some((`receive`, cached)) ⇒ cached
case _ ⇒
val zipped = pipeline.foldRight[Receive](receive)((outer, inner) ⇒ outer(inner).orElse[Any, Unit](inner))
val zipped = pipeline.foldRight[Receive] {
case msg => super.aroundReceive(receive, msg)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this is the exact semantics we need here, is it ok to call super.aroundReceive multiple times? Seems a bit weird.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right @ktoso, in a mixin context super.aroundReceive might not be just the default impl, and if any of the interceptors intentionally discard messages super.aroundReceive won't be called.

We should still use the original receive as zero but with fallback in unhandled. receive.orElse(PartialFunction(unhandled))

I saw that this was your first approach for fixing it @jeremystone. What was wrong with it that you changed it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When @ktoso suggested that super.aroundReceive should be called somewhere, I agreed: what right has ReceivePipeline to enforce the specific use of unhandled as the fallback when the base actor might deliberately have overridden aroundReceive with some other behaviour.

Placing the super.aroundReceive call in the middle of the fold makes the fallback less invasive. But now as you point out, @ktonga, it might not get called at all. Clearly, calling super.aroundReceive at the end of the method as well will not do as then it will then often be called twice.

My gut feeling is that it is almost as if ReceivePipeline is ceding too much power over the flow control to the interceptors (allowing them to call each other directly). An alternative would be to have each interceptor as a PartialFunction[Any, InterceptorResult] with something like:

sealed trait InterceptorResult
case class Inner(transformedMsg: Any) extends InterceptorResult
case object HandledCompletely extends InterceptorResult

The ReceivePipeline would construct a Receive from these interceptors (so that it would be responsible for the message flow between interceptors) and just pass this to super.aroundReceive along with the original message at the end (as per the original implementation).

Of course this would be an API changer.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea @jeremystone, seems to be a definitive solution for the flow control problem. I don't think it is a problem to change the API since it is in the contrib module.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. I'd be happy to knock something up for this - and see how it looks.

@ktoso
Copy link
Member

ktoso commented Jun 16, 2015

Would be nice @ktonga if you could have a look in this PR as well, since you authored the initial impl - thanks a lot in advance!

@ktoso
Copy link
Member

ktoso commented Jul 3, 2015

So if I understand the discussion and code above correctly this only needs a rebase and is ready to be merged, right? (Would like to avoid the Merge branch commit)

@akka-ci akka-ci added validating PR is currently being validated by Jenkins tested PR that was successfully built and tested by Jenkins and removed tested PR that was successfully built and tested by Jenkins validating PR is currently being validated by Jenkins labels Jul 3, 2015
@akka-ci
Copy link

akka-ci commented Jul 3, 2015

Test PASSed.
Refer to this link for build results (access rights to CI server needed):
https://jenkins.akka.io/job/pr-validator-per-commit-jenkins/3129/

@jeremystone
Copy link
Contributor Author

Just got round to pushing change addressing flow control as discussed, so
this needs review. Is a minor API change so need docs fixing up if it's OK.
On 3 Jul 2015 3:26 pm, "Konrad Malawski" notifications@github.com wrote:

So if I understand the discussion and code above correctly this only needs
a rebase and is ready to be merged, right?


Reply to this email directly or view it on GitHub
#17678 (comment).

private def toReceive(handler: Handler) = new Receive {
// Cache the result locally to avoid evaluating potentially
// side-effecting code twice in isDefinedAt/apply...
var resultCache = Option.empty[(Any, HandlerResult)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resultCache is never cleaned, it could unnecessarily prevent the message to be GCed. Maybe apply should empty it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point.

Perhaps better approach is to not cache the result at all and simply override applyOrElse - as per recommendations for non-literal PFs. (If we can safely assume that the caller is aware of this and does not separately call isDefinedAt and then apply.) Namely:

  private def toReceive(handler: Handler) = new Receive {
    def isDefinedAt(m: Any): Boolean = evaluate(m) != Undefined
    def apply(m: Any): Unit = evaluate(m)

    override def applyOrElse[A1 <: Any, B1 >: Unit](m: A1, default: A1 => B1): B1 = {
      val result = handler(m)

      if (result == Undefined) default(m)
    }

    private def evaluate(m: Any) = handler(m)
  }

@jeremystone
Copy link
Contributor Author

Before we go any further with this, one thing that is missing (that could be achieved before) is the ability to do something within an interceptor after the message has been processed by the actor's receive (e.g. for the documented use case of timing message processing).

Looks like this could be provided by altering the Inner InterceptorResult to something like:

  case class Inner(transformedMsg: Any, after: Unit => Unit) extends InterceptorResult

along with some appropriate convenience factory methods providing defaults.

Then inside the combinedAdvisor ...

      outerInterceptor.andThen {
        case Inner(msg, after) =>
          val result = innerHandler(msg)
          after()
          result
        case HandledCompletely => Done
      }

Any thoughts on this?

@ktoso
Copy link
Member

ktoso commented Jul 8, 2015

needs a try innerHandler() finally after() but looks good.

@akka-ci akka-ci added validating PR is currently being validated by Jenkins tested PR that was successfully built and tested by Jenkins and removed tested PR that was successfully built and tested by Jenkins validating PR is currently being validated by Jenkins labels Jul 9, 2015
@akka-ci
Copy link

akka-ci commented Jul 9, 2015

Test PASSed.
Refer to this link for build results (access rights to CI server needed):
https://jenkins.akka.io/job/pr-validator-per-commit-jenkins/3166/

@ktoso ktoso added the t:core label Jul 15, 2015
@ktoso
Copy link
Member

ktoso commented Jul 28, 2015

AFAICS this is ready right? If so, could you please squash it into one commit titling it =con #17670 Fix potential ReceivePipeline MatchError? Thanks @jeremystone !

@ktoso ktoso added the reviewed Ready to merge from review perspetive, but awaiting some additional action (e.g. order of merges) label Jul 28, 2015
@jeremystone
Copy link
Contributor Author

Yes...so long as everyone happy with alterations to API. Will not be in a
position to squash the commit myself till the weekend.

There's also some documentation to do. Do you want this as part of the same
commit or as separate issue and pr?
On 28 Jul 2015 1:44 pm, "Konrad Malawski" notifications@github.com wrote:

AFAICS this is ready right? If so, could you please squash it into one
commit titling it =con #17670 Fix potential ReceivePipeline MatchError?
Thanks @jeremystone https://github.com/jeremystone !


Reply to this email directly or view it on GitHub
#17678 (comment).

@ktoso
Copy link
Member

ktoso commented Jul 28, 2015

Docs in the same commit would be excellent :) Thanks!

@akka-ci akka-ci added the validating PR is currently being validated by Jenkins label Aug 2, 2015
@akka-ci akka-ci added tested PR that was successfully built and tested by Jenkins and removed validating PR is currently being validated by Jenkins labels Aug 2, 2015
@akka-ci
Copy link

akka-ci commented Aug 2, 2015

Test PASSed.
Refer to this link for build results (access rights to CI server needed):
https://jenkins.akka.io/job/pr-validator-per-commit-jenkins/3312/

for the messages of your interest and at some point delegate on the inner :class:`Receive`
you get by parameter. We will talk about ignored and unhandled messages later.
Multiple interceptors can be added to actors that mixin the :class:`ReceivePipeline` trait.
These interceptors can be thought of as layers: each outer interceptor advising the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we should use the advice word, the API does not expose any "advice" in methods/types.
I know it's AOP wording and am familiar with it, but would like to avoid it leaking into the docs, since the API does not use such words.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. Could use decorate/decoration/delegate terminology as before, though the interceptor at the API level is now a more functional "description of the advice" than it was - so not sure this is right either unless this fact is made clear in the docs.
I will try to come up with an alternative.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before I commit anything, how about the following which uses the decorator/delegate terminology and renames InterceptorResult to Delegation.

(Internally the code uses advice/advisor terminology - albeit privately - so will rename to line up and lessen any confusion here, too.)

Interceptors

Multiple interceptors can be added to actors that mixin the ReceivePipeline trait.
These interceptors internally define layers of decorators around the actor's behavior. The first interceptor
defines an outer decorator which delegates to a decorator corresponding to the second interceptor and so on,
until the last interceptor which defines a decorator for the actor's Receive.

[etc etc as before...]

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, and please excuse the delayed response!
Yes this sounds good to me, thanks!

@akka-ci akka-ci added validating PR is currently being validated by Jenkins needs-attention Indicates a PR validation failure (set by CI infrastructure) and removed tested PR that was successfully built and tested by Jenkins validating PR is currently being validated by Jenkins labels Aug 16, 2015
@akka-ci
Copy link

akka-ci commented Aug 16, 2015

Test FAILed.
Refer to this link for build results (access rights to CI server needed):
https://jenkins.akka.io/job/pr-validator-per-commit-jenkins/3418/

@akka-ci akka-ci added validating PR is currently being validated by Jenkins tested PR that was successfully built and tested by Jenkins and removed needs-attention Indicates a PR validation failure (set by CI infrastructure) validating PR is currently being validated by Jenkins labels Aug 16, 2015
@akka-ci
Copy link

akka-ci commented Aug 16, 2015

Test PASSed.
Refer to this link for build results (access rights to CI server needed):
https://jenkins.akka.io/job/pr-validator-per-commit-jenkins/3419/

@ktoso
Copy link
Member

ktoso commented Aug 18, 2015

This is LGTM AFAICS, though needs to be squashed into one commit with title =con #17670 Fix potential ReceivePipeline MatchError

@akka-ci akka-ci added validating PR is currently being validated by Jenkins tested PR that was successfully built and tested by Jenkins and removed tested PR that was successfully built and tested by Jenkins validating PR is currently being validated by Jenkins labels Aug 18, 2015
@akka-ci
Copy link

akka-ci commented Aug 18, 2015

Test PASSed.
Refer to this link for build results (access rights to CI server needed):
https://jenkins.akka.io/job/pr-validator-per-commit-jenkins/3446/

@patriknw
Copy link
Member

LGTM
@ktoso can we merge this?

@ktoso
Copy link
Member

ktoso commented Aug 20, 2015

Yes I think so, been LGTMed a lot ;-)

ktoso added a commit that referenced this pull request Aug 20, 2015
=con #17670 Fix potential ReceivePipeline MatchError
@ktoso ktoso merged commit d2f08a3 into akka:master Aug 20, 2015
@jeremystone
Copy link
Contributor Author

Cool!
On 20 Aug 2015 10:35 am, "Konrad Malawski" notifications@github.com wrote:

Merged #17678 #17678.


Reply to this email directly or view it on GitHub
#17678 (comment).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
reviewed Ready to merge from review perspetive, but awaiting some additional action (e.g. order of merges) t:core tested PR that was successfully built and tested by Jenkins
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ReceivePipeline can cause MatchError in decorated actor
6 participants