Golang panic crash prevention - go

In Golang a panic without a recover will crash the process, so I end up putting the following code snippet at the beginning of every function:
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
just in order to prevent my program from crashing. Now I'm wondering, is it really the way to go? Because I think it looks a little bit strange to put the same code everywhere.
It seems to me, the Java way, bubbling the exceptions up to the calling function, until the main function is a better way to control the exceptions/panics. I understand it's by Go's design, but what is the advantage of immediately crashing the process just like what Go does?

You should only recover from a panic if you know exactly why. A Go program will panic under essentially two circumstances:
A program logic error (such as a nil pointer dereference or out-of-bounds array or slice access)
An intentional panic (called using panic(...)) from either your code or code that your code calls
In the first case, a crash is appropriate because it means that your program has entered a bad state and shouldn't keep executing. In the second case, you should only recover from the panic if you expect it. The best way to explain this is simply to say that it's extremely rare, and you'll know that case if you see it. I'm almost positive that whatever code you're writing, you don't need to recover from panics.

Generally, even with exceptions, you catch them at a "FaultBarrier". It's usually the place where all new threads are spawned. The point is to catch and log unexpected failures.
In Go, you use return values for all expected failures. The framework in which you work will generally have a fault barrier to catch a session (ie: usually an http transaction) and log the problem. The only other place I see recover happening is things like non-idempotent Close function. If you have a situation where you can't tell if something is already closed but know it must be closed, then you could end up doing a recover so that a second close panic will be ignored, rather than failing what you are doing all the way up to the FaultBarrier.

I think panic is not same as exception. You can handle exception and running of routine will be continued. Whereas panic causes to termination current routine and you can not skip it.
Exception emits by OS generally and causes to run related routine. Instead panic emits by programmer manually and causes to exit goroutine.
You can define multiple exception for every piece of code within a function. Panic recovery mechanism works for whole function.
Exception designed to be handled whereas Panic designed to termination and panic recovering mechanism seems to be just a trick to control termination.
So exception handling is comparable with error handling.
But how you can take advantages of it in Golang?
I will describe my use case to answer your question.
There is two type of blocking error, Panic and Fatal. You can not recover from fatal error.
Sometimes you need to kill the process. But sometimes you need to restart it.
I used recover() mechanism to recovering from panic error in order to shutting down current goroutine and restarting main functionality.
So I must be careful about error type. Some situations need to emit fatal error such as missing necessary config file. And sometimes it is reasonable to restart the app. Think about all situations that you like to restart the app after crash. Such as:
overloaded service (which causes to DoS)
missing DBMS
unexpected error origin from other used go packages
crashing imagick process for example
and so on
So,
recover() is very beneficial in my case. It gives a chance to shutdown the process clearly before exiting.
Side Note: You can develop an bootstrapper app which will runs main app as detached process. It must re-run main app, if that process exited abnormally.
Use logging in order to debug while keeping your app run.

Related

Can we use recover function to handle all panics instead of treating errors as values?

I know that the Go idiomatic way to handle errors is that it's treated as a value which is checked using an if statement to see if it's nil or not.
However, it soon gets tedious in a long function where you would need to do this if err!=nil{...} in multiple places.
I am aware that error handling is one of the pain points in the Go community.
I was just thinking why can't we do something like this,
func Xyz(param1 map[string]interface{}, param2 context string) (return1 map[string]interface{}, err error)
{
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("error: %s\n", r)
}
}()
.....
.....
.....
// Code causes a panic
}
In your function have a deferred function call which makes use of recover so that if any panic occurs the call stack will start unwinding and the recover function will be invoked causing the program to not terminate, handle itself and return the error back to the caller.
Here is a Go Playground example,
https://play.golang.org/p/-bG-xEfSO-Q
My question is what is the downside of this approach? Is there anything that we lose by this?
I understand that the recover function only works for the same goroutine. Let's assume that this is on the same goroutine.
Can you? Yes.
This is in fact done in a few cases, even in the standard library. See encoding/json for examples.
But this should only ever be done within the confines of your private API. That is to say, you should not write an API, whose exposed behavior includes a possible panic-as-error-state. So ensure that you recover all panics, and convert them to errors (or otherwise handle them) before returning a value to the consumer of your API.
What is the downside of this approach? Is there anything that we lose by this?
A few things come to mind:
It clearly violates the principle of least astonishment, which is why you should absolutely never let this practice cross your public API boundary.
It's cumbersome, any time you need to act based on the error type/content, as it requires you to recover the error, convert it back to its original type (which may or may not be an error type), do your inspection/action, then possibly re-panic. It's much simpler to do all this with just a simple error value.
Closely related to #2, treating errors as values gives you a lot of fine-grained control over when and how to behave in response to an error condition. panic is a very blunt instrument in this regard, so you lose a lot of control.
panic + recover does not perform as well as simply returning an error. In situations where an error truly is exceptional, this may not matter (i.e. in the encoding/json example, it's used when a write fails, which will abort the entire operation, so being efficient is not of high importance).
I'm sure there are other reasons people can come up with. Google is full of blog posts on the topic. For your continued reading, here's one random example.

How can Go code plan for exceptions (panic)?

I do not know Go. I was going through the docs to get an idea of the language and came up against the Defer Panic Recover functionality.
Panic seems to work like exceptions. However I could not find how my code would guard against these exceptions, which can be thrown by layers deep below the ones I call. Java has checked exceptions. Does Go have something similar?
How does this work?
Edit: There seem to be 2 ways to think about this
Panic is very rare and it should be allowed to kill the program as described here
Panic can be used in the regular flow of code - as an example of defer panic as explained here, which describes how to use it for malformed input.
My question pertains to usage of panic in situations like 2, which seems to be easily doable.
Sure, you can use panic to control the regular flow of the program, but I think the more idiomatic way is to just return an error to the caller in such cases (like the case of malformed user input).
Panic is normally used in exceptional cases that imply some kind of a bug in the program. You can call the panic yourself if something goes wrong, but it's also called automatically in the case of nil pointer dereference, out of bounds array access and other similiar cases. Often things like this should crash the program, which it does by default, but if your program is some kind of service, you probably don't want the whole program to crash, you just want it to stop what it was doing when it encountered the crash and return to it's normal state.
Take a web server for example, if some strange request causes an out of bounds array access due to some bug. You don't want the whole server to crash, instead you want it to exit the handler function for that request, but keep listening for new requests. The way to do that is described pretty well in your example two. So you defer a recover() function in the place where you want the panic to stop. If the recover returns an error you know your program was paniccing. You can then do some logging, maybe send an email to yourself saying that something is wrong, etc., and then continue normal program flow. But if you had some validation function that noticed there's something wrong with the request, you probably wouldn't want it to panic. Instead you'd want it to return an error the the caller (request handler) which could then answer the request with 400 bad request, and then exit the handler normally.

Replacing golang error codes with panic for all error handling

We have medium sized application written in go. From all the code lines about 60 percent goes to code error handling. Something like this:
if err != nil {
return err
}
After some time, writing this lines over and over again becomes tiresome and we are now thinking to replace all error codes with panics.
I know panics aren't meant to be used like that.
What can be potential pitfall and does anyone have experience with something similar ?
The main pitfall would be a widespread use of hammers to drive screws. Panic is for unrecoverable/unexpected errors, error return values are for recoverable/expected errors.
Replace the word "panic" with "crash", because that is conceptually what a panic is. Do you honestly want to write an application that by design crashes whenever anything goes in any way remotely wrong? That would be the most fragile application on Earth, the very antithesis of fault tolerance.
As all the comments suggest you could be signing up for a disaster and let me add, whosoever is going to maintain this code will go insane trying to figure out what exactly went wrong when things do go awry if you don't do proper error handling (even if that person is you, it's going to haunt you).
Just repeating what others have said - the problem is being unable to differentiate between these two
a predictable & possibly recoverable condition
perhaps what is unrecoverable (need to exit the application or be handled at a higher level - this may be coming from your own application's conditions or some third party library, but is forcing you out of your normal execution path)
Panics are reserved for the latter types.

Why did Go add panic and recover in addition to error handling? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
Why did Go end up adopting exception handling with panic/recover, when the language is so idiomatic and a strong advocate of error codes? What scenarios did the designers of Go envision not handled by error codes and necessitate panic/recover?
I understand convention says limit panic/recover, but does the runtime also limit them in ways that they can't be used as general throw/catch in C++?
Some history:
In the early days of Go (before version 1.0) there was no recover(). A call to panic() would terminate an application without any way to stop that.
I've found the original discussion that led to adding recover(), you can read it on the golang-nuts discussion forum:
Proposal for an exception-like mechanism
Beware: the discussion dates back to March, 25, 2010, and it is quite exhausting and long (150 posts through 6 pages).
Eventually it was added on 2010-03-30:
This release contains three language changes:
The functions panic and recover, intended for reporting and recovering from
failure, have been added to the spec:
http://golang.org/doc/go_spec.html#Handling_panics
In a related change, panicln is gone, and panic is now a single-argument
function. Panic and recover are recognized by the gc compilers but the new
behavior is not yet implemented.
Multi-return values and conventions provide a cleaner way to handle errors in Go.
That does not mean however that in some (rare) cases the panic-recover is not useful.
Quoting from the official FAQ: Why does Go not have exceptions?
Go also has a couple of built-in functions to signal and recover from truly exceptional conditions. The recovery mechanism is executed only as part of a function's state being torn down after an error, which is sufficient to handle catastrophe but requires no extra control structures and, when used well, can result in clean error-handling code.
Here is a "real-life" example for when/how it can be useful: quoting from blog post Defer, Panic and Recover:
For a real-world example of panic and recover, see the json package from the Go standard library. It decodes JSON-encoded data with a set of recursive functions. When malformed JSON is encountered, the parser calls panic to unwind the stack to the top-level function call, which recovers from the panic and returns an appropriate error value (see the 'error' and 'unmarshal' methods of the decodeState type in decode.go).
Another example is when you write code (e.g. package) which calls a user-supplied function. You can't trust the provided function that it won't panic. One way is not to deal with it (let the panic wind up), or you may choose to "protect" your code by recovering from those panics. A good example of this is the http server provided in the standard library: you are the one providing functions that the server will call (the handlers or handler functions), and if your handlers panic, the server will recover from those panics and not let your complete application die.
How you should use them:
The convention in the Go libraries is that even when a package uses panic internally, its external API still presents explicit error return values.
Related and useful readings:
http://blog.golang.org/defer-panic-and-recover
http://dave.cheney.net/2012/01/18/why-go-gets-exceptions-right
https://golang.org/doc/faq#exceptions
http://evanfarrer.blogspot.co.uk/2012/05/go-programming-language-has-exceptions.html
I think that your question is the result of a mental model you maintain which is instilled by popular mainstream languages such as Java, C++, C#, PHP and zillions of other which simply got exceptions wrong.
The thing is, exceptions per se are not a wrong concept but abusing them to handle cases which are, in reality, not exceptional is. My personal pet peeve is the filesystem handling API of Java (and .NET, which copied that of Java almost verbatim): why on Earth failure to open a file results in an exception if that file does not exist? The filesystem is an inherently racy medium, and is specified to be racy, so the only correct way to ensure a file exists before opening it for reading is to just open it and then check for "file does not exist" error: the case of the file not existing is not exceptional at all.
Hence Go clearly separates exceptional cases from plain normal errors.
The motto of the stance Go maintains on handling errors is "Errors are values" and thus normal expected errors are handled as values, and panic() serves to handle exceptional cases. A good simple example:
An attempt to dereference a nil pointer results in a panic.
The rationale: your code went on and tried to dereference a pointer which does not point to any value. The immediately following code clearly expects that value to be available—as the result of the dereferencing operation. Hence the control flow clearly can't proceed normally in any sensible way, and that's why this is an exceptional situation: in a correct program, dereferencing of nil pointers cannot occur.
The remote end of a TCP stream abruptly closed its side of the stream and the next attempt to read from it resulted in an error.
That's pretty normal situation: one cannot sensbily expect a TCP session to be rock-solid: network outages, packet drops, unexpected power blackouts do occur, and we have to be prepared for unexpected stream closures by our remote peers.
A minor twist to panic() is that Go does not force you to blindly follow certain dogmas, and you can freely "abuse" panic/recover in tightly-controlled specific cases such as breaking out from a deeply-nested processing loop by panicking with an error value of a specific type known and checked for at the site performing recover.
Further reading:
"Why should I have written ZeroMQ in C, not C++".
"Errors are values".
"Is function parameter validation using errors a good pattern in Go?".
I think the reason is concurrency model. Go is highly concurrent language in nature and core syntax. Scenario when some computation localized to concurrent process failure, but hole system continue to work can be viewed as normal. To my mind panic and recover are about failure handling, not exceptions.
Because this is more powerful as it allows to create more resilient programs.
In C you can't recover from a NULL pointer dereference.
In Go you can catch it with recover.
This allows for example to create in Go an HTTP server that can catch a NULL pointer dereference when handling a request and return an error 500 instead of crashing the whole server.

Is function parameter validation using errors a good pattern in Go?

Is parameter validation using error return codes considered good practice ? I mean where should somebody use errors vs panics (are there any guidelines?).
For instance:
Is checking for non-nil + returning an error if it is nil a good
practice ?
Or checking for correct integer ranges etc.
I imagine that using errors that often would make Go feel very C-ish and would look pretty bad. Are panics a good alternative in those situations ?
Or should a Gopher use the Python/Ruby/JS-approach "just let it fail" ?
I'm a bit confused because panics are for real "errors" in my understanding. But using errors all the time is just bad.
And even if I would return error code: What could I do if somebody passes wrong parameter to my function but ignores the errors codes ? -> Nothing! So honestly I would say panics are nice for those situations but in a language where error codes are used over panics this is not very clear.
"Escaping" panics1 in Go (I mean, those which might be produced by the functions comprising the public API of your package) are to deal with errors programmers do. So, if your function gets a pointer to an object, and that can't be nil (say, to indicate that the value is missing) just go on and dereference the pointer to make the runtime panic itself if it happens to be nil. If a function expects an integer that must be in a certain range, panic if it's not in that range — because in a correct program all values which might be passed to your function are in that range, and if they don't then either the programmer failed to obey the API or they did not sanitize the value acquired from the outside which, again, is not your fault.
On the other hand, problems like failure to open a file or pefrorm some other action your function is supposed to perform when called correctly should not cause panics and the function should return an appropriate error instead.
Note that the recommendation for explicit checking for null parameters in the functions of public APIs in .NET and Java code has different goal of making such kinds of errors sort-of more readable. But since 99% of .NET and Java code just lets all the exceptions propagate to the top level (and then be displayed or may be logged) it's just replacing one (generated by runtime) exception with another. It might make errors more obvious—the execution fails in the API function, not somewhere deeper down the call stack—but adds unnecessary cruft to these API functions. So yes, this is opinionated but my subjective opinion is: to let it just crash is OK in Go—you'll get a descriptive stack trace.
TL;DR
With regard to processing of run-time problems,
panics are for programming errors;
returning errors is for problems with carrying out the intended tasks of functions.
1 Another legitimate use for panics is quick "cold-path" returning from deep recursive processing/computation; in this case panic should be caught and processed by your package, and the corresponding public API functions should return errors. See this and this for more info.
The answer to this is subjective. Here are my thoughts:
Regarding panic, I like this quote from Go By Example (ref)
A panic typically means something went unexpectedly wrong. Mostly we use it to fail fast on errors that shouldn’t occur during normal operation, or that we aren’t prepared to handle gracefully.
In the description of your use case, I would argue that you should raise an errors and handle the errors. I would further argue that it is good practice to check the error status when one is provided by the function you are using and that the user should check if one is provided in the documentation.
Panics I would use to stop the execution if I run across an error that is returned from the function you are writing that I check and don't have a way to recover from.

Resources