# Simple async pool driver for postgress. # Inspired by: https://github.com/treeform/pg/ when (NimMajor, NimMinor) < (1, 4): {.push raises: [Defect].} else: {.push raises: [].} import std/[sequtils,nre,strformat,sets], stew/results, chronos import ./dbconn, ../common type PgAsyncPoolState {.pure.} = enum Closed, Live, Closing type PgDbConn = object dbConn: DbConn open: bool busy: bool preparedStmts: HashSet[string] ## [stmtName's] type # Database connection pool PgAsyncPool* = ref object connString: string maxConnections: int state: PgAsyncPoolState conns: seq[PgDbConn] proc new*(T: type PgAsyncPool, dbUrl: string, maxConnections: int): DatabaseResult[T] = var connString: string try: let regex = re("""^postgres:\/\/([^:]+):([^@]+)@([^:]+):(\d+)\/(.+)$""") let matches = find(dbUrl,regex).get.captures let user = matches[0] let password = matches[1] let host = matches[2] let port = matches[3] let dbName = matches[4] connString = fmt"user={user} host={host} port={port} dbname={dbName} password={password}" except KeyError,InvalidUnicodeError, RegexInternalError, ValueError, StudyError, SyntaxError: return err("could not parse postgres string: " & getCurrentExceptionMsg()) let pool = PgAsyncPool( connString: connString, maxConnections: maxConnections, state: PgAsyncPoolState.Live, conns: newSeq[PgDbConn](0) ) return ok(pool) func isLive(pool: PgAsyncPool): bool = pool.state == PgAsyncPoolState.Live func isBusy(pool: PgAsyncPool): bool = pool.conns.mapIt(it.busy).allIt(it) proc close*(pool: PgAsyncPool): Future[Result[void, string]] {.async.} = ## Gracefully wait and close all openned connections if pool.state == PgAsyncPoolState.Closing: while pool.state == PgAsyncPoolState.Closing: await sleepAsync(0.milliseconds) # Do not block the async runtime return ok() pool.state = PgAsyncPoolState.Closing # wait for the connections to be released and close them, without # blocking the async runtime while pool.conns.anyIt(it.busy): await sleepAsync(0.milliseconds) for i in 0..