-
Notifications
You must be signed in to change notification settings - Fork 853
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
Add shadow-traffic / traffic mirroring feature #105
Comments
It's doable in theory. A few things to be careful about: |
Interesting points. Agree with the advanced / edge cases. Maybe the mirroring feature would have some limitations (eg. no modification to the request except the url). |
Load balancing shouldn't be a problem. I'd expect the mirror target to be a separate backend group rather than a specific endpoint instance, and load balancing is per group. |
As for the modifications and such its just a matter of caution. We need similar caution for other reasons such as if we had a retry-on-failure feature. |
Mirroring is something we have talked about a bit as interesting in principle, but more complex in practice. |
In our case, A/B testing with retry / failover wouldn't fit the purpose of what we call the shadow stack. Especially because this means we would double the latency from a caller perspective in case of a failure and add load on the boxes running the app. |
Thank you @softeering, that helps clarify the scenario / use case. |
As mentioned in the linked issue I'm also looking for something like this to distribute messages to multiple environments. I hope this feature will be considered. |
We received out of band feedback requesting mirroring support specifically for IHttpProxy. Most of the above concerns apply, but there are fewer components to contend with. |
I have been using the IHttpProxy and would love a way to have mirroring out of the box. Currently I set up a second HttpMessageInvoker and just copy everything I need onto that which has worked for the GET requests I'm currently using it for. But I will be using it for POSTs in the future so if there is some pitfalls then a proper way of doing it would be great. |
@Towmeykaw can you show a rough outline of your code for that? And where is that called in relation to IHttpProxy? Here's an outline for how mirroring could be implemented as a DelegatingHandler. It's careful to avoid either request from affecting the other. private class MirrorHandler : DelegatingHandler
{
public MirrorHandler(HttpMessageHandler innerHandler) : base(innerHandler)
{
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var mirrorRequest = CopyRequest(request);
RetargetRequest(mirrorRequest);
// Dispatch the real one so even if it throws we'll still send the mirror request.
var realTask = Task.Run(() => base.SendAsync(request, cancellationToken));
var mirrorTask = Task.Run(async () =>
{
try
{
// TODO: Provide a different cancellation token here:
using var mirrorResult = await base.SendAsync(mirrorRequest, cancellationToken);
// Report
}
catch (Exception ex)
{
// Report
}
});
return realTask;
}
private HttpRequestMessage CopyRequest(HttpRequestMessage request)
{
throw new NotImplementedException();
}
private void RetargetRequest(HttpRequestMessage mirrorRequest)
{
// Update RequestUri and the Host header as needed
throw new NotImplementedException();
}
} I'd only recommend this for use with IHttpProxy. When using the full proxy model there are a lot of other considerations like load balancing, health checks, etc. so mirroring would be implemented as middleware instead. The CopyRequest step is a lot more complicated in middleware, HttpContext has a lot more state. As for POSTs, that's where things get hard. The simplest approach would be to pre-buffer the body and attach a copy to each request. This is problematic for a couple of reasons:
These can be mitigated with some complicated streams that stream content to both destinations, but I don't think you can fully insulate one request from the other in this scenario. E.g. If the real request is rejected or fails then the client may abort sending the body, causing the mirror request to fail differently. |
@Tratcher For my first attempt I was very careful not to affect the proxy as I was running against production data. So I just created a separate HttpMessageInvoker and called it after the real request. This is a very basic setup which was just to get one feature tested but in a few weeks I will probably have to set it up for posting so will make some tests with the DelegatingHandler and pre-buffering. It might work for my use case as the Post bodies are usually just small json messages.
|
|
What should we add or change to make your life better?
Is there a plan to support request mirroring to a second endpoint?
Let's say the reverse-proxy receives a request on 8080. In addition to forwarding it to the configured endpoint, it would "duplicate" the request and send it to a second endpoint (most probably in a fire-and-forget manner).
Configuration could define a percentage of requests to mirror, request modification etc...
Why is this important to you?
In a production system, when testing a new version, it is very useful to be able to get real PROD traffic in without impacting the production environment. Being able to shadow some traffic to another fleet of boxes running the new version helps us a lot when releasing impacting changes to high-throughput services (1M+ requests per second)
The text was updated successfully, but these errors were encountered: