My poor use of golang's defer woke me up
blog.daemonl.com
blog.daemonl.com
You may look at it and rewrite huge chunks because you're a far better programmer now, but trust me, re-writing code is way easier than writing it from scratch
Anyway, good blog post.
> but trust me, re-writing code is way easier than writing it from scratch
Not always true, and not even often true.
> Not always true, and not even often true.
In my experience, virtually always true. Just rereading the code you wrote before will bring back the understanding you had when you wrote it (unless you intentionally wrote obfuscated code, I suppose?), and it'll be immediately obvious to several-years-on you what the shortcomings were of that idea. If you have the time, a full rewrite almost always turns out to be better code than the old version, as long as you can hold off on trying new experiments in the process.
Re-writing code is actually almost always harder than writing it from scratch, but we do it for other benefits: interoperability with legacy components, legacy of expected behavior (warts and all), risk (the old code is debugged), and culture (programs in the team know that code). But if you don't have those requirements, you will often come out behind in rewriting all code rather than going with a green field.
It also depends on whether the work one is doing is cutting edge (lots of experimentation and learning required) or basic dev work over relatively well known concepts.
There is value in a prototype - even if you don't actually use any piece of the prototype in the final product.
G (as in shift-g) jumps to the end of the file in less. Or use tail instead.
Also, less has build in help if you press 'h'.
0%, 45%, etc work like you'd expect
Here I thought this was going to be about defer and how it is error-prone compared to RAII and how it is a modern-day alloca with the same type of scope problems, like being unsafe to use in loops, and how it has a weird order of execution with arguments being evaluated immediately and statement evaluated later on.
Instead it's just about having poor project management. A missed opportunity I guess.
There is definitely something to be written on that, but - well I'm not the guy. Yet.
The second speaks of database connections:
http://jmoiron.net/blog/gos-database-sql/
The general approach:
1. Use a single DB connection, it will pool automatically
2. Use this pattern for all single row queries:
err = db.QueryRow(`...`, ...).Scan(&...)
if err == sql.ErrNoRows {
// Handle no rows
} else if err != nil {
// Handle actual error
}
// All fine
3. Use this pattern for all multi-row queries where you want to return a slice of structs containing the row values. Note that it is fine to call rows.Close() as soon as possible in addition to deferring it, defer takes care of handling it whenever something goes wrong and the explicit call returns the connection as soon as possible: rows, err := db.Query(`...`, ...)
if err != nil {
// Handle connection or statement error
}
defer rows.Close()
things := []rowStruct{}
for rows.Next() {
thing := rowStruct{}
err = rows.Scan(
&thing.id,
&thing.value,
)
if err != nil {
// Handle row parsing error
}
things = append(things, thing)
}
err = rows.Err()
if err != nil {
// Handle any errors within rows
}
rows.Close()
4. Use transactions as serial things, if you need to call another query whilst in a loop where you can't rows.Close(), then read the rows into a slice and range over the slice. You must never have two queries running in the same transaction... so code to do one thing before you do another, and be mindful of this if you are passing the transaction to other funcs.An extra bit of info:
5. defer doesn't just have to be used to call rows.Close(), if you want to know when things happen you can wrap the defer and log:
rows, err := db.Query(`...`,...)
if err != nil {
// Handle connection or statement error
}
defer func() {
log.Println(`Closing rows`)
rows.Close()
}()
On which point, beware there are some theoretically uncaught errors, for example tx.Rollback() can return an error http://golang.org/pkg/database/sql/#Tx.Rollback but if you have called it using defer tx.Rollback() after creating a transaction you'll never know. I hope that the only reason that might error is that something has already ended the transaction, but there is definitely scope for deferred finalisation within a func to cause errors that you might miss and it's worth considering the pattern above if you have any mysterious behaviour going on.I'm actually fighting with some interesting things now with error handling, I wan't aware I had to do my own retry on deadlocks.
Ugh, it's just one of those days I feel like I'm not as good at this as I thought I was.
It all depends where the deadlocks are though, you can easily achieve them in the Go code as well as the database queries.
I'm not around much today as I'm with a client this morning and lunch, but if you're stuck later I may well be in https://gophers.slack.com/ . Happy to help out if I can, as I'm sure most others will be.
https://github.com/aktau/gomig/blob/a63d309848907a72782dd94e...
It's not the prettiest, but I needed it fixed soon. Will refactor later :).
Read about it here as well: http://blog.golang.org/defer-panic-and-recover
Thanks for sharing.
I think that's what HN kind of is...