rexyai / RestRserve

R web API framework for building high-performance microservices and app backends
https://restrserve.org
276 stars 32 forks source link

error creating database (mongolite) connection in handler #205

Closed ncullen93 closed 10 months ago

ncullen93 commented 10 months ago

Creating a global mongolite db connection and then trying to grab records in a GET handler works the first time but then leads to a segfault error any subsequent requests. So, I tried to do the recommended thing of creating a db connection in each request. However, for mongo this leads to the following error:

objc[2671]: +[NSNumber initialize] may have been in progress in another thread when fork() was called. We cannot safely call it or ignore it in the fork() child process. Crashing instead. Set a breakpoint on objc_initializeAfterForkError to debug.

Not sure if this is on mongolite's side, but it would be nice to at least see an example or hear from others who have made some kind of database solution work with RestRserve.

Basic reprex:

library(RestRserve)
library(mongolite)

fetch_models_handler <- function(req, res) {
  conn <- mongolite::mongo(collection, db, url)
 # then, eventually make a find() call and disconnect
 # adding conn$disconnect() doesnt help
}

app = Application$new(
  content_type = "application/json"
)

app$add_get(
  path = "/fetchModels",
  FUN = fetch_models_handler
)

backend = BackendRserve$new()
backend$start(app, http_port = 8080)
ncullen93 commented 10 months ago

Same segfault error when trying to create a DBI connection to a hosted database. It seems to work when the database is local, however.

Like adding using this to make a connection inside a handler causes a big segfault if it is not a local database:

  conn <- DBI::dbConnect(RPostgres::Postgres(),
                         host   ,
                         dbname,
                         user    ,
                         password,
                         port   )

Segfault looks like this:

 *** caught segfault ***
address 0x110, cause 'memory not mapped'

Traceback:
 1: connection_create(names(opts), as.vector(opts), check_interrupts)
 2: .local(drv, ...)
 3: DBI::dbConnect(RPostgres::Postgres(),  ...)
 4: DBI::dbConnect(RPostgres::Postgres(), ...)
 5: FUN(request, response)
 6: eval(quoted_code, env)
 7: eval(quoted_code, env)
 8: withCallingHandlers(eval(quoted_code, env), error = capture_calls)
 9: doTryCatch(return(expr), name, parentenv, handler)
10: tryCatchOne(expr, names, parentenv, handlers[[1L]])
11: tryCatchList(expr, classes, parentenv, handlers)
12: tryCatch(withCallingHandlers(eval(quoted_code, env), error = capture_calls),     error = identity)
13: try_capture_stack(expr)
14: private$eval_with_error_handling({    handler_id = private$match_handler(request, response)    FUN = private$handlers[[handler_id]]    self$logger$trace("", context = list(request_id = request$id,         message = sprintf("call handler '%s'", handler_id)))    FUN(request, response)})
15: eval(quoted_code, env)
16: eval(quoted_code, env)
17: withCallingHandlers(eval(quoted_code, env), error = capture_calls)
18: doTryCatch(return(expr), name, parentenv, handler)
19: tryCatchOne(expr, names, parentenv, handlers[[1L]])
20: tryCatchList(expr, classes, parentenv, handlers)
21: tryCatch(withCallingHandlers(eval(quoted_code, env), error = capture_calls),     error = identity)
22: try_capture_stack(expr)
23: private$eval_with_error_handling({    response$reset()    response$set_content_type(self$content_type)    self$logger$debug("", context = list(request_id = request$id,         request = list(method = request$method, path = request$path,             parameters_query = request$parameters_query, parameters_path = request$parameters_path,             headers = request$headers)))    mw_called = list()    mw_flag = "process_request"    need_call_handler = TRUE    for (id in seq_along(private$middleware)) {        mw_id = private$middleware[[id]][["id"]]        self$logger$trace("", context = list(request_id = request$id,             middleware = mw_id, message = sprintf("call %s middleware",                 mw_flag)))        FUN = private$middleware[[id]][[mw_flag]]        mw_status = private$eval_with_error_handling(FUN(request,             response))        mw_called[[id]] = id        if (!isTRUE(mw_status)) {            need_call_handler = FALSE            break        }    }    if (isTRUE(need_call_handler)) {        private$eval_with_error_handling({            handler_id = private$match_handler(request, response)            FUN = private$handlers[[handler_id]]            self$logger$trace("", context = list(request_id = request$id,                 message = sprintf("call handler '%s'", handler_id)))            FUN(request, response)        })    }    mw_flag = "process_response"    for (id in rev(mw_called)) {        mw_id = private$middleware[[id]][["id"]]        self$logger$trace("", context = list(request_id = request$id,             middleware = mw_id, message = sprintf("call %s middleware",                 mw_flag)))        FUN = private$middleware[[id]][[mw_flag]]        mw_status = private$eval_with_error_handling(FUN(request,             response))    }    self$logger$debug("", context = list(request_id = request$id,         response = list(status_code = response$status_code, headers = response$headers)))})
24: app$process_request()
25: self$convert_response(app$process_request())
26: .http.request("/fetchModels", NULL, NULL, as.raw(c(0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2d, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x3a, 0x20, 0x47, 0x45, 0x54, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x30, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x2d, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x63, 0x75, 0x72, 0x6c, 0x2f, 0x37, 0x2e, 0x37, 0x37, 0x2e, 0x30, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x3a, 0x20, 0x2a, 0x2f, 0x2a, 0x0a)))
27: doTryCatch(return(expr), name, parentenv, handler)
28: tryCatchOne(expr, names, parentenv, handlers[[1L]])
29: tryCatchList(expr, classes, parentenv, handlers)
30: tryCatch(expr, error = function(e) {    call <- conditionCall(e)    if (!is.null(call)) {        if (identical(call[[1L]], quote(doTryCatch)))             call <- sys.call(-4L)        dcall <- deparse(call, nlines = 1L)        prefix <- paste("Error in", dcall, ": ")        LONG <- 75L        sm <- strsplit(conditionMessage(e), "\n")[[1L]]        w <- 14L + nchar(dcall, type = "w") + nchar(sm[1L], type = "w")        if (is.na(w))             w <- 14L + nchar(dcall, type = "b") + nchar(sm[1L],                 type = "b")        if (w > LONG)             prefix <- paste0(prefix, "\n  ")    }    else prefix <- "Error : "    msg <- paste0(prefix, conditionMessage(e), "\n")    .Internal(seterrmessage(msg[1L]))    if (!silent && isTRUE(getOption("show.error.messages"))) {        cat(msg, file = outFile)        .Internal(printDeferredWarnings())    }    invisible(structure(msg, class = "try-error", condition = e))})
31: try(.http.request("/fetchModels", NULL, NULL, as.raw(c(0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2d, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x3a, 0x20, 0x47, 0x45, 0x54, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x30, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x2d, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x63, 0x75, 0x72, 0x6c, 0x2f, 0x37, 0x2e, 0x37, 0x37, 0x2e, 0x30, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x3a, 0x20, 0x2a, 0x2f, 0x2a, 0x0a))),     silent = TRUE)
32: (function (..., config.file = "/etc/Rserve.conf") {    if (is.null(run_Rserve))         stop("Runnig inside an embedded Rserve instance - starting Rserve recursively is not supported")    .Call(run_Rserve, as.character(config.file), sapply(list(...),         as.character))})(http.port = 8080, port = 6311)
33: do.call(Rserve::run.Rserve, ARGS)
34: backend$start(app)
An irrecoverable exception occurred. R is aborting now ...
dselivanov commented 10 months ago

Could you please provide reproducible example for postgres? I've used postgresql with restrserve in production for years without issue

On Tue, 21 Nov 2023, 20:33 ncullen93, @.***> wrote:

Same segfault error when trying to create a DBI connection to a hosted database. It seems to work when the database is local, however.

Like adding using this to make a connection inside a handler causes a big segfault:

conn <- DBI::dbConnect(RPostgres::Postgres(), host , dbname, user , password, port )

Segfault looks like this:

caught segfault address 0x110, cause 'memory not mapped'

Traceback: 1: connection_create(names(opts), as.vector(opts), check_interrupts) 2: .local(drv, ...) 3: DBI::dbConnect(RPostgres::Postgres(), host = "ec2-107-21-67-46.compute-1.amazonaws.com", ...) 4: DBI::dbConnect(RPostgres::Postgres(), ...) 5: FUN(request, response) 6: eval(quoted_code, env) 7: eval(quoted_code, env) 8: withCallingHandlers(eval(quoted_code, env), error = capture_calls) 9: doTryCatch(return(expr), name, parentenv, handler) 10: tryCatchOne(expr, names, parentenv, handlers[[1L]]) 11: tryCatchList(expr, classes, parentenv, handlers) 12: tryCatch(withCallingHandlers(eval(quoted_code, env), error = capture_calls), error = identity) 13: try_capture_stack(expr) 14: private$eval_with_error_handling({ handler_id = private$match_handler(request, response) FUN = private$handlers[[handler_id]] self$logger$trace("", context = list(request_id = request$id, message = sprintf("call handler '%s'", handler_id))) FUN(request, response)}) 15: eval(quoted_code, env) 16: eval(quoted_code, env) 17: withCallingHandlers(eval(quoted_code, env), error = capture_calls) 18: doTryCatch(return(expr), name, parentenv, handler) 19: tryCatchOne(expr, names, parentenv, handlers[[1L]]) 20: tryCatchList(expr, classes, parentenv, handlers) 21: tryCatch(withCallingHandlers(eval(quoted_code, env), error = capture_calls), error = identity) 22: try_capture_stack(expr) 23: private$eval_with_error_handling({ response$reset() response$set_content_type(self$content_type) self$logger$debug("", context = list(request_id = request$id, request = list(method = request$method, path = request$path, parameters_query = request$parameters_query, parameters_path = request$parameters_path, headers = request$headers))) mw_called = list() mw_flag = "process_request" need_call_handler = TRUE for (id in seq_along(private$middleware)) { mw_id = private$middleware[[id]][["id"]] self$logger$trace("", context = list(request_id = request$id, middleware = mw_id, message = sprintf("call %s middleware", mw_flag))) FUN = private$middleware[[id]][[mw_flag]] mw_status = private$eval_with_error_handling(FUN(request, response)) mw_called[[id]] = id if (!isTRUE(mw_status)) { need_call_handler = FALSE break } } if (isTRUE(need_call_handler)) { private$eval_with_error_handling({ handler_id = private$match_handler(request, response) FUN = private$handlers[[handler_id]] self$logger$trace("", context = list(request_id = request$id, message = sprintf("call handler '%s'", handler_id))) FUN(request, response) }) } mw_flag = "process_response" for (id in rev(mw_called)) { mw_id = private$middleware[[id]][["id"]] self$logger$trace("", context = list(request_id = request$id, middleware = mw_id, message = sprintf("call %s middleware", mw_flag))) FUN = private$middleware[[id]][[mw_flag]] mw_status = private$eval_with_error_handling(FUN(request, response)) } self$logger$debug("", context = list(request_id = request$id, response = list(status_code = response$status_code, headers = response$headers)))}) 24: app$process_request() 25: self$convert_response(app$process_request()) 26: .http.request("/fetchModels", NULL, NULL, as.raw(c(0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2d, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x3a, 0x20, 0x47, 0x45, 0x54, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x30, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x2d, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x63, 0x75, 0x72, 0x6c, 0x2f, 0x37, 0x2e, 0x37, 0x37, 0x2e, 0x30, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x3a, 0x20, 0x2a, 0x2f, 0x2a, 0x0a))) 27: doTryCatch(return(expr), name, parentenv, handler) 28: tryCatchOne(expr, names, parentenv, handlers[[1L]]) 29: tryCatchList(expr, classes, parentenv, handlers) 30: tryCatch(expr, error = function(e) { call <- conditionCall(e) if (!is.null(call)) { if (identical(call[[1L]], quote(doTryCatch))) call <- sys.call(-4L) dcall <- deparse(call, nlines = 1L) prefix <- paste("Error in", dcall, ": ") LONG <- 75L sm <- strsplit(conditionMessage(e), "\n")[[1L]] w <- 14L + nchar(dcall, type = "w") + nchar(sm[1L], type = "w") if (is.na(w)) w <- 14L + nchar(dcall, type = "b") + nchar(sm[1L], type = "b") if (w > LONG) prefix <- paste0(prefix, "\n ") } else prefix <- "Error : " msg <- paste0(prefix, conditionMessage(e), "\n") .Internal(seterrmessage(msg[1L])) if (!silent && isTRUE(getOption("show.error.messages"))) { cat(msg, file = outFile) .Internal(printDeferredWarnings()) } invisible(structure(msg, class = "try-error", condition = e))}) 31: try(.http.request("/fetchModels", NULL, NULL, as.raw(c(0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2d, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x3a, 0x20, 0x47, 0x45, 0x54, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x30, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x2d, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x63, 0x75, 0x72, 0x6c, 0x2f, 0x37, 0x2e, 0x37, 0x37, 0x2e, 0x30, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x3a, 0x20, 0x2a, 0x2f, 0x2a, 0x0a))), silent = TRUE) 32: (function (..., config.file = "/etc/Rserve.conf") { if (is.null(run_Rserve)) stop("Runnig inside an embedded Rserve instance - starting Rserve recursively is not supported") .Call(run_Rserve, as.character(config.file), sapply(list(...), as.character))})(http.port = 8080, port = 6311) 33: do.call(Rserve::run.Rserve, ARGS) 34: backend$start(app) An irrecoverable exception occurred. R is aborting now ...

— Reply to this email directly, view it on GitHub https://github.com/rexyai/RestRserve/issues/205#issuecomment-1820839851, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABHC5XNTMVDIBH6HEPXSKZLYFSNRJAVCNFSM6AAAAAA7ULYUPOVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTQMRQHAZTSOBVGE . You are receiving this because you are subscribed to this thread.Message ID: @.***>

ncullen93 commented 10 months ago

So weird... just deployed and it works perfectly find on heroku. Must be a local issue, but it's only with hosted databases. Sorry about that. I will close the issue and try to figure it out on my end.

s-u commented 10 months ago

@ncullen93 the error suggests the you were running it on macOS in an interactive session - or that you included a package which tried to initialize interactive session/run-time. Please make sure the app is started via Rscript on the command line and not in any GUI.