Browse Source

Add benchmark for Frank (#5726)

* Add benchmark for Frank

* Add Red Hat Linux Transport for comparison

* Update versions

* Keep Server header

* Production-like HTML rendering and OOB JSON rendering

* Update Program.fs

* Update Program.fs
Ryan Riley 5 years ago
parent
commit
cea20bde8a

+ 39 - 0
frameworks/FSharp/frank/.gitignore

@@ -0,0 +1,39 @@
+[Oo]bj/
+[Bb]in/
+TestResults/
+.nuget/
+*.sln.ide/
+_ReSharper.*/
+.idea/
+packages/
+artifacts/
+PublishProfiles/
+.vs/
+*.user
+*.suo
+*.cache
+*.docstates
+_ReSharper.*
+nuget.exe
+*net45.csproj
+*net451.csproj
+*k10.csproj
+*.psess
+*.vsp
+*.pidb
+*.userprefs
+*DS_Store
+*.ncrunchsolution
+*.*sdf
+*.ipch
+*.swp
+*~
+.build/
+.testPublish/
+launchSettings.json
+BenchmarkDotNet.Artifacts/
+BDN.Generated/
+binaries/
+global.json
+.ionide/
+*.sln

+ 27 - 0
frameworks/FSharp/frank/README.md

@@ -0,0 +1,27 @@
+# Frank Tests on Linux
+This includes tests for plaintext, json, and fortunes HTML serialization.
+
+## Infrastructure Software Versions
+
+**Language**
+
+* F# 4.7
+
+**Platforms**
+
+* .NET Core (Windows and Linux)
+
+**Web Servers**
+
+* [Kestrel](https://github.com/aspnet/KestrelHttpServer)
+
+**Web Stack**
+
+* [Frank](https://github.com/frank-fs/frank)
+* ASP.NET Core
+
+## Paths & Source for Tests
+
+* [Plaintext](src/App/Program.fs): "/plaintext"
+* [JSON serialization](src/App/Program.fs): "/json"
+* [Fortunes using Dapper](src/App/Program.fs): "/fortunes"

+ 47 - 0
frameworks/FSharp/frank/benchmark_config.json

@@ -0,0 +1,47 @@
+{
+  "framework": "frank",
+  "tests": [
+    {
+      "default": {
+        "plaintext_url": "/plaintext",
+        "json_url": "/json",
+        "fortune_url": "/fortunes",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "Postgres",
+        "framework": "Frank",
+        "language": "F#",
+        "orm": "micro",
+        "platform": ".NET",
+        "flavor": "CoreCLR",
+        "webserver": "Kestrel",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "Frank, Dapper",
+        "notes": "",
+        "versus": "aspcore"
+      },
+      "rhtx": {
+        "plaintext_url": "/plaintext",
+        "json_url": "/json",
+        "fortune_url": "/fortunes",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "Postgres",
+        "framework": "Frank",
+        "language": "F#",
+        "orm": "micro",
+        "platform": ".NET",
+        "flavor": "CoreCLR",
+        "webserver": "Kestrel",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "Frank, Dapper, Red Hat Linux Transport",
+        "notes": "",
+        "versus": "aspcore"
+      }
+    }
+  ]
+}

+ 11 - 0
frameworks/FSharp/frank/frank-rhtx.dockerfile

@@ -0,0 +1,11 @@
+FROM mcr.microsoft.com/dotnet/core/sdk:3.1.300 AS build
+WORKDIR /app
+COPY src/App .
+RUN dotnet publish -c Release -o out
+
+FROM mcr.microsoft.com/dotnet/core/aspnet:3.1.4 AS runtime
+ENV ASPNETCORE_URLS http://+:8080
+WORKDIR /app
+COPY --from=build /app/out ./
+
+ENTRYPOINT ["dotnet", "App.dll", "KestrelTransport=LinuxTransport"]

+ 11 - 0
frameworks/FSharp/frank/frank.dockerfile

@@ -0,0 +1,11 @@
+FROM mcr.microsoft.com/dotnet/core/sdk:3.1.300 AS build
+WORKDIR /app
+COPY src/App .
+RUN dotnet publish -c Release -o out
+
+FROM mcr.microsoft.com/dotnet/core/aspnet:3.1.4 AS runtime
+ENV ASPNETCORE_URLS http://+:8080
+WORKDIR /app
+COPY --from=build /app/out ./
+
+ENTRYPOINT ["dotnet", "App.dll"]

+ 26 - 0
frameworks/FSharp/frank/src/App/App.fsproj

@@ -0,0 +1,26 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netcoreapp3.1</TargetFramework>
+    <DebugType>portable</DebugType>
+    <AssemblyName>App</AssemblyName>
+    <OutputType>Exe</OutputType>
+    <EnableDefaultContentItems>false</EnableDefaultContentItems>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Dapper" Version="2.0.35" />
+    <PackageReference Include="Frank" Version="6.1.0" />
+    <PackageReference Include="FSharp.Data.JsonSchema" Version="0.1.0" />
+    <PackageReference Include="Giraffe" Version="4.1.0" />
+    <PackageReference Include="Npgsql" Version="4.1.3.1" />
+    <PackageReference Update="FSharp.Core" Version="4.7.2" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Compile Include="Models.fs" />
+    <Compile Include="HtmlViews.fs" />
+    <Compile Include="Program.fs" />
+  </ItemGroup>
+
+</Project>

+ 33 - 0
frameworks/FSharp/frank/src/App/HtmlViews.fs

@@ -0,0 +1,33 @@
+module HtmlViews
+
+open Giraffe.GiraffeViewEngine
+open Models
+
+let private fortunesHead = 
+    head [] [
+        title []  [ rawText "Fortunes" ]
+    ]
+
+let private layout (content: XmlNode list) =
+    html [] [
+        fortunesHead
+        body [] content
+    ]
+
+let private fortunesTableHeader = 
+    tr [] [
+        th [] [ rawText "id" ]
+        th [] [ rawText "message" ]
+    ]
+
+let fortunes (fortunes: Fortune seq) =
+    [
+        table [] [ 
+            yield fortunesTableHeader
+            for f in fortunes ->
+                tr [] [
+                    td [] [ rawText <| string f.id ]
+                    td [] [ encodedText <| f.message ]
+                ] 
+        ]
+    ] |> layout

+ 14 - 0
frameworks/FSharp/frank/src/App/Models.fs

@@ -0,0 +1,14 @@
+module Models
+
+open System.Collections.Generic
+open System
+
+ [<CLIMutable>]
+type Fortune = { id: int; message: string }
+
+[<Literal>]
+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 FortuneComparer = { new IComparer<Fortune> with 
+    member self.Compare(a,b) = String.CompareOrdinal(a.message, b.message)
+}

+ 89 - 0
frameworks/FSharp/frank/src/App/Program.fs

@@ -0,0 +1,89 @@
+module App.App
+
+open System
+open System.IO
+open System.Text
+open System.Text.Json.Serialization
+open System.Text.Json
+open System.Threading.Tasks
+open Microsoft.AspNetCore.Hosting
+open Microsoft.AspNetCore.Http
+open Microsoft.Extensions.Logging
+open Dapper
+open Giraffe
+open Frank.Builder
+open FSharp.Control.Tasks
+open Npgsql
+open Models
+
+let inline contentLength x = new Nullable<int64> ( int64 x )
+
+let json' : HttpContext -> Task =
+    let options = JsonSerializerOptions()
+    options.Converters.Add(JsonFSharpConverter())
+    fun ctx ->
+        ctx.Response.ContentType <- "application/json"
+        ctx.Response.StatusCode <- 200
+        let data = struct {|message="Hello, World!"|}
+        JsonSerializer.SerializeAsync(ctx.Response.Body, data)
+
+let text' (msg:string): HttpContext -> Task =
+    let bytes = Encoding.UTF8.GetBytes(msg)
+    fun ctx ->
+        ctx.Response.ContentLength <- contentLength bytes.Length
+        ctx.Response.ContentType <- "text/plain"
+        ctx.Response.StatusCode <- 200
+        ctx.Response.Body.WriteAsync(bytes, 0, bytes.Length)
+
+// Pulled from Giraffe example
+let fortunes' = 
+    let extra = { id = 0; message = "Additional fortune added at request time." }
+    fun next (ctx: HttpContext) ->
+        let conn = new NpgsqlConnection(ConnectionString)
+        ctx.Response.RegisterForDispose conn
+        task {
+            let! data = conn.QueryAsync<Fortune>("SELECT id, message FROM fortune")
+
+            let fortunes = 
+                let xs = data.AsList()
+                xs.Add extra
+                xs.Sort FortuneComparer
+                xs
+
+            return! htmlView (HtmlViews.fortunes fortunes) next ctx
+        }
+
+// Resources
+
+let plaintext = 
+    resource "/plaintext" {
+        name "Plain text"
+        get (text' "Hello, World!")
+    }
+
+let json =
+    resource "/json" {
+        name "JSON"
+        get json'
+    }
+
+let fortunes =
+    resource "/fortunes" {
+        name "Fortunes"
+        get fortunes'
+    }
+
+// App
+
+[<EntryPoint>]
+let main args = 
+    webHost args {
+        useDefaults
+        configure (fun bldr ->
+            bldr.ConfigureLogging(fun c -> c.ClearProviders() |> ignore)
+                .UseKestrel())
+        resource plaintext
+        resource json
+        resource fortunes
+    }
+    0