Skip to content

Commit

Permalink
[MySQL] Add connection attribute example for Go
Browse files Browse the repository at this point in the history
  • Loading branch information
andygrunwald committed Mar 17, 2024
1 parent 4f2b470 commit 8fba7d9
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 1 deletion.
2 changes: 1 addition & 1 deletion mysql/README.md
Expand Up @@ -8,7 +8,7 @@ Programmming languages:

- [Python (with PyMySQL)](./python-PyMySQL)
- [Python (with mysql-connector-python)](./python-mysql-connector-python)
- Go: Not supported yet, see [Support for sending connection attributes #737 @ go-sql-driver/mysql](https://github.com/go-sql-driver/mysql/pull/737)
- [Go](./go)
- PHP: Not supported yet, see [Add possibility to add MySQL Connection Attributes (incl. custom ones) @ PHP Bugtracker](https://bugs.php.net/bug.php?id=81314)

## How it works
Expand Down
74 changes: 74 additions & 0 deletions mysql/go/README.md
@@ -0,0 +1,74 @@
![MySQL logo](../../images/mysql-logo.png)

# your _MySQL_ connection deserves a name: Go edition (with github.com/go-sql-driver/mysql)

An example on how to assign a name to a [MySQL](https://www.mysql.com/) connection in Go.

## Get it running

1. Start the MySQL docker container:
```sh
$ docker run --rm \
--publish 3306:3306 \
--env MYSQL_ROOT_PASSWORD=secret \
--env MYSQL_DATABASE=dummy \
--name ycdan-mysql \
--detach \
mysql:8.0.36 mysqld --default-authentication-plugin=mysql_native_password
```

2. Compile the example program:
```sh
$ go build -o your-connection-deserves-a-name
```

3. Start the example program:
```sh
$ ./your-connection-deserves-a-name
```

You should see something like

```
2024/03/17 09:04:05 Connecting to MySQL on root:secret@tcp(127.0.0.1:3306)/dummy?connectionAttributes=program_name:currency-conversion-app
2024/03/17 09:04:05 Connecting to MySQL on root:secret@tcp(127.0.0.1:3306)/dummy?connectionAttributes=program_name:currency-conversion-app ... Successful
2024/03/17 09:04:05
2024/03/17 09:04:05 Keeping the connection open ...
2024/03/17 09:04:05 You can connect to the MySQL database and execute the query:
2024/03/17 09:04:05 SELECT
2024/03/17 09:04:05 session_connect_attrs.ATTR_VALUE AS program_name,
2024/03/17 09:04:05 processlist.*
2024/03/17 09:04:05 FROM information_schema.processlist
2024/03/17 09:04:05 LEFT JOIN performance_schema.session_connect_attrs ON (
2024/03/17 09:04:05 processlist.ID = session_connect_attrs.PROCESSLIST_ID
2024/03/17 09:04:05 AND session_connect_attrs.ATTR_NAME = "program_name"
2024/03/17 09:04:05 )
2024/03/17 09:04:05 Hit CTRL + C or cancel the process to stop.
2024/03/17 09:04:05
```

4. Login into your database and execute the query:
```sql
SELECT
session_connect_attrs.ATTR_VALUE AS program_name,
processlist.*
FROM information_schema.processlist
LEFT JOIN performance_schema.session_connect_attrs ON (
processlist.ID = session_connect_attrs.PROCESSLIST_ID
AND session_connect_attrs.ATTR_NAME = "program_name"
)
```

The result should look similar to:

```
program_name | ID | USER | HOST | DB | [...]
--------------------+----+------+------------------+-------+------
unit-conversion-app | 11 | root | 172.17.0.1:56382 | dummy | [...]
```

## Don't know what this is all about?

Read the original blog post [_your database connection deserves a name @ andygrunwald.com_](https://andygrunwald.com/blog/your-database-connection-deserves-a-name/ "Article your database connection deserves a name at Andy Grunwalds blog").

Additionally, you can check out the [projects README](https://github.com/andygrunwald/your-connection-deserves-a-name#readme).
8 changes: 8 additions & 0 deletions mysql/go/go.mod
@@ -0,0 +1,8 @@
module github.com/andygrunwald/your-connection-deserves-a-name/mysql/go

go 1.22.1

require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/go-sql-driver/mysql v1.8.0 // indirect
)
4 changes: 4 additions & 0 deletions mysql/go/go.sum
@@ -0,0 +1,4 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/go-sql-driver/mysql v1.8.0 h1:UtktXaU2Nb64z/pLiGIxY4431SJ4/dR5cjMmlVHgnT4=
github.com/go-sql-driver/mysql v1.8.0/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
86 changes: 86 additions & 0 deletions mysql/go/main.go
@@ -0,0 +1,86 @@
package main

import (
"database/sql"
"fmt"
"log"
"net/url"
"time"

_ "github.com/go-sql-driver/mysql"
)

const (
MySQLUsername = "root"
MySQLPassword = "secret"
MySQLHostname = "127.0.0.1"
MySQLPort = 3306
MySQLDatabase = "dummy"
ConnectionAttributeName = "program_name"
ConnectionName = "currency-conversion-app"
)

func main() {
// Doing all the magic: Setting a proper connection/application name.
//
// Connection attributes are key-value pairs that application programs can pass to the server at connect time.
// This property can be added to the connection string.
//
// The application name will be maintained in the `session_connect_attrs` table of your MySQL-Server.
//
// MySQL docs:
// - Performance Schema Connection Attribute Tables: https://dev.mysql.com/doc/refman/8.0/en/performance-schema-connection-attribute-tables.html
// - Connection Attribute Limits: https://dev.mysql.com/doc/refman/8.0/en/performance-schema-connection-attribute-tables.html#performance-schema-connection-attribute-limits

dsn := fmt.Sprintf("%s@tcp(%s:%d)/%s?connectionAttributes=%s:%s", url.UserPassword(MySQLUsername, MySQLPassword), MySQLHostname, MySQLPort, MySQLDatabase, ConnectionAttributeName, ConnectionName)

// Building the connection
log.Printf("Connecting to MySQL on %s\n", dsn)
client, err := sql.Open("mysql", dsn)
if err != nil {
panic(err)
}
defer func() {
if err = client.Close(); err != nil {
panic(err)
}
}()

// Dummy values for demo purpose
client.SetConnMaxLifetime(time.Minute * 3)
client.SetMaxOpenConns(10)
client.SetMaxIdleConns(10)

// Establish the connection, because depending on the DB driver, we might only
// validate the credentials, but don't built up a connection.
err = client.Ping()
if err != nil {
panic(err)
}
log.Printf("Connecting to MySQL on %s ... Successful\n", dsn)

log.Println("")
log.Println("Keeping the connection open ...")
log.Println("You can connect to the MySQL database and execute the query:")
log.Println(" SELECT")
log.Println(" session_connect_attrs.ATTR_VALUE AS program_name,")
log.Println(" processlist.*")
log.Println(" FROM information_schema.processlist")
log.Println(" LEFT JOIN performance_schema.session_connect_attrs ON (")
log.Println(" processlist.ID = session_connect_attrs.PROCESSLIST_ID")
log.Println(" AND session_connect_attrs.ATTR_NAME = \"program_name\"")
log.Println(" )")
log.Println("Hit CTRL + C or cancel the process to stop.")
log.Println("")

// Just looping to keep the process running.
// In detail we do not keep the connection active, but rely on the standard timeout.
// This way we provide time for the user to check the connection name behaviour.
for {
err = client.Ping()
if err != nil {
panic(err)
}
time.Sleep(5 * time.Second)
}
}

0 comments on commit 8fba7d9

Please sign in to comment.