Skip to content

Commit c1d18c5

Browse files
authored
[F#] Upgrade Giraffe to .NET 9 and more (#10234)
* Upgrade Giraffe to .NET 9 and more - Upgrade to .NET 9 - Use Fantomas to format the code - Add .editorconfig to configure it - Upgrade libraries - Add Newtonsoft as a separate implementation due to Giraffe upgrade - Add new JSON serializers options (System.Text.Json, Newtonsoft.Json, FSharpFriendly) - Add a chiseled Dockerfile - Add Single Database Query test - Add Multiple Database Queries test * Update .github/workflows/build.yml to use ubuntu-24.04 * Revert "Update .github/workflows/build.yml to use ubuntu-24.04" This reverts commit 354b738. * Fix: rename giraffe-fsharpfriendly.dockerfile into giraffe-fsharp-systemtextjson.dockerfile
1 parent 8228bc9 commit c1d18c5

File tree

11 files changed

+422
-161
lines changed

11 files changed

+422
-161
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
root = true
2+
3+
[*.{fs,fsi,fsx}]
4+
indent_size = 4
5+
indent_style = space
6+
max_line_length = 90
7+
fsharp_space_before_uppercase_invocation = true
8+
fsharp_space_before_member = true
9+
fsharp_space_before_colon = false
10+
fsharp_space_before_semicolon = false
11+
fsharp_multiline_bracket_style = stroustrup
12+
fsharp_newline_between_type_definition_and_members = true
13+
fsharp_align_function_signature_to_indentation = true
14+
fsharp_alternative_long_member_definitions = true
15+
fsharp_multi_line_lambda_closing_newline = true
16+
fsharp_experimental_keep_indent_in_branch = true
17+
fsharp_bar_before_discriminated_union_declaration = true
Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,24 @@
11
# Giraffe Benchmarks on Linux
22

3-
This application tests Giraffe in 2 modes:
3+
This application tests Giraffe in 3 modes:
44

55
- Default: Using Giraffe's Endpoint Routing APIs with the `System.Text.Json` serializer
66
- Newtonsoft: Testing the JSON endpoint with the `NewtonsoftJson` serializer
7+
- FSharpFriendly: Testing the JSON endpoint with the `FSharp.SystemTextJson` serializer
78

89
## Infrastructure Software Versions
910

1011
**Language**
1112

12-
* F# 8.0
13+
* F# 9.0
1314

1415
**Platforms**
1516

16-
* .NET 6 (Windows and Linux)
17+
* .NET 9 (Windows and Linux)
1718

1819
**Web Servers**
1920

20-
* [Kestrel](https://github.com/aspnet/KestrelHttpServer)
21+
* [Kestrel](https://github.com/dotnet/aspnetcore/tree/main/src/Servers/Kestrel)
2122

2223
**Web Stack**
2324

@@ -26,9 +27,8 @@ This application tests Giraffe in 2 modes:
2627

2728
## Paths & Source for Tests
2829

29-
All source code is inside `Program.fs`.
30-
3130
App listens for a single command line argument to pick the desired JSON implementation:
3231

33-
- `system`: `System.Text.Json`
34-
- `newtonsoft`: `Newtonsoft.Json`
32+
- `system`: `System.Text.Json`
33+
- `newtonsoft`: `Newtonsoft.Json`
34+
- `fsharpfriendly`: `FSharp.SystemTextJson`

frameworks/FSharp/giraffe/benchmark_config.json

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
"default": {
66
"plaintext_url": "/plaintext",
77
"json_url": "/json",
8+
"db_url": "/db",
9+
"query_url": "/queries?queries=",
810
"fortune_url": "/fortunes",
911
"port": 8080,
1012
"approach": "Realistic",
@@ -39,6 +41,46 @@
3941
"display_name": "Giraffe, NewtonsoftJson",
4042
"notes": "",
4143
"versus": "aspcore"
44+
},
45+
"fsharp-systemtextjson": {
46+
"json_url": "/json",
47+
"port": 8080,
48+
"approach": "Realistic",
49+
"classification": "Micro",
50+
"database": "None",
51+
"framework": "Giraffe",
52+
"language": "F#",
53+
"orm": "Raw",
54+
"platform": ".NET",
55+
"flavor": "CoreCLR",
56+
"webserver": "Kestrel",
57+
"os": "Linux",
58+
"database_os": "Linux",
59+
"display_name": "Giraffe, FSharp.SystemTextJson",
60+
"notes": "",
61+
"versus": "aspcore"
62+
},
63+
"chiseled": {
64+
"plaintext_url": "/plaintext",
65+
"json_url": "/json",
66+
"db_url": "/db",
67+
"query_url": "/queries?queries=",
68+
"fortune_url": "/fortunes",
69+
"port": 8080,
70+
"approach": "Realistic",
71+
"classification": "fullstack",
72+
"database": "Postgres",
73+
"framework": "giraffe",
74+
"language": "F#",
75+
"orm": "micro",
76+
"platform": ".NET",
77+
"flavor": "CoreCLR",
78+
"webserver": "Kestrel",
79+
"os": "Linux",
80+
"database_os": "Linux",
81+
"display_name": "Giraffe Chiseled, Default with Dapper",
82+
"notes": "",
83+
"versus": "aspcore"
4284
}
4385
}
4486
]

frameworks/FSharp/giraffe/config.toml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ name = "giraffe"
44
[main]
55
urls.plaintext = "/plaintext"
66
urls.json = "/json"
7+
urls.db = "/db"
8+
urls.query = "/queries?queries="
79
urls.fortune = "/fortunes"
810
approach = "Realistic"
911
classification = "fullstack"
@@ -26,3 +28,31 @@ orm = "Raw"
2628
platform = ".NET"
2729
webserver = "Kestrel"
2830
versus = "aspcore"
31+
32+
[fsharp-systemtextjson]
33+
urls.json = "/json"
34+
approach = "Realistic"
35+
classification = "Micro"
36+
database = "None"
37+
database_os = "Linux"
38+
os = "Linux"
39+
orm = "Raw"
40+
platform = ".NET"
41+
webserver = "Kestrel"
42+
versus = "aspcore"
43+
44+
[chiseled]
45+
urls.plaintext = "/plaintext"
46+
urls.json = "/json"
47+
urls.db = "/db"
48+
urls.query = "/queries?queries="
49+
urls.fortune = "/fortunes"
50+
approach = "Realistic"
51+
classification = "fullstack"
52+
database = "Postgres"
53+
database_os = "Linux"
54+
os = "Linux"
55+
orm = "micro"
56+
platform = ".NET"
57+
webserver = "Kestrel"
58+
versus = "aspcore"
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
FROM mcr.microsoft.com/dotnet/sdk:9.0.306 AS build
2+
WORKDIR /app
3+
COPY src/App .
4+
RUN dotnet publish -c Release -o out
5+
6+
FROM mcr.microsoft.com/dotnet/aspnet:9.0-noble-chiseled AS runtime
7+
ENV ASPNETCORE_URLS http://+:8080
8+
9+
# Full PGO
10+
ENV DOTNET_TieredPGO 1
11+
ENV DOTNET_TC_QuickJitForLoops 1
12+
ENV DOTNET_ReadyToRun 0
13+
14+
WORKDIR /app
15+
COPY --from=build /app/out ./
16+
17+
EXPOSE 8080
18+
19+
ENTRYPOINT ["dotnet", "App.dll", "system"]
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
FROM mcr.microsoft.com/dotnet/sdk:9.0.306 AS build
2+
WORKDIR /app
3+
COPY src/App .
4+
RUN dotnet publish -c Release -o out
5+
6+
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS runtime
7+
ENV ASPNETCORE_URLS http://+:8080
8+
9+
# Full PGO
10+
ENV DOTNET_TieredPGO 1
11+
ENV DOTNET_TC_QuickJitForLoops 1
12+
ENV DOTNET_ReadyToRun 0
13+
14+
WORKDIR /app
15+
COPY --from=build /app/out ./
16+
17+
EXPOSE 8080
18+
19+
ENTRYPOINT ["dotnet", "App.dll", "fsharpfriendly"]

frameworks/FSharp/giraffe/giraffe-newtonsoft.dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
FROM mcr.microsoft.com/dotnet/sdk:8.0.204 AS build
1+
FROM mcr.microsoft.com/dotnet/sdk:9.0.306 AS build
22
WORKDIR /app
33
COPY src/App .
44
RUN dotnet publish -c Release -o out
55

6-
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
6+
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS runtime
77
ENV ASPNETCORE_URLS http://+:8080
88

99
# Full PGO

frameworks/FSharp/giraffe/giraffe.dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
FROM mcr.microsoft.com/dotnet/sdk:8.0.204 AS build
1+
FROM mcr.microsoft.com/dotnet/sdk:9.0.306 AS build
22
WORKDIR /app
33
COPY src/App .
44
RUN dotnet publish -c Release -o out
55

6-
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
6+
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS runtime
77
ENV ASPNETCORE_URLS http://+:8080
88

99
# Full PGO

frameworks/FSharp/giraffe/src/App/App.fsproj

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
<Project Sdk="Microsoft.NET.Sdk.Web">
22

33
<PropertyGroup>
4-
<TargetFramework>net8.0</TargetFramework>
4+
<TargetFramework>net9.0</TargetFramework>
55
<EnableDefaultContentItems>false</EnableDefaultContentItems>
66
</PropertyGroup>
77

88
<ItemGroup>
9-
<PackageReference Update="FSharp.Core" Version="8.0.200" />
10-
<PackageReference Include="Dapper" Version="2.1.44" />
11-
<PackageReference Include="Giraffe" Version="6.4.0" />
12-
<PackageReference Include="Npgsql" Version="8.0.3" />
9+
<PackageReference Update="FSharp.Core" Version="9.0.303" />
10+
<PackageReference Include="Dapper" Version="2.1.66" />
11+
<PackageReference Include="Giraffe" Version="8.1.0-alpha-001" />
12+
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
13+
<PackageReference Include="Npgsql" Version="9.0.4" />
1314
</ItemGroup>
1415

1516
<ItemGroup>
17+
<Compile Include="Newtonsoft.fs" />
1618
<Compile Include="Program.fs" />
1719
</ItemGroup>
1820

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
module NewtonsoftJson
2+
3+
open System.IO
4+
open System.Text
5+
open System.Threading.Tasks
6+
open Microsoft.IO
7+
open Newtonsoft.Json
8+
open Newtonsoft.Json.Serialization
9+
open Giraffe
10+
11+
type Serializer
12+
(settings: JsonSerializerSettings, rmsManager: RecyclableMemoryStreamManager)
13+
=
14+
let serializer = JsonSerializer.Create settings
15+
let utf8EncodingWithoutBom = UTF8Encoding (false)
16+
17+
static member DefaultSettings =
18+
JsonSerializerSettings (
19+
ContractResolver = CamelCasePropertyNamesContractResolver ()
20+
)
21+
22+
interface Json.ISerializer with
23+
member __.SerializeToString (x: 'T) =
24+
JsonConvert.SerializeObject (x, settings)
25+
26+
member __.SerializeToBytes (x: 'T) =
27+
JsonConvert.SerializeObject (x, settings) |> Encoding.UTF8.GetBytes
28+
29+
member __.SerializeToStreamAsync (x: 'T) (stream: Stream) =
30+
task {
31+
use memoryStream =
32+
rmsManager.GetStream ("giraffe-json-serialize-to-stream")
33+
34+
use streamWriter = new StreamWriter (memoryStream, utf8EncodingWithoutBom)
35+
36+
use jsonTextWriter = new JsonTextWriter (streamWriter)
37+
serializer.Serialize (jsonTextWriter, x)
38+
jsonTextWriter.Flush ()
39+
memoryStream.Seek (0L, SeekOrigin.Begin) |> ignore
40+
do! memoryStream.CopyToAsync (stream, 65536)
41+
}
42+
:> Task
43+
44+
member __.Deserialize<'T> (json: string) =
45+
JsonConvert.DeserializeObject<'T> (json, settings)
46+
47+
member __.Deserialize<'T> (bytes: byte array) =
48+
let json = Encoding.UTF8.GetString bytes
49+
JsonConvert.DeserializeObject<'T> (json, settings)
50+
51+
member __.DeserializeAsync<'T> (stream: Stream) =
52+
task {
53+
use memoryStream = rmsManager.GetStream ("giraffe-json-deserialize")
54+
55+
do! stream.CopyToAsync (memoryStream)
56+
memoryStream.Seek (0L, SeekOrigin.Begin) |> ignore
57+
use streamReader = new StreamReader (memoryStream)
58+
use jsonTextReader = new JsonTextReader (streamReader)
59+
return serializer.Deserialize<'T> (jsonTextReader)
60+
}

0 commit comments

Comments
 (0)