-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
HystrixRequestContext in case of async requests #895
Comments
Hi Amit, I have a similar question and found your post. From what I understand, the request context is not automatically copied or inherited by child threads. If you're scheduling with RxJava (e.g.
Now, the problem that I have with this is that it does not copy anything; It simply references the parent request context. And now comes my question: We initialize the hystrix request context with a servlet filter as mentioned in the wiki. Using the above method works fine, if all child threads complete before the filter calls Can anyone from the Netflix team advise on how to use the context with asynchronous actions? Is there a way to copy the context? Or is there a way to "remember" that there are still actions using the context and only close it when all actions completed? |
Hi ruhkopf, I believe I am also facing similar issue, not sure though. You can find the details of the issue I am facing here. https://groups.google.com/d/msg/hystrixoss/-bJmnhgmJwU/ZX2-8EPrBQAJ |
That is the question I was hoping to get an answer for. :-) I'm currently playing around with a map that remembers every async thread per request context and only really shuts down the context if there are no more threads running. It looks like it might work, but it adds overhead.
Yes, blocking at the end of the request works. However, what if we don't want to do this? In my case I have a task that takes some time. I don't want to block the request until it's finished... |
@amitcse / @ruhkopf Sorry for the delay in answering. I still need to work up an example for Each invocation of a HystrixCollapser puts the arg in a batch ( Once the timer ticks, it executes this task, which then has the proper This should work in the same way for |
Also, for the question of how to fit the async model into a blocking model (like Servlets), the general solution is to represent the entire "work" of the request as an Then, as you put that work on non-Tomcat threadpools, you can block the Tomcat thread via Here's one of @benjchristensen's presentations that goes into more detail on this topic: |
@amitcse I've created a simple unit test that demonstrates the ObservableCollapser. It also shows that a RxJavaPlugin is necessary when scheduling asynchronously. I hope it's useful. It seems to work well: https://github.com/ruhkopf/ExampleAsyncHystrixObservableCollapserExample @mattrjacobs Thanks for providing some details around the request collapser and the link to Ben's presentation. It's a great intro! About blocking: That's what we're pretty much doing right now. Our service layer is based on Observables and then we block in our REST layer. This works fine. However, I now have the use case were a (synchronous) web service call should subscribe to an Observable asynchronously and then walk away (i.e. never blocking/waiting). I'm not sure how/if I could still leverage the benefits of the request context (e.g. request collapsing). I'm going to add another unit test soon, that demonstrates this. |
@ruhkopf Thanks for the test. I also did something similar, wherein I copy the HystrixRequestContext to the new thread from my executor service, on which the async request happens. I am not seeing any arbitrary exceptions about RequestContext now. @mattrjacobs This HystrixRequestContext for async requests seems to be solved by copying the HystrixRequestContext on the new thread/scheduler, but now I am facing an issue with the semaphores wherein semaphores are not being released in case of exceptions properly. I will create a new thread for that. Need your help there. |
I was able to recreate the situation you described by adding logging to an existing unit test: mattrjacobs@c6ffedb The output of HystrixObservableCollapserTest.testTwoRequestsWhichShouldEachEmitTwice is: Which matches your description exactly. I need to think about how best to handle this. The workaround described by @ruhkopf absolutely works, but I'd like to see if Hystrix can handle this case better. Will update this thread as I make progress on that. |
Thanks for looking into this @mattrjacobs. It would be great to find a better way to handle this, because unfortunately the workaround does not work for all scenarios: Since I pass the request context from the calling thread to the scheduled thread, it breaks if the scheduled action runs after the caller has already completed. (In my specified case, the caller is a http servlet request and the Hystrix servlet filter then closes the Hystrix Context at the end). I've added another test that demonstrates this. Please see the test demonstrateSyncRequestThatSchedulesAnAsyncTask in IssueWithBackgroundTasks. The observable that runs in the background fails with:
I understand why it happens. Because we don't block and wait for the background task to finish, the parent completes first and removes the context. Then the background task runs and can't find the context (it was already removed). It all makes sense, but I don't know how to solve it. If only there was a way to identify actions that should run in the background vs. actions that must complete in one request. Then it would be easy to just create a new request context for the background actions and inherit the current one for foreground actions. Do you know what I mean? |
A quick note (don't have a full solution yet): A class already exists ( |
My comment above (#895 (comment)) was based on a unrealistic unit test. I changed the unit test in #902 in the following ways:
When those are put together, the execution of the batch command is semaphore-isolated, and so runs on the calling thread. The HystrixTimer thread executes the command, so the batch command runs there. No thread-hopping is needed in user-specified code, and Hystrix manages all of the passing around of I'll now play around with the tests provided by @ruhkopf to understand those |
@ruhkopf Take a look at https://github.com/mattrjacobs/ExampleAsyncHystrixObservableCollapserExample/commit/3032249a88580304ef7bf2377fcf043e1ee2baaa. Here, the unit test is passing without the use of RxJavaPlugins. If you use that code as-is, then the HystrixObservableCommand is executed on the HystrixTimer thread. Without thread-hopping, the Please let me know if this helps, or if there are any other cases I haven't considered. Having your repo to code against is very helpful, so feel free to illustrate with examples. Also, once you're satisfied with how things look, would you consider submitting some of those examples to hystrix-examples? It's been on my TODO list for awhile to get |
@ruhkopf Here's a different solution to the lack of context. In this case, it uses a |
@mattrjacobs I like the idea of executing the |
@amitcse Yes, the defaults are for batch commands (either
Part of my confusion was that both of these were being overridden in the unit test I added logging to. |
Thanks for your help @mattrjacobs The example that shows how to use the HystrixContextScheduler is very useful. With this, the RX plugin is no longer necessary. I still have an issue when scheduling a background task. It's more like an architecture issue/question. Could you please have a look at the other test class (IssueWithBackgoundTasks)? I'm interested to hear your thoughts. I guess the background task should rather use the global context instead of a request context... I'd be happy to add the examples. Just let me know how (fork and pull request?) |
@ruhkopf Will look at that example on Monday and give you feedback. For contributing, fork and pull request is exactly right |
Thanks, what branch should I base my changes on? |
1.4.x. I'll forward-port to master On Mon, Sep 21, 2015 at 3:29 PM, Patrick Ruhkopf notifications@github.com
|
This was based on the discussion in Netflix#895. I've modified the original example to be compatible with JDK 6, removed the dependency to ICU and refactored the test class to be part of the command class to match the pattern used for other examples.
Done: #905 Any thoughts yet on my other question? Let me add some more background information. Here's what I currently have:
All is fine when the work is completed inside one request (i.e. block at the end). However, now let's say we add auditing, for example, when someone accesses a product, we also want to store this request in the database. This should happen in the background, i.e. we don't want to wait until this action completes. Instead we return the product immediately and asynchronously subscribe to the auditing service. Without modifying any code, we then run into an issue, because the Hystrix servlet shuts down the context and then the asynchronous command to the auditing service fails (Hystrix context not initialized). This can be seen in One solution would be to change the collapser mode to global instead of request (and disable request log) for this command instance. However, from a design perspective this is not great, because it's invasive. My service layer at the moment does not expose any commands, only observables. That makes it impossible to change any settings of one command instance, without modifying the signature of the API. I am wondering if there is a way to a) make Hystrix request log/collapser smarter to detect this (I'm not sure how) or use a specific scheduler that would provide the request context for this case... |
My first thought is that converting the background task from collapser -> command should work. Collapsing requires a HystrixRequestContext, but a Let me see if I can get a working code example that follows this approach. |
Here's a working example where I replaced the background task with a A different approach would be to continue to use the Putting it differently, you can't use a REQUEST-scoped |
Ok, thanks for getting back. I was afraid this would be the answer. ;-) Unfortunately this means I will have to change my API. Currently, I only expose I noticed another thing, too: I was surprised to see my real world code fail, even when not using the collapser. I compared it with your example (thanks, btw, for providing it) and could not find a difference. Then I started debugging and saw the following code in
I am using a custom
I'm confused by this comment. Why must it be ensured? Why not optionally? Unforuntately this breaks my code. Do you think this is something that could be changed? |
Or maybe at least allow to extend from |
@ruhkopf Refer to this comment from Ben. Also, you can modify your custom strategy as suggested by pbetkier on the same thread. |
Thanks @amitcse, that works. |
Thanks @amitcse for that pointer. That's exactly right. @ruhkopf - I will add this to the plugin docs, so that others don't get bitten by this. Regarding |
FYI, I actually needed to implement
Re use case: My API has a function like |
This was based on the discussion in Netflix#895. I've modified the original example to be compatible with JDK 6, removed the dependency to ICU and refactored the test class to be part of the command class to match the pattern used for other examples.
Given the work done in #951, I think this is better-handled now. OK to close this, @ruhkopf ? |
Yep, I would say so. Thanks again for your help. |
Great, thanks for following up. Closing |
Hi,
In case when HystrixObservableCommand is used to wrap a non-blocking observable, and the non-blocking request is done on ExecutorService, (the subscription is also done on the executor service) will the HystrixRequestContext have to be explicitly copied to the newly created threads for the HystrixObservableCollapser to work ??
The HystrixObservableCollapser is used in Rx composition (flatMap).
The text was updated successfully, but these errors were encountered: