I have a web application I am starting. Works fine upon startup but if I leave it (for say, an hour) and hit it with another request the query hangs. I thought about closing it after each query then opening up a new connection but the docs explicitly say "It is rare to Close a DB, as the DB handle is meant to be long-lived and shared between many goroutines.". What am I doing wrong?
package main
import (
"database/sql"
"log"
"net/http"
_ "github.com/lib/pq"
)
var Db *sql.DB
func main() {
var err error
Db, err = sql.Open("postgres", "user=me password=openupitsme host=my.host.not.yours dbname=mydb sslmode=require")
if err != nil {
log.Fatal("Cannot connect to db: ", err)
}
http.HandleFunc("/page", myHandler)
http.ListenAndServe(":8080", nil)
}
func myHandler(w http.ResponseWriter, r *http.Request) {
log.Println("Handling Request....", r)
query := `SELECT pk FROM mytable LIMIT 1`
rows, err := Db.Query(query)
if err != nil {
log.Println(err)
}
defer rows.Close()
for rows.Next() {
var pk int64
if err := rows.Scan(&pk); err != nil {
log.Println(err)
}
log.Println(pk)
}
log.Println("Request Served...")
}
EDIT #1: My postgres log shows:
2015-07-08 18:10:01 EDT [7710-1] user@here LOG: could not receive data from client: Connection reset by peer
2015-07-08 18:20:01 EDT [7756-1] user@here LOG: could not receive data from client: Connection reset by peer
I have experienced similar issues. In our case, the problem was caused by a connection tracking firewall located between the client machine and the database.
Such firewalls keep track of TCP level connections, and in order to limit resource usage, then will time out connections which to them appear inactive for an extended period. The symptoms we observed in this case were very similar to yours: at the client end, the connection appears to be hanging, while at the server end you can see connection reset by peer
.
One way to prevent this is to ensure that TCP Keepalives are enabled, and that the keepalive interval is less than the timeout of the firewalls, routers, etc which are causing your connection issue. This is controlled by the libpq connection parameters keepalives
, keepalives_idle
, keepalives_interval
and keepalives_count
which you can set in the connection string. See the manual for a description of these parameters.
keepalive
determines if the keepalive function is enabled or not. It defaults to 1
(enabled) so you probably do not need to specify this.keepalives_idle
determines the amount of idle time before it will send a keepalive. If you do not specify this, it will default to the default for the operating system.
In a Linux system you can see the default by examining /proc/sys/net/ipv4/tcp_keepalive_time
- in my server it is set to 7200 seconds, which would be too long in your case, since your observation is that the connection is dropped after ~1 hour.
You could try setting it to, say, 2500 seconds.
The Linux Documentation Project provides a useful TCP Keepalive HOWTO document that describes how they work in some detail.
Note that not all operating systems support TCP keepalives. If you are unable to enable keepalives here are some other options you might like to consider:
If it is in your control, reconfigure the firewall/router which is dropping the connection so that it will not do so for Postgresql client connections
At an application level, you might be able to send some traffic that will keep the DB handles active - for example sending a statement such as SELECT 1;
every hour or so. If your programming environment provides connection caching (from the comments I gather it does) then this might be tricky.