{"action":"created","issue":{"url":"https://api.github.com/repos/dotnet/orleans/issues/3362","repository_url":"https://api.github.com/repos/dotnet/orleans","labels_url":"https://api.github.com/repos/dotnet/orleans/issues/3362/labels{/name}","comments_url":"https://api.github.com/repos/dotnet/orleans/issues/3362/comments","events_url":"https://api.github.com/repos/dotnet/orleans/issues/3362/events","html_url":"https://github.com/dotnet/orleans/pull/3362","id":253921213,"number":3362,"title":"Hosted Client","user":{"login":"ReubenBond","id":203839,"avatar_url":"https://avatars1.githubusercontent.com/u/203839?v=4","gravatar_id":"","url":"https://api.github.com/users/ReubenBond","html_url":"https://github.com/ReubenBond","followers_url":"https://api.github.com/users/ReubenBond/followers","following_url":"https://api.github.com/users/ReubenBond/following{/other_user}","gists_url":"https://api.github.com/users/ReubenBond/gists{/gist_id}","starred_url":"https://api.github.com/users/ReubenBond/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ReubenBond/subscriptions","organizations_url":"https://api.github.com/users/ReubenBond/orgs","repos_url":"https://api.github.com/users/ReubenBond/repos","events_url":"https://api.github.com/users/ReubenBond/events{/privacy}","received_events_url":"https://api.github.com/users/ReubenBond/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":{"url":"https://api.github.com/repos/dotnet/orleans/milestones/25","html_url":"https://github.com/dotnet/orleans/milestone/25","labels_url":"https://api.github.com/repos/dotnet/orleans/milestones/25/labels","id":3231896,"number":25,"title":"2.1.0","description":"","creator":{"login":"sergeybykov","id":8248806,"avatar_url":"https://avatars1.githubusercontent.com/u/8248806?v=4","gravatar_id":"","url":"https://api.github.com/users/sergeybykov","html_url":"https://github.com/sergeybykov","followers_url":"https://api.github.com/users/sergeybykov/followers","following_url":"https://api.github.com/users/sergeybykov/following{/other_user}","gists_url":"https://api.github.com/users/sergeybykov/gists{/gist_id}","starred_url":"https://api.github.com/users/sergeybykov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sergeybykov/subscriptions","organizations_url":"https://api.github.com/users/sergeybykov/orgs","repos_url":"https://api.github.com/users/sergeybykov/repos","events_url":"https://api.github.com/users/sergeybykov/events{/privacy}","received_events_url":"https://api.github.com/users/sergeybykov/received_events","type":"User","site_admin":false},"open_issues":4,"closed_issues":3,"state":"open","created_at":"2018-03-30T20:28:19Z","updated_at":"2018-05-24T23:11:27Z","due_on":null,"closed_at":null},"comments":26,"created_at":"2017-08-30T08:40:46Z","updated_at":"2018-05-24T23:32:46Z","closed_at":null,"author_association":"CONTRIBUTOR","pull_request":{"url":"https://api.github.com/repos/dotnet/orleans/pulls/3362","html_url":"https://github.com/dotnet/orleans/pull/3362","diff_url":"https://github.com/dotnet/orleans/pull/3362.diff","patch_url":"https://github.com/dotnet/orleans/pull/3362.patch"},"body":"This PR adds seamless support to silos for calling grain methods, using streams, local objects, and any other client functionality from outside of the context of an activation or system target.\r\n\r\nThe primary scenarios this enables are:\r\n* Efficient co-hosting of HTTP/etc servers (ASP.NET, Orleans Dashboard, gRPC) - no extra serialization/scheduling overhead.\r\n* Ability to make grain calls from injected services, such as `IPlacementDirectors`, without having to use `TaskScheduler` capturing tricks.\r\n\r\nSince this enables grain calls from non-grain threads, this also adds some guard rails: methods on `Grain`/`Grain<T>` are barred from outside of an activation context by virtue of `RuntimeContext` checks on `GrainRuntime` and `GrainStorageBridge`.\r\n\r\nCI looks good, VSO looks good, load tests look good.\r\n\r\nReviewed by @dVakulen.\r\n\r\nI changed the name from LocalClient/Silo-Local Client to Hosted Client. I think that sounds better, but please let me know if you feel that's an unsuitable name.\r\n\r\n## How it works\r\n\r\n* When accessing runtime from an outside thread for the first time, we instantiate & install `IHostedClient` into `ISiloMessageCenter` and `IClientObserverRegistrar`.\r\n* `IHostedClient` creates and registers its own ClientId so that it can receive calls.\r\n* Client-bound calls are routed to `IHostedClient` if the id matches\r\n* `IRuntimeClient` methods behave differently depending on whether `RuntimeContext.Current` is `null` (hosted client) or not (running in activation context). Generally speaking, instead of throwing an exception when the context is null, we handle the operation via the hosted client.\r\n* Grain method calls go through the regular `InsideRuntimeClient` pathway, but calls to grain observers (aka LocalObject aka InvokableObject) are routed to the `IHostedClient` implementation, which maintains a thread to schedule `Messages` which are destined for local objects. This logic was largely extracted from `OutsideRuntimeClient` and into a shared class, `InvokableObjectManager`. So this thread is only used for those calls and not for regular calls. This Grain Observer flow is an area to optimize in future (via shared executor service), but is not in need of optimization now: performance for those objects ought to be the same as it is for the external client today.\r\n\r\n## OLD, busted description (ignore this)\r\nThis PR is a work-in-progress of an `IClusterClient` implementation which communicates directly with a silo in the same process.\r\n\r\nThe current implementation requires serialization for the `Message` objects sent between the client and silo. This is because `GrainReferences` must have their `IRuntimeClient` property set to the appropriate value (in this case either the `LocalClient` instance or the `InsideRuntimeClient` instance).\r\n\r\nAn alternative implementation might be feasible: instead of providing a completely separate `IRuntimeClient` implementation we could modify `InsideRuntimeClient` so that when it's being called from a non-Orleans context (`RuntimeContext.Current == null`) it defers to the implementations in this PR's `LocalClient`. As a result, issues such as #3273 and #3256 might not arise. However this also means that it's more difficult to catch bugs where we *should* be executing within a `SystemTarget` or `Grain` but have not correctly set the `RuntimeContext`. It's not inherently wrong to make grain calls from within a grain via `Task.Run()`, but that is the primary way we catch things which *are* bad (eg, modifying grain state). Another way to catch these kinds of bugs is to insert checks on `Grain` and `Grain<T>` members: that way, accessing `GrainFactory` or calling `WriteStateAsync()` from within `Task.Run()` would be disallowed (it's permitted today). I think this is a good approach - we can gain some coverage there, even if we're also losing some coverage because of this PR.\r\n\r\n@gabikliot & others, do you have thoughts on that previous paragraph?\r\n\r\nThis ought to help with use cases such as alternative networking layers such as for HTTP & gRPC support.\r\n\r\n* Installs a second 'gateway' called `ILocalClientGateway` into the silo\r\n* Multiple `ILocalClient` implementations can be created and customized based upon the silo instance (maybe this should be changed to one!)\r\n* Supports grain calls, observers, and streaming, but I have not yet added in-depth tests.\r\n* Still a work in progress"},"comment":{"url":"https://api.github.com/repos/dotnet/orleans/issues/comments/391897029","html_url":"https://github.com/dotnet/orleans/pull/3362#issuecomment-391897029","issue_url":"https://api.github.com/repos/dotnet/orleans/issues/3362","id":391897029,"user":{"login":"galvesribeiro","id":4714040,"avatar_url":"https://avatars3.githubusercontent.com/u/4714040?v=4","gravatar_id":"","url":"https://api.github.com/users/galvesribeiro","html_url":"https://github.com/galvesribeiro","followers_url":"https://api.github.com/users/galvesribeiro/followers","following_url":"https://api.github.com/users/galvesribeiro/following{/other_user}","gists_url":"https://api.github.com/users/galvesribeiro/gists{/gist_id}","starred_url":"https://api.github.com/users/galvesribeiro/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/galvesribeiro/subscriptions","organizations_url":"https://api.github.com/users/galvesribeiro/orgs","repos_url":"https://api.github.com/users/galvesribeiro/repos","events_url":"https://api.github.com/users/galvesribeiro/events{/privacy}","received_events_url":"https://api.github.com/users/galvesribeiro/received_events","type":"User","site_admin":false},"created_at":"2018-05-24T23:32:46Z","updated_at":"2018-05-24T23:32:46Z","author_association":"MEMBER","body":":shipit: "}}
{"actor":{"display_login":"galvesribeiro"}}