I've got a centralized database connector:

    (define dbh
         (virtual-connection
             (connection-pool
                 (thunk (postgresl-connect ...)))))

This gets passed around from handle to handle and into various temporary or
ongoing worker threads.  Thinking about it, I'd like to check that I've
understood it properly:

- The virtual connection (VC) itself is very lightweight and it's fine for
it to persist throughout the run of the program.

- The connection pool (CP) inside the VC will maintain a (small?)
collection of persistent connections to hand out upon request, so I'm not
paying the connection-setup-time penalty every time I use it.

- It's also fine to pass the VC into other threads.  It will be shared
state between the threads, but the CP will keep their connections isolated
and when the threads terminate it won't interfere.  (Ignore pathological
cases -- obviously if I give it to enough threads and they all use it at
once then we might exceed the DB limits on number of handles, speed,
bandwidth, etc.)

- The CP will create more connections as needed, so there's no need to
worry about running out (barring pathological cases).

- db connections will get garbage collected normally, at the marked points:

#lang racket

(require db)

(begin ; outer begin
  (define dbh
    (virtual-connection
     (connection-pool
      (thunk (postgresql-connect #:user     "x"
                                                 #:database "x"
                                                 #:password "x"
                                                 #:port     5432
   ))))) ; closes 'define dbh'

  (define (foo dbh-A)  ; dbh will be passed in
    (begin ; inner begin
      (define dbh-B dbh)     ; (eq? dbh-A dbh-B) => #t
      (for ((i 10))
        (thread
         (thunk
          (sleep (floor (* 10 (random))))
          (query-value dbh-A "select 7")
          (disconnect dbh-A) ; this is a no-op since dbh-A is a virtual
connection, not an actual connection
          (with-handlers ((exn:fail? (lambda (e) (displayln e))))
            (prepare dbh "select * from users where username = $1") ;
raises "cannot prepare statement with virtual connection" exception
            );with-handlers
          (sleep (floor (* 10 (random))))))))
    ); closes foo
  (foo dbh)
  ); closes outer begin

(sleep 20) ; without this all the other threads would be closed when the
main thread ended

By the time we get to 'sleep 20' dbh is out of scope but still visible to
the various worker threads.  dbh will not be GC'd until all those threads
have ended, even after all those threads have executed the 'disconnect'
line and thrown the exception.


Assuming I've understood all that correctly, my last question would be how
to get around the 'can't do prepare with a virtual connection' issue for
situations where I've been passed a connection (perhaps from third party
code) and it might or might not be virtual. First, to dispose of some
quibbles:

- One answer is "well, don't do that."  Write a contract on the function
that mandates the connection being non-virtual.

- Another is "well, don't do that."  Test if the connection is virtual and,
if so, don't use prepare.

- Another is "well, don't do that."  Pass around a dsn instead of a VC and
generate connections as needed.

None of these is terribly satisfying.  The first violates the principle of
"be generous in what you accept and strict in what you emit", the second
gives up a lot of speed if we were in a situation where we wanted to use
'prepare' in the first place, and the third isn't feasible since I won't
always have control over what a third-party library emits.


My ideal solution would be something like this:

(define (myfunc a-handle)
    (define dbh
        (if (virtual-connection? a-handle)
            (my-function-to-do-connect-with-dsn (get-dsn-from a-handle))
            a-handle))
    (define sth (prepare dbh "select foo from bar where baz = $1"))
    ...stuff...
)

In other words, check if the connection is virtual and, if so, extract the
dsn from it and use that to create a non-virtual connection.

Is there a way to do that?  I've been through the db module docs and Google
but not found a way.  Did I miss something?

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to