Golang MySQL error - packets.go:33: unexpected EOF - mysql

I am switching my entire code base from PHP to Go and during several processes that run, I randomly get this error:
[mysql] 2016/10/11 09:17:16 packets.go:33: unexpected EOF
Here is my db package that handles all connections to the database:
package db
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
"pkg/db"
)
var connection *sql.DB
var err error
func GetConnection() *sql.DB {
if connection != nil {
fmt.Println("********** CHECKING PING")
err = connection.Ping()
if err == nil {
fmt.Println("************ CONNECTION STILL ACTIVE")
return connection
} else {
fmt.Println("********** PING ERROR: " + err.Error())
}
}
connection, err = sql.Open("mysql", db.DEVUSER + ":" + db.DEVUSER_PASSWORD + "#tcp(localhost:3306)/main?parseTime=true")
if err != nil {
panic(err)
}
return connection
}
Is there anything I'm doing wrong with this db package that causes this error to be thrown? What exactly does this error mean? I make sure to return the current connection if there is one open so for multiple requests it uses the same connection object.
Here's an excerpt from the mysql packets.go:
// Read packet to buffer 'data'
func (mc *mysqlConn) readPacket() ([]byte, error) {
var payload []byte
for {
// Read packet header
data, err := mc.buf.readNext(4)
if err != nil {
errLog.Print(err)
mc.Close()
return nil, driver.ErrBadConn
}
// Packet Length [24 bit]
pktLen := int(uint32(data[0]) | uint32(data[1])<<8 | uint32(data[2])<<16)
if pktLen < 1 {
errLog.Print(ErrMalformPkt)
mc.Close()
return nil, driver.ErrBadConn
}
// Check Packet Sync [8 bit]
if data[3] != mc.sequence {
if data[3] > mc.sequence {
return nil, ErrPktSyncMul
}
return nil, ErrPktSync
}
mc.sequence++
// Read packet body [pktLen bytes]
data, err = mc.buf.readNext(pktLen)
if err != nil {
errLog.Print(err)
mc.Close()
return nil, driver.ErrBadConn
}
isLastPacket := (pktLen < maxPacketSize)
// Zero allocations for non-splitting packets
if isLastPacket && payload == nil {
return data, nil
}
payload = append(payload, data...)
if isLastPacket {
return payload, nil
}
}
}
The first "errLog.Print(err)" is line 33 in the "Read packet header" section.
Any help is greatly appreciated!
I added a few log.Println to the connection package and let the process run, and right where I get this error, this is what the console prints:
********** CHECKING PING
************ CONNECTION STILL ACTIVE
[mysql] 2016/10/11 11:57:27 packets.go:33: unexpected EOF
********** CHECKING PING
************ CONNECTION STILL ACTIVE

Looks like the link to the github issue provided the fix. The fix, at least for my situation was setting the MaxIdleConnections to 0. I have kept a server up for 24 hours, running queries against it every several hours and have yet to reproduce the error.
Thanks to #city for the link.

import (
"database/sql"
"time"
)
//..snip...
db, err = sql.Open("mysql", url)
db.SetConnMaxLifetime(time.Minute * 4) // <-- this
did it for me. Explanation: here

func parent() {
conn, err := db.ClientCat.Conn(ctx)
if err != nil {
return nil, customError.MySqlConnectionError(ctx, errors.New("Connection_not_Established"))
}
//execute some query
defer conn.Close() //*******this won't close until child() finishes
child()
}
func child() {
//under high traffic it won't get connection as they are taken by parent method
//usually we have 10,20,100 etc.. connections available as per configuration, under high traffic all will be taken by parent
conn, err := db.ClientCat.Conn(ctx)
if err != nil {
return nil, customError.MySqlConnectionError(ctx, errors.New("Connection_not_Established"))
}
//execute some query
defer conn.Close()
}
this can also happen in above scenario, please verify.
if system is under load and we have received hundreds of requests then this issue can happen.
basically every time request was going to make connection in child method, it was waiting for connection for a long time then timing out as connections are held by parent method.
also parent method won't be over till child method completes. but child method will wait for connection. So once our parent method has made number of connection = max connection then even in parent method it can start failing to get connection for next requests

DSN adds net_write_timeout option
root:root#tcp(localhost:3306)/prod?net_write_timeout=6000

Related

Issue with connections pool on mysql

I'm having some issues with the api I'm developing: sometimes (yes, not always) when I make a request to the golang server from my angular app, it gaves me this error: "sql: database is closed" when I'm tring to execute "QueryContext", but I figured that it happens more frequently on a func that request a larger data from database (200 record top).
Is there a way to check if the connection is still open\valid? shouldn't golang's connection pool do it automatically? (I have other api more "light" in the same server with the same database and everything work smootly)
Is there any MySql setting I should change?(mysql has defaullt settings)
golang version: 1.16,
Mysql 8.0.17
Hre is an example of my code:
on package database.go
func OpenConnection() (*sql.DB, error)
{
connection, err = sql.Open("mysql", "root#/my_database")
if err != nil {
log.Println("Error opening the connection with the database")
return nil, err
}
return connection, nil
}
On main.go
func main() {
---
http.HandleFunc("/apicall1", customFunc)
http.HandleFunc("/apicall2", customFunc)
http.HandleFunc("/apicall3", customFunc)
}
func customFunc(w http.ResponseWriter, r *http.Request) {
conn, err := database.OpenConnection()
if err != nil {
//handle error 500 response
}
defer conn.Close()
switch(r.URL.Path) {
case "url1": my_package.Func1(conn)
case "url2": my_package.Func2(conn)
case "url3": my_package.Func3(conn)
...
default: //handle not found response
}
}
You need to configure your sql.DB for better performance from your pool and to avoid hitting max limit of your db allowed connection, here as an example I have made max 100 connection and idle 25 you can change it as per the load. Also it's a nice idea to ping db after making successful connection, just to ensure connection is success.
func OpenConnection() (*sql.DB, error)
{
connection, err = sql.Open("mysql", "root#/my_database")
if err != nil {
log.Println("Error opening the connection with the database")
return nil, err
}
connection.DB().SetMaxIdleConns(25)
connection.DB().SetMaxOpenConns(100)
dberr = connection.DB().Ping()
if dberr != nil {
log.Println("failed to ping db repository : %s", dberr)
return nil, dberr
}
return connection, nil
}
Reference - https://www.alexedwards.net/blog/configuring-sqldb

Error while creating connection with phpmyadmin using golang "commands out of sync. Did you run multiple statements at once?"

I'm facing some issue while fetching the data from the MySQL database using golang below is my code and the error that I'm facing
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
func ConnectMsqlDb() (db *sql.DB, err error) {
db, err = sql.Open("mysql", fmt.Sprintf("%s:%s#tcp(%s:"+sqlDbPort+")/"+sqlDB, sqlUserName, sqlPassword, dbServerIP))
if err != nil {
return nil, err
}
//defer db.Close()
err = db.Ping()
if err != nil {
return nil, err
}
return db, nil
}
func GetSqlData() (err error, data interface{}) {
db, err := ConnectMsqlDb()
if err != nil {
// here it will returning me the error
return err, nil
}
rows, err := db.Query("SELECT * FROM users")
if err != nil {
return err, nil
}
for rows.Next() {
}
defer db.Close()
fmt.Println(rows)
return err, rows
}
func main() {
err, data := GetSqlData()
fmt.Println("data", data, "err", err)
}
error
data commands out of sync. Did you run multiple statements at once?
Can anyone tell me why I'm facing this issue
If the error is comming while opening a connection to mysqld , it could be possible that MySQL server (mysqld) is blocking the host from connecting to it. It means that mysqld has received many connection requests from the given host that were interrupted in the middle.
Read why? To confirm you could see the DB's logs as well. So, a way to unblock it is to flush the cached hosts using the MySQL CLI:
mysql> FLUSH HOSTS;
And then try again to connect.
Plus, give this answer a read as well. Might help.
You can check the state of the socket used by mysqld using (For Linux):
netstat -nt
Check if there's any previously hanging connection from the host to mysqld. If yes, then kill it and try again.

Golang Gorilla Session not working in Chrome Browser But works on Safari and Firefox

I am having trouble deploying my golang web app into a live server.
While running this code locally, it was working perfectly fine.
Note that I did the following checks to try to solve this problem:
I made sure that my systemd had the environment variable SESSION_KEY.
( Printed out the os.Getenv("SESSION_KEY") and it returned the key.
I tried to check all the returned errors from the below functions.
( All of the errors are nil; however when I try to get_session after the fact, it returns false for the ok variable and 0 for the uid)
I tried working this code on firefox, safari, and chrome. ( Chrome is the only one that does not work. )
I only pulled out the code that had to do with "github.com/gorilla/sessions" package.
var store = sessions.NewCookieStore([]byte(os.Getenv("SESSION_KEY")))
func init() {
fmt.Println(store)
fmt.Println("SESSION KEY BELOW: ", os.Getenv("SESSION_KEY"))
store.Options = &sessions.Options{
Domain: "buckler.solutions",
Path: "/",
MaxAge: 3600 * 8, // 8 hours
HttpOnly: true,
}
}
func get_session(request *http.Request) (int, error) {
// META: Gets the session cookie and returns the result.
session, err := store.Get(request, "session-name")
fmt.Println("ERROR FROM store.Get: ", err)
untyped_uid, untyped_ok := session.Values["uid"]
fmt.Println("SESSIONUID before cast: ", untyped_uid)
uid, ok := untyped_uid.(int)
fmt.Println("SESSIONUID after cast: ", uid)
if !untyped_ok || !ok {
fmt.Println("GET SESSION ERRORS.")
fmt.Println("UNTYPED_OK: ", untyped_ok)
fmt.Println("OK: ", ok)
return -1, errors.New("no session")
} else {
return uid, nil
}
}
func set_session(response http.ResponseWriter, request *http.Request, uid int) {
// META: Sets the session cookie and saves it.
session, err := store.Get(request, "session-name")
fmt.Println("SESSION in SET SESSION: ", session)
fmt.Println("SESSION SET ERROR:", err)
session.Values["uid"] = uid
fmt.Println("UID: ", uid)
err = session.Save(request, response)
fmt.Println("SESSION SAVE ERROR:", err)
}
func clear_session(response http.ResponseWriter, request *http.Request) {
// META: Clears the session so that it won't remember the user.
session, err := store.Get(request, "session-name")
fmt.Println("ERROR CLEARING SESSION: ", err)
session.Values["uid"] = -1
session.Save(request, response)
}
I think some proper error handling would help you quickly debug your problem and see just exactly what may be the problem.
In Golang its standard practice to handle the error right after its declared.
Example: in your get_session method
session, err := store.Get(request, "session-name")
if err != nil {
return -1, err
}
The problem in your code is that you arent stopping execution if an error occurs which makes it 10x harder to figure out where something might've went wrong.
Sometimes its even helpful (for debugging) to add small comments that will help you even pinpoint the error more, like:
session, err := store.Get(request, "session-name")
if err != nil {
fmt.Println("hey an error occurred trying to get the session: ",err.Error())
return -1, err
}
I think once you put in some proper error handling you can debug your issue quickly. (And handle every error, dont ignore any)

How to fix leaking MySQL conns with Go?

I have data on my local computer in 2 MySQL databases (dbConnOuter and dbConnInner) that I want to process and collate into a 3rd database (dbConnTarget).
The code runs for about 17000 cycles, then stops with these error messages:
[mysql] 2018/08/06 18:20:57 packets.go:72: unexpected EOF
[mysql] 2018/08/06 18:20:57 packets.go:405: busy buffer
As far as I can tell I'm properly closing the connections where I'm reading from and I'm using Exec for writing, that I believe handles its own resources. I've also tried prepared statements, but it didn't help, the result was the same.
Below is the relevant part of my code, and it does similar database operations prior to this without any issues.
As this is one of my first experiments with Go, I can't yet see where I might be wasting my resources.
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
)
var dbConnOuter *sql.DB
var dbConnInner *sql.DB
var dbConnTarget *sql.DB
func main() {
dbConnOuter = connectToDb(dbUserDataOne)
dbConnInner = connectToDb(dbUserDataTwo)
dbConnTarget = connectToDb(dbUserDataThree)
// execute various db processing functions
doStuff()
}
func connectToDb(dbUser dbUser) *sql.DB {
dbConn, err := sql.Open("mysql", fmt.Sprintf("%v:%v#tcp(127.0.0.1:3306)/%v", dbUser.username, dbUser.password, dbUser.dbname))
if err != nil {
panic(err)
}
dbConn.SetMaxOpenConns(500)
return dbConn
}
// omitted similar db processing functions that work just fine
func doStuff() {
outerRes, err := dbConnOuter.Query("SELECT some outer data")
if err != nil {
panic(err)
}
defer outerRes.Close()
for outerRes.Next() {
outerRes.Scan(&data1)
innerRes, err := dbConnInner.Query("SELECT some inner data using", data1)
if err != nil {
panic(err)
}
innerRes.Scan(&data2, &data3)
innerRes.Close()
dbConnTarget.Exec("REPLACE INTO whatever", data1, data2, data3)
}
}

How to match request to response while proxying packets?

I'm working on MySQL proxy with browser based GUI.
Core of an app looks like:
//Trying to connect to server
rightConn, err := net.Dial("tcp", l.rightAddr)
if err != nil {
return
}
defer rightConn.Close()
wg.Add(2)
//Start passing packets from client to server
go func() {
defer wg.Done()
l.Pipe(leftConn, rightConn)
}()
//Start passing packets from server to client
go func() {
defer wg.Done()
l.Pipe(rightConn, leftConn)
}()
wg.Wait()
And here's definition of Pipe:
func (l *Lottip) Pipe(right, left net.Conn) {
for {
pkt, err := mysql.ReadPacket(right)
if err != nil {
break
}
if _, err = mysql.WritePacket(pkt, left); err != nil {
break
}
}
}
The reason i'm using my custom proxy function instead of
go io.Copy(left, right)
go io.Copy(right, left)
is i have to parse each MySQL's Request/Response packet and prepare it for further processing.
As we all know MySQL client can send a lot of queries and get responses in random order.
The problem is i cannot get how to match request to it's response. While i'm piping using 2 goroutines(1 per direction) i can easily read server response but i don't know to what request it responding.
I've examined request/response between MySQL client and server via Wireshark and found no mention for packet_id or query_id or similar