Reuse MySQL connections within requests
This commit is contained in:
parent
0ff42f357f
commit
b1dfe6c807
@ -15,7 +15,7 @@ Establishes a connection to a MySQL server and returns a pointer to the connecti
|
||||
|
||||
This connection handle is then used with helpers such as `mysql_query()`, `mysql_error()`, and `mysql_disconnect()`.
|
||||
|
||||
MySQL handles are request-scoped framework resources. If a request exits without calling `mysql_disconnect()`, UCE closes any remaining MySQL handles during request cleanup. Call `mysql_disconnect()` when you want to close early, but never store a `MySQL*` in globals, sessions, or other state that can outlive the current request.
|
||||
MySQL handles are request-scoped framework resources. Repeated `mysql_connect()` calls with the same host and credentials reuse one server connection within the current request. Each call creates a lease; `mysql_disconnect()` releases that lease, and UCE closes the pooled server connection during request cleanup. Never store a `MySQL*` in globals, sessions, or other state that can outlive the current request.
|
||||
|
||||
:example
|
||||
MySQL* db = mysql_connect();
|
||||
|
||||
@ -10,7 +10,7 @@ m : pointer to an existing MySQL connection struct
|
||||
:content
|
||||
Closes an existing connection to a MySQL server.
|
||||
|
||||
Call this when you want to release a `MySQL*` connection handle before the request ends. UCE also closes any remaining MySQL handles automatically during request cleanup, including handles skipped by exceptions or fatal request recovery. A `MySQL*` is only valid inside the request that opened it; do not cache it across requests.
|
||||
Releases the lease returned by `mysql_connect()`. The request-local server connection remains available for another same-credential `mysql_connect()` call and is closed by UCE during request cleanup, including after exceptions or fatal request recovery. A `MySQL*` is only valid inside the request that opened it; do not use it after release or cache it across requests.
|
||||
|
||||
:example
|
||||
MySQL* db = mysql_connect();
|
||||
|
||||
@ -3,10 +3,9 @@
|
||||
#include <stdlib.h>
|
||||
#include "mysql-connector.h"
|
||||
|
||||
// MySQL handles are request-scoped framework resources. User code may call
|
||||
// mysql_disconnect() to close early, but leaked/exception-skipped handles are
|
||||
// still closed by cleanup_mysql_connections() at request end. Handles must not
|
||||
// be cached across requests or stored in globals/sessions.
|
||||
// Same-credential mysql_connect() calls lease one request-local connection.
|
||||
// mysql_disconnect() releases a lease; cleanup_mysql_connections() owns the
|
||||
// actual close at request end, including exception/fatal recovery paths.
|
||||
static void mysql_register_request_connection(MySQL* db)
|
||||
{
|
||||
if(!context || !db)
|
||||
@ -16,6 +15,31 @@ static void mysql_register_request_connection(MySQL* db)
|
||||
connections.push_back(db);
|
||||
}
|
||||
|
||||
MySQL* mysql_connect(String host, String username, String password)
|
||||
{
|
||||
if(context)
|
||||
{
|
||||
for(void* raw : context->resources.mysql_connections)
|
||||
{
|
||||
MySQL* db = (MySQL*)raw;
|
||||
if(db && db->request_pooled && db->connection && db->request_host == host && db->request_username == username && db->request_password == password)
|
||||
{
|
||||
db->request_leases++;
|
||||
return(db);
|
||||
}
|
||||
}
|
||||
}
|
||||
MySQL* db = new MySQL();
|
||||
db->request_cleanup_delete = true;
|
||||
db->request_pooled = context != 0;
|
||||
db->request_leases = 1;
|
||||
db->request_host = host;
|
||||
db->request_username = username;
|
||||
db->request_password = password;
|
||||
db->connect(host, username, password);
|
||||
return(db);
|
||||
}
|
||||
|
||||
bool MySQL::connect(String host, String username, String password)
|
||||
{
|
||||
// Register regardless of outcome: tracking is about the wrapper's
|
||||
@ -325,6 +349,7 @@ void MySQL::disconnect()
|
||||
if(connection)
|
||||
mysql_close((MYSQL*)connection);
|
||||
connection = NULL;
|
||||
request_leases = 0;
|
||||
if(context)
|
||||
{
|
||||
auto& connections = context->resources.mysql_connections;
|
||||
@ -389,6 +414,7 @@ void cleanup_mysql_connections()
|
||||
MySQL* db = (MySQL*)context->resources.mysql_connections.back();
|
||||
context->resources.mysql_connections.pop_back();
|
||||
bool should_delete = db->request_cleanup_delete;
|
||||
db->request_pooled = false;
|
||||
db->disconnect();
|
||||
if(should_delete)
|
||||
delete db;
|
||||
|
||||
@ -21,6 +21,11 @@ struct MySQL {
|
||||
u64 insert_id = 0;
|
||||
String statement_info = ""; //
|
||||
bool request_cleanup_delete = false;
|
||||
bool request_pooled = false;
|
||||
u32 request_leases = 0;
|
||||
String request_host;
|
||||
String request_username;
|
||||
String request_password;
|
||||
|
||||
std::vector<MySQLFieldInfo> field_info;
|
||||
|
||||
@ -33,26 +38,24 @@ struct MySQL {
|
||||
DValue query(String q, StringMap params);
|
||||
DValue get_pending_result();
|
||||
|
||||
// Unregisters from request connection tracking, so stack-allocated instances
|
||||
// cannot leave dangling pointers behind for request cleanup. Heap handles
|
||||
// returned by mysql_connect() are also request-owned and auto-closed at the
|
||||
// end of the request if user code does not call mysql_disconnect() first.
|
||||
// Unregisters non-pooled instances from request tracking. mysql_connect()
|
||||
// handles remain request-owned until cleanup after their leases are released.
|
||||
~MySQL() { disconnect(); }
|
||||
|
||||
};
|
||||
|
||||
inline MySQL* mysql_connect(String host = "localhost", String username = "root", String password = "")
|
||||
{
|
||||
MySQL* m = new MySQL();
|
||||
m->request_cleanup_delete = true;
|
||||
m->connect(host, username, password);
|
||||
return(m);
|
||||
}
|
||||
MySQL* mysql_connect(String host = "localhost", String username = "root", String password = "");
|
||||
|
||||
inline void mysql_disconnect(MySQL* m)
|
||||
{
|
||||
if(!m)
|
||||
return;
|
||||
if(m->request_pooled)
|
||||
{
|
||||
if(m->request_leases > 0)
|
||||
m->request_leases--;
|
||||
return;
|
||||
}
|
||||
m->disconnect();
|
||||
delete m;
|
||||
}
|
||||
|
||||
@ -804,6 +804,7 @@ public:
|
||||
// never cache these opaque handles across requests.
|
||||
std::vector<SQLite*> sqlite_handles;
|
||||
std::vector<MySQL*> mysql_handles;
|
||||
std::vector<MySQL*> mysql_request_pool;
|
||||
#endif
|
||||
~WasmWorkspace()
|
||||
{
|
||||
@ -820,7 +821,7 @@ public:
|
||||
for(auto* db : sqlite_handles)
|
||||
if(db)
|
||||
delete db; // ~SQLite disconnects
|
||||
for(auto* db : mysql_handles)
|
||||
for(auto* db : mysql_request_pool)
|
||||
if(db)
|
||||
delete db; // ~MySQL disconnects
|
||||
#endif
|
||||
@ -2409,11 +2410,32 @@ private:
|
||||
String op = request["op"].to_string();
|
||||
if(op == "connect")
|
||||
{
|
||||
MySQL* db = new MySQL();
|
||||
bool ok = db->connect(request["host"].to_string(), request["username"].to_string(), request["password"].to_string());
|
||||
String host = request["host"].to_string();
|
||||
String username = request["username"].to_string();
|
||||
String password = request["password"].to_string();
|
||||
MySQL* db = 0;
|
||||
for(auto* pooled : self->mysql_request_pool)
|
||||
if(pooled && pooled->connection && pooled->request_host == host && pooled->request_username == username && pooled->request_password == password)
|
||||
{
|
||||
db = pooled;
|
||||
break;
|
||||
}
|
||||
bool ok = db != 0;
|
||||
if(!db)
|
||||
{
|
||||
db = new MySQL();
|
||||
db->request_pooled = true;
|
||||
db->request_host = host;
|
||||
db->request_username = username;
|
||||
db->request_password = password;
|
||||
ok = db->connect(host, username, password);
|
||||
if(ok && db->connection)
|
||||
self->mysql_request_pool.push_back(db);
|
||||
}
|
||||
u64 handle = 0;
|
||||
if(ok && db->connection)
|
||||
{
|
||||
db->request_leases++;
|
||||
self->mysql_handles.push_back(db);
|
||||
handle = self->mysql_handles.size();
|
||||
}
|
||||
@ -2443,7 +2465,8 @@ private:
|
||||
}
|
||||
else if(op == "disconnect" && db)
|
||||
{
|
||||
delete db;
|
||||
if(db->request_leases > 0)
|
||||
db->request_leases--;
|
||||
self->mysql_handles[(size_t)handle - 1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user