Proposal: retry try statements #127
Replies: 66 comments
-
int retryCount = 0;
try
{
targetInfo = await GetNetworkUserInfo();
}
catch(TimeoutException) when (++retryCount <= 3)
{
await Task.Delay(10000);
try
{
targetInfo = await GetNetworkUserInfo();
}
catch(Exception)
{
try; //which "try" block does this point to?
}
} |
Beta Was this translation helpful? Give feedback.
-
@Logerfo What loop does |
Beta Was this translation helpful? Give feedback.
-
@orthoxerox unfortunately, c# break doesn't support argument like PHP does. We could have try labels, though: int retryCount = 0;
try first
{
targetInfo = await GetNetworkUserInfo();
}
catch(TimeoutException) when (++retryCount <= 3)
{
await Task.Delay(10000);
try second
{
targetInfo = await GetNetworkUserInfo();
}
catch(Exception)
{
try first;
}
} |
Beta Was this translation helpful? Give feedback.
-
I don't understand why put the "when (++retryCount <= 3)" on the "catch" line. If you have many catch each catch must have the same condition otherwise it will be hard to understand the flow and debug (I thinks).
|
Beta Was this translation helpful? Give feedback.
-
Because that is what by C# supports at this point, and different exceptions may have different retry conditions. Take this hypothetical, wrong and probably far-fetched example: string connId = "db1";
int count = 0;
try
{
db.ConnectionString = GetConnectionString(connId);
await db.OpenAsync();
}
catch(TimeoutException) when (++count <= 3)
{
try;
}
catch(BadCredentialsException) when (connId != "db2")
{
connId = "db2";
try;
}
We could, but I don't really think it's necessary. It should probably go in a different proposal as well. |
Beta Was this translation helpful? Give feedback.
-
You are coupling the actual procedure and retry logic. you probably should be using a separate method/type to handle that logic. However I can see this could be covered by modifying generators (#122) [[Retry(3)]]
{
DoSomething();
} or something like that. |
Beta Was this translation helpful? Give feedback.
-
You are making a sweeping comment on code structure that I don't think is really relevant here.
[[Retry(3)]]
{
DoSomething();
} You try to fit my simple example without giving any thought to the point that some exceptions are recoverable. I used Also, this feature is fairly simple. It is no more complicated to implement than |
Beta Was this translation helpful? Give feedback.
-
I think I'd rather see this as a In @Logerfo's example (shown below for reference) int retryCount = 0;
try
{
targetInfo = await GetNetworkUserInfo();
}
catch(TimeoutException) when (++retryCount <= 3)
{
await Task.Delay(10000);
try
{
targetInfo = await GetNetworkUserInfo();
}
catch(Exception)
{
try; //which "try" block does this point to?
}
} The If we went the path of using exception filters, I'd like to see I could also see this as being try
{
targetInfo = await GetNetworkUserInfo();
}
catch(TimeoutException)
{
await Task.Delay(10000);
try
{
targetInfo = await GetNetworkUserInfo();
}
catch(Exception)
{
retry(3);
}
} The compiler could expand that to using the exception filter and retry looping. |
Beta Was this translation helpful? Give feedback.
-
I propose
|
Beta Was this translation helpful? Give feedback.
-
@scottdorman both int retryCount = 0;
try
{
targetInfo = await GetNetworkUserInfo();
}
catch(TimeoutException)
{
await Task.Delay(10000);
if(retryCount++ > 0)
try(2); //will be hit at the first try
else
try(3); //will be hit at the second try
} |
Beta Was this translation helpful? Give feedback.
-
You also wouldn't have code like
{
var ___retryCountBackingField = 0;
try
{
targetInfo = await GetNetworkUserInfo();
}
catch(TimeoutException)
{
await Task.Delay(10000);
try
{
targetInfo = await GetNetworkUserInfo();
}
catch(Exception) when (++___retryCountBackingField <= 3)
{
retry(3);
}
}
} |
Beta Was this translation helpful? Give feedback.
-
yes it is, I've declared it: int retryCount = 0; I've used this variable just to exemplify a code flow possibility involving multiple |
Beta Was this translation helpful? Give feedback.
-
@Logerfo In your example, yes, you've declared it. In my suggestion, I'm saying that you wouldn't have it/need it as it would be automatically generated. Since it's not an accessible variable, you wouldn't be able to have code like you're suggesting. Yes, you could still have multiple |
Beta Was this translation helpful? Give feedback.
-
@scottdorman what I'm trying to say is that the condition to flow to different |
Beta Was this translation helpful? Give feedback.
-
Here is another example: {
var ___retryCountBackingField1 = 0;
var ___retryCountBackingField2 = 0;
var ___retryCountBackingField3 = 0;
try // outer
{
targetInfo = await GetNetworkUserInfo();
}
catch (TimeoutException) when (++___retryCountBackingField1 <= 4)
{
await Task.Delay(10000);
try // inner
{
targetInfo = await GetNetworkUserInfo();
}
catch (NullReferenceException) when (++___retryCountBackingField2 <= 3)
{
retry(3); // retries the "inner" try block up to 3 times.
}
catch (System.Net.HttpListenerException) when (++___retryCountBackingField3 <= 2)
{
retry(2); // retries the "inner" try block up to 2 times.
}
retry(4); // retries the "outer" try block up to 4 times.
}
} This still has two If the "inner" So, the retry and counter variable is specific to the exception type your handling and only gets incremented/retried when you encounter that exception. In this example, if the inner most |
Beta Was this translation helpful? Give feedback.
-
I've been a little bit disappointed by how much we've been focusing on my example rather than the proposal itself. The idea here isn't specifically to solve the 'retry count' problem, but more generally be able to re-run a try expression for whatever reason. In my opinion the finally block should only be run once when the code runs out of the try..catch, not for every retry. You can't express this in C# currently since placing a label in the try block is inaccessible from the catch block. |
Beta Was this translation helpful? Give feedback.
-
@GeirGrusom How often you find yourself handle exceptions and how often you need to recover from them? |
Beta Was this translation helpful? Give feedback.
-
@eyalsk I don't actually do it that often but I don't use This feature on the other hand is very small, and it is at least able to do something you otherwise can't express without rearranging code. You can't loop without rerunning the Not every proposal has to be something you use every day. Features that saves you from writing awkward code once in a while also has merit. |
Beta Was this translation helpful? Give feedback.
-
Well, I don't disagree, I just think that if you don't use it everyday and use it once in a while then the feature should have a much bigger impact for them to consider it but I could be wrong. :) |
Beta Was this translation helpful? Give feedback.
-
Sure. I'm not exactly heavily invested in this. I implemented this feature in a DSL I made but in that language, it's a more common case because it was designed to give a simple solution for when credit card charge fails on subscriptions. C# can't do that but I still thought it's a little odd to have
So I'll readily admit that it's not really a huge issue, and I wouldn't exactly shed a tear if it's not implemented. It just seems like it's a fairly obvious piece that's missing among edit: maybe I have to explain why C# can't do what the DSL does: it's because the DSL stores its state on |
Beta Was this translation helpful? Give feedback.
-
Is there some way we can move the original proposal forward? It's not that common, but just ran into it again and it's kind of awkward to implement otherwise. |
Beta Was this translation helpful? Give feedback.
-
I would like to see this become part of the language.. but in the meantime you can roll your own. This class can be used to wrap up the retry logic and extended to create reusable retry configurations.
|
Beta Was this translation helpful? Give feedback.
-
If the goal is to loop, we could have try
{
...
}
while catch (Exception ex)
{
...
}
finally { ... } |
Beta Was this translation helpful? Give feedback.
-
Three attempts (explicit loop condition): int x = 0;
try
{
...
}
while catch (Exception ex)
{
...
}
while (++x < 3); |
Beta Was this translation helpful? Give feedback.
-
A few thoughts on
|
Beta Was this translation helpful? Give feedback.
-
use https://github.com/App-vNext/Polly Not sure if it should be a part of the language though. If so, maybe a keyword like while(something)
{
if (something fails) retry; // would retry iteration
} I don't think there's any language with out of the box support for resilience |
Beta Was this translation helpful? Give feedback.
-
In my experience, most cases where something fails sporadically, especially when accessing network resources, are locations where you'd want to implement some sort of backoff, to prevent causing an effective denial of service attack. I don't see any easy way to implement that with this suggestion, that wouldn't be just as easy to write without it. As such, I don't think this a suitable feature for C# - it implements too little to be useful, in my opinion. I feel the suggestion posted by @chrisaut is likely the best compromise here for those who want this - gotos are not necessarily evil when used correctly, and IMO, this is a perfect use case for it. |
Beta Was this translation helpful? Give feedback.
-
You cannot |
Beta Was this translation helpful? Give feedback.
-
The latter case I didn't actually know about, but it makes sense now that I think about it. Either way, any type of loop would likely work just as well. |
Beta Was this translation helpful? Give feedback.
-
FWIW Ruby has a |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Sometimes an exception has been thrown because of a temporary state, such as network error. In order to replay the try block in C# 6 you would implement a loop. This will cause the finally block to run for every retry which may be undesirable. You cannot jump from a catch to a try scope since the label is out of scope.
I propose that a standalone
try;
replays the try block. Alternatively, a new keywordretry
could be used.The finally block is only executed once regardless of how many times the block was retried.
Beta Was this translation helpful? Give feedback.
All reactions