dom96 / httpbeast

A highly performant, multi-threaded HTTP 1.1 server written in Nim.
MIT License
450 stars 53 forks source link

Add startup callback to settings #89

Closed ThomasTJdev closed 1 year ago

ThomasTJdev commented 1 year ago

This PR implements a startup proc option to the Settings* allowing to run a custom proc when each thread is initialized (when the eventLoop is called).

The PR includes a test case where two global {.threadvar.} variables are initialized at thread-start. Without the startup proc the user would need to check whether the variables are initiated in each thread (on every call) or otherwise create them.

bung87 commented 1 year ago

so it's the first time run test against to nim v2, even I just want add feature, I have to write extra code make test green

bung87 commented 1 year ago

and second time I look into tester's startServer logic, I really hope it can change to start random port and retrieve that port then make request to that address, so no kill and start on same port.

ThomasTJdev commented 1 year ago

Without the startup proc the user would need to check whether the variables are initiated in each thread (on every call) or otherwise create them.

Is that really so slow?

For the code, no - it's just checking a bool, but there's still the overhead of doing the check everywhere and the waiting for the first connection before initializing database connections, activate custom thread monitoring, parsing init-files.

The code example below outlines how the current situation is - if you need a database dbConn or just a global threadvar. And the problem "scales" up to e.g. Jester, where the same checks is needed.

var isDbInit {.threadvar.}: bool
var db {.threadvar.}: DbConn

var isVarsInit {.threadvar.}: bool
var versionNr  {.threadvar.}: string

proc initDb() =
  db = openDbConn()

proc initVars() =
  let dict = parseCfg("..")
  versionNr = dict("..")

proc onRequest(req: Request): Future[void] =
  if req.httpMethod == some(HttpGet):
    case req.path.get()
    of "/":
      # We need the DB to respond, but we need to check whether it is initialized
      # or otherwise for the connection.
      if not isDbInit:
        initDb()
      let dbValue = getValue(db, sql"...")
      req.send("name:$#." % [dbValue])
    else:
      # We need a global variable, but we need to check whether it is initialized
      # or otherwise initialize them.
      if not isVarsInit:
        initVars()
      req.send("version:$#." % [versionNr])

run(onRequest, initSettings())
dom96 commented 1 year ago

fwiw this should be a feature offered by Nim's threading

The Nim 2.0 fix seems like a workaround to a bug in Nim as well.

But this is simple enough that I'll just merge it to unblock you since you use httpbeast in production I presume.