database-connectionmariadbconnection-poolingdvibed

Database connection pattern


I use mysql-native. This driver is suppport vibed's connection pool. On dlang newsgroup mysql-native developer Nick Sabalausky wrote:

"If you're using a connection pool, you shouldn't need to worry about closing the connection. The whole point is that the connections stay open until you need to use one again. When your program ends, then connections will close by themselves."

"You create the pool once (wherever/whenever you want to). Then, every time you want to use the database you obtain a connection by calling MySqlPool.lockConnection."

"Calling 'close' will always close the connection. If you got you connection from the pool, then it will automatically return to the pool when you're no longer using it. No need to do anything special for that."

The question about how pool should be done? I have read about singleton pattern and can't unserstand is it this case.

I wrote next code:

database class:
import std.stdio;
import std.string;
import mysql;
import vibe.d;

import config;
import user;

class Database
{
    Config config;
    MySqlPool mydb;
    Connection connection;

    this(Config config)
    {
        this.config = config;
        mydb = new MySqlPool(config.dbhost, config.dbuser, config.dbpassword, config.dbname, config.dbport);    
    }

    void connect()
    {
        if(connection is null)
        {
            connection = mydb.lockConnection();
        }
        scope(exit) connection.close();
    }

}

users class/struct:

module user;

import mysql;
import vibe.d;

struct User
{
    int id;
    string login;
    string password;
    string usergroup;
}
    void getUserByName(string login)
    {
        User user;
        Prepared prepared = prepare(connection, `SELECT id, login, password, usergroup from users WHERE login=?`); // need to get connection accessible here to make request to DB
        prepared.setArgs(login);
        ResultRange result = prepared.query();
        if (result.empty) 
            logWarn(`user: "%s" do not exists`, login);
        else
        {
                Row row = result.front;
                user.id = row[0].coerce!(int);
                user.login = row[1].coerce!string;
                user.password = row[2].coerce!string;
                user.usergroup = row[3].coerce!string;

        logInfo(`user: "%s" is exists`, login);
        }

    }

The problem that I can't understand what is proper way to getting access to connection instance. It seems that it's very stupid ideas to create every new database connection class inside users structure. But how to do it's in better way? To make Connection connection global? Is it's good? Or there is more correct way?


Solution

  • scope(exit) connection.close();
    

    Delete that line. It's closing the connection you just received from the pool before the connect function returns. All you're doing there is opening a connection just to immediately close it again.

    Change getUserByName to take a connection as an argument (typically as the first argument). Typically, whatever code needs to call getUserByName should either open a connection, or get a connenction from the pool via lockConnection, and then pass that connection to getUserByName and whatever other DB-related functions it needs to use. Then, after your code is done calling getUserByName (and whatever other DB functions it needs to call), you either just don't worry about the connection anymore and let your vibed fiber finish (if you're using vibed and got the connection from a pool) or you close the connection (if you did NOT get the connection from a vibed pool).