Got stuck on PostgreSQL with Caqti_async

After reading the post by @bobbypriambodo , I would like to create a Caqti_async version:

open Async

let new_url = "postgresql://dbuser:password@"

let pool =
  match Caqti_async.connect_pool ~max_size:10 (Uri.of_string new_url) with
  | Ok pool -> pool
  | Error err -> failwith ( err)

type todo = { id : int; content : string }

type error = Database_error of string

let or_error m =
  match%bind m with
  | Ok a -> Ok a |> return
  | Error e -> Error (Database_error ( e)) |> return

let migrate_query =
  Caqti_request.exec Caqti_type.unit
    {|CREATE TABLE todos (
      content VARCHAR

let migrate () =
  let migrate' (module C : Caqti_async.CONNECTION) = C.exec migrate_query () in
  Caqti_async.Pool.use migrate' pool |> or_error

Along with dune:

 (name lib)
 (libraries core async caqti caqti-async caqti-driver-postgresql)
  (pps ppx_let ppx_jane)))

And mli file

open Async

type todo = {
    id: int;
    content: string;

type error=
| Database_error of string

val migrate : unit -> (unit, error) result Deferred.t

However, I got stuck on the utop:

 $ dune utop lib
 # open Lib;;
# Todos.migrate ();;
(*stuck here, not any response....*)

I’ve tried the Lwt version, it works well with the same postgresql:// schema. Just wondering if there anything that I missed in my code?

I think the problem is the the async scheduler does not start. Just printing to the topelvel with Async.print_endline I got no output before calling Async.Scheduler.go (). On the other hand if I wrap the above code into a command as described in, it works.


Thanks, just curious why LWT does not need to start the scheduler, meanwhile async have to?

Is the promise resolved? As I understand it, with Lwt a bind will not return control to the scheduler if the promise is already resolved, but will instead invoke its callback, whereas Async will do so (it will yield). Note: I have used Lwt but never used Async, so any knowledge I have of Async is second hand.

1 Like

I’m not familiar with Lwt at all, so I don’t know if I’m missing some context here.

One advantage of forcing the scheduler to be started explicitly rather than just whenever you create some promise is that it’s easier to write daemons. It is tricky to fork a process when it has multiple threads, such as when the Async scheduler has been started, so daemonizing a program when the Async scheduler is currently running is not supported.

Instead, Async allows you to defer starting the scheduler until after your program daemonizes, meaning no threads have been created yet. Then, the scheduler can be called and the rest of the program can proceed.

1 Like