Estoy desarrollando una nueva aplicación en R+Shiny. Entre otras cosas, el servidor Shiny necesita acceder a los datos de una BBDD en MySQL. Tengo código en R que accede a MySQL sin problemas, abre y cierra las conexiones bien, sin leaks por dejar conexiones abiertas. Bueno, eso es lo que creía… cuando por alguna razón la aplicación fallaba, podían darse casos en los que alguna conexión se quedaba abierta. Por una o dos, no pasaba «nada». Pero probando y probando, llegué a este error:
«cannot allocate a new connection — maximum of 16 connections already opened»
Vaya… el primer problema es: ¿cómo desconecto las conexiones abiertas? La respuesta a esto en la lista de distribución de R:
cons <- dbListConnections(MySQL()) for(con in cons) dbDisconnect(con) |
Y ahora, ¿cómo evitar el error? Lo ideal sería establecer una especie de conexión singleton que se reutilizara desde todo el código. La respuesta a esto, en StackOverflow (cómo no ;-))
library(RMySQL) getConnection <- function(group) { if (!exists('.connection', where=.GlobalEnv)) { .connection <<- dbConnect(MySQL(), group=group) } else if (class(try(dbGetQuery(.connection, "SELECT 1"))) == "try-error") { dbDisconnect(.connection) .connection <<- dbConnect(MySQL(), group=group) } return(.connection) } |
Este código comprueba si existe una conexión en el entorno global. Si no existe, la crea y la devuelve.
Si existe, comprueba que se pueda usar. Si no se puede usar, desconecta y vuelve a crear la conexión, para devolverla.
Es decir, funciona como una caché (o puede verse también como un objeto singleton de tipo connection).
Podríamos ignorar el parámetro group de la función getConnection y en su lugar, usar:
dbConnect(MySQL(), user=login, password=pass, db=database, host=host) |
allí donde fuera necesario.