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.
Related
I am having an issue with Go and performing standard operations on my PostgreSQL database.
I first started coding with GORM, and was getting the following error message while connecting:
dial tcp: lookup tcp/fullstack_api: getaddrinfow: The specified class was not found.
After switching to the standard "database/sql" package with the _ "github.com/lib/pq" postgreSQL dialect, connecting was no longer throwing this error. However, now I get this error when trying to perform any query on the connected database, which I assume GORM was doing initially.
The following code causes this error on my system:
// Connect initiates a DB connection.
func (dbConn *PostgresConnection) Connect() error {
handle, connErr := sql.Open("postgres", dbConn.getConnectionString())
if connErr != nil { // Does NOT cause an error
return connErr
}
if pingErr := handle.Ping(); pingErr != nil { // Causes the above error
return pingErr
}
dbConn.handle = handle
return nil
}
I have checked that the PostgreSQL service is running, and the database exists.
While writing this question, I checked my connection string / env variables again.
I realized that I had a stupid copy/paste error from the day before that I hadn't validated:
Connection string: host=127.0.0.1 port=5432 port=new_database user=db_user password=XXXXXX
As you can see, there is an additional port variable that should have been the dbname. After fixing this issue, everything worked as expected.
Connection string: host=127.0.0.1 port=5432 dbname=new_database user=db_user password=XXXXXX
TLDR: Always re-validate every piece of your connection information when getting this (cryptic) error!
I have successfully connected to a Postgres database using the go sql package:
...
db, err := sql.Open("postgres", connStr)
I then use the returned database to execute a (long running) query:
rows, err := db.Query(...)
And am getting the error:
dial tcp xx.xxx.xxx.xx:5432: connect: connection timed out
I have a couple of questions regarding this:
why is the connection timing out?
is there anything I can do to prevent it timing out?
sql.Open() may just validate its arguments without creating a connection to
the database. To verify that the data source name is valid, call Ping.
The sql.Open() function has only created an object, your pool is currently empty. In simple words connection with the database hasn't been established yet.
You need to call db.Ping() to make sure your pool has a working connection.
I tried to connect to Google CloudSQL PostgresSQL using Gorm golang and seems like it is not working.
Here's the code
func InitDB() *gorm.DB {
psqlInfo := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", os.Getenv("DB_HOST"), os.Getenv("DB_PORT"), os.Getenv("DB_USER"), os.Getenv("DB_PASSWORD"), os.Getenv("DB_NAME"))
db, err := gorm.Open("postgres", psqlInfo)
if err != nil {
fmt.Println("Failed to connect to the Database")
}
fmt.Println("Connected to the Database")
DB = db
return DB
}
If im using the localhost config everything works fine. See my .env file for cloudSQL config
DB_HOST=trader-234509:us-central1:testv1
DB_PORT=5432
DB_USER=username
DB_NAME=testv1
DB_PASSWORD=
Error is saying
dial tcp: lookup trader-234509:us-central1:testv1: no such host
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x40 pc=0x164681d]
My local config (This one works fine)
DB_HOST=localhost
DB_PORT=5432
DB_USER=username
DB_NAME=test
DB_PASSWORD=
Did i do anything wrong?
Here's roughly how I connect from AppEngine:
import (
_ "github.com/GoogleCloudPlatform/cloudsql-proxy/proxy/dialers/postgres"
"fmt"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
func New() (*gorm.DB, error) {
user := "theuser"
password := "apassword"
dbHost := "my-project:the-region:my-db-instance-name"
databaseName := "mydbname"
connString := fmt.Sprintf("host=%s user=%s dbname=%s sslmode=disable password=%s", dbHost, user, databaseName, password)
return gorm.Open(postgres.New(postgres.Config{
DriverName: "cloudsqlpostgres",
DSN: connString,
}))
}
Cloud SQL doesn't support direct connection to instance name for 3rd party application yet, for details: https://cloud.google.com/sql/docs/mysql/connect-external-app
Based on my experience, there are 2 solutions:
As per above instruction, you can setup a Cloud Proxy following the steps and the connections flow would be: Golang app -> Cloud Proxy -> Cloud SQL
This approach is flexible and you're able to control the connection using firewall.
But you have to spend extra $$ to maintain the server and this cloud proxy instance, I've also heard that the cloud proxy may have some performance issues but I don't have exact evidence so far
Assign a Private IP to the Cloud SQL instance, refer to https://cloud.google.com/sql/docs/mysql/private-ip, so that you application can access the DB directly using this IP.
The trade-off is obvious that the app must be in the same network of the project, but this is a much more convenient approach, esp. all your applications are hosted in the same network
I didn't try out the public IP access approach as Cloud Proxy is just what I need if remote network connection is needed
In short, you need to deploy your code in a VM or other Google Managed service using option 2, or setup the cloud proxy to support your local debugging for option 1
Hope it helps
For a project i'm trying to connection a cloud function to a cloud sql database setup as described in this quickstart guide.
The function is configured in the same region, the service account has the Role Cloud SQL-Client. I called the function through my computer like this:
gcloud functions call <function-name> --region=<region> --data '{"recipient":"hello","requester":"hello","message":"test"}'
The connection to the function is working, it seems like just the authentication to the database doesn't work but i don't get where i failed.
I checked the password, user and connection name multiple times, reset the password and it still doesn't work.
I found the issue here related to connecting cloud functions to cloud sql.
I tried surrounding the password in the dsn-string with single-quotes just to be sure escaping of characters in the password isn't a problem.
I also checked the environment variables coming in and they are the ones i entered in the configuration.
The function just pings the database for test purposes:
package receiver
import (
"database/sql"
"fmt"
"net/http"
"os"
// Import Postgres SQL driver
_ "github.com/lib/pq"
)
// Receives a message and stores it
func Receive(w http.ResponseWriter, r *http.Request) {
connectionName := os.Getenv("POSTGRES_INSTANCE_CONNECTION_NAME")
dbUser := os.Getenv("POSTGRES_USER")
dbPassword := os.Getenv("POSTGRES_PASSWORD")
dsn := fmt.Sprintf("user=%s password='%s' host=/cloudsql/%s dbname=messages", dbUser, dbPassword, connectionName)
var err error
db, err := sql.Open("postgres", dsn)
if err != nil {
fmt.Fprintf(w, "Could not open db: %v \n", err)
}
// Only allow 1 connection to the database to avoid overloading
db.SetMaxIdleConns(1)
db.SetMaxOpenConns(1)
defer db.Close()
if pingerror := db.Ping(); pingerror != nil {
fmt.Fprintf(w, "Failed to ping database: %s \n", pingerror)
return
}
}
The variable POSTGRES_INSTANCE_CONNECTION_NAME is formatted as described here as ProjectID:Region:InstanceID.
Expected is a success message or no error and i'm actually getting this message:
pq: password authentication failed for user "postgres"
Note: I also created a function containing the demo code from here with my sql database settings and the error is the same. It seems like i missed some step while setting up the user or sql instance. But i can't find out which.
Feels strange to answer my own question but here it is: For some reason connecting with the postgres user doesn't work. Finally i created a new database user for the function and a password containing only alphanumeric characters.
The unix socket at /cloudsql/{connectionName} is only provided in the GCF runtime. When running locally, you either need change your connection string or use the Cloud SQL proxy to simulate a unix socket at the same path.
I test mORMot component.
I compiled standart demo "28 - Simple RESTful ORM Server"
Run it and get an eror.
Code:
var
aModel: TSQLModel;
aProps: TSQLDBConnectionProperties;
aRestServer: TSQLRestServerDB;
aHttpServer: TSQLHttpServer;
begin
// set logging abilities
SQLite3Log.Family.Level := LOG_VERBOSE;
//SQLite3Log.Family.EchoToConsole := LOG_VERBOSE;
SQLite3Log.Family.PerThreadLog := ptIdentifiedInOnFile;
// ODBC driver e.g. from http://ftp.postgresql.org/pub/odbc/versions/msi
aProps := TODBCConnectionProperties.Create('','Driver=PostgreSQL Unicode'+
{$ifdef CPU64}'(x64)'+{$endif}';Database=postgres;'+
'Server=127.0.0.1;Port=5433;UID=postgres;Pwd=postgres','','');
//readln;
try
// get the shared data model
aModel := DataModel;
// use PostgreSQL database for all tables
VirtualTableExternalRegisterAll(aModel,aProps);
try
// create the main mORMot server
aRestServer := TSQLRestServerDB.Create(aModel,':memory:',false); // authentication=false
try
// optionally execute all PostgreSQL requests in a single thread
aRestServer.AcquireExecutionMode[execORMGet] := amBackgroundORMSharedThread;
aRestServer.AcquireExecutionMode[execORMWrite] := amBackgroundORMSharedThread;
// create tables or fields if missing
aRestServer.CreateMissingTables;
// serve aRestServer data over HTTP
aHttpServer := TSQLHttpServer.Create(SERVER_PORT,[aRestServer],'+',useHttpApiRegisteringURI);
try
aHttpServer.AccessControlAllowOrigin := '*'; // allow cross-site AJAX queries
writeln('Background server is running.'#10);
write('Press [Enter] to close the server.');
readln;
finally
aHttpServer.Free;
end;
finally
aRestServer.Free;
end;
finally
aModel.Free;
end;
finally
aProps.Free;
end;
end.
error
{"Message":"TODBCLib error: [08001] Could not connect to the server;\nNo connection could be made because the target machine actively refused it.\r\n [127.0.0.1:5433]
How to clear it.
I just tested it with Delphi Seattle 10 and the ODBC driver from http://ftp.postgresql.org/pub/odbc/versions/msi/psqlodbc_09_03_0400.zip - with no problem.
Ensure your PostgreSQL server has been defined to run on the same port as expected by the source. Edit the 5433 value into 5432 if you used the default port number.
Being paranoid, I try to always change the default port, which reduces network scan attacks (at least from fast scan). I never use port 22 for ssh, nor 5432 for PostgreSQL. Sorry for the inconvenience.