Search code examples
asynchronousf#npgsql

is async really making sense here? NpgSQL with F#


I have the following transaction:

let insertPositionsAsync accountId timestamp (positions: PositionInfo list) : Async<Result<int, ExchangeError>> =
    async {
        try
            use connection = getConnection location
            do! connection.OpenAsync()

            use writer =
                connection.BeginBinaryImport(
                        $"COPY {accountId}.{tablePositionsName} (ts,instrument,average_price,leverage,unrealized_pnl,side,initial_margin,maintenance_margin,position_initial_margin,open_order_initial_margin,quantity,max_notional)
                        FROM STDIN (FORMAT BINARY)"
                    )

            for t in positions do
                do! writer.StartRowAsync()                                                     |> Async.AwaitTask
                do! writer.WriteAsync(timestamp,                       NpgsqlDbType.Timestamp) |> Async.AwaitTask
                do! writer.WriteAsync(t.Instrument.Ticker,             NpgsqlDbType.Varchar)   |> Async.AwaitTask
                do! writer.WriteAsync(t.AveragePrice,                  NpgsqlDbType.Double)    |> Async.AwaitTask
                do! writer.WriteAsync(t.Leverage,                      NpgsqlDbType.Integer)   |> Async.AwaitTask
                do! writer.WriteAsync(t.UnrealizedPnl,                 NpgsqlDbType.Double)    |> Async.AwaitTask
                do! writer.WriteAsync(t.Side.ToString().ToLower(),     NpgsqlDbType.Varchar)   |> Async.AwaitTask
                do! writer.WriteAsync(t.InitialMargin,                 NpgsqlDbType.Double)    |> Async.AwaitTask
                do! writer.WriteAsync(t.MaintenanceMargin,             NpgsqlDbType.Double)    |> Async.AwaitTask
                do! writer.WriteAsync(t.PositionInitialMargin,         NpgsqlDbType.Double)    |> Async.AwaitTask
                do! writer.WriteAsync(t.OpenOrderInitialMargin,        NpgsqlDbType.Double)    |> Async.AwaitTask
                do! writer.WriteAsync(t.Quantity,                      NpgsqlDbType.Double)    |> Async.AwaitTask
                do! writer.WriteAsync(t.MaxNotional,                   NpgsqlDbType.Double)    |> Async.AwaitTask

            let! c = writer.CompleteAsync()
            return Ok (int c)

        with ex ->
            error $"insertPositionsAsync {ex.Humanize()}"
            return Error (ServiceException ex)
    }

my understanding is that the loop:

            for t in positions do
                do! writer.StartRowAsync()                                                     |> Async.AwaitTask
                do! writer.WriteAsync(timestamp, NpgsqlDbType.Timestamp) |> Async.AwaitTask
                ...

            let! c = writer.CompleteAsync()

is happening in the driver and it's just collecting data in some local storage. Does it make sense to have all these async blocks then? (performance wise).

But the async API must exist for some reason. What could I be missing?


Solution

  • There's a discussion of this API here. It starts with someone saying:

    It would be helpful to have asynchronous versions of BeginBinaryImport and BeginBinaryExport so they can be used from e.g. api endpoints without blocking the server.

    For Import, given the write happens on Close/Dispose I'm guessing the write methods would not become async

    However, one one of the developers then comments that:

    Write can also block. So you need WriteAsync too.