Переподключитесь к базе данных PostgreSQL с помощью пакета пула R - PullRequest
0 голосов
/ 04 марта 2019

У меня есть API, созданный с помощью R сантехник , который подключается к базе данных PostgreSQL с использованием RPostgreSQL и пул (хотя это также применимо, если я использовалБлестящее приложение):

# create the connection pool
pool <- dbPool(
  drv = PostgreSQL(),
  host = Sys.getenv("DB_HOST"),
  port = 5432,
  dbname = "db",
  user = Sys.getenv("DB_USER"),
  password = Sys.getenv("DB_PASSWORD")
)

# start the API
pr <- plumb("plumber.R")

# on stop, close the pool
pr$registerHooks(
  list("exit" = function() { poolClose(pool) })
)

Я хочу импортировать новые данные каждый день.Самый простой способ - создать новую базу данных и продвинуть ее в производство:

CREATE DATABASE db_new;
-- create the tables
-- bulk-insert the data
SELECT pg_terminate_backend (pid) FROM pg_stat_activity WHERE datname = 'db';
DROP DATABASE db;
ALTER DATABASE db_new RENAME TO db;

Это быстро и сводит к минимуму время простоя.Проблема в том, что pool теряет соединение с базой данных и не пытается автоматически восстановить соединение:

> tbl(pool, "users")
Error in postgresqlExecStatement(conn, statement, ...) : 
  RS-DBI driver: (could not Retrieve the result : FATAL:  terminating connection due to administrator command
server closed the connection unexpectedly
    This probably means the server terminated abnormally
    before or while processing the request.
)

Даже если я не заменял базу данных каждый день, серверы БД время от времени перезапускаются, и этотакже приведет к поломке моего приложения.Похоже, переподключение не является функцией пула, RPostgreSQL или DBI.Кто-нибудь знает способ решения этой проблемы?

1 Ответ

0 голосов
/ 31 мая 2019

Недавно я столкнулся с подобной проблемой из-за того, что соединения MySQL были закрыты при превышении wait_timeout экземпляра.Я наткнулся на ваше сообщение в RStudio Community и был вдохновлен вашим решением.Если вы все еще используете его и ищете решение, которое позволяет избежать лишнего запроса при переносе используемых вами фактических функций, вот пример, демонстрирующий то, что я придумал, вместе с примером, доказывающим, что он работает:

library(dplyr, warn.conflicts = FALSE)
library(pool)
library(RMariaDB)

generate_safe_query <- function(pool) {
  function(db_function, ...) {
    tryCatch({
      db_function(pool, ...)
    }, error = function(e) {
      if (grepl("Lost connection to MySQL server during query", e$message)) {
        # Preserve `validationInterval` so that it can be restored
        validation_interval <- pool$validationInterval
        # Trigger destruction of dead connection
        pool$validationInterval <- 0
        refreshed_connection <- poolCheckout(pool)
        poolReturn(refreshed_connection)
        # Restore original `validationInterval`
        pool$validationInterval <- validation_interval
        # Execute the query with the new connection
        db_function(pool, ...)
      } else {
        # Unexpected error
        stop(e)
      }
    })
  }
}

mysql_pool <- dbPool(MariaDB(),
                     host = "127.0.0.1",
                     username = "root",
                     password = "",
                     dbname = "test")

safe_query <- generate_safe_query(mysql_pool)

# Works
safe_query(tbl, "notes")
#> # Source:   table<notes> [?? x 2]
#> # Database: mysql 8.0.15 [root@127.0.0.1:/test]
#>      id note 
#>   <int> <chr>
#> 1     1 NOTE1

# Set the `wait_timeout` to 5 seconds for this session
invisible(safe_query(dbExecute, "SET SESSION wait_timeout = 5"))

# Wait longer than `wait_timeout` to trigger a disconnect
Sys.sleep(6)

# Still works; warning will appear notifying that connection was
# destroyed and replaced with a new one
safe_query(tbl, "notes")
#> Warning: It wasn't possible to activate and/or validate the object. Trying
#> again with a new object.
#> # Source:   table<notes> [?? x 2]
#> # Database: mysql 8.0.15 [root@127.0.0.1:/test]
#>      id note 
#>   <int> <chr>
#> 1     1 NOTE1

safe_query(poolClose)
# Or, equivalently: 
# poolClose(mysql_pool)

Создано в 2019-05-30 пакетом Представления (v0.3.0)

Функция, возвращаемая generate_safe_query, будетработать с любой функцией запроса к базе данных (например, dbExecute, dbGetQuery и т. д.).Очевидно, что вы захотите обновить сообщение об ошибке, которое оно соответствует вашим потребностям.

Я также открыл свою собственную тему сообщества для опции, которая, по моему мнению, должна быть включена в dbPoolэто уменьшило бы необходимость в таких обходных путях.

...