Adds support for Actor Reentrancy#219
Conversation
|
Hold off on reviewing. Will add some tests. Also it appears an existing GRPC test (which should be unrelated) is failing. |
Forgive me if you know this already: You can actually denote a PR as a 'draft' to indicate that it isn't ready to be reviewed/merged. When you go to create a new PR, there's a little dropdown on the right side of the submit button that will let you 'Create as draft' |
Codecov Report
@@ Coverage Diff @@
## master #219 +/- ##
==========================================
- Coverage 88.68% 88.53% -0.16%
==========================================
Files 49 52 +3
Lines 1954 2068 +114
==========================================
+ Hits 1733 1831 +98
- Misses 221 237 +16
Continue to review full report at Codecov.
|
|
@wcs1only I think this is ready for review now. By the way, how exactly are the samples run? Do you think I should add a sample to this PR or do that in a separate PR? |
|
@halspang can you verify this looks good as far as the actors functionality goes (the right things are sent in the right spots) |
halspang
left a comment
There was a problem hiding this comment.
Overall looks good. Just a few comments. For the header/reentrancy itself I think we're good aside from the config parsing.
| class ActorReentrancyConfig: | ||
| def __init__( | ||
| self, | ||
| enabled: Optional[bool] = False, |
There was a problem hiding this comment.
While the reentrancy config itself is optional, this isn't an optional field in the config. How does this affect the actual serialization?
There was a problem hiding this comment.
This does the right thing because of the default value, but the Optional qualifier is unnecessary. I'll remove this.
| # import to avoid circular dependency | ||
| from dapr.actor.runtime.reentrancy_context import reentrancy_ctx |
There was a problem hiding this comment.
Can you explain how this makes a circular dependency?
There was a problem hiding this comment.
I honestly can't. When the import was at the module / library level all kinds of tests failed because seemingly unrelated things could not import (or a version was imported that was missing things). Doing this import here fixed the issue. The same thing is done in other places in the Python SDK. Basically what is described here: https://stackabuse.com/python-circular-imports
There was a problem hiding this comment.
IIRC, the actual circular dependency was on dapr.actor.runtime which in turn depends on the http client.
| headers: Dict[str, Union[bytes, str]] = ( | ||
| {DAPR_REENTRANCY_ID_HEADER: reentrancy_id} if reentrancy_id else {}) |
There was a problem hiding this comment.
Can this erase other headers that were already set/needed? Or is this the only header? I'm thinking of things like content-type or length.
There was a problem hiding this comment.
Headers weren't used before and passing of custom headers hasn't been supported with Actors in Python. The invoke_method only supports a single payload that is byte-encoded. This also only returns bytes.
The behavior prior to this PR is as follows:
Invoke_method always sends an empty dictionary for the headers via send_bytes(). Then that function checks whether the Content Type and DAPR API Token headers are presented (which they aren't) and if not uses defaults. See the send_bytes method definition and the test modification I made.
This introduces no breaking change.
| response = _run(ActorRuntime.dispatch( | ||
| FakeMultiInterfacesActor.__name__, 'test-id', | ||
| "ReentrantMethod", test_request_body, reentrancy_id=reentrancy_id)) |
There was a problem hiding this comment.
To confirm, this is just validating that we can call a reentrant actor normally right? Not actually testing the reentrant portion?
There was a problem hiding this comment.
This tests the dispatcher portion of the reentrant call normally. A similar test exists for non-reentrant calls among the existing tests.
| async def run_parallel_actors(): | ||
| slow = dispatchReentrantCall( | ||
| FakeSlowReentrantActor.__name__, "ReentrantMethod", slow_reentrancy_id) | ||
| normal = dispatchReentrantCall( | ||
| FakeReentrantActor.__name__, "ReentrantMethod", normal_reentrancy_id) |
There was a problem hiding this comment.
Should this be the passthrough method? That's the only time the reentrant ID is fetched from the ContextVar.
There was a problem hiding this comment.
I don't understand this comment. Could you explain @halspang?
This is just a bunch of Python weirdness to run two async methods truly in parallel and gather the results to force the interleaving of the processing. It is a sanity check to make sure the ContextVars truly work as we expected, that is, ContextVars are unique to each AsyncTask and are always copied forward into nested AsyncTasks.
I understand this looks weird - @wcs1only and I paired on this.
There was a problem hiding this comment.
I'm fine with the python itself, my comment was more on how we'd handle the ContextVar in the code. Right now, this path only exercises the setting of the reentrant ID. If we use the passthrough method, it should also have to fetch the reentrant ID from the ContextVar. That's the path I'd like to check with interleaving.
There was a problem hiding this comment.
@berndverst what do you think about my previous comment?
|
Thanks for the review @halspang. Made a small change (removing the Optional qualifier which wasn't effective anyway). Otherwise take a look at my reply to your comments. Hope it all makes sense :) |
halspang
left a comment
There was a problem hiding this comment.
Spoke over a call about my last comment and I think what I'm looking for is more of an IT or auto-example. Once we have a good idea on what to make the actor do, we can add that in but we don't need to in this PR.
wcs1only
left a comment
There was a problem hiding this comment.
Looks good. +1 for adding an example that exercises this.
| # import to avoid circular dependency | ||
| from dapr.actor.runtime.reentrancy_context import reentrancy_ctx |
There was a problem hiding this comment.
IIRC, the actual circular dependency was on dapr.actor.runtime which in turn depends on the http client.
Description
Adds support for Actor Reentrancy
Issue reference
#214
dapr/dapr#2901
Please reference the issue this PR will close: #214
Checklist
Please make sure you've completed the relevant tasks for this PR, out of the following list: