Here’s an overview of what I’ve done, but no quality claim is made. In package main there’s a global:
type DB struct { *sql.DB } var database DB In func main() I call an initialization function that reads a JSON configuration file and does sql.Open with dbname, user, password, host, port, and sslmode from the file, result assigned to database.DB. There’s a database.Ping() then database.SetMaxIdleConns and database.SetMaxOpenConns. Any error causes a panic. The rest of package main has many methods on DB. An example is all of my handlers call database.validSession(r). The method approach is so that I can configure a test database when writing unit tests for package main as suggested by Google search results awhile back. In some cases database corruption can happen in my app by concurrent DB.Query calls followed by DB.Exec. I don’t change the postgres transaction isolation, but some methods are on TX instead of DB because of these race conditions. type TX struct { *sql.Tx } // error handling for sql.DB.Begin and sql.Tx.Commit is always a panic func (db DB) Begin() TX { func (tx TX) Commit() { One of my TX methods has a forWrite bool to add a FOR UPDATE to the query because SELECT won’t lock the row for a transaction without it in postgres at the default transaction isolation level. Here’s an example query: err = db.QueryRow("SELECT "+session_name+" FROM "+session_table+" WHERE "+ session_key+"=$1;", keyCookie.Value).Scan(&name) I have an open issue for figuring out how to make this better/cleaner, but at least all of my queries rely on consts for key names. Error handling for database interaction is quite variable depending on the use of the surrounding function. Any unhandleable error results in a panic, and sql.ErrNoRows is useful. I haven’t used the context package, or a middleware pattern besides copy-paste of function calls in handlers. Some of my code is messy with these patterns. Commit can’t always be deferred which brings us back to writing it in at every return point. The choices of where transactions are opened in relation to the method uses seems arbitrary because of layering fixes over time without a rewrite. I haven’t written package main unit tests yet so I can’t comment on how much value methods on DB and TX add. But problems are mostly straightforward to fix for now. My current function structure causes transactions to overlap within one path of execution sometimes (a TX is open during a DB query or exec), but this seems to not cause problems since they are guaranteed to be accessing different tables in my case. Here’s the app source, all database interaction is in package main: https://github.com/pciet/wichess Concurrent request load testing helped me find 5+ problems with my database interactions. Transactions aren't completely straightforward to get right. Matt On Friday, January 5, 2018 at 9:16:34 AM UTC-6, Manlio Perillo wrote: > > Recently I have developed a few web applications in Go, using a PostgreSQL > database, but I have yet understand the best way to use the database/sql > package. > > The simplest method seems to start a transaction in the HTTP handler and > pass a *sql.Tx value to all the functions that need to access the database. > However I'm afraid that this method is "too" simple. > > One possible problem is an HTTP handler acting as a middleware, and both > the "middleware" an the "normal" HTTP handler needs a transaction. > Using the "simplest method", the middleware and the handler will end up > using two distinct transaction, and this may not be what one expects. > > > The other solution is to do what frameworks like Django do, storing > something like > > type Interface interface { > func Exec(query string, ...) ... > func Query(query string, ...) ... > func InTransaction) bool > } > > as a per request data, including transaction state, (ab)using > Context.WithValue. But I suspect that this is not a good idea. > > > Thanks Manlio > -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.