diff --git a/frameworks/FSharp/oxpecker/src/App/App.fsproj b/frameworks/FSharp/oxpecker/src/App/App.fsproj
index e9aa702a5a1..48564a7779b 100644
--- a/frameworks/FSharp/oxpecker/src/App/App.fsproj
+++ b/frameworks/FSharp/oxpecker/src/App/App.fsproj
@@ -13,10 +13,10 @@
-
-
-
-
-
+
+
+
+
+
\ No newline at end of file
diff --git a/frameworks/FSharp/oxpecker/src/App/Common.fs b/frameworks/FSharp/oxpecker/src/App/Common.fs
index 47a63e2108e..8d21c46aa64 100644
--- a/frameworks/FSharp/oxpecker/src/App/Common.fs
+++ b/frameworks/FSharp/oxpecker/src/App/Common.fs
@@ -9,7 +9,7 @@ module Common =
[]
[]
type JsonMessage = {
- message : string
+ message: string
}
[]
@@ -26,7 +26,9 @@ module Common =
}
[]
- let ConnectionString = "Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;Maximum Pool Size=1024;NoResetOnClose=true;Enlist=false;Max Auto Prepare=3"
+ let ConnectionString = "Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;Maximum Pool Size=1024;NoResetOnClose=true;Enlist=false"
+ []
+ let MultiplexedConnectionString = ConnectionString + ";Max Auto Prepare=3;Multiplexing=true"
let FortuneComparer = {
new IComparer with
diff --git a/frameworks/FSharp/oxpecker/src/App/Db.fs b/frameworks/FSharp/oxpecker/src/App/Db.fs
index 2c471bada1d..65b3062a8e9 100644
--- a/frameworks/FSharp/oxpecker/src/App/Db.fs
+++ b/frameworks/FSharp/oxpecker/src/App/Db.fs
@@ -2,8 +2,7 @@ namespace App
open System
open System.Data
-open System.Data.Common
-open System.Text
+open Microsoft.Extensions.ObjectPool
open Npgsql
@@ -15,6 +14,7 @@ module Db =
use db = new NpgsqlConnection(ConnectionString)
use cmd = db.CreateCommand(CommandText = "SELECT id, message FROM fortune")
do! db.OpenAsync()
+ do! cmd.PrepareAsync()
use! rdr = cmd.ExecuteReaderAsync(CommandBehavior.CloseConnection)
while! rdr.ReadAsync() do
result.Add { id = rdr.GetInt32(0); message = rdr.GetString(1) }
@@ -31,7 +31,7 @@ module Db =
TypedValue = Random.Shared.Next(1, 10001)
)
cmd.Parameters.Add(id) |> ignore
- cmd
+ struct(cmd, id)
let private readSingleRow (cmd: NpgsqlCommand) =
task {
@@ -43,41 +43,46 @@ module Db =
let loadSingleRow () =
task {
use db = new NpgsqlConnection(ConnectionString)
+ let struct(cmd', _) = createReadCommand db
+ use cmd = cmd'
do! db.OpenAsync()
- use cmd = createReadCommand db
+ do! cmd.PrepareAsync()
return! readSingleRow cmd
}
let private readMultipleRows (count: int) (conn: NpgsqlConnection) =
let result = Array.zeroCreate count
task {
- use cmd = createReadCommand conn
+ let struct(cmd', idParam) = createReadCommand conn
+ use cmd = cmd'
for i in 0..result.Length-1 do
- cmd.Parameters["@Id"].Value <- Random.Shared.Next(1, 10001)
let! row = readSingleRow cmd
result[i] <- row
+ idParam.TypedValue <- Random.Shared.Next(1, 10001)
return result
}
let loadMultipleRows (count: int) =
task {
- use db = new NpgsqlConnection(ConnectionString)
+ use db = new NpgsqlConnection(MultiplexedConnectionString)
do! db.OpenAsync()
return! readMultipleRows count db
}
let private maxBatch = 500
let private queries = Array.zeroCreate (maxBatch + 1)
+ let private stringBuilderPool = DefaultObjectPoolProvider().CreateStringBuilderPool()
let private batchUpdateString batchSize =
match queries[batchSize] with
| null ->
let lastIndex = batchSize - 1
- let sb = StringBuilder()
+ let sb = stringBuilderPool.Get()
sb.Append("UPDATE world SET randomNumber = temp.randomNumber FROM (VALUES ") |> ignore
for i in 0..lastIndex-1 do
sb.AppendFormat("(@Id_{0}, @Rn_{0}), ", i) |> ignore
sb.AppendFormat("(@Id_{0}, @Rn_{0}) ORDER BY 1) AS temp(id, randomNumber) WHERE temp.id = world.id", lastIndex) |> ignore
let result = sb.ToString()
+ stringBuilderPool.Return(sb)
queries[batchSize] <- result
result
| q ->
@@ -95,15 +100,15 @@ module Db =
for i in 0..results.Length-1 do
let randomNumber = Random.Shared.Next(1, 10001)
let struct(rnParamName, idParamName) = paramNames[i]
- let random = NpgsqlParameter(ParameterName = rnParamName, DbType = DbType.Int32, TypedValue = randomNumber)
- command.Parameters.Add(random) |> ignore
- let id = NpgsqlParameter(ParameterName = idParamName, DbType = DbType.Int32, TypedValue = results[i].id)
- command.Parameters.Add(id) |> ignore
+ command.Parameters.Add(NpgsqlParameter(
+ ParameterName = rnParamName, DbType = DbType.Int32, TypedValue = randomNumber)) |> ignore
+ command.Parameters.Add(NpgsqlParameter(
+ ParameterName = idParamName, DbType = DbType.Int32, TypedValue = results[i].id)) |> ignore
results[i] <- { results[i] with randomnumber = randomNumber }
let doMultipleUpdates (count: int) =
task {
- use conn = new NpgsqlConnection(ConnectionString)
+ use conn = new NpgsqlConnection(MultiplexedConnectionString)
do! conn.OpenAsync()
let! results = readMultipleRows count conn
use cmd = conn.CreateCommand(CommandText = batchUpdateString count)
diff --git a/frameworks/FSharp/oxpecker/src/App/Program.fs b/frameworks/FSharp/oxpecker/src/App/Program.fs
index 895c54c9693..ce301a87b89 100644
--- a/frameworks/FSharp/oxpecker/src/App/Program.fs
+++ b/frameworks/FSharp/oxpecker/src/App/Program.fs
@@ -2,13 +2,21 @@ namespace App
open System
open Oxpecker
-open System.Runtime.InteropServices
[]
-module HtmlViews =
+module HttpHandlers =
+ open System.Text
+ open Microsoft.AspNetCore.Http
+ open SpanJson
open Oxpecker.ViewEngine
- let private head, tail =
+ let private extra =
+ {
+ id = 0
+ message = "Additional fortune added at request time."
+ }
+
+ let private fortunesHeadAndTail =
(fun (content: HtmlElement) ->
html() {
head() {
@@ -26,32 +34,11 @@ module HtmlViews =
} :> HtmlElement
) |> RenderHelpers.prerender
- let fortunes (fortunesData: ResizeArray) =
- let fragment = __()
- for fortune in CollectionsMarshal.AsSpan fortunesData do
- tr() {
- td() { raw <| string fortune.id }
- td() { fortune.message }
- }
- |> fragment.AddChild
- RenderHelpers.combine head tail fragment
-
-[]
-module HttpHandlers =
- open System.Text
- open Microsoft.AspNetCore.Http
- open SpanJson
-
- let private extra =
- {
- id = 0
- message = "Additional fortune added at request time."
- }
-
let rec private renderFortunes (ctx: HttpContext) (data: ResizeArray) =
data.Add extra
data.Sort FortuneComparer
- data |> HtmlViews.fortunes |> ctx.WriteHtmlViewChunked
+ RenderHelpers.CombinedElement(fortunesHeadAndTail, data)
+ |> ctx.WriteHtmlViewChunked
let fortunes : EndpointHandler =
fun ctx ->
diff --git a/frameworks/FSharp/oxpecker/src/App/RenderHelpers.fs b/frameworks/FSharp/oxpecker/src/App/RenderHelpers.fs
index 003fc09d8ce..7a94b2552ba 100644
--- a/frameworks/FSharp/oxpecker/src/App/RenderHelpers.fs
+++ b/frameworks/FSharp/oxpecker/src/App/RenderHelpers.fs
@@ -1,8 +1,27 @@
module RenderHelpers
open System.Text
+ open App
open Oxpecker.ViewEngine
+ type HeadAndTail =
+ {
+ Head: string
+ Tail: string
+ }
+
+ []
+ type CombinedElement(ht: HeadAndTail, fortunesData: ResizeArray) =
+ interface HtmlElement with
+ member this.Render(sb) =
+ sb.Append(ht.Head) |> ignore
+ for fortune in fortunesData do
+ (tr() {
+ td() { raw <| string fortune.id }
+ td() { fortune.message }
+ }).Render(sb)
+ sb.Append(ht.Tail) |> ignore
+
let prerender (view: HtmlElement -> HtmlElement) =
let sb = StringBuilder()
let mutable head = ""
@@ -13,12 +32,4 @@
sb.Clear() |> ignore }
let readyView = view fakeHole
readyView.Render(sb)
- (head, sb.ToString())
-
- let inline combine (head: string) (tail: string) (hole: HtmlElement) =
- { new HtmlElement with
- member this.Render(sb) =
- sb.Append(head) |> ignore
- hole.Render(sb)
- sb.Append(tail) |> ignore
- }
\ No newline at end of file
+ { Head = head; Tail = sb.ToString() }
\ No newline at end of file