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

Refactor Props #5416

Merged
merged 26 commits into from
Dec 14, 2021
Merged

Refactor Props #5416

merged 26 commits into from
Dec 14, 2021

Conversation

Zetanova
Copy link
Contributor

@Zetanova Zetanova commented Dec 4, 2021

refactor of class Props to reduce view allocations, seal the class,
and adding the possibility to use the same IIndirectActorProducer for multiple Props instances.

There is new an new obsolete IIndirectActorProducerWithActorType for legacy Akka.DI.Core,
but I don't know the legacy DI well to know if there will be some binary combability issues.

Improvements for each props instance:
1x less object instance with min. 1 reference field (the producer instance)
1x less reference field (outtype field)
View less vcalls

@@ -38,7 +38,7 @@ public void Initialize(IDependencyResolver dependencyResolver)
/// <returns>A <see cref="Akka.Actor.Props"/> configuration object for the given actor type.</returns>
public Props Props(Type actorType)
{
return new Props(typeof(DIActorProducer), new object[] { dependencyResolver, actorType });
Copy link
Contributor Author

Choose a reason for hiding this comment

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

If this was the only place where the old DIActorProducer got with new object[] { dependencyResolver, actorType } created, then we could remove the obsolete Props.CreateProducer method.

Copy link
Member

Choose a reason for hiding this comment

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

That's true. Can mark that API for deletion in 1.5

Copy link
Contributor Author

Choose a reason for hiding this comment

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

how do we mark deletion in version?
simply put some text in the obsolete attribute message?

Copy link
Member

Choose a reason for hiding this comment

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

Correct - just mention that in the ObsoleteAttribute's text

if (producer is IIndirectActorProducerWithActorType p)
return (producer, p.ActorType, NoArgs);

//maybe throw new ArgumentException($"Unsupported legacy actor producer [{type.FullName}]", nameof(type));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

here I am not sure, maybe to throw is better

Copy link
Member

Choose a reason for hiding this comment

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

If it's actually unsupported now, we should throw here.

@@ -689,92 +669,20 @@ public FactoryConsumer(Func<TActor> factory)
_factory = factory;
}

public ActorBase Produce()
public ActorBase Produce(Props props)
{
return _factory.Invoke();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

maybe insert here a safety type check with props.Type ?

Copy link
Member

Choose a reason for hiding this comment

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

To do what with it exactly? I don't recall what the FactoryConsumer does anymore

Copy link
Contributor Author

Choose a reason for hiding this comment

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

FactoryConsumer was not used, I reactivated it for the Producer by Custom-Functon

return CompareDeploy(other) && CompareSupervisorStrategy(other) && CompareArguments(other) &&
CompareInputType(other);
return Type == other.Type
&& _producer.GetType() == other._producer.GetType()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added here a simple producer type compare, there was no comparison on the producer before

Copy link
Contributor Author

Choose a reason for hiding this comment

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

One other idea would be to let test if the producer implements IEqualityComparer<Akka.Props>
or maybe add a field for it into the producer interface.

@Zetanova
Copy link
Contributor Author

Zetanova commented Dec 5, 2021

All tests running successfully on my local machine.
The probe.ExpectMsg(...) got somehow very racy, didn't make any changes related to this method.

Is the build server just over committed or throttled and just slow?

@Zetanova
Copy link
Contributor Author

Zetanova commented Dec 6, 2021

@Aaronontheweb Do we have any unit tests for the TestKit methods itself, that are checking the thread-id?
the test method like probe.ExpectMsg(...) should not run on a dispachter thread.

I do not have currently an other idea.
On view racy tests the first probe.ExpectMsg(...) was successful and the other inside a Within(...) block just failed.
The probe.ExpectMsg(...) method dequeues in blocking from a BlockingCollection with timeout and cancellation set.

@Aaronontheweb
Copy link
Member

@Zetanova the test suite has become much more flakey since the .NET 6 upgrade due to the new "deadlock detection" algos that the managed thread pool uses. We're going to need to refactor all of it to be async probably, which we'd already started gradually doing.

@Zetanova
Copy link
Contributor Author

Zetanova commented Dec 6, 2021

What exactly happens with the new "deadlock detection" ?

The message is not pushed into the Queue of TestProp.
This can only mean 3 things:

  1. the adding thread of to the queue and the waiting thread are the same.
  2. the actor-system had internal errors and try-catch-ignore them
  3. the actor-system add log-errors over AkkaLogger that itself uses a Mailbox.
    The effect is that Log messages getting ignored at startup or do not write out after a failed test

@Aaronontheweb
Copy link
Member

What exactly happens with the new "deadlock detection" ?

I'm not entirely sure, but the behavior of the ThreadPool now means that new threads will be spawned when a thread is blocking for longer than it "should" based on some heuristics. We need to basically start awaiting on things inside our test suite to avoid triggering this, rather than blocking.

@Zetanova
Copy link
Contributor Author

Zetanova commented Dec 6, 2021

Don't think that's, even with a new thread the message should be in the BlockingCollection in under 300ms.

Would be most likely better to find and fix first the issue in the TestKit and then update the test methods to async
I found one logically racy test ShouldFailIfMoreExceptionsThenSpecifiedAreLogged and fixed it in this PR.

For probe.ExpectMsg(...) and Within(...) i will look if there are already tests written for thread-Id-checks
and if not add one or two for it

@Zetanova
Copy link
Contributor Author

Zetanova commented Dec 7, 2021

Some tests failed only in "debug" test run, but not with a simple "run" test
Both in debug builds

I found In the CircuitBreaker a sync method that waited only 50ms for execution.
The task just didn't get activated/run in the period by the threadpool/task-scheduler
I could refactor the method to fix it.

But the underlining problem is most likely that scheduled task just do not activated in time,
specially with a very low timeout (under 50ms)

@Zetanova
Copy link
Contributor Author

Zetanova commented Dec 7, 2021

This failed test in streams is strange
The actor system begins to shutdown near instantly at test-start
even before the timeout in the test itself

This indicates some startup-error and lost-log messages,
like mention in a comments above

image

@Zetanova
Copy link
Contributor Author

Zetanova commented Dec 7, 2021

@Aaronontheweb two failing tests are left, most likely it's just the racy akka-system startup or?
The test do not fail locally in debug-test-run and normal test-run

@Zetanova
Copy link
Contributor Author

Zetanova commented Dec 8, 2021

@Aaronontheweb I added now a hack into the TestPrope.Create method
fcf18c2

This produces now errors local and a lot on the build-server, strange only in Akka.Cluster.Scharding and Akka.Persistence
and all other failed test are gone.

@Aaronontheweb
Copy link
Member

You have a lot of test failures now (77) - looks like one of your changes broke something in the logging system that the TestKit uses.

@Zetanova
Copy link
Contributor Author

Zetanova commented Dec 8, 2021

I put a hack inside the commit to instantly await a log message and it is failing now

Need to analyze why it is not support one some tests

@Zetanova
Copy link
Contributor Author

Zetanova commented Dec 8, 2021

I removed now the testprope startup await hack,
will try to find the racy component locally and create a new PR for it,
but it is not from this PR

@Aaronontheweb if its ok for can be merged

@Aaronontheweb
Copy link
Member

Finally looking at this now

Copy link
Member

@Aaronontheweb Aaronontheweb left a comment

Choose a reason for hiding this comment

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

Mostly looks good, but have some changes that either need to be explained in more detail or removed

@@ -38,7 +38,7 @@ public void Initialize(IDependencyResolver dependencyResolver)
/// <returns>A <see cref="Akka.Actor.Props"/> configuration object for the given actor type.</returns>
public Props Props(Type actorType)
{
return new Props(typeof(DIActorProducer), new object[] { dependencyResolver, actorType });
Copy link
Member

Choose a reason for hiding this comment

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

That's true. Can mark that API for deletion in 1.5

@@ -1072,10 +1072,14 @@ namespace Akka.Actor
}
public interface IIndirectActorProducer
{
System.Type ActorType { get; }
Akka.Actor.ActorBase Produce();
Copy link
Member

Choose a reason for hiding this comment

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

I need to check to see if there are any instances of plugins that call this - I don't think so but I want to double check.

Copy link
Member

Choose a reason for hiding this comment

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

I think we're good

src/core/Akka/Event/Logging.cs Show resolved Hide resolved
src/core/Akka/Actor/Props.cs Show resolved Hide resolved
@@ -689,92 +669,20 @@ public FactoryConsumer(Func<TActor> factory)
_factory = factory;
}

public ActorBase Produce()
public ActorBase Produce(Props props)
{
return _factory.Invoke();
Copy link
Member

Choose a reason for hiding this comment

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

To do what with it exactly? I don't recall what the FactoryConsumer does anymore

src/core/Akka/Actor/Props.cs Show resolved Hide resolved
@@ -32,12 +30,12 @@ namespace Akka.Actor
/// </code>
/// </example>
/// </summary>
public class Props : IEquatable<Props>, ISurrogated
public sealed class Props : IEquatable<Props>, ISurrogated
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 how I feel about this, in terms of keeping this class open for extension. I checked the Phobos source to see if we subclass Props anywhere there and we do not (we do subclass Deploy, one of the sub-components of Props) so if that doesn't need to extend Props I can't imagine many other plugins that do...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think that it has subclasses anywhere.
It's a miss design to use subclasses as decorators and/or to change the behavior.

Props is a data/payload structure and the IIndirectActorProducer should contain the behavior.

Copy link
Member

@Aaronontheweb Aaronontheweb left a comment

Choose a reason for hiding this comment

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

LGTM - we're doing the 1.4.29 release today and I don't want to incorporate this change right away without having some further testing on subsequent PRs (I did the same thing with my PipeTo changes between 1.4.28 and 1.4.29), but this will make it into 1.4.30

@Aaronontheweb Aaronontheweb enabled auto-merge (squash) December 14, 2021 01:33
{
return actorFactory();
if (props.Type != _actorType)
Copy link
Member

Choose a reason for hiding this comment

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

Do we need to be more careful about inheritance checks here?

Copy link
Member

Choose a reason for hiding this comment

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

This is part of the deprecated DI library so I'm not as worried about it - but did we end up changing something here that we shouldn't have?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I insert here only a safty check the props.Type and _actorType should have the same type,
because it's only copy

{
var t = ((SendOrPostCallback, object, ActorCellKeepingSynchronizationContext, ActorCell))s;
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 torn on this. I feel like once we multitarget netcore, this can be made better (because then, I think the boxing/unboxing can be elided here). But for now we are paying box/unbox penalty.

But as it stands, is it worth deconstructing the tuple immediately to help with reads?

Copy link
Member

Choose a reason for hiding this comment

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

This only affects the TestKit so I think we should be ok

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't know what's faster here ValueTuple or System.Tuple, wanted only remove the scope.

It does not matter here, but when a scope is used with ThreadPool,
the context will not be GC until the action runs, it can lead to a self-reference in some scenarios.

Comment on lines 331 to +334
var args = newExpression.GetArguments();
args = args.Length > 0 ? args : NoArgs;

return new Props(new ActivatorProducer(typeof(TActor), args), DefaultDeploy, args){ SupervisorStrategy = supervisorStrategy };
return new Props(ActivatorProducer.Instance, DefaultDeploy, typeof(TActor), args) { SupervisorStrategy = supervisorStrategy };
Copy link
Member

@to11mtm to11mtm Dec 14, 2021

Choose a reason for hiding this comment

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

If we're already getting the args, would there be any benefit in also sniffing out the Ctor in .GetArguments()? (Can we even do that in a way that works OK with expectations of Props?)

That method was originally CopyPasta'd from OddJob, where we weren't newing up objects but rather were getting arguments for method calls. If you can actually hang on to what constructor is called (would be easy) there are opportunities to elide penalties involved with Activator.CreateInstance

Copy link
Member

Choose a reason for hiding this comment

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

Can you file an issue for this so we remember to do this as part of 1.5?

Copy link
Contributor Author

@Zetanova Zetanova Dec 15, 2021

Choose a reason for hiding this comment

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

@to11mtm We could move the logic into a custom-actor-producer and use the instance for mass creation of the same actor type with the same or different props-instances,
everything else will always require runtime binding or dynamic-compile and use it only once

Comment on lines 331 to +332
var args = newExpression.GetArguments();
args = args.Length > 0 ? args : NoArgs;
Copy link
Member

@to11mtm to11mtm Dec 14, 2021

Choose a reason for hiding this comment

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

Consider, if you don't do the deed commented below about pulling ConstructorInfo:

var args = newExpression.Arguments > 0 ? newExpression.GetArguments() : NoArgs;

In the call chain for .GetArguments() it looks like we might allocate an empty array if a 0-arg ctor is passed in. The suggestion will elide that allocation.

Copy link
Member

Choose a reason for hiding this comment

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

That's a good idea

{

Deploy = deploy;
// have to preserve the "CreateProducer" call here to preserve backwards compat with Akka.DI.Core
Copy link
Member

Choose a reason for hiding this comment

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

Thank you. :)

@Aaronontheweb Aaronontheweb merged commit 12c26c2 into akkadotnet:dev Dec 14, 2021
Aaronontheweb added a commit to Aaronontheweb/akka.net that referenced this pull request Dec 14, 2021
Aaronontheweb added a commit to Aaronontheweb/akka.net that referenced this pull request Dec 20, 2021
Aaronontheweb added a commit that referenced this pull request Dec 20, 2021
)

* remove the forced waiting on the underlying transport to start up

* racy spec fix: `BackoffOnRestartSupervisor_must_respect_maxNrOfRetries_property_of_OneForOneStrategy` (#5442)

issue was that using `AutoReset` could cause a race where if the scheduler could reset the count in-flight and throw off the estimates used by the tests. Using a `ManualReset` avoids this issue.

* deleted commented out code

* Make updates to `FishUntil` (#5433)

* Make updates to `FishUntil`

Implement nitpicks from #5430

* Nitpick

* cleaned up `FishUntilMessage`

* removed `FluentAssertions` reference

* fixed bad `TestKit.ReceiveTests`

* Custom frame size computation support in Framing (#5444)

* Custom frame size computation support in Framing

* Speed up of framing spec

* optimize `Props` `NewExpression` allocations (#5428)

per #5416 (comment)

Co-authored-by: Ismael Hamed <1279846+ismaelhamed@users.noreply.github.com>
Arkatufus added a commit to Arkatufus/akka.net that referenced this pull request Dec 20, 2021
Aaronontheweb pushed a commit that referenced this pull request Dec 20, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants