diff --git a/frameworks/C++/poco/README.md b/frameworks/C++/poco/README.md new file mode 100644 index 00000000000..54086c5e482 --- /dev/null +++ b/frameworks/C++/poco/README.md @@ -0,0 +1,15 @@ +# POCO C++ Libraries Benchmarking Test + +- [POCO Github Repository](https://github.com/pocoproject/poco) +- [POCO Website](https://pocoproject.org/) + +## Software Versions + +- [buildpack-deps noble](https://hub.docker.com/_/buildpack-deps) +- [g++ 14](https://gcc.gnu.org/gcc-14/) +- [c++17](https://en.cppreference.com/w/cpp/17) +- [POCO 1.13.1](https://pocoproject.org/releases/poco-1.13.1/poco-1.13.1-all.zip) + +## Test URLs + +- `PLAINTEXT` - [http://127.0.0.1:8080/plaintext](http://127.0.0.1:8080/plaintext) diff --git a/frameworks/C++/poco/benchmark.cpp b/frameworks/C++/poco/benchmark.cpp index 4032a08f9f5..9277b02b4d1 100644 --- a/frameworks/C++/poco/benchmark.cpp +++ b/frameworks/C++/poco/benchmark.cpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include #include @@ -15,7 +17,9 @@ #define PLAIN_CONTENT_TYPE "text/plain" #define RES_BODY "Hello, World!" #define SERVER_NAME "poco" +#define MAX_CONNECTIONS 16384 +using namespace Poco; using namespace Poco::Net; using namespace Poco::Util; using namespace std; @@ -58,7 +62,12 @@ class MyServerApp : public ServerApplication { HTTPServerParams* hsp = new HTTPServerParams; hsp->setMaxThreads(stoi(args[1])); hsp->setKeepAlive(true); - HTTPServer s(new MyRequestHandlerFactory, ServerSocket(stoi(args[0]), 4000), hsp); + hsp->setMaxKeepAliveRequests(MAX_CONNECTIONS); + hsp->setMaxQueued(MAX_CONNECTIONS); + hsp->setThreadPriority(Thread::PRIO_HIGHEST); + ServerSocket socket(stoi(args[0]), MAX_CONNECTIONS); + socket.setBlocking(false); + HTTPServer s(new MyRequestHandlerFactory, socket, hsp); s.start(); waitForTerminationRequest(); s.stop(); diff --git a/frameworks/C++/poco/poco.dockerfile b/frameworks/C++/poco/poco.dockerfile index 1f592e48872..53aef0a5650 100644 --- a/frameworks/C++/poco/poco.dockerfile +++ b/frameworks/C++/poco/poco.dockerfile @@ -1,11 +1,11 @@ -FROM buildpack-deps:xenial +FROM buildpack-deps:noble RUN apt-get update -yqq && apt-get install -yqq software-properties-common unzip cmake -RUN apt-get install -yqq g++-4.8 libjson0-dev -RUN update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 50 +RUN apt-get install -yqq g++-14 +RUN update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-14 50 -ENV POCO_VERSION 1.6.1 +ENV POCO_VERSION 1.13.3 ENV POCO_HOME /poco WORKDIR ${POCO_HOME} @@ -20,10 +20,10 @@ ENV LD_LIBRARY_PATH ${POCO_HOME}/lib/Linux/x86_64 COPY benchmark.cpp benchmark.cpp -RUN g++-4.8 \ +RUN g++-14 \ -O3 \ -DNDEBUG \ - -std=c++0x \ + -std=c++17 \ -o \ poco \ benchmark.cpp \ diff --git a/frameworks/CSharp/appmpower/appmpower-odbc-my.dockerfile b/frameworks/CSharp/appmpower/appmpower-odbc-my.dockerfile index a5bbf22ae6f..9df17020863 100644 --- a/frameworks/CSharp/appmpower/appmpower-odbc-my.dockerfile +++ b/frameworks/CSharp/appmpower/appmpower-odbc-my.dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/sdk:8.0.100 AS build +FROM mcr.microsoft.com/dotnet/sdk:9.0.100 AS build RUN apt-get update RUN apt-get -yqq install clang zlib1g-dev RUN apt-get update @@ -8,12 +8,12 @@ COPY src . RUN dotnet publish -c Release -o out /p:Database=mysql # Construct the actual image that will run -FROM mcr.microsoft.com/dotnet/aspnet:8.0.0 AS runtime +FROM mcr.microsoft.com/dotnet/aspnet:9.0.0 AS runtime RUN apt-get update # The following installs standard versions unixodbc and pgsqlodbc # unixodbc still needs to be installed even if compiled locally -RUN apt-get install -y unixodbc wget curl +RUN apt-get install -y unixodbc-dev unixodbc wget curl RUN apt-get update WORKDIR /odbc @@ -45,6 +45,8 @@ WORKDIR /app COPY --from=build /app/out ./ RUN cp /usr/lib/libm* /app +#RUN cp /usr/lib/aarch64-linux-gnu/libodbc* /app +RUN cp /usr/lib/x86_64-linux-gnu/libodbc* /app EXPOSE 8080 diff --git a/frameworks/CSharp/appmpower/appmpower-odbc-pg.dockerfile b/frameworks/CSharp/appmpower/appmpower-odbc-pg.dockerfile index 4080684bab6..ebeab3b6186 100644 --- a/frameworks/CSharp/appmpower/appmpower-odbc-pg.dockerfile +++ b/frameworks/CSharp/appmpower/appmpower-odbc-pg.dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/sdk:8.0.100 AS build +FROM mcr.microsoft.com/dotnet/sdk:9.0.100 AS build RUN apt-get update RUN apt-get -yqq install clang zlib1g-dev libkrb5-dev libtinfo5 @@ -7,10 +7,10 @@ COPY src . RUN dotnet publish -c Release -o out /p:Database=postgresql # Construct the actual image that will run -FROM mcr.microsoft.com/dotnet/aspnet:8.0.0 AS runtime +FROM mcr.microsoft.com/dotnet/aspnet:9.0.0 AS runtime RUN apt-get update -RUN apt-get install -y unixodbc odbc-postgresql +RUN apt-get install -y unixodbc-dev unixodbc odbc-postgresql # unixodbc still needs to be installed even if compiled locally ENV PATH=/usr/local/unixODBC/bin:$PATH @@ -27,6 +27,10 @@ ENV ASPNETCORE_URLS http://+:8080 WORKDIR /app COPY --from=build /app/out ./ +#RUN cp /usr/lib/aarch64-linux-gnu/libodbc* /app +RUN cp /usr/lib/x86_64-linux-gnu/libodbc* /app + + EXPOSE 8080 ENTRYPOINT ["./appMpower"] \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/appmpower.dockerfile b/frameworks/CSharp/appmpower/appmpower.dockerfile index 3d521c490d8..d10a8ad3c2e 100644 --- a/frameworks/CSharp/appmpower/appmpower.dockerfile +++ b/frameworks/CSharp/appmpower/appmpower.dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/sdk:8.0.100 AS build +FROM mcr.microsoft.com/dotnet/sdk:9.0.100 AS build RUN apt-get update RUN apt-get -yqq install clang zlib1g-dev libkrb5-dev libtinfo5 @@ -8,7 +8,7 @@ COPY src . RUN dotnet publish -c Release -o out # Construct the actual image that will run -FROM mcr.microsoft.com/dotnet/aspnet:8.0.0 AS runtime +FROM mcr.microsoft.com/dotnet/aspnet:9.0.0 AS runtime # Full PGO ENV DOTNET_TieredPGO 1 ENV DOTNET_TC_QuickJitForLoops 1 diff --git a/frameworks/CSharp/appmpower/src/appMpower.Orm/NativeMethods.cs b/frameworks/CSharp/appmpower/src/appMpower.Orm/NativeMethods.cs index 64337e6269e..6c20aded37a 100644 --- a/frameworks/CSharp/appmpower/src/appMpower.Orm/NativeMethods.cs +++ b/frameworks/CSharp/appmpower/src/appMpower.Orm/NativeMethods.cs @@ -17,6 +17,9 @@ public static class NativeMethods private readonly static WorldSerializer _worldSerializer = new WorldSerializer(); private readonly static WorldsSerializer _worldsSerializer = new WorldsSerializer(); + private readonly static FortunesSerializer _fortunesSerializer = new FortunesSerializer(); + private static readonly byte[] _delimiter = new byte[] { 0xFF, 0xFF, 0xFF, 0xFF }; + [UnmanagedCallersOnly(EntryPoint = "Dbms")] public static void Dbms(int dbms) @@ -66,6 +69,7 @@ public static unsafe IntPtr Db(int* length, IntPtr* handlePointer) */ } + /* [UnmanagedCallersOnly(EntryPoint = "Fortunes")] public static unsafe IntPtr Fortunes(int* length, IntPtr* handlePointer) { @@ -81,6 +85,61 @@ public static unsafe IntPtr Fortunes(int* length, IntPtr* handlePointer) return byteArrayPointer; } + */ + + [UnmanagedCallersOnly(EntryPoint = "Fortunes")] + public static unsafe IntPtr Fortunes(int* length, IntPtr* handlePointer) + { + List fortunes = RawDb.LoadFortunesRows().GetAwaiter().GetResult(); + + int totalSize = 0; + + foreach (var fortune in fortunes) + { + totalSize += sizeof(int) // for Id + + Encoding.UTF8.GetByteCount(fortune.Message ?? "") // for Message + + _delimiter.Length; // for delimiter + } + + // Allocate the total buffer + byte[] buffer = new byte[totalSize]; + int offset = 0; + + // Write each object to the buffer + foreach (var fortune in fortunes) + { + // Write Id + BitConverter.TryWriteBytes(buffer.AsSpan(offset, sizeof(int)), fortune.Id); + offset += sizeof(int); + + // Write Message + int descriptionLength = Encoding.UTF8.GetBytes(fortune.Message ?? "", buffer.AsSpan(offset)); + offset += descriptionLength; + + // Write Delimiter + _delimiter.CopyTo(buffer, offset); + offset += _delimiter.Length; + } + + byte[] byteArray = buffer.ToArray(); + *length = byteArray.Length; + + /* + var memoryStream = new MemoryStream(); + using var utf8JsonWriter = new Utf8JsonWriter(memoryStream, _jsonWriterOptions); + + _fortunesSerializer.Serialize(utf8JsonWriter, fortunes); + + byte[] byteArray = memoryStream.ToArray(); + *length = (int)utf8JsonWriter.BytesCommitted; + */ + + GCHandle handle = GCHandle.Alloc(byteArray, GCHandleType.Pinned); + IntPtr byteArrayPointer = handle.AddrOfPinnedObject(); + *handlePointer = GCHandle.ToIntPtr(handle); + + return byteArrayPointer; + } [UnmanagedCallersOnly(EntryPoint = "Query")] public static unsafe IntPtr Query(int queries, int* length, IntPtr* handlePointer) diff --git a/frameworks/CSharp/appmpower/src/appMpower.Orm/Serializers/FortunesSerializer.cs b/frameworks/CSharp/appmpower/src/appMpower.Orm/Serializers/FortunesSerializer.cs new file mode 100644 index 00000000000..d1697c0c0cc --- /dev/null +++ b/frameworks/CSharp/appmpower/src/appMpower.Orm/Serializers/FortunesSerializer.cs @@ -0,0 +1,24 @@ +using System.Text.Json; +using appMpower.Orm.Objects; + +namespace appMpower.Orm.Serializers +{ + public class FortunesSerializer : IJsonSerializer> + { + public void Serialize(Utf8JsonWriter utf8JsonWriter, List fortunes) + { + utf8JsonWriter.WriteStartArray(); + + foreach (Fortune fortune in fortunes) + { + utf8JsonWriter.WriteStartObject(); + utf8JsonWriter.WriteNumber("id", fortune.Id); + utf8JsonWriter.WriteString("message", fortune.Message); + utf8JsonWriter.WriteEndObject(); + } + + utf8JsonWriter.WriteEndArray(); + utf8JsonWriter.Flush(); + } + } +} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/appMpower.Orm/appMpower.Orm.csproj b/frameworks/CSharp/appmpower/src/appMpower.Orm/appMpower.Orm.csproj index dd9c929f19f..858f6f66ae4 100644 --- a/frameworks/CSharp/appmpower/src/appMpower.Orm/appMpower.Orm.csproj +++ b/frameworks/CSharp/appmpower/src/appMpower.Orm/appMpower.Orm.csproj @@ -1,7 +1,7 @@  - net8.0 + net9.0 enable true @@ -36,7 +36,7 @@ - + diff --git a/frameworks/CSharp/appmpower/src/appMpower/Middleware/FortunesMiddleware.cs b/frameworks/CSharp/appmpower/src/appMpower/Middleware/FortunesMiddleware.cs index 2acf9af1716..6321d515887 100644 --- a/frameworks/CSharp/appmpower/src/appMpower/Middleware/FortunesMiddleware.cs +++ b/frameworks/CSharp/appmpower/src/appMpower/Middleware/FortunesMiddleware.cs @@ -2,7 +2,11 @@ using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; +using System.Text.Encodings.Web; +using System.Text.Json; +using System.Text.Unicode; using System.Threading.Tasks; +using appMpower.Objects; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Primitives; @@ -11,11 +15,21 @@ namespace appMpower; public class FortunesMiddleware { + static readonly HtmlEncoder htmlEncoder = CreateHtmlEncoder(); + static HtmlEncoder CreateHtmlEncoder() + { + var settings = new TextEncoderSettings(UnicodeRanges.BasicLatin, UnicodeRanges.Katakana, UnicodeRanges.Hiragana); + settings.AllowCharacter('\u2014'); // allow EM DASH through + return HtmlEncoder.Create(settings); + } + private readonly static KeyValuePair _headerServer = new KeyValuePair("Server", new StringValues("k")); private readonly static KeyValuePair _headerContentType = new KeyValuePair("Content-Type", new StringValues("text/html; charset=UTF-8")); + private static readonly byte[] _delimiter = new byte[] { 0xFF, 0xFF, 0xFF, 0xFF }; + private readonly RequestDelegate _next; public FortunesMiddleware(RequestDelegate next) @@ -27,6 +41,66 @@ public unsafe Task Invoke(HttpContext httpContext) { if (httpContext.Request.Path.StartsWithSegments("/fortunes", StringComparison.Ordinal)) { + int payloadLength; + IntPtr handlePointer; + + IntPtr bytePointer = NativeMethods.Fortunes(out payloadLength, out handlePointer); + + /* + byte[] json = new byte[payloadLength]; + Marshal.Copy(bytePointer, json, 0, payloadLength); + NativeMethods.FreeHandlePointer(handlePointer); + + string s = Encoding.UTF8.GetString(json, 0, json.Length); + + var options = new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }; + + List fortunes = JsonSerializer.Deserialize>(s, options); + + var response = httpContext.Response; + response.Headers.Add(_headerServer); + + var result = Results.Extensions.RazorSlice>(fortunes); + result.HtmlEncoder = htmlEncoder; + + return result.ExecuteAsync(httpContext); + */ + + byte[] byteArray = new byte[payloadLength]; + Marshal.Copy(bytePointer, byteArray, 0, payloadLength); + + List fortunes = new List(); + + // Convert the byte array into segments split by the delimiter + int delimiterLength = _delimiter.Length; + int start = 0; + int index; + + while ((index = FindDelimiterIndex(byteArray, _delimiter, start)) >= 0) + { + // Use a span over the segment of bytes for the current object + var objectDataSpan = new ReadOnlySpan(byteArray, start, index - start); + Fortune fortune = ConvertBytesToObject(objectDataSpan); + fortunes.Add(fortune); + + // Move past the delimiter + start = index + delimiterLength; + } + + NativeMethods.FreeHandlePointer(handlePointer); + + var response = httpContext.Response; + response.Headers.Add(_headerServer); + + var result = Results.Extensions.RazorSlice>(fortunes); + result.HtmlEncoder = htmlEncoder; + + return result.ExecuteAsync(httpContext); + + /* var response = httpContext.Response; response.Headers.Add(_headerServer); response.Headers.Add(_headerContentType); @@ -43,10 +117,51 @@ public unsafe Task Invoke(HttpContext httpContext) new KeyValuePair("Content-Length", payloadLength.ToString())); return response.Body.WriteAsync(json, 0, payloadLength); + */ } return _next(httpContext); } + + private static int FindDelimiterIndex(byte[] array, byte[] delimiter, int startIndex) + { + int endIndex = array.Length - delimiter.Length; + + for (int i = startIndex; i <= endIndex; i++) + { + bool isMatch = true; + + for (int j = 0; j < delimiter.Length; j++) + { + if (array[i + j] != delimiter[j]) + { + isMatch = false; + break; + } + } + + if (isMatch) + { + return i; + } + } + + return -1; + } + + private static Fortune ConvertBytesToObject(ReadOnlySpan data) + { + int offset = 0; + + // Read Id + int id = BitConverter.ToInt32(data.Slice(offset, sizeof(int))); + offset += sizeof(int); + + // Read Message (remaining bytes in the span) + string message = Encoding.UTF8.GetString(data.Slice(offset)); + + return new Fortune(id, message); + } } public static class FortunesMiddlewareExtensions diff --git a/frameworks/CSharp/appmpower/src/appMpower/Objects/Fortune.cs b/frameworks/CSharp/appmpower/src/appMpower/Objects/Fortune.cs new file mode 100644 index 00000000000..187a3c06980 --- /dev/null +++ b/frameworks/CSharp/appmpower/src/appMpower/Objects/Fortune.cs @@ -0,0 +1,22 @@ +using System; + +namespace appMpower.Objects +{ + public struct Fortune : IComparable, IComparable + { + public Fortune(int id, string message) + { + Id = id; + Message = message; + } + + public int Id { get; set; } + + public string Message { get; set; } + + public int CompareTo(object obj) => throw new InvalidOperationException("The non-generic CompareTo should not be used"); + + // Performance critical, using culture insensitive comparison + public int CompareTo(Fortune other) => string.CompareOrdinal(Message, other.Message); + } +} \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/appMpower/Slices/Fortunes.cshtml b/frameworks/CSharp/appmpower/src/appMpower/Slices/Fortunes.cshtml new file mode 100644 index 00000000000..ebf2c38d5ec --- /dev/null +++ b/frameworks/CSharp/appmpower/src/appMpower/Slices/Fortunes.cshtml @@ -0,0 +1,2 @@ +@inherits RazorSliceHttpResult> +Fortunes@foreach (var item in Model){}
idmessage
@WriteNumber(item.Id, default, CultureInfo.InvariantCulture, false)@item.Message
\ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/appMpower/Slices/_ViewImports.cshtml b/frameworks/CSharp/appmpower/src/appMpower/Slices/_ViewImports.cshtml new file mode 100644 index 00000000000..98a692ef65a --- /dev/null +++ b/frameworks/CSharp/appmpower/src/appMpower/Slices/_ViewImports.cshtml @@ -0,0 +1,10 @@ +@inherits RazorSliceHttpResult + +@using System.Globalization; +@using Microsoft.AspNetCore.Razor; +@using Microsoft.AspNetCore.Http.HttpResults; +@using RazorSlices; +@using appMpower.Objects; + +@tagHelperPrefix __disable_tagHelpers__: +@removeTagHelper *, Microsoft.AspNetCore.Mvc.Razor \ No newline at end of file diff --git a/frameworks/CSharp/appmpower/src/appMpower/appMpower.csproj b/frameworks/CSharp/appmpower/src/appMpower/appMpower.csproj index 5661d01ce61..ba6fe6f7059 100644 --- a/frameworks/CSharp/appmpower/src/appMpower/appMpower.csproj +++ b/frameworks/CSharp/appmpower/src/appMpower/appMpower.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 Exe true @@ -24,6 +24,7 @@ + diff --git a/frameworks/CSharp/aspnetcore/README.md b/frameworks/CSharp/aspnetcore/README.md index 2e0ddeab3b4..00092de375d 100644 --- a/frameworks/CSharp/aspnetcore/README.md +++ b/frameworks/CSharp/aspnetcore/README.md @@ -6,5 +6,5 @@ See [.NET Core](http://dot.net) and [ASP.NET Core](https://github.com/dotnet/asp **Language** -* C# 8.0 +* C# 13.0 diff --git a/frameworks/CSharp/aspnetcore/appsettings.postgresql.json b/frameworks/CSharp/aspnetcore/appsettings.postgresql.json index abc42e6ce8a..423af4d19b0 100644 --- a/frameworks/CSharp/aspnetcore/appsettings.postgresql.json +++ b/frameworks/CSharp/aspnetcore/appsettings.postgresql.json @@ -1,4 +1,4 @@ { - "ConnectionString": "Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;SSL Mode=Disable;Maximum Pool Size=18;NoResetOnClose=true;Enlist=false;Max Auto Prepare=4;Multiplexing=true;Write Coalescing Buffer Threshold Bytes=1000", + "ConnectionString": "Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;SSL Mode=Disable;Maximum Pool Size=512;NoResetOnClose=true;Enlist=false;Max Auto Prepare=4", "Database": "postgresql" } diff --git a/frameworks/CSharp/aspnetcore/aspnetcore-aot.dockerfile b/frameworks/CSharp/aspnetcore/aspnetcore-aot.dockerfile index a114ee58727..a7396e3b919 100644 --- a/frameworks/CSharp/aspnetcore/aspnetcore-aot.dockerfile +++ b/frameworks/CSharp/aspnetcore/aspnetcore-aot.dockerfile @@ -1,12 +1,12 @@ -FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build RUN apt-get update RUN apt-get -yqq install clang zlib1g-dev WORKDIR /app COPY src/Platform . RUN dotnet publish -c Release -o out /p:DatabaseProvider=Npgsql /p:PublishAot=true /p:OptimizationPreference=Speed /p:GarbageCollectionAdaptationMode=0 -FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime -ENV URLS http://+:8080 +FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS runtime +ENV URLS=http://+:8080 WORKDIR /app COPY --from=build /app/out ./ diff --git a/frameworks/CSharp/aspnetcore/aspnetcore-minimal.dockerfile b/frameworks/CSharp/aspnetcore/aspnetcore-minimal.dockerfile index 5ab25aa2bb9..24893c9717a 100644 --- a/frameworks/CSharp/aspnetcore/aspnetcore-minimal.dockerfile +++ b/frameworks/CSharp/aspnetcore/aspnetcore-minimal.dockerfile @@ -1,10 +1,13 @@ -FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build WORKDIR /app COPY src/Minimal . RUN dotnet publish -c Release -o out -FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime +FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS runtime ENV URLS http://+:8080 +ENV DOTNET_GCDynamicAdaptationMode=0 +ENV DOTNET_ReadyToRun=0 +ENV DOTNET_HillClimbing_Disable=1 WORKDIR /app COPY --from=build /app/out ./ diff --git a/frameworks/CSharp/aspnetcore/aspnetcore-mvc.dockerfile b/frameworks/CSharp/aspnetcore/aspnetcore-mvc.dockerfile index b0d126973cb..6922a53bf2a 100644 --- a/frameworks/CSharp/aspnetcore/aspnetcore-mvc.dockerfile +++ b/frameworks/CSharp/aspnetcore/aspnetcore-mvc.dockerfile @@ -1,10 +1,13 @@ -FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build WORKDIR /app COPY src/Mvc . RUN dotnet publish -c Release -o out -FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime +FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS runtime ENV URLS http://+:8080 +ENV DOTNET_GCDynamicAdaptationMode=0 +ENV DOTNET_ReadyToRun=0 +ENV DOTNET_HillClimbing_Disable=1 WORKDIR /app COPY --from=build /app/out ./ diff --git a/frameworks/CSharp/aspnetcore/aspnetcore-mysql.dockerfile b/frameworks/CSharp/aspnetcore/aspnetcore-mysql.dockerfile index 505414aa173..ecc0a8331c3 100644 --- a/frameworks/CSharp/aspnetcore/aspnetcore-mysql.dockerfile +++ b/frameworks/CSharp/aspnetcore/aspnetcore-mysql.dockerfile @@ -1,10 +1,13 @@ -FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build WORKDIR /app COPY src/Platform . RUN dotnet publish -c Release -o out /p:DatabaseProvider=MySqlConnector -FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime +FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS runtime ENV URLS http://+:8080 +ENV DOTNET_GCDynamicAdaptationMode=0 +ENV DOTNET_ReadyToRun=0 +ENV DOTNET_HillClimbing_Disable=1 WORKDIR /app COPY --from=build /app/out ./ diff --git a/frameworks/CSharp/aspnetcore/aspnetcore.dockerfile b/frameworks/CSharp/aspnetcore/aspnetcore.dockerfile index 5a846103428..64510ffe786 100644 --- a/frameworks/CSharp/aspnetcore/aspnetcore.dockerfile +++ b/frameworks/CSharp/aspnetcore/aspnetcore.dockerfile @@ -1,10 +1,13 @@ -FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build WORKDIR /app COPY src/Platform . RUN dotnet publish -c Release -o out /p:DatabaseProvider=Npgsql -FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime -ENV URLS http://+:8080 +FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS runtime +ENV URLS=http://+:8080 +ENV DOTNET_GCDynamicAdaptationMode=0 +ENV DOTNET_ReadyToRun=0 +ENV DOTNET_HillClimbing_Disable=1 WORKDIR /app COPY --from=build /app/out ./ diff --git a/frameworks/CSharp/aspnetcore/benchmark_config.json b/frameworks/CSharp/aspnetcore/benchmark_config.json index 426f94ea5f7..831a9f27d81 100644 --- a/frameworks/CSharp/aspnetcore/benchmark_config.json +++ b/frameworks/CSharp/aspnetcore/benchmark_config.json @@ -30,6 +30,7 @@ "json_url": "/json", "db_url": "/db", "query_url": "/queries/", + "fortune_url": "/fortunes", "update_url": "/updates/", "cached_query_url": "/cached-worlds/", "port": 8080, diff --git a/frameworks/CSharp/aspnetcore/src/Minimal/Minimal.csproj b/frameworks/CSharp/aspnetcore/src/Minimal/Minimal.csproj index 783ec88ff32..12f3446d8b7 100644 --- a/frameworks/CSharp/aspnetcore/src/Minimal/Minimal.csproj +++ b/frameworks/CSharp/aspnetcore/src/Minimal/Minimal.csproj @@ -1,7 +1,7 @@  - net8.0 + net9.0 enable enable latest @@ -9,9 +9,9 @@ - - - + + + diff --git a/frameworks/CSharp/aspnetcore/src/Minimal/Program.cs b/frameworks/CSharp/aspnetcore/src/Minimal/Program.cs index 9ac63edcca2..7aaac6db1ac 100644 --- a/frameworks/CSharp/aspnetcore/src/Minimal/Program.cs +++ b/frameworks/CSharp/aspnetcore/src/Minimal/Program.cs @@ -1,13 +1,11 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.Text.Encodings.Web; -using System.Text.Unicode; -using Microsoft.AspNetCore.Http.HttpResults; -using RazorSlices; using Minimal; using Minimal.Database; using Minimal.Models; +using System.Text.Encodings.Web; +using System.Text.Unicode; var builder = WebApplication.CreateBuilder(args); @@ -36,14 +34,14 @@ app.MapGet("/db", async (Db db) => await db.LoadSingleQueryRow()); -var createFortunesTemplate = RazorSlice.ResolveSliceFactory>("/Templates/Fortunes.cshtml"); var htmlEncoder = CreateHtmlEncoder(); app.MapGet("/fortunes", async (HttpContext context, Db db) => { var fortunes = await db.LoadFortunesRows(); - var template = (RazorSliceHttpResult>)createFortunesTemplate(fortunes); - template.HtmlEncoder = htmlEncoder; - return template; + var result = Results.Extensions.RazorSlice>(fortunes); + result.HtmlEncoder = htmlEncoder; + + return result; }); app.MapGet("/queries/{count?}", async (Db db, string? count) => await db.LoadMultipleQueriesRows(count)); diff --git a/frameworks/CSharp/aspnetcore/src/Minimal/Templates/Fortunes.cshtml b/frameworks/CSharp/aspnetcore/src/Minimal/Slices/Fortunes.cshtml similarity index 100% rename from frameworks/CSharp/aspnetcore/src/Minimal/Templates/Fortunes.cshtml rename to frameworks/CSharp/aspnetcore/src/Minimal/Slices/Fortunes.cshtml diff --git a/frameworks/CSharp/aspnetcore/src/Minimal/Templates/_ViewImports.cshtml b/frameworks/CSharp/aspnetcore/src/Minimal/Slices/_ViewImports.cshtml similarity index 100% rename from frameworks/CSharp/aspnetcore/src/Minimal/Templates/_ViewImports.cshtml rename to frameworks/CSharp/aspnetcore/src/Minimal/Slices/_ViewImports.cshtml diff --git a/frameworks/CSharp/aspnetcore/src/Mvc/Mvc.csproj b/frameworks/CSharp/aspnetcore/src/Mvc/Mvc.csproj index 5bffd479c0f..68c70002683 100644 --- a/frameworks/CSharp/aspnetcore/src/Mvc/Mvc.csproj +++ b/frameworks/CSharp/aspnetcore/src/Mvc/Mvc.csproj @@ -1,7 +1,7 @@  - net8.0 + net9.0 enable enable latest @@ -9,8 +9,8 @@ - - + + diff --git a/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Caching.cs b/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Caching.cs index eb5fff0199e..a99586c96e2 100644 --- a/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Caching.cs +++ b/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Caching.cs @@ -6,7 +6,7 @@ namespace PlatformBenchmarks; -public partial class BenchmarkApplication +public sealed partial class BenchmarkApplication { private static async Task Caching(PipeWriter pipeWriter, int count) { diff --git a/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Fortunes.cs b/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Fortunes.cs index 52a0939559c..0279754a128 100644 --- a/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Fortunes.cs +++ b/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Fortunes.cs @@ -1,7 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -#if !AOT +using System; using System.IO.Pipelines; using System.Runtime.CompilerServices; using System.Threading.Tasks; @@ -9,7 +9,7 @@ namespace PlatformBenchmarks; -public partial class BenchmarkApplication +public sealed partial class BenchmarkApplication { private async Task FortunesRaw(PipeWriter pipeWriter) { @@ -19,7 +19,7 @@ await RawDb.LoadFortunesRows(), FortunesTemplateFactory); } - private ValueTask OutputFortunes(PipeWriter pipeWriter, TModel model, SliceFactory templateFactory) + private ValueTask OutputFortunes(PipeWriter pipeWriter, TModel model, Func> templateFactory) { // Render headers var preamble = """ @@ -39,7 +39,7 @@ private ValueTask OutputFortunes(PipeWriter pipeWriter, TModel model, Sl // Kestrel PipeWriter span size is 4K, headers above already written to first span & template output is ~1350 bytes, // so 2K chunk size should result in only a single span and chunk being used. var chunkedWriter = GetChunkedWriter(pipeWriter, chunkSizeHint: 2048); - var renderTask = template.RenderAsync(chunkedWriter, null, HtmlEncoder); + var renderTask = template.RenderAsync(chunkedWriter, HtmlEncoder); if (renderTask.IsCompletedSuccessfully) { @@ -51,18 +51,17 @@ private ValueTask OutputFortunes(PipeWriter pipeWriter, TModel model, Sl return AwaitTemplateRenderTask(renderTask, chunkedWriter, template); } - private static async ValueTask AwaitTemplateRenderTask(ValueTask renderTask, ChunkedBufferWriter chunkedWriter, RazorSlice template) + private static async ValueTask AwaitTemplateRenderTask(ValueTask renderTask, ChunkedPipeWriter chunkedWriter, RazorSlice template) { await renderTask; EndTemplateRendering(chunkedWriter, template); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void EndTemplateRendering(ChunkedBufferWriter chunkedWriter, RazorSlice template) + private static void EndTemplateRendering(ChunkedPipeWriter chunkedWriter, RazorSlice template) { - chunkedWriter.End(); + chunkedWriter.Complete(); ReturnChunkedWriter(chunkedWriter); template.Dispose(); } } -#endif diff --git a/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.HttpConnection.cs b/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.HttpConnection.cs index cbd375cb61b..892ecc48457 100644 --- a/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.HttpConnection.cs +++ b/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.HttpConnection.cs @@ -12,7 +12,7 @@ namespace PlatformBenchmarks; -public partial class BenchmarkApplication : IHttpConnection +public sealed partial class BenchmarkApplication : IHttpConnection { private State _state; @@ -193,15 +193,15 @@ private static BufferWriter GetWriter(PipeWriter pipeWriter, int => new(new(pipeWriter), sizeHint); [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ChunkedBufferWriter GetChunkedWriter(PipeWriter pipeWriter, int chunkSizeHint) + private static ChunkedPipeWriter GetChunkedWriter(PipeWriter pipeWriter, int chunkSizeHint) { var writer = ChunkedWriterPool.Get(); - writer.SetOutput(new WriterAdapter(pipeWriter), chunkSizeHint); + writer.SetOutput(pipeWriter, chunkSizeHint); return writer; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void ReturnChunkedWriter(ChunkedBufferWriter writer) => ChunkedWriterPool.Return(writer); + private static void ReturnChunkedWriter(ChunkedPipeWriter writer) => ChunkedWriterPool.Return(writer); private struct WriterAdapter : IBufferWriter { diff --git a/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Json.cs b/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Json.cs index 5babe2b61eb..dbfea6b0454 100644 --- a/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Json.cs +++ b/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Json.cs @@ -9,7 +9,7 @@ namespace PlatformBenchmarks; -public partial class BenchmarkApplication +public sealed partial class BenchmarkApplication { private readonly static uint _jsonPayloadSize = (uint)JsonSerializer.SerializeToUtf8Bytes( new JsonMessage { message = "Hello, World!" }, diff --git a/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.MultipleQueries.cs b/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.MultipleQueries.cs index 0541e20de1f..82ea5a4a8a2 100644 --- a/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.MultipleQueries.cs +++ b/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.MultipleQueries.cs @@ -8,7 +8,7 @@ namespace PlatformBenchmarks; -public partial class BenchmarkApplication +public sealed partial class BenchmarkApplication { private static async Task MultipleQueries(PipeWriter pipeWriter, int count) { diff --git a/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Plaintext.cs b/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Plaintext.cs index 6e4c4eec1c8..57c90e64e92 100644 --- a/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Plaintext.cs +++ b/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Plaintext.cs @@ -7,7 +7,7 @@ namespace PlatformBenchmarks; -public partial class BenchmarkApplication +public sealed partial class BenchmarkApplication { private static ReadOnlySpan _plaintextPreamble => "HTTP/1.1 200 OK\r\n"u8 + diff --git a/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.SingleQuery.cs b/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.SingleQuery.cs index 350eb43ad13..dd8add86fab 100644 --- a/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.SingleQuery.cs +++ b/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.SingleQuery.cs @@ -7,7 +7,7 @@ namespace PlatformBenchmarks; -public partial class BenchmarkApplication +public sealed partial class BenchmarkApplication { private static async Task SingleQuery(PipeWriter pipeWriter) { diff --git a/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Updates.cs b/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Updates.cs index 6962aa0cf2c..66bfdba1858 100644 --- a/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Updates.cs +++ b/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Updates.cs @@ -7,7 +7,7 @@ namespace PlatformBenchmarks; -public partial class BenchmarkApplication +public sealed partial class BenchmarkApplication { private static async Task Updates(PipeWriter pipeWriter, int count) { diff --git a/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.cs b/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.cs index e256e958c75..2db511586a3 100644 --- a/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.cs +++ b/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.cs @@ -10,9 +10,8 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.Extensions.ObjectPool; -#if !AOT +using Platform.Templates; using RazorSlices; -#endif namespace PlatformBenchmarks { @@ -42,26 +41,24 @@ public sealed partial class BenchmarkApplication public static RawDb RawDb { get; set; } - private static readonly DefaultObjectPool> ChunkedWriterPool + private static readonly DefaultObjectPool ChunkedWriterPool = new(new ChunkedWriterObjectPolicy()); - private sealed class ChunkedWriterObjectPolicy : IPooledObjectPolicy> + private sealed class ChunkedWriterObjectPolicy : IPooledObjectPolicy { - public ChunkedBufferWriter Create() => new(); + public ChunkedPipeWriter Create() => new(); - public bool Return(ChunkedBufferWriter writer) + public bool Return(ChunkedPipeWriter writer) { writer.Reset(); return true; } } -#if !AOT #if NPGSQL - private readonly static SliceFactory> FortunesTemplateFactory = RazorSlice.ResolveSliceFactory>("/Templates/FortunesUtf8.cshtml"); + private readonly static Func, RazorSlice>> FortunesTemplateFactory = FortunesUtf8.Create; #else - private readonly static SliceFactory> FortunesTemplateFactory = RazorSlice.ResolveSliceFactory>("/Templates/FortunesUtf16.cshtml"); -#endif + private readonly static Func, RazorSlice>> FortunesTemplateFactory = FortunesUtf16.Create; #endif [ThreadStatic] @@ -167,9 +164,7 @@ private bool ProcessRequest(ref BufferWriter writer) private Task ProcessRequestAsync() => _requestType switch { -#if !AOT RequestType.FortunesRaw => FortunesRaw(Writer), -#endif RequestType.SingleQuery => SingleQuery(Writer), RequestType.Caching => Caching(Writer, _queries), RequestType.Updates => Updates(Writer, _queries), diff --git a/frameworks/CSharp/aspnetcore/src/Platform/ChunkedBufferWriter.cs b/frameworks/CSharp/aspnetcore/src/Platform/ChunkedPipeWriter.cs similarity index 85% rename from frameworks/CSharp/aspnetcore/src/Platform/ChunkedBufferWriter.cs rename to frameworks/CSharp/aspnetcore/src/Platform/ChunkedPipeWriter.cs index 700519edb58..5a1dd80c678 100644 --- a/frameworks/CSharp/aspnetcore/src/Platform/ChunkedBufferWriter.cs +++ b/frameworks/CSharp/aspnetcore/src/Platform/ChunkedPipeWriter.cs @@ -5,35 +5,40 @@ using System.Buffers; using System.Buffers.Text; using System.Diagnostics; +using System.IO.Pipelines; using System.Numerics; using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; namespace PlatformBenchmarks; -internal sealed class ChunkedBufferWriter : IBufferWriter where TWriter : IBufferWriter +internal sealed class ChunkedPipeWriter : PipeWriter { private const int DefaultChunkSizeHint = 2048; private static readonly StandardFormat DefaultHexFormat = GetHexFormat(DefaultChunkSizeHint); private static ReadOnlySpan ChunkTerminator => "\r\n"u8; - private TWriter _output; + private PipeWriter _output; private int _chunkSizeHint; private StandardFormat _hexFormat = DefaultHexFormat; private Memory _currentFullChunk; private Memory _currentChunk; private int _buffered; + private long _unflushedBytes; private bool _ended = false; public Memory Memory => _currentChunk; - public TWriter Output => _output; + public PipeWriter Output => _output; public int Buffered => _buffered; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetOutput(TWriter output, int chunkSizeHint = DefaultChunkSizeHint) + public void SetOutput(PipeWriter output, int chunkSizeHint = DefaultChunkSizeHint) { _buffered = 0; + _unflushedBytes = 0; _chunkSizeHint = chunkSizeHint; _output = output; @@ -44,6 +49,7 @@ public void SetOutput(TWriter output, int chunkSizeHint = DefaultChunkSizeHint) public void Reset() { _buffered = 0; + _unflushedBytes = 0; _output = default; _ended = false; _hexFormat = DefaultHexFormat; @@ -51,16 +57,21 @@ public void Reset() _currentChunk = default; } + public override bool CanGetUnflushedBytes => true; + + public override long UnflushedBytes => _unflushedBytes; + [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Advance(int count) + public override void Advance(int count) { ThrowIfEnded(); _buffered += count; + _unflushedBytes += count; _currentChunk = _currentChunk[count..]; } - public Memory GetMemory(int sizeHint = 0) + public override Memory GetMemory(int sizeHint = 0) { ThrowIfEnded(); @@ -71,9 +82,14 @@ public Memory GetMemory(int sizeHint = 0) return _currentChunk; } - public Span GetSpan(int sizeHint = 0) => GetMemory(sizeHint).Span; + public override Span GetSpan(int sizeHint = 0) => GetMemory(sizeHint).Span; + + public override void CancelPendingFlush() + { + _output.CancelPendingFlush(); + } - public void End() + public override void Complete(Exception exception = null) { ThrowIfEnded(); @@ -82,6 +98,17 @@ public void End() _ended = true; } + public override ValueTask FlushAsync(CancellationToken cancellationToken = default) + { + CommitCurrentChunk(isFinal: false); + + var flushTask = _output.FlushAsync(cancellationToken); + + _unflushedBytes = 0; + + return flushTask; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static StandardFormat GetHexFormat(int maxValue) { diff --git a/frameworks/CSharp/aspnetcore/src/Platform/Platform.csproj b/frameworks/CSharp/aspnetcore/src/Platform/Platform.csproj index 548e446ee0a..7db51dc3ec7 100644 --- a/frameworks/CSharp/aspnetcore/src/Platform/Platform.csproj +++ b/frameworks/CSharp/aspnetcore/src/Platform/Platform.csproj @@ -1,12 +1,11 @@  - net8.0 + net9.0 true true preview 38063504-d08c-495a-89c9-daaad2f60f31 - AOT;$(DefineConstants) @@ -19,14 +18,14 @@ - - - - + + + + - - $(MSBuildThisFileDirectory)Templates/**;$(DefaultItemExcludes) - + + + diff --git a/frameworks/CSharp/aspnetcore/src/Platform/Program.cs b/frameworks/CSharp/aspnetcore/src/Platform/Program.cs index 9b22d3f5b40..28fc5d757dd 100644 --- a/frameworks/CSharp/aspnetcore/src/Platform/Program.cs +++ b/frameworks/CSharp/aspnetcore/src/Platform/Program.cs @@ -52,7 +52,6 @@ public static IWebHost BuildWebHost(string[] args) #if DEBUG .AddUserSecrets() #endif - .AddEnvironmentVariables() .AddEnvironmentVariables() .AddCommandLine(args) .Build(); diff --git a/frameworks/CSharp/genhttp/Benchmarks/Benchmarks.csproj b/frameworks/CSharp/genhttp/Benchmarks/Benchmarks.csproj index 9b49099c3bb..b96158a711b 100644 --- a/frameworks/CSharp/genhttp/Benchmarks/Benchmarks.csproj +++ b/frameworks/CSharp/genhttp/Benchmarks/Benchmarks.csproj @@ -1,40 +1,38 @@  - - - - net8.0 - 10.0 - - GenHTTP Benchmarks - Test suite to be executed with TechEmpower FrameworkBenchmarks. - - Benchmarks.Program - Exe - - true - true - - - - - - - - - - - - - - - - - - - - - - - - + + + + net9.0 + 13.0 + true + Exe + + GenHTTP Benchmarks + Test suite to be executed with TechEmpower FrameworkBenchmarks. + + true + true + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frameworks/CSharp/genhttp/Benchmarks/Model/DatabaseContext.cs b/frameworks/CSharp/genhttp/Benchmarks/Model/DatabaseContext.cs index 2bc6ca7ed0d..f27f1d8e245 100644 --- a/frameworks/CSharp/genhttp/Benchmarks/Model/DatabaseContext.cs +++ b/frameworks/CSharp/genhttp/Benchmarks/Model/DatabaseContext.cs @@ -1,52 +1,43 @@ using Microsoft.EntityFrameworkCore; -namespace Benchmarks.Model +namespace Benchmarks.Model; + +public sealed class DatabaseContext : DbContext { + private static DbContextOptions _options; - public sealed class DatabaseContext : DbContext - { - private static DbContextOptions _Options; + private static DbContextOptions _noTrackingOptions; - private static DbContextOptions _NoTrackingOptions; + #region Factory - #region Factory + public static DatabaseContext Create() => new(_options ??= GetOptions(true)); - public static DatabaseContext Create() - { - return new DatabaseContext(_Options ??= GetOptions(true)); - } - - public static DatabaseContext CreateNoTracking() - { - return new DatabaseContext(_NoTrackingOptions ??= GetOptions(false)); - } + public static DatabaseContext CreateNoTracking() => new(_noTrackingOptions ??= GetOptions(false)); - private static DbContextOptions GetOptions(bool tracking) - { - var optionsBuilder = new DbContextOptionsBuilder(); - - optionsBuilder.UseNpgsql("Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;Maximum Pool Size=256;NoResetOnClose=true;Enlist=false;Max Auto Prepare=4"); + private static DbContextOptions GetOptions(bool tracking) + { + var optionsBuilder = new DbContextOptionsBuilder(); - if (!tracking) - { - optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); - } + optionsBuilder.UseNpgsql("Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;SSL Mode=Disable;Maximum Pool Size=512;NoResetOnClose=true;Enlist=false;Max Auto Prepare=4"); - return optionsBuilder.Options; + if (!tracking) + { + optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); } - private DatabaseContext(DbContextOptions options) : base(options) { } + return optionsBuilder.Options; + } - #endregion + private DatabaseContext(DbContextOptions options) : base(options) { } - #region Entities + #endregion - public DbSet World { get; set; } + #region Entities - public DbSet Fortune { get; set; } + public DbSet World { get; set; } - #endregion + public DbSet Fortune { get; set; } - } + #endregion } diff --git a/frameworks/CSharp/genhttp/Benchmarks/Model/Fortune.cs b/frameworks/CSharp/genhttp/Benchmarks/Model/Fortune.cs index c3ca5d1d6a7..919f997e01b 100644 --- a/frameworks/CSharp/genhttp/Benchmarks/Model/Fortune.cs +++ b/frameworks/CSharp/genhttp/Benchmarks/Model/Fortune.cs @@ -1,25 +1,17 @@ -using System; -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace Benchmarks.Model -{ - - [Table("fortune")] - public class Fortune : IComparable, IComparable - { - - [Column("id")] - public int ID { get; set; } +namespace Benchmarks.Model; - [Column("message")] - [StringLength(2048)] - public string Message { get; set; } - - public int CompareTo(object obj) => CompareTo((Fortune)obj); +[Table("fortune")] +public class Fortune +{ - public int CompareTo(Fortune other) => string.CompareOrdinal(Message, other.Message); + [Column("id")] + public int Id { get; set; } - } + [Column("message")] + [StringLength(2048)] + public string Message { get; set; } } diff --git a/frameworks/CSharp/genhttp/Benchmarks/Model/World.cs b/frameworks/CSharp/genhttp/Benchmarks/Model/World.cs index ec19183f331..45da4ab828d 100644 --- a/frameworks/CSharp/genhttp/Benchmarks/Model/World.cs +++ b/frameworks/CSharp/genhttp/Benchmarks/Model/World.cs @@ -1,18 +1,15 @@ using System.ComponentModel.DataAnnotations.Schema; -namespace Benchmarks.Model -{ - - [Table("world")] - public class World - { +namespace Benchmarks.Model; - [Column("id")] - public int Id { get; set; } +[Table("world")] +public class World +{ - [Column("randomnumber")] - public int RandomNumber { get; set; } + [Column("id")] + public int Id { get; set; } - } + [Column("randomnumber")] + public int RandomNumber { get; set; } -} +} \ No newline at end of file diff --git a/frameworks/CSharp/genhttp/Benchmarks/Program.cs b/frameworks/CSharp/genhttp/Benchmarks/Program.cs index efd8a162ac9..aa2eb68938a 100644 --- a/frameworks/CSharp/genhttp/Benchmarks/Program.cs +++ b/frameworks/CSharp/genhttp/Benchmarks/Program.cs @@ -1,35 +1,20 @@ -using GenHTTP.Engine; - +using Benchmarks.Tests; +using Benchmarks.Utilities; +using GenHTTP.Engine.Kestrel; using GenHTTP.Modules.IO; using GenHTTP.Modules.Layouting; using GenHTTP.Modules.Webservices; -using Benchmarks.Tests; -using Benchmarks.Utilities; - -namespace Benchmarks -{ - - public static class Program - { - - public static int Main(string[] args) - { - var tests = Layout.Create() - .Add("plaintext", Content.From(Resource.FromString("Hello, World!"))) - .Add("fortunes", new FortuneHandlerBuilder()) - .AddService("json") - .AddService("db") - .AddService("queries") - .AddService("updates") - .AddService("cached-worlds") - .Add(ServerHeader.Create()); - - return Host.Create() - .Handler(tests) - .Run(); - } - - } - -} +var tests = Layout.Create() + .Add("plaintext", Content.From(Resource.FromString("Hello, World!"))) + .Add("fortunes", new FortuneHandler()) + .AddService("json") + .AddService("db") + .AddService("queries") + .AddService("updates") + .AddService("cached-worlds") + .Add(ServerHeader.Create()); + +return await Host.Create() + .Handler(tests) + .RunAsync(); diff --git a/frameworks/CSharp/genhttp/Benchmarks/Resources/Fortunes.html b/frameworks/CSharp/genhttp/Benchmarks/Resources/Fortunes.html deleted file mode 100644 index b7e0d81eb8a..00000000000 --- a/frameworks/CSharp/genhttp/Benchmarks/Resources/Fortunes.html +++ /dev/null @@ -1,7 +0,0 @@ - - - @foreach (var cookie in Model.Cookies) - { - - } -
idmessage
@cookie.ID@HttpUtility.HtmlEncode(cookie.Message)
\ No newline at end of file diff --git a/frameworks/CSharp/genhttp/Benchmarks/Resources/Template.html b/frameworks/CSharp/genhttp/Benchmarks/Resources/Template.html index 7b521ecb334..90bf1592593 100644 --- a/frameworks/CSharp/genhttp/Benchmarks/Resources/Template.html +++ b/frameworks/CSharp/genhttp/Benchmarks/Resources/Template.html @@ -1,9 +1,22 @@  - @Model.Meta.Title + Fortunes - @Model.Content + + + + + + + {for cookie in cookies: + + + + + } +
idmessage
{ cookie.id }{ cookie.message }
+ - \ No newline at end of file + diff --git a/frameworks/CSharp/genhttp/Benchmarks/Tests/CacheResource.cs b/frameworks/CSharp/genhttp/Benchmarks/Tests/CacheResource.cs index c5c91258a94..e0e4bc50d91 100644 --- a/frameworks/CSharp/genhttp/Benchmarks/Tests/CacheResource.cs +++ b/frameworks/CSharp/genhttp/Benchmarks/Tests/CacheResource.cs @@ -1,84 +1,84 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - +using Benchmarks.Model; +using GenHTTP.Modules.Webservices; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; -using Benchmarks.Model; - -using GenHTTP.Modules.Webservices; +namespace Benchmarks.Tests; -namespace Benchmarks.Tests +public sealed class CacheResource { + private static readonly Random Random = new(); - public sealed class CacheResource + private static readonly MemoryCache Cache = new(new MemoryCacheOptions { - private static readonly Random _Random = new Random(); - - private static readonly MemoryCache _Cache = new MemoryCache(new MemoryCacheOptions() { ExpirationScanFrequency = TimeSpan.FromMinutes(60) }); - - private static readonly object[] _CacheKeys = Enumerable.Range(0, 10001).Select((i) => new CacheKey(i)).ToArray(); - - public sealed class CacheKey : IEquatable - { - private readonly int _value; - - public CacheKey(int value) => _value = value; + ExpirationScanFrequency = TimeSpan.FromMinutes(60) + }); - public bool Equals(CacheKey key) => key._value == _value; + private static readonly object[] CacheKeys = Enumerable.Range(0, 10001).Select(i => new CacheKey(i)).ToArray(); - public override bool Equals(object obj) => ReferenceEquals(obj, this); + [ResourceMethod(":queries")] + public ValueTask> GetWorldsFromPath(string queries) => GetWorlds(queries); - public override int GetHashCode() => _value; + [ResourceMethod] + public async ValueTask> GetWorlds(string queries) + { + var count = 1; - public override string ToString() => _value.ToString(); + int.TryParse(queries, out count); + if (count < 1) + { + count = 1; } + else if (count > 500) + { + count = 500; + } + + var result = new List(count); - [ResourceMethod(":queries")] - public ValueTask> GetWorldsFromPath(string queries) => GetWorlds(queries); + await using var context = DatabaseContext.CreateNoTracking(); - [ResourceMethod] - public async ValueTask> GetWorlds(string queries) + for (var i = 0; i < count; i++) { - var count = 1; + var id = Random.Next(1, 10001); - int.TryParse(queries, out count); + var key = CacheKeys[id]; - if (count < 1) count = 1; - else if (count > 500) count = 500; + var data = Cache.Get(key); - var result = new List(count); + if (data != null) + { + result.Add(data); + } + else + { + var resolved = await context.World.FirstOrDefaultAsync(w => w.Id == id).ConfigureAwait(false); - using var context = DatabaseContext.CreateNoTracking(); + Cache.Set(key, resolved); - for (var i = 0; i < count; i++) - { - var id = _Random.Next(1, 10001); + result.Add(resolved); + } + } - var key = _CacheKeys[id]; + return result; + } - var data = _Cache.Get(key); + public sealed class CacheKey : IEquatable + { + private readonly int _value; - if (data != null) - { - result.Add(data); - } - else - { - var resolved = await context.World.FirstOrDefaultAsync(w => w.Id == id).ConfigureAwait(false); + public CacheKey(int value) + { + _value = value; + } - _Cache.Set(key, resolved); + public bool Equals(CacheKey key) => key._value == _value; - result.Add(resolved); - } - } + public override bool Equals(object obj) => ReferenceEquals(obj, this); - return result; - } + public override int GetHashCode() => _value; + public override string ToString() => _value.ToString(); } - } diff --git a/frameworks/CSharp/genhttp/Benchmarks/Tests/DbResource.cs b/frameworks/CSharp/genhttp/Benchmarks/Tests/DbResource.cs index 64256d0a215..ac33808fdfd 100644 --- a/frameworks/CSharp/genhttp/Benchmarks/Tests/DbResource.cs +++ b/frameworks/CSharp/genhttp/Benchmarks/Tests/DbResource.cs @@ -1,29 +1,21 @@ -using System; -using System.Threading.Tasks; - -using Microsoft.EntityFrameworkCore; - +using Benchmarks.Model; using GenHTTP.Modules.Webservices; +using Microsoft.EntityFrameworkCore; -using Benchmarks.Model; +namespace Benchmarks.Tests; -namespace Benchmarks.Tests +public sealed class DbResource { + private static readonly Random Random = new(); - public sealed class DbResource + [ResourceMethod] + public async ValueTask GetRandomWorld() { - private static readonly Random _Random = new(); - - [ResourceMethod] - public async ValueTask GetRandomWorld() - { - var id = _Random.Next(1, 10001); - - using var context = DatabaseContext.CreateNoTracking(); + var id = Random.Next(1, 10001); - return await context.World.FirstOrDefaultAsync(w => w.Id == id).ConfigureAwait(false); - } + await using var context = DatabaseContext.CreateNoTracking(); + return await context.World.FirstOrDefaultAsync(w => w.Id == id).ConfigureAwait(false); } } diff --git a/frameworks/CSharp/genhttp/Benchmarks/Tests/FortuneHandler.cs b/frameworks/CSharp/genhttp/Benchmarks/Tests/FortuneHandler.cs index 6add5edbaeb..c7a488f5e78 100644 --- a/frameworks/CSharp/genhttp/Benchmarks/Tests/FortuneHandler.cs +++ b/frameworks/CSharp/genhttp/Benchmarks/Tests/FortuneHandler.cs @@ -1,116 +1,85 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using System.Linq; -using System.Web; -using System.IO; - -using Microsoft.EntityFrameworkCore; - +using System.Web; +using Benchmarks.Model; +using Cottle; using GenHTTP.Api.Content; -using GenHTTP.Api.Content.Templating; using GenHTTP.Api.Protocol; using GenHTTP.Modules.IO; -using GenHTTP.Modules.Razor; - -using Benchmarks.Model; +using GenHTTP.Modules.Pages; +using GenHTTP.Modules.Pages.Rendering; -namespace Benchmarks.Tests -{ +using Microsoft.EntityFrameworkCore; - #region Factory +namespace Benchmarks.Tests; - public class FortuneHandlerBuilder : IHandlerBuilder - { +public class FortuneHandler : IHandler +{ - public IHandler Build(IHandler parent) - { - return new FortuneHandler(parent); - } + #region Get-/Setters - } + private TemplateRenderer Template { get; } #endregion - #region Supporting data structures + #region Initialization - public sealed class FortuneModel : BasicModel + public FortuneHandler() { + var resource = Resource.FromAssembly("Template.html").Build(); - public List Cookies { get; } - - public FortuneModel(IRequest request, IHandler handler, List cookies) : base(request, handler) - { - Cookies = cookies; - } - + Template = Renderer.From(resource); } #endregion - public class FortuneHandler : IHandler, IPageRenderer - { - - #region Get-/Setters - - public IHandler Parent { get; } + #region Functionality - private IHandler Page { get; } + public ValueTask PrepareAsync() => new(); - private IRenderer Template { get; } - - #endregion - - #region Initialization - - public FortuneHandler(IHandler parent) + public async ValueTask HandleAsync(IRequest request) + { + var data = new Dictionary { - Parent = parent; + ["cookies"] = Value.FromEnumerable(await GetFortunes()) + }; - Page = ModRazor.Page(Resource.FromAssembly("Fortunes.html"), (r, h) => GetFortunes(r, h)) - .Title("Fortunes") - .AddAssemblyReference() - .AddUsing("System.Web") - .Build(this); + return request.GetPage(await Template.RenderAsync(data)).Build(); + } - Template = ModRazor.Template(Resource.FromAssembly("Template.html")).Build(); - } + private static async ValueTask> GetFortunes() + { + await using var context = DatabaseContext.CreateNoTracking(); - #endregion + var fortunes = await context.Fortune.ToListAsync().ConfigureAwait(false); - #region Functionality + var result = new List(fortunes.Count + 1); - public async ValueTask PrepareAsync() + foreach (var fortune in fortunes) { - await Page.PrepareAsync(); - await Template.PrepareAsync(); + result.Add(Value.FromDictionary(new Dictionary() + { + ["id"] = fortune.Id, + ["message"] = HttpUtility.HtmlEncode(fortune.Message) + })); } - public ValueTask CalculateChecksumAsync() => new(17); - - public IAsyncEnumerable GetContentAsync(IRequest request) => AsyncEnumerable.Empty(); - - public ValueTask RenderAsync(TemplateModel model) => Template.RenderAsync(model); - - public ValueTask RenderAsync(TemplateModel model, Stream target) => Template.RenderAsync(model, target); - - public ValueTask HandleAsync(IRequest request) => Page.HandleAsync(request); - - private static async ValueTask GetFortunes(IRequest request, IHandler handler) + result.Add(Value.FromDictionary(new Dictionary() { - using var context = DatabaseContext.CreateNoTracking(); - - var fortunes = await context.Fortune.ToListAsync().ConfigureAwait(false); - - fortunes.Add(new Fortune() { Message = "Additional fortune added at request time." }); - - fortunes.Sort(); + ["id"] = 0, + ["message"] = "Additional fortune added at request time." + })); - return new FortuneModel(request, handler, fortunes); - } + result.Sort((one, two) => + { + var firstMessage = one.Fields["message"].AsString; + var secondMessage = two.Fields["message"].AsString; - #endregion + return string.Compare(firstMessage, secondMessage, StringComparison.Ordinal); + }); + return result; } + #endregion + } diff --git a/frameworks/CSharp/genhttp/Benchmarks/Tests/JsonResource.cs b/frameworks/CSharp/genhttp/Benchmarks/Tests/JsonResource.cs index 8158b3acd0d..1fae7c88922 100644 --- a/frameworks/CSharp/genhttp/Benchmarks/Tests/JsonResource.cs +++ b/frameworks/CSharp/genhttp/Benchmarks/Tests/JsonResource.cs @@ -1,21 +1,20 @@ using GenHTTP.Modules.Webservices; -namespace Benchmarks.Tests -{ +namespace Benchmarks.Tests; - public sealed class JsonResult - { +public sealed class JsonResult +{ - public string Message { get; set; } + public string Message { get; set; } +} - } +public sealed class JsonResource +{ - public sealed class JsonResource + [ResourceMethod] + public JsonResult GetMessage() => new() { - - [ResourceMethod] - public JsonResult GetMessage() => new() { Message = "Hello, World!" }; - - } + Message = "Hello, World!" + }; } diff --git a/frameworks/CSharp/genhttp/Benchmarks/Tests/QueryResource.cs b/frameworks/CSharp/genhttp/Benchmarks/Tests/QueryResource.cs index 52a4b4994e1..592d9083242 100644 --- a/frameworks/CSharp/genhttp/Benchmarks/Tests/QueryResource.cs +++ b/frameworks/CSharp/genhttp/Benchmarks/Tests/QueryResource.cs @@ -1,47 +1,44 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - +using Benchmarks.Model; +using GenHTTP.Modules.Webservices; using Microsoft.EntityFrameworkCore; -using Benchmarks.Model; - -using GenHTTP.Modules.Webservices; +namespace Benchmarks.Tests; -namespace Benchmarks.Tests +public sealed class QueryResource { + private static readonly Random Random = new(); + + [ResourceMethod(":queries")] + public ValueTask> GetWorldsFromPath(string queries) => GetWorlds(queries); - public sealed class QueryResource + [ResourceMethod] + public async ValueTask> GetWorlds(string queries) { - private static readonly Random _Random = new Random(); + var count = 1; - [ResourceMethod(":queries")] - public ValueTask> GetWorldsFromPath(string queries) => GetWorlds(queries); + int.TryParse(queries, out count); - [ResourceMethod] - public async ValueTask> GetWorlds(string queries) + if (count < 1) { - var count = 1; - - int.TryParse(queries, out count); - - if (count < 1) count = 1; - else if (count > 500) count = 500; - - var result = new List(count); + count = 1; + } + else if (count > 500) + { + count = 500; + } - using var context = DatabaseContext.CreateNoTracking(); + var result = new List(count); - for (int _ = 0; _ < count; _++) - { - var id = _Random.Next(1, 10001); + using var context = DatabaseContext.CreateNoTracking(); - result.Add(await context.World.FirstOrDefaultAsync(w => w.Id == id).ConfigureAwait(false)); - } + for (var _ = 0; _ < count; _++) + { + var id = Random.Next(1, 10001); - return result; + result.Add(await context.World.FirstOrDefaultAsync(w => w.Id == id).ConfigureAwait(false)); } + return result; } -} +} \ No newline at end of file diff --git a/frameworks/CSharp/genhttp/Benchmarks/Tests/UpdateResource.cs b/frameworks/CSharp/genhttp/Benchmarks/Tests/UpdateResource.cs index dcb697333a0..9b2ce8e270c 100644 --- a/frameworks/CSharp/genhttp/Benchmarks/Tests/UpdateResource.cs +++ b/frameworks/CSharp/genhttp/Benchmarks/Tests/UpdateResource.cs @@ -1,66 +1,64 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - +using Benchmarks.Model; +using GenHTTP.Modules.Webservices; using Microsoft.EntityFrameworkCore; -using Benchmarks.Model; +namespace Benchmarks.Tests; -using GenHTTP.Modules.Webservices; - -namespace Benchmarks.Tests +public sealed class UpdateResource { + private static readonly Random Random = new(); + + [ResourceMethod(":queries")] + public ValueTask> UpdateWorldsFromPath(string queries) => UpdateWorlds(queries); - public sealed class UpdateResource + [ResourceMethod] + public async ValueTask> UpdateWorlds(string queries) { - private static Random _Random = new Random(); + var count = 1; - [ResourceMethod(":queries")] - public ValueTask> UpdateWorldsFromPath(string queries) => UpdateWorlds(queries); + int.TryParse(queries, out count); - [ResourceMethod] - public async ValueTask> UpdateWorlds(string queries) + if (count < 1) { - var count = 1; - - int.TryParse(queries, out count); - - if (count < 1) count = 1; - else if (count > 500) count = 500; + count = 1; + } + else if (count > 500) + { + count = 500; + } - var result = new List(count); + var result = new List(count); - var ids = Enumerable.Range(1, 10000).Select(x => _Random.Next(1, 10001)).Distinct().Take(count).ToArray(); + var ids = Enumerable.Range(1, 10000).Select(x => Random.Next(1, 10001)).Distinct().Take(count).ToArray(); - using (var context = DatabaseContext.Create()) + using (var context = DatabaseContext.Create()) + { + foreach (var id in ids) { - foreach (var id in ids) - { - var record = await context.World.FirstOrDefaultAsync(w => w.Id == id).ConfigureAwait(false); + var record = await context.World.FirstOrDefaultAsync(w => w.Id == id).ConfigureAwait(false); - var old = record.RandomNumber; + var old = record.RandomNumber; - var current = old; + var current = old; - for (int i = 0; i < 5; i++) - { - current = _Random.Next(1, 10001); + for (var i = 0; i < 5; i++) + { + current = Random.Next(1, 10001); - if (current != old) break; + if (current != old) + { + break; } + } - record.RandomNumber = current; + record.RandomNumber = current; - result.Add(record); + result.Add(record); - await context.SaveChangesAsync(); - } + await context.SaveChangesAsync(); } - - return result; } + return result; } - -} +} \ No newline at end of file diff --git a/frameworks/CSharp/genhttp/Benchmarks/Utilities/ServerHeader.cs b/frameworks/CSharp/genhttp/Benchmarks/Utilities/ServerHeader.cs index 7fff879379e..5b0a8b090d9 100644 --- a/frameworks/CSharp/genhttp/Benchmarks/Utilities/ServerHeader.cs +++ b/frameworks/CSharp/genhttp/Benchmarks/Utilities/ServerHeader.cs @@ -1,11 +1,7 @@ -namespace Benchmarks.Utilities -{ - - public static class ServerHeader - { +namespace Benchmarks.Utilities; - public static ServerHeaderConcernBuilder Create() => new ServerHeaderConcernBuilder(); - - } +public static class ServerHeader +{ + public static ServerHeaderConcernBuilder Create() => new(); } diff --git a/frameworks/CSharp/genhttp/Benchmarks/Utilities/ServerHeaderConcern.cs b/frameworks/CSharp/genhttp/Benchmarks/Utilities/ServerHeaderConcern.cs index 28b3bee2ba7..3536a8c8988 100644 --- a/frameworks/CSharp/genhttp/Benchmarks/Utilities/ServerHeaderConcern.cs +++ b/frameworks/CSharp/genhttp/Benchmarks/Utilities/ServerHeaderConcern.cs @@ -1,55 +1,42 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -using GenHTTP.Api.Content; +using GenHTTP.Api.Content; using GenHTTP.Api.Protocol; -namespace Benchmarks.Utilities -{ - - public sealed class ServerHeaderConcern : IConcern - { +namespace Benchmarks.Utilities; - #region Get-/Setters +public sealed class ServerHeaderConcern : IConcern +{ - public IHandler Content { get; } + #region Initialization - public IHandler Parent { get; } + public ServerHeaderConcern(IHandler content) + { + Content = content; + } - #endregion + #endregion - #region Initialization + #region Get-/Setters - public ServerHeaderConcern(IHandler parent, Func contentFactory) - { - Parent = parent; - Content = contentFactory(this); - } + public IHandler Content { get; } - #endregion + #endregion - #region Functionality + #region Functionality - public IAsyncEnumerable GetContentAsync(IRequest request) => Content.GetContentAsync(request); + public ValueTask PrepareAsync() => Content.PrepareAsync(); - public ValueTask PrepareAsync() => Content.PrepareAsync(); + public async ValueTask HandleAsync(IRequest request) + { + var response = await Content.HandleAsync(request); - public async ValueTask HandleAsync(IRequest request) + if (response != null) { - var response = await Content.HandleAsync(request); - - if (response != null) - { - response.Headers.Add("Server", "TFB"); - } - - return response; + response.Headers.Add("Server", "TFB"); } - #endregion - + return response; } + #endregion + } diff --git a/frameworks/CSharp/genhttp/Benchmarks/Utilities/ServerHeaderConcernBuilder.cs b/frameworks/CSharp/genhttp/Benchmarks/Utilities/ServerHeaderConcernBuilder.cs index b935ac66553..1f96404b09a 100644 --- a/frameworks/CSharp/genhttp/Benchmarks/Utilities/ServerHeaderConcernBuilder.cs +++ b/frameworks/CSharp/genhttp/Benchmarks/Utilities/ServerHeaderConcernBuilder.cs @@ -1,18 +1,10 @@ -using System; +using GenHTTP.Api.Content; -using GenHTTP.Api.Content; +namespace Benchmarks.Utilities; -namespace Benchmarks.Utilities +public sealed class ServerHeaderConcernBuilder : IConcernBuilder { - public sealed class ServerHeaderConcernBuilder : IConcernBuilder - { - - public IConcern Build(IHandler parent, Func contentFactory) - { - return new ServerHeaderConcern(parent, contentFactory); - } - - } + public IConcern Build(IHandler content) => new ServerHeaderConcern(content); } diff --git a/frameworks/CSharp/genhttp/README.md b/frameworks/CSharp/genhttp/README.md index fcdb7ce9100..9c5c5d1199a 100644 --- a/frameworks/CSharp/genhttp/README.md +++ b/frameworks/CSharp/genhttp/README.md @@ -6,11 +6,11 @@ See the [GenHTTP website](https://genhttp.org) for more information. **Language** -* C# 8.0 +* C# 13.0 **Platforms** -* .NET Core +* .NET 8/9 **Web Servers** diff --git a/frameworks/CSharp/genhttp/benchmark_config.json b/frameworks/CSharp/genhttp/benchmark_config.json index 3d8304e5dc6..bf1780e753a 100644 --- a/frameworks/CSharp/genhttp/benchmark_config.json +++ b/frameworks/CSharp/genhttp/benchmark_config.json @@ -17,7 +17,7 @@ "language": "C#", "orm": "Raw", "platform": ".NET", - "webserver": "GenHTTP", + "webserver": "Kestrel", "os": "Linux", "database_os": "Linux", "display_name": "GenHTTP", diff --git a/frameworks/CSharp/genhttp/config.toml b/frameworks/CSharp/genhttp/config.toml index a70294262d8..f984318f53b 100644 --- a/frameworks/CSharp/genhttp/config.toml +++ b/frameworks/CSharp/genhttp/config.toml @@ -16,5 +16,5 @@ database_os = "Linux" os = "Linux" orm = "Raw" platform = ".NET" -webserver = "GenHTTP" +webserver = "Kestrel" versus = "None" diff --git a/frameworks/CSharp/genhttp/genhttp.dockerfile b/frameworks/CSharp/genhttp/genhttp.dockerfile index 4331fd966f8..202df7ddedf 100644 --- a/frameworks/CSharp/genhttp/genhttp.dockerfile +++ b/frameworks/CSharp/genhttp/genhttp.dockerfile @@ -1,19 +1,19 @@ -FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build -WORKDIR /app -COPY Benchmarks . -RUN dotnet publish -c Release -o out +FROM mcr.microsoft.com/dotnet/sdk:9.0-alpine AS build +WORKDIR /source -FROM mcr.microsoft.com/dotnet/runtime:8.0 AS runtime -ENV DOTNET_SYSTEM_NET_SOCKETS_INLINE_COMPLETIONS 1 +# copy csproj and restore as distinct layers +COPY Benchmarks/*.csproj . +RUN dotnet restore -r linux-musl-x64 -# Full PGO -ENV DOTNET_TieredPGO 1 -ENV DOTNET_TC_QuickJitForLoops 1 -ENV DOTNET_ReadyToRun 0 +# copy and publish app and libraries +COPY Benchmarks/ . +RUN dotnet publish -c release -o /app -r linux-musl-x64 --no-restore --self-contained +# final stage/image +FROM mcr.microsoft.com/dotnet/runtime-deps:9.0-alpine WORKDIR /app -COPY --from=build /app/out ./ +COPY --from=build /app . -EXPOSE 8080 +ENTRYPOINT ["./Benchmarks"] -ENTRYPOINT ["dotnet", "Benchmarks.dll"] \ No newline at end of file +EXPOSE 8080 diff --git a/frameworks/CSharp/reaper/reaper.dockerfile b/frameworks/CSharp/reaper/reaper.dockerfile index 1e8171b8dd4..7da5d03009f 100644 --- a/frameworks/CSharp/reaper/reaper.dockerfile +++ b/frameworks/CSharp/reaper/reaper.dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build WORKDIR /src COPY src . RUN apt-get update \ @@ -7,7 +7,7 @@ RUN apt-get update \ WORKDIR "/src/Benchmark" RUN dotnet publish "Benchmark.csproj" -c Release -o /app/publish -FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final +FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS final WORKDIR /app EXPOSE 8080 COPY --from=build /app/publish . diff --git a/frameworks/CSharp/reaper/src/Benchmark/Benchmark.csproj b/frameworks/CSharp/reaper/src/Benchmark/Benchmark.csproj index e0e1ed8ee20..b0e9fd09211 100644 --- a/frameworks/CSharp/reaper/src/Benchmark/Benchmark.csproj +++ b/frameworks/CSharp/reaper/src/Benchmark/Benchmark.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 enable enable Linux @@ -18,8 +18,8 @@ - - + + diff --git a/frameworks/CSharp/reaper/src/Benchmark/JsonEndpoint.cs b/frameworks/CSharp/reaper/src/Benchmark/JsonEndpoint.cs index 26fba3b743f..f2ba38be4ca 100644 --- a/frameworks/CSharp/reaper/src/Benchmark/JsonEndpoint.cs +++ b/frameworks/CSharp/reaper/src/Benchmark/JsonEndpoint.cs @@ -11,9 +11,10 @@ public class JsonResponse [ReaperRoute(HttpVerbs.Get, "/json")] public class JsonEndpoint : ReaperEndpointXR { - public override Task HandleAsync() + public override Task ExecuteAsync() { Context.Response.ContentLength = 27; - return Task.FromResult(new JsonResponse { Message = "Hello, World!" }); + Result = new JsonResponse { Message = "Hello, World!" }; + return Task.CompletedTask; } } \ No newline at end of file diff --git a/frameworks/CSharp/reaper/src/Benchmark/PlainTextEndpoint.cs b/frameworks/CSharp/reaper/src/Benchmark/PlainTextEndpoint.cs index a316a55b6b4..eb88ca8bd58 100644 --- a/frameworks/CSharp/reaper/src/Benchmark/PlainTextEndpoint.cs +++ b/frameworks/CSharp/reaper/src/Benchmark/PlainTextEndpoint.cs @@ -6,11 +6,12 @@ namespace Benchmark; [ReaperRoute(HttpVerbs.Get, "/plaintext")] public class PlainTextEndpoint : ReaperEndpointXR { - public override Task HandleAsync() + public override Task ExecuteAsync() { Context.Response.StatusCode = 200; Context.Response.ContentType = "text/plain"; Context.Response.ContentLength = 13; - return Task.FromResult("Hello, World!"); + Result = "Hello, World!"; + return Task.CompletedTask; } } \ No newline at end of file diff --git a/frameworks/CSharp/reaper/src/Benchmark/Program.cs b/frameworks/CSharp/reaper/src/Benchmark/Program.cs index 7a44cf047bd..1c109c2ee8b 100644 --- a/frameworks/CSharp/reaper/src/Benchmark/Program.cs +++ b/frameworks/CSharp/reaper/src/Benchmark/Program.cs @@ -1,14 +1,8 @@ -using System.Text.Json.Serialization; -using Benchmark; using Reaper; var builder = WebApplication.CreateSlimBuilder(args); builder.Logging.ClearProviders(); builder.Logging.Configure(o => o.ActivityTrackingOptions = ActivityTrackingOptions.None); -builder.Services.ConfigureHttpJsonOptions(o => -{ - o.SerializerOptions.TypeInfoResolverChain.Insert(0, SourceGenerationContext.Default); -}); builder.UseReaper(); var app = builder.Build(); @@ -16,7 +10,4 @@ app.UseReaperMiddleware(); app.MapReaperEndpoints(); -app.Run(); - -[JsonSerializable(typeof(JsonResponse))] -internal partial class SourceGenerationContext : JsonSerializerContext { } \ No newline at end of file +app.Run(); \ No newline at end of file diff --git a/frameworks/CSharp/reaper/src/Benchmark/appsettings.Development.json b/frameworks/CSharp/reaper/src/Benchmark/appsettings.Development.json index 0c208ae9181..1b2d3bafd88 100644 --- a/frameworks/CSharp/reaper/src/Benchmark/appsettings.Development.json +++ b/frameworks/CSharp/reaper/src/Benchmark/appsettings.Development.json @@ -5,4 +5,4 @@ "Microsoft.AspNetCore": "Warning" } } -} +} \ No newline at end of file diff --git a/frameworks/Crystal/kemal/kemal.dockerfile b/frameworks/Crystal/kemal/kemal.dockerfile index baafb59538e..d5890857891 100644 --- a/frameworks/Crystal/kemal/kemal.dockerfile +++ b/frameworks/Crystal/kemal/kemal.dockerfile @@ -1,4 +1,4 @@ -FROM crystallang/crystal:1.12.1 +FROM crystallang/crystal:1.14.0 WORKDIR /kemal COPY views views diff --git a/frameworks/Crystal/kemal/shard.lock b/frameworks/Crystal/kemal/shard.lock index c7bf83e3078..9340b31d88b 100644 --- a/frameworks/Crystal/kemal/shard.lock +++ b/frameworks/Crystal/kemal/shard.lock @@ -18,7 +18,7 @@ shards: kemal: git: https://github.com/kemalcr/kemal.git - version: 1.5.0 + version: 1.6.0 pg: git: https://github.com/will/crystal-pg.git diff --git a/frameworks/Crystal/kemal/shard.yml b/frameworks/Crystal/kemal/shard.yml index 2277be2b8b7..6ab59c26264 100644 --- a/frameworks/Crystal/kemal/shard.yml +++ b/frameworks/Crystal/kemal/shard.yml @@ -9,7 +9,7 @@ dependencies: version: 0.28.0 kemal: github: kemalcr/kemal - version: 1.5.0 + version: 1.6.0 redis: github: stefanwille/crystal-redis version: 2.8.0 diff --git a/frameworks/Crystal/lucky/lucky.dockerfile b/frameworks/Crystal/lucky/lucky.dockerfile index 8309958ace4..1f352cf53bd 100644 --- a/frameworks/Crystal/lucky/lucky.dockerfile +++ b/frameworks/Crystal/lucky/lucky.dockerfile @@ -1,4 +1,4 @@ -FROM crystallang/crystal:1.12.1 +FROM crystallang/crystal:1.14.0 WORKDIR /lucky COPY shard.lock shard.lock @@ -14,7 +14,7 @@ ENV LUCKY_ENV production RUN shards build bench --release --no-debug -ENV DATABASE_URL postgres://benchmarkdbuser:benchmarkdbpass@tfb-database:5432/hello_world?initial_pool_size=56&max_idle_pool_size=56 +ENV DATABASE_URL postgres://benchmarkdbuser:benchmarkdbpass@tfb-database:5432/hello_world?initial_pool_size=10&max_idle_pool_size=10 EXPOSE 8080 diff --git a/frameworks/Crystal/lucky/shard.lock b/frameworks/Crystal/lucky/shard.lock index 86720033909..34f8bfcfea6 100644 --- a/frameworks/Crystal/lucky/shard.lock +++ b/frameworks/Crystal/lucky/shard.lock @@ -2,7 +2,7 @@ version: 2.0 shards: avram: git: https://github.com/luckyframework/avram.git - version: 1.2.0 + version: 1.3.0 backtracer: git: https://github.com/sija/backtracer.cr.git @@ -30,7 +30,7 @@ shards: habitat: git: https://github.com/luckyframework/habitat.git - version: 0.4.8 + version: 0.4.9 lucky: git: https://github.com/luckyframework/lucky.git @@ -66,7 +66,7 @@ shards: splay_tree_map: git: https://github.com/wyhaines/splay_tree_map.cr.git - version: 0.2.2 + version: 0.3.0 wordsmith: git: https://github.com/luckyframework/wordsmith.git diff --git a/frameworks/Elixir/phoenix/config/dev.exs b/frameworks/Elixir/phoenix/config/dev.exs index fef1a0e3888..ad06286bc58 100644 --- a/frameworks/Elixir/phoenix/config/dev.exs +++ b/frameworks/Elixir/phoenix/config/dev.exs @@ -1,13 +1,15 @@ import Config -# For development, we disable any cache and enable -# debugging and code reloading. -# -# The watchers configuration can be used to run external -# watchers to your application. For example, we use it -# with brunch.io to recompile .js and .css sources. config :hello, HelloWeb.Endpoint, - http: [port: 8080], + adapter: Bandit.PhoenixAdapter, + http: [ + port: 4000, + ip: {0, 0, 0, 0}, + http_options: [ + compress: false, + log_protocol_errors: :verbose + ], + ], debug_errors: true, code_reloader: true, cache_static_lookup: false @@ -23,10 +25,10 @@ config :hello, HelloWeb.Endpoint, ] config :hello, Hello.Repo, - username: "benchmarkdbuser", - password: "benchmarkdbpass", + username: "postgres", + password: "postgres", database: "hello_world", - hostname: "tfb-database" + hostname: "localhost" # Do not include metadata nor timestamps in development logs config :logger, :console, format: "[$level] $message\n" diff --git a/frameworks/Elixir/phoenix/config/prod.exs b/frameworks/Elixir/phoenix/config/prod.exs index c920950ee04..e2c578f4656 100755 --- a/frameworks/Elixir/phoenix/config/prod.exs +++ b/frameworks/Elixir/phoenix/config/prod.exs @@ -8,7 +8,10 @@ config :hello, HelloWeb.Endpoint, http_options: [ compress: false, log_protocol_errors: false - ] + ], + thousand_island_options: [ + transport_options: [ backlog: 8192 ] + ], ], compress: false, check_origin: false, diff --git a/frameworks/Elixir/phoenix/lib/hello_web.ex b/frameworks/Elixir/phoenix/lib/hello_web.ex index 1114ed22e51..e3411588296 100644 --- a/frameworks/Elixir/phoenix/lib/hello_web.ex +++ b/frameworks/Elixir/phoenix/lib/hello_web.ex @@ -29,7 +29,6 @@ defmodule HelloWeb do log: false import Plug.Conn - import HelloWeb.Gettext unquote(verified_routes()) end @@ -39,8 +38,6 @@ defmodule HelloWeb do quote do use Phoenix.Component - import HelloWeb.Gettext - # Routes generation with the ~p sigil unquote(verified_routes()) end diff --git a/frameworks/Elixir/phoenix/lib/hello_web/controllers/page_controller.ex b/frameworks/Elixir/phoenix/lib/hello_web/controllers/page_controller.ex index 59f2f5c2f35..fe03f3337c1 100644 --- a/frameworks/Elixir/phoenix/lib/hello_web/controllers/page_controller.ex +++ b/frameworks/Elixir/phoenix/lib/hello_web/controllers/page_controller.ex @@ -78,7 +78,7 @@ defmodule HelloWeb.PageController do json(conn, world_updates) end - def plaintext(conn, _params) do + def plaintext(conn, _params) do conn |> put_resp_header("content-type", "text/plain") |> send_resp(200, "Hello, World!") diff --git a/frameworks/Elixir/phoenix/lib/hello_web/gettext.ex b/frameworks/Elixir/phoenix/lib/hello_web/gettext.ex deleted file mode 100644 index a5bacc56875..00000000000 --- a/frameworks/Elixir/phoenix/lib/hello_web/gettext.ex +++ /dev/null @@ -1,24 +0,0 @@ -defmodule HelloWeb.Gettext do - @moduledoc """ - A module providing Internationalization with a gettext-based API. - - By using [Gettext](https://hexdocs.pm/gettext), - your module gains a set of macros for translations, for example: - - import HelloWeb.Gettext - - # Simple translation - gettext("Here is the string to translate") - - # Plural translation - ngettext("Here is the string to translate", - "Here are the strings to translate", - 3) - - # Domain-based translation - dgettext("errors", "Here is the error message to translate") - - See the [Gettext Docs](https://hexdocs.pm/gettext) for detailed usage. - """ - use Gettext, otp_app: :hello -end diff --git a/frameworks/Elixir/phoenix/mix.exs b/frameworks/Elixir/phoenix/mix.exs index c11d586e566..8e2e600c889 100755 --- a/frameworks/Elixir/phoenix/mix.exs +++ b/frameworks/Elixir/phoenix/mix.exs @@ -29,15 +29,13 @@ defmodule Hello.Mixfile do # Type `mix help deps` for examples and options defp deps do [ - {:bandit, "1.5.7"}, - {:gettext, "~> 0.20"}, + {:bandit, "~> 1.6.1"}, {:ecto_sql, "~> 3.10"}, {:jason, "~> 1.2"}, {:phoenix, "~> 1.7"}, {:phoenix_live_view, "~> 0.18"}, {:phoenix_ecto, "~> 4.4"}, {:phoenix_html, "~> 4.1"}, - {:plug_cowboy, "~> 2.5"}, {:postgrex, ">= 0.0.0"}, {:nebulex, "~> 2.6"} ] diff --git a/frameworks/Elixir/phoenix/mix.lock b/frameworks/Elixir/phoenix/mix.lock index 0c47cab2458..5fd905d6784 100644 --- a/frameworks/Elixir/phoenix/mix.lock +++ b/frameworks/Elixir/phoenix/mix.lock @@ -1,32 +1,25 @@ %{ - "bandit": {:hex, :bandit, "1.5.7", "6856b1e1df4f2b0cb3df1377eab7891bec2da6a7fd69dc78594ad3e152363a50", [:mix], [{:hpax, "~> 1.0.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "f2dd92ae87d2cbea2fa9aa1652db157b6cba6c405cb44d4f6dd87abba41371cd"}, - "castore": {:hex, :castore, "1.0.8", "dedcf20ea746694647f883590b82d9e96014057aff1d44d03ec90f36a5c0dc6e", [:mix], [], "hexpm", "0b2b66d2ee742cb1d9cb8c8be3b43c3a70ee8651f37b75a8b982e036752983f1"}, - "cowboy": {:hex, :cowboy, "2.12.0", "f276d521a1ff88b2b9b4c54d0e753da6c66dd7be6c9fca3d9418b561828a3731", [:make, :rebar3], [{:cowlib, "2.13.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "8a7abe6d183372ceb21caa2709bec928ab2b72e18a3911aa1771639bef82651e"}, - "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, - "cowlib": {:hex, :cowlib, "2.13.0", "db8f7505d8332d98ef50a3ef34b34c1afddec7506e4ee4dd4a3a266285d282ca", [:make, :rebar3], [], "hexpm", "e1e1284dc3fc030a64b1ad0d8382ae7e99da46c3246b815318a4b848873800a4"}, + "bandit": {:hex, :bandit, "1.6.1", "9e01b93d72ddc21d8c576a704949e86ee6cde7d11270a1d3073787876527a48f", [:mix], [{:hpax, "~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "5a904bf010ea24b67979835e0507688e31ac873d4ffc8ed0e5413e8d77455031"}, + "castore": {:hex, :castore, "1.0.10", "43bbeeac820f16c89f79721af1b3e092399b3a1ecc8df1a472738fd853574911", [:mix], [], "hexpm", "1b0b7ea14d889d9ea21202c43a4fa015eb913021cb535e8ed91946f4b77a8848"}, "db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"}, - "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"}, - "ecto": {:hex, :ecto, "3.12.4", "267c94d9f2969e6acc4dd5e3e3af5b05cdae89a4d549925f3008b2b7eb0b93c3", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ef04e4101688a67d061e1b10d7bc1fbf00d1d13c17eef08b71d070ff9188f747"}, + "decimal": {:hex, :decimal, "2.2.0", "df3d06bb9517e302b1bd265c1e7f16cda51547ad9d99892049340841f3e15836", [:mix], [], "hexpm", "af8daf87384b51b7e611fb1a1f2c4d4876b65ef968fa8bd3adf44cff401c7f21"}, + "ecto": {:hex, :ecto, "3.12.5", "4a312960ce612e17337e7cefcf9be45b95a3be6b36b6f94dfb3d8c361d631866", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6eb18e80bef8bb57e17f5a7f068a1719fbda384d40fc37acb8eb8aeca493b6ea"}, "ecto_sql": {:hex, :ecto_sql, "3.12.1", "c0d0d60e85d9ff4631f12bafa454bc392ce8b9ec83531a412c12a0d415a3a4d0", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.12", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "aff5b958a899762c5f09028c847569f7dfb9cc9d63bdb8133bff8a5546de6bf5"}, - "expo": {:hex, :expo, "1.0.0", "647639267e088717232f4d4451526e7a9de31a3402af7fcbda09b27e9a10395a", [:mix], [], "hexpm", "18d2093d344d97678e8a331ca0391e85d29816f9664a25653fd7e6166827827c"}, - "gettext": {:hex, :gettext, "0.25.0", "98a95a862a94e2d55d24520dd79256a15c87ea75b49673a2e2f206e6ebc42e5d", [:mix], [{:expo, "~> 0.5.1 or ~> 1.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "38e5d754e66af37980a94fb93bb20dcde1d2361f664b0a19f01e87296634051f"}, - "hpax": {:hex, :hpax, "1.0.0", "28dcf54509fe2152a3d040e4e3df5b265dcb6cb532029ecbacf4ce52caea3fd2", [:mix], [], "hexpm", "7f1314731d711e2ca5fdc7fd361296593fc2542570b3105595bb0bc6d0fad601"}, + "hpax": {:hex, :hpax, "1.0.1", "c857057f89e8bd71d97d9042e009df2a42705d6d690d54eca84c8b29af0787b0", [:mix], [], "hexpm", "4e2d5a4f76ae1e3048f35ae7adb1641c36265510a2d4638157fbcb53dda38445"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"}, - "nebulex": {:hex, :nebulex, "2.6.3", "78af348ed9f8a338871b41e0b6de718c1808e627ce03fbe86598cbda2bdda2f5", [:mix], [{:decorator, "~> 1.4", [hex: :decorator, repo: "hexpm", optional: true]}, {:shards, "~> 1.1", [hex: :shards, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "09cdcbb62f8463ffcec7cae4936425ce91e25d92a6cd37e48b5dda7c851958d5"}, - "phoenix": {:hex, :phoenix, "1.7.14", "a7d0b3f1bc95987044ddada111e77bd7f75646a08518942c72a8440278ae7825", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "c7859bc56cc5dfef19ecfc240775dae358cbaa530231118a9e014df392ace61a"}, - "phoenix_ecto": {:hex, :phoenix_ecto, "4.6.2", "3b83b24ab5a2eb071a20372f740d7118767c272db386831b2e77638c4dcc606d", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.1", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "3f94d025f59de86be00f5f8c5dd7b5965a3298458d21ab1c328488be3b5fcd59"}, + "nebulex": {:hex, :nebulex, "2.6.4", "4b00706e0e676474783d988962abf74614480e13c0a32645acb89bb32b660e09", [:mix], [{:decorator, "~> 1.4", [hex: :decorator, repo: "hexpm", optional: true]}, {:shards, "~> 1.1", [hex: :shards, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "25bdabf3fb86035c8151bba60bda20f80f96ae0261db7bd4090878ff63b03581"}, + "phoenix": {:hex, :phoenix, "1.7.17", "2fcdceecc6fb90bec26fab008f96abbd0fd93bc9956ec7985e5892cf545152ca", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "50e8ad537f3f7b0efb1509b2f75b5c918f697be6a45d48e49a30d3b7c0e464c9"}, + "phoenix_ecto": {:hex, :phoenix_ecto, "4.6.3", "f686701b0499a07f2e3b122d84d52ff8a31f5def386e03706c916f6feddf69ef", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.1", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "909502956916a657a197f94cc1206d9a65247538de8a5e186f7537c895d95764"}, "phoenix_html": {:hex, :phoenix_html, "4.1.1", "4c064fd3873d12ebb1388425a8f2a19348cef56e7289e1998e2d2fa758aa982e", [:mix], [], "hexpm", "f2f2df5a72bc9a2f510b21497fd7d2b86d932ec0598f0210fed4114adc546c6f"}, "phoenix_live_view": {:hex, :phoenix_live_view, "0.20.17", "f396bbdaf4ba227b82251eb75ac0afa6b3da5e509bc0d030206374237dfc9450", [:mix], [{:floki, "~> 0.36", [hex: :floki, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a61d741ffb78c85fdbca0de084da6a48f8ceb5261a79165b5a0b59e5f65ce98b"}, "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"}, "phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"}, "plug": {:hex, :plug, "1.16.1", "40c74619c12f82736d2214557dedec2e9762029b2438d6d175c5074c933edc9d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a13ff6b9006b03d7e33874945b2755253841b238c34071ed85b0e86057f8cddc"}, - "plug_cowboy": {:hex, :plug_cowboy, "2.7.1", "87677ffe3b765bc96a89be7960f81703223fe2e21efa42c125fcd0127dd9d6b2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "02dbd5f9ab571b864ae39418db7811618506256f6d13b4a45037e5fe78dc5de3"}, "plug_crypto": {:hex, :plug_crypto, "2.1.0", "f44309c2b06d249c27c8d3f65cfe08158ade08418cf540fd4f72d4d6863abb7b", [:mix], [], "hexpm", "131216a4b030b8f8ce0f26038bc4421ae60e4bb95c5cf5395e1421437824c4fa"}, - "postgrex": {:hex, :postgrex, "0.19.0", "f7d50e50cb42e0a185f5b9a6095125a9ab7e4abccfbe2ab820ab9aa92b71dbab", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "dba2d2a0a8637defbf2307e8629cb2526388ba7348f67d04ec77a5d6a72ecfae"}, - "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, + "postgrex": {:hex, :postgrex, "0.19.3", "a0bda6e3bc75ec07fca5b0a89bffd242ca209a4822a9533e7d3e84ee80707e19", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "d31c28053655b78f47f948c85bb1cf86a9c1f8ead346ba1aa0d0df017fa05b61"}, "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, - "thousand_island": {:hex, :thousand_island, "1.3.5", "6022b6338f1635b3d32406ff98d68b843ba73b3aa95cfc27154223244f3a6ca5", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2be6954916fdfe4756af3239fb6b6d75d0b8063b5df03ba76fd8a4c87849e180"}, + "thousand_island": {:hex, :thousand_island, "1.3.7", "1da7598c0f4f5f50562c097a3f8af308ded48cd35139f0e6f17d9443e4d0c9c5", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "0139335079953de41d381a6134d8b618d53d084f558c734f2662d1a72818dd12"}, "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"}, - "websock_adapter": {:hex, :websock_adapter, "0.5.6", "0437fe56e093fd4ac422de33bf8fc89f7bc1416a3f2d732d8b2c8fd54792fe60", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "e04378d26b0af627817ae84c92083b7e97aca3121196679b73c73b99d0d133ea"}, + "websock_adapter": {:hex, :websock_adapter, "0.5.8", "3b97dc94e407e2d1fc666b2fb9acf6be81a1798a2602294aac000260a7c4a47d", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "315b9a1865552212b5f35140ad194e67ce31af45bcee443d4ecb96b5fd3f3782"}, } diff --git a/frameworks/Elixir/phoenix/phoenix.dockerfile b/frameworks/Elixir/phoenix/phoenix.dockerfile index dcf8df3dd20..4de857bbec4 100644 --- a/frameworks/Elixir/phoenix/phoenix.dockerfile +++ b/frameworks/Elixir/phoenix/phoenix.dockerfile @@ -9,6 +9,8 @@ FROM ${BUILDER_IMAGE} AS builder ARG MIX_ENV="prod" +RUN apk add --no-cache git + RUN mix local.hex --force && \ mix local.rebar --force diff --git a/frameworks/FSharp/oxpecker/oxpecker.dockerfile b/frameworks/FSharp/oxpecker/oxpecker.dockerfile index 4f6676892c3..6424bcad50c 100644 --- a/frameworks/FSharp/oxpecker/oxpecker.dockerfile +++ b/frameworks/FSharp/oxpecker/oxpecker.dockerfile @@ -1,11 +1,13 @@ -FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build WORKDIR /app COPY src/App . RUN dotnet publish -c Release -o out -FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime +FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS runtime +ENV DOTNET_GCDynamicAdaptationMode=0 ENV DOTNET_ReadyToRun 0 +ENV DOTNET_HillClimbing_Disable=1 ENV ASPNETCORE_hostBuilder__reloadConfigOnChange false ENV URLS http://+:8080 diff --git a/frameworks/FSharp/oxpecker/src/App/App.fsproj b/frameworks/FSharp/oxpecker/src/App/App.fsproj index e9aa702a5a1..d54b0983e8b 100644 --- a/frameworks/FSharp/oxpecker/src/App/App.fsproj +++ b/frameworks/FSharp/oxpecker/src/App/App.fsproj @@ -1,7 +1,7 @@  - net8.0 + net9.0 false @@ -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..d8b3e407d74 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;Max Auto Prepare=4" + [] + let MultiplexedConnectionString = ConnectionString + ";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..e4466dc08c8 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 @@ -31,7 +30,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,19 +42,21 @@ 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 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 } @@ -68,16 +69,18 @@ module 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 +98,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 diff --git a/frameworks/Go/fiber/src/go.mod b/frameworks/Go/fiber/src/go.mod index 36e65622cc4..30a1e290884 100644 --- a/frameworks/Go/fiber/src/go.mod +++ b/frameworks/Go/fiber/src/go.mod @@ -23,8 +23,8 @@ require ( github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.55.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect - golang.org/x/crypto v0.27.0 // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.25.0 // indirect - golang.org/x/text v0.18.0 // indirect + golang.org/x/crypto v0.31.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect ) diff --git a/frameworks/Go/fiber/src/go.sum b/frameworks/Go/fiber/src/go.sum index 62741e5425b..dd7b67f4e56 100644 --- a/frameworks/Go/fiber/src/go.sum +++ b/frameworks/Go/fiber/src/go.sum @@ -44,16 +44,16 @@ github.com/valyala/quicktemplate v1.8.0 h1:zU0tjbIqTRgKQzFY1L42zq0qR3eh4WoQQdIdq github.com/valyala/quicktemplate v1.8.0/go.mod h1:qIqW8/igXt8fdrUln5kOSb+KWMaJ4Y8QUsfd1k6L2jM= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= -golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/frameworks/Go/goravel/src/fiber/go.mod b/frameworks/Go/goravel/src/fiber/go.mod index b31a4b9ba88..87ba6260420 100644 --- a/frameworks/Go/goravel/src/fiber/go.mod +++ b/frameworks/Go/goravel/src/fiber/go.mod @@ -65,7 +65,7 @@ require ( github.com/gofiber/template v1.8.3 // indirect github.com/gofiber/template/html/v2 v2.1.1 // indirect github.com/gofiber/utils v1.1.0 // indirect - github.com/golang-jwt/jwt/v4 v4.5.0 // indirect + github.com/golang-jwt/jwt/v4 v4.5.1 // indirect github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/golang-migrate/migrate/v4 v4.17.1 // indirect github.com/golang-module/carbon/v2 v2.3.12 // indirect diff --git a/frameworks/Go/goravel/src/fiber/go.sum b/frameworks/Go/goravel/src/fiber/go.sum index e036eea01cf..2106df44b89 100644 --- a/frameworks/Go/goravel/src/fiber/go.sum +++ b/frameworks/Go/goravel/src/fiber/go.sum @@ -285,8 +285,9 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= +github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= diff --git a/frameworks/Go/goravel/src/gin/go.mod b/frameworks/Go/goravel/src/gin/go.mod index 93ac7f3ec4e..e010a6645c6 100644 --- a/frameworks/Go/goravel/src/gin/go.mod +++ b/frameworks/Go/goravel/src/gin/go.mod @@ -65,7 +65,7 @@ require ( github.com/go-sql-driver/mysql v1.8.1 // indirect github.com/go-stack/stack v1.8.0 // indirect github.com/goccy/go-json v0.10.2 // indirect - github.com/golang-jwt/jwt/v4 v4.5.0 // indirect + github.com/golang-jwt/jwt/v4 v4.5.1 // indirect github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/golang-migrate/migrate/v4 v4.17.1 // indirect github.com/golang-module/carbon/v2 v2.3.12 // indirect diff --git a/frameworks/Go/goravel/src/gin/go.sum b/frameworks/Go/goravel/src/gin/go.sum index 92b222fbcbb..fefa43c5558 100644 --- a/frameworks/Go/goravel/src/gin/go.sum +++ b/frameworks/Go/goravel/src/gin/go.sum @@ -287,8 +287,9 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= +github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= diff --git a/frameworks/Go/hertz/go.mod b/frameworks/Go/hertz/go.mod index b04b09619ce..ef35c591ff5 100644 --- a/frameworks/Go/hertz/go.mod +++ b/frameworks/Go/hertz/go.mod @@ -28,9 +28,9 @@ require ( github.com/tidwall/pretty v1.2.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect - golang.org/x/crypto v0.17.0 // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.15.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/crypto v0.31.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect google.golang.org/protobuf v1.33.0 // indirect ) diff --git a/frameworks/Go/hertz/go.sum b/frameworks/Go/hertz/go.sum index 0d3f782c63b..c1251e56bc9 100644 --- a/frameworks/Go/hertz/go.sum +++ b/frameworks/Go/hertz/go.sum @@ -77,20 +77,20 @@ golang.org/x/arch v0.0.0-20201008161808-52c3e6f60cff/go.mod h1:flIaEI6LNU6xOCD5P golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20220110181412-a018aaa089fe/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/frameworks/Go/pine/README.md b/frameworks/Go/pine/README.md new file mode 100755 index 00000000000..1307a047c2c --- /dev/null +++ b/frameworks/Go/pine/README.md @@ -0,0 +1,11 @@ +# Pine Benchmarking Test + +## Test URLs + +### JSON + +http://localhost:8080/json + +### PLAINTEXT + +http://localhost:8080/plaintext diff --git a/frameworks/Go/pine/benchmark_config.json b/frameworks/Go/pine/benchmark_config.json new file mode 100755 index 00000000000..6c605348103 --- /dev/null +++ b/frameworks/Go/pine/benchmark_config.json @@ -0,0 +1,26 @@ +{ + "framework": "pine", + "tests": [ + { + "default": { + "json_url": "/json", + "plaintext_url": "/plaintext", + "port": 8080, + "approach": "Realistic", + "classification": "Micro", + "database": "None", + "framework": "Pine", + "language": "Go", + "flavor": "None", + "orm": "None", + "platform": "None", + "webserver": "None", + "os": "Linux", + "database_os": "Linux", + "display_name": "Pine", + "notes": "", + "versus": "go" + } + } + ] +} diff --git a/frameworks/Go/pine/pine.dockerfile b/frameworks/Go/pine/pine.dockerfile new file mode 100644 index 00000000000..4517e2bd98d --- /dev/null +++ b/frameworks/Go/pine/pine.dockerfile @@ -0,0 +1,10 @@ +FROM docker.io/golang:1.23 + +COPY ./src /pine +WORKDIR /pine + +RUN go mod download + +EXPOSE 8080 + +CMD go run . diff --git a/frameworks/Go/pine/src/go.mod b/frameworks/Go/pine/src/go.mod new file mode 100644 index 00000000000..6b8dddc4972 --- /dev/null +++ b/frameworks/Go/pine/src/go.mod @@ -0,0 +1,5 @@ +module pine + +go 1.23.0 + +require github.com/BryanMwangi/pine v1.0.6 diff --git a/frameworks/Go/pine/src/go.sum b/frameworks/Go/pine/src/go.sum new file mode 100644 index 00000000000..f28dc6678fd --- /dev/null +++ b/frameworks/Go/pine/src/go.sum @@ -0,0 +1,2 @@ +github.com/BryanMwangi/pine v1.0.6 h1:35JN1FQkStoCikeVQJ2423mO5STLNEPkA/AgnjslAmg= +github.com/BryanMwangi/pine v1.0.6/go.mod h1:j6+gT+N2HeeJHc9Z60rUOnEmNC+s/Gdmh2e9oB/eScI= diff --git a/frameworks/Go/pine/src/main.go b/frameworks/Go/pine/src/main.go new file mode 100644 index 00000000000..2f263adebcc --- /dev/null +++ b/frameworks/Go/pine/src/main.go @@ -0,0 +1,28 @@ +package main + +import ( + "log" + + "github.com/BryanMwangi/pine" +) + +func plaintextHandler(c *pine.Ctx) error { + c.Set("Server", "Pine") + return c.SendString("Hello, World!") +} + +func jsonHandler(c *pine.Ctx) error { + c.Set("Server", "Pine") + return c.JSON(map[string]string{ + "message": "Hello, World!", + }) +} + +func main() { + app := pine.New() + app.Get("/plaintext", plaintextHandler) + app.Get("/json", jsonHandler) + + // Start the server on port 3000 + log.Fatal(app.Start(":8080")) +} diff --git a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/DbRepository.java b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/DbRepository.java index d1f75b558c3..41af9c03b8e 100644 --- a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/DbRepository.java +++ b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/DbRepository.java @@ -1,23 +1,15 @@ package io.helidon.benchmark.nima.models; -import java.util.Collections; import java.util.List; import java.util.concurrent.ThreadLocalRandom; -import jakarta.json.Json; -import jakarta.json.JsonBuilderFactory; - public interface DbRepository { - JsonBuilderFactory JSON = Json.createBuilderFactory(Collections.emptyMap()); - World getWorld(int id); List getWorlds(int count); - World updateWorld(World world); - List updateWorlds(int count); List getFortunes(); diff --git a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/HikariJdbcRepository.java b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/HikariJdbcRepository.java index fd9760939df..7bf7e8c72b8 100644 --- a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/HikariJdbcRepository.java +++ b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/HikariJdbcRepository.java @@ -80,15 +80,6 @@ public List getWorlds(int count) { } } - @Override - public World updateWorld(World world) { - try (Connection c = getConnection()) { - return updateWorld(world, c); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - @Override public List updateWorlds(int count) { try (Connection c = getConnection()) { diff --git a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientRepository.java b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientRepository.java index e5166b10fbc..291131eca17 100644 --- a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientRepository.java +++ b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientRepository.java @@ -2,17 +2,13 @@ import java.util.ArrayList; import java.util.List; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import java.util.logging.Logger; import io.helidon.config.Config; - +import io.vertx.core.Future; import io.vertx.core.Vertx; import io.vertx.core.VertxOptions; -import io.vertx.core.Future; import io.vertx.pgclient.PgConnectOptions; import io.vertx.pgclient.PgPool; import io.vertx.sqlclient.PoolOptions; @@ -26,45 +22,40 @@ public class PgClientRepository implements DbRepository { private static final Logger LOGGER = Logger.getLogger(PgClientRepository.class.getName()); + private static final int UPDATE_QUERIES = 500; - private final SqlClient queryPool; private final SqlClient updatePool; - private final int batchSize; - private final long updateTimeout; - private final int maxRetries; - private final PreparedQuery> getFortuneQuery; private final PreparedQuery> getWorldQuery; - private final PreparedQuery> updateWorldQuery; + private final PreparedQuery>[] updateWorldSingleQuery; + @SuppressWarnings("unchecked") public PgClientRepository(Config config) { - Vertx vertx = Vertx.vertx(new VertxOptions() - .setPreferNativeTransport(true)); + Vertx vertx = Vertx.vertx(new VertxOptions().setPreferNativeTransport(true)); PgConnectOptions connectOptions = new PgConnectOptions() .setPort(config.get("port").asInt().orElse(5432)) .setCachePreparedStatements(config.get("cache-prepared-statements").asBoolean().orElse(true)) .setHost(config.get("host").asString().orElse("tfb-database")) .setDatabase(config.get("db").asString().orElse("hello_world")) .setUser(config.get("username").asString().orElse("benchmarkdbuser")) - .setPassword(config.get("password").asString().orElse("benchmarkdbpass")); + .setPassword(config.get("password").asString().orElse("benchmarkdbpass")) + .setPipeliningLimit(100000); int sqlPoolSize = config.get("sql-pool-size").asInt().orElse(64); PoolOptions clientOptions = new PoolOptions().setMaxSize(sqlPoolSize); LOGGER.info("sql-pool-size is " + sqlPoolSize); - batchSize = config.get("update-batch-size").asInt().orElse(20); - LOGGER.info("update-batch-size is " + batchSize); - updateTimeout = config.get("update-timeout-millis").asInt().orElse(5000); - LOGGER.info("update-timeout-millis is " + updateTimeout); - maxRetries = config.get("update-max-retries").asInt().orElse(3); - LOGGER.info("update-max-retries is " + maxRetries); - - queryPool = PgPool.client(vertx, connectOptions, clientOptions); + + SqlClient queryPool = PgPool.client(vertx, connectOptions, clientOptions); updatePool = PgPool.client(vertx, connectOptions, clientOptions); getWorldQuery = queryPool.preparedQuery("SELECT id, randomnumber FROM world WHERE id = $1"); - updateWorldQuery = queryPool.preparedQuery("UPDATE world SET randomnumber = $1 WHERE id = $2"); getFortuneQuery = queryPool.preparedQuery("SELECT id, message FROM fortune"); + + updateWorldSingleQuery = new PreparedQuery[UPDATE_QUERIES]; + for (int i = 0; i < UPDATE_QUERIES; i++) { + updateWorldSingleQuery[i] = queryPool.preparedQuery(singleUpdate(i + 1)); + } } @Override @@ -97,60 +88,11 @@ public List getWorlds(int count) { } } - @Override - public World updateWorld(World world) { - try { - return updateWorldQuery.execute(Tuple.of(world.id, world.id)) - .toCompletionStage() - .thenApply(rows -> world) - .toCompletableFuture().get(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - @Override public List updateWorlds(int count) { List worlds = getWorlds(count); - if (batchSize > 1) { // batching updates - for (World w : worlds) { - w.randomNumber = randomWorldNumber(); - } - if (count <= batchSize) { - LOGGER.finest(() -> "Updating single batch of size " + count); - updateWorldsRetry(worlds, 0, 0); - } else { - int batches = count / batchSize + (count % batchSize == 0 ? 0 : 1); - for (int i = 0; i < batches; i++) { - final int from = i * batchSize; - LOGGER.finest(() -> "Updating batch from " + from + " to " + (from + batchSize)); - updateWorldsRetry(worlds, from, 0); - } - } - } else { // no batching for size 1 - for (World w : worlds) { - w.randomNumber = randomWorldNumber(); - updateWorld(w); - } - } - return worlds; - } - - private List updateWorldsRetry(List worlds, int from, int retries) { - if (retries > maxRetries) { - throw new RuntimeException("Too many transaction retries"); - } - CompletableFuture> cf = null; try { - cf = updateWorlds(worlds, from, updatePool); - cf.get(updateTimeout, TimeUnit.MILLISECONDS); - return worlds; - } catch (ExecutionException | TimeoutException e) { - cf.cancel(true); - retries++; - final int finalRetries = retries; - LOGGER.fine(() -> "Retrying batch update after cancellation (retries=" + finalRetries + ")"); - return updateWorldsRetry(worlds, from, retries); // retry + return updateWorlds(worlds, count, updatePool); } catch (Exception e) { throw new RuntimeException(e); } @@ -172,16 +114,36 @@ public List getFortunes() { } } - private CompletableFuture> updateWorlds(List worlds, int from, SqlClient pool) { - List tuples = new ArrayList<>(); - int to = Math.min(from + batchSize, worlds.size()); - for (int i = from; i < to; i++) { - World w = worlds.get(i); - tuples.add(Tuple.of(w.randomNumber, w.id)); + private List updateWorlds(List worlds, int count, SqlClient pool) + throws ExecutionException, InterruptedException { + int size = worlds.size(); + List updateParams = new ArrayList<>(size * 2); + for (World world : worlds) { + updateParams.add(world.id); + world.randomNumber = randomWorldNumber(); + updateParams.add(world.randomNumber); } - return updateWorldQuery.executeBatch(tuples) + return updateWorldSingleQuery[count - 1].execute(Tuple.wrap(updateParams)) .toCompletionStage() .thenApply(rows -> worlds) - .toCompletableFuture(); + .toCompletableFuture() + .get(); + } + + private static String singleUpdate(int count) { + StringBuilder sql = new StringBuilder(); + sql.append("UPDATE WORLD SET RANDOMNUMBER = CASE ID"); + for (int i = 0; i < count; i++) { + int k = i * 2 + 1; + sql.append(" WHEN $").append(k).append(" THEN $").append(k + 1); + } + sql.append(" ELSE RANDOMNUMBER"); + sql.append(" END WHERE ID IN ($1"); + for (int i = 1; i < count; i++) { + int k = i * 2 + 1; + sql.append(",$").append(k); + } + sql.append(")"); + return sql.toString(); } } \ No newline at end of file diff --git a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/World.java b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/World.java index 39deafea11b..dbcba61a8ca 100644 --- a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/World.java +++ b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/World.java @@ -1,18 +1,8 @@ package io.helidon.benchmark.nima.models; -import java.util.Collections; - -import jakarta.json.Json; -import jakarta.json.JsonBuilderFactory; -import jakarta.json.JsonObject; - public final class World { - static final String ID_KEY = "id"; - static final String ID_RANDOM_NUMBER = "randomNumber"; - static final JsonBuilderFactory JSON = Json.createBuilderFactory(Collections.emptyMap()); - public int id; public int randomNumber; @@ -20,8 +10,4 @@ public World(int id, int randomNumber) { this.id = id; this.randomNumber = randomNumber; } - - public JsonObject toJson() { - return JSON.createObjectBuilder().add(ID_KEY, id).add(ID_RANDOM_NUMBER, randomNumber).build(); - } } diff --git a/frameworks/Java/helidon/nima/src/main/resources/application.yaml b/frameworks/Java/helidon/nima/src/main/resources/application.yaml index 41f4d64ec12..d2d8e8943b4 100644 --- a/frameworks/Java/helidon/nima/src/main/resources/application.yaml +++ b/frameworks/Java/helidon/nima/src/main/resources/application.yaml @@ -39,7 +39,3 @@ password: benchmarkdbpass sql-pool-size: 300 db-repository: "pgclient" # "pgclient" (default) or "hikari" -# The following for pgclient only -update-batch-size: 4 -update-timeout-millis: 10000 -update-max-retries: 3 diff --git a/frameworks/Java/hserver-business/README.md b/frameworks/Java/hserver-business/README.md new file mode 100644 index 00000000000..4e033e0d570 --- /dev/null +++ b/frameworks/Java/hserver-business/README.md @@ -0,0 +1,39 @@ +# hserver-business Benchmarking Test +This is the HServer portion of a [benchmarking test suite](../) comparing a variety of web development platforms. + +### Tests +* [JSON test source](src/main/java/com/test/hserver/controller/TestController.java) +* [Plaintext test source](src/main/java/com/test/hserver/controller/TestController.java) +* [Data-Store test source](src/main/java/com/test/hserver/controller/TestController.java) +* [Data-Update test source](src/main/java/com/test/hserver/controller/TestController.java) +* [Fortunes test source](src/main/java/com/test/hserver/controller/TestController.java) + +## Infrastructure Software Versions + +* [HServer](https://gitee.com/HServer/HServer) +* [Java OpenJDK 1.8](http://openjdk.java.net/) + +## Test URLs + +### JSON Encoding Test + +http://localhost:8888/json + +### Plain Text Test + +http://localhost:8888/plaintext + +### Data-Store/Database Mapping Test + +http://localhost:8888/db?queries=2 + +### Update Test + +http://localhost:8888/updates?queries=2 + +### Fortunes Test + +http://localhost:8888/fortunes + +### Query Test +http://localhost:8888/queries?queries=2 diff --git a/frameworks/Java/hserver-business/benchmark_config.json b/frameworks/Java/hserver-business/benchmark_config.json new file mode 100644 index 00000000000..bd9e4a47600 --- /dev/null +++ b/frameworks/Java/hserver-business/benchmark_config.json @@ -0,0 +1,31 @@ + +{ + "framework": "hserver-business", + "tests": [ + { + "default": { + "db_url": "/db", + "fortune_url": "/fortunes", + "plaintext_url": "/plaintext", + "json_url": "/json", + "query_url": "/queries?queries=", + "update_url": "/updates?queries=", + "port": 8888, + "approach": "Realistic", + "classification": "Fullstack", + "database": "Postgres", + "framework": "hserver-business", + "language": "Java", + "flavor": "None", + "orm": "Full", + "platform": "hserver-business", + "webserver": "hserver-business", + "os": "Linux", + "database_os": "Linux", + "display_name": "hserver-business", + "notes": "", + "versus": "hserver-business" + } + } + ] +} diff --git a/frameworks/Java/hserver-business/config.toml b/frameworks/Java/hserver-business/config.toml new file mode 100644 index 00000000000..ef213a045ff --- /dev/null +++ b/frameworks/Java/hserver-business/config.toml @@ -0,0 +1,17 @@ +[framework] +name = "hserver-business" + +[main] +urls.plaintext = "/plaintext" +urls.json = "/json" +urls.db = "/db" +urls.query = "/queries?queries=" +urls.update = "/updates?queries=" +urls.fortune = "/fortunes" +approach = "Realistic" +classification = "Fullstack" +os = "Linux" +orm = "Full" +platform = "hserver-business" +webserver = "hserver-business" +versus = "hserver-business" diff --git a/frameworks/Java/hserver-business/hserver-business.dockerfile b/frameworks/Java/hserver-business/hserver-business.dockerfile new file mode 100644 index 00000000000..d58e8b83ffa --- /dev/null +++ b/frameworks/Java/hserver-business/hserver-business.dockerfile @@ -0,0 +1,14 @@ +FROM maven:3.6.1-jdk-11-slim as maven +WORKDIR /hserver-business +COPY pom.xml pom.xml +COPY src src +RUN mvn package --quiet + +FROM openjdk:11.0.3-jdk-slim +WORKDIR /hserver-business +COPY --from=maven /hserver-business/target/hserver-business-1.0.jar app.jar + +EXPOSE 8888 + +CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-XX:+AggressiveOpts", "-Dio.netty.buffer.checkBounds=false", "-Dio.netty.buffer.checkAccessible=false", "-Dio.netty.iouring.iosqeAsyncThreshold=32000", "-jar", "app.jar"] + diff --git a/frameworks/Java/hserver-business/pom.xml b/frameworks/Java/hserver-business/pom.xml new file mode 100644 index 00000000000..2d72e90fb48 --- /dev/null +++ b/frameworks/Java/hserver-business/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + com.test.hserver + hserver-business + 1.0 + + + hserver-parent + cn.hserver + 3.6.M3 + + + UTF-8 + 3.3.1 + 42.7.2 + + + + + + hserver + cn.hserver + + + + hserver-plugin-web + cn.hserver + + + com.zaxxer + HikariCP + ${version.hikaricp} + + + org.postgresql + postgresql + ${version.postgres} + + + + + + hserver-plugin-maven + cn.hserver + + + + diff --git a/frameworks/Java/hserver-business/src/main/java/com/test/hserver/StartApp.java b/frameworks/Java/hserver-business/src/main/java/com/test/hserver/StartApp.java new file mode 100644 index 00000000000..b4e215081da --- /dev/null +++ b/frameworks/Java/hserver-business/src/main/java/com/test/hserver/StartApp.java @@ -0,0 +1,18 @@ +package com.test.hserver; + + +import cn.hserver.HServerApplication; +import cn.hserver.core.ioc.annotation.HServerBoot; +import cn.hserver.core.server.context.ConstConfig; + + +/** + * @author hxm + */ +@HServerBoot +public class StartApp { + + public static void main(String[] args) { + HServerApplication.run(StartApp.class, 8888, args); + } +} diff --git a/frameworks/Java/hserver-business/src/main/java/com/test/hserver/bean/Fortune.java b/frameworks/Java/hserver-business/src/main/java/com/test/hserver/bean/Fortune.java new file mode 100644 index 00000000000..ce67c19ea45 --- /dev/null +++ b/frameworks/Java/hserver-business/src/main/java/com/test/hserver/bean/Fortune.java @@ -0,0 +1,24 @@ +package com.test.hserver.bean; + +public final class Fortune implements Comparable { + public final int id; + + public final String message; + + public Fortune(int id, String message) { + this.id = id; + this.message = message; + } + @Override + public int compareTo(Fortune other) { + return message.compareTo(other.message); + } + + public int getId() { + return id; + } + + public String getMessage() { + return message; + } +} \ No newline at end of file diff --git a/frameworks/Java/solon/src/main/java/hello/model/Message.java b/frameworks/Java/hserver-business/src/main/java/com/test/hserver/bean/Message.java similarity index 50% rename from frameworks/Java/solon/src/main/java/hello/model/Message.java rename to frameworks/Java/hserver-business/src/main/java/com/test/hserver/bean/Message.java index 1df5276ca8d..ec823bfb974 100644 --- a/frameworks/Java/solon/src/main/java/hello/model/Message.java +++ b/frameworks/Java/hserver-business/src/main/java/com/test/hserver/bean/Message.java @@ -1,15 +1,7 @@ -package hello.model; +package com.test.hserver.bean; -/** - * @author pmg1991 - * @version V1.0 - */ public class Message { - private String message; - - public Message(String message) { - this.message = message; - } + private String message = "Hello, World!"; public String getMessage() { return message; @@ -18,4 +10,4 @@ public String getMessage() { public void setMessage(String message) { this.message = message; } -} +} \ No newline at end of file diff --git a/frameworks/Java/hserver-business/src/main/java/com/test/hserver/bean/World.java b/frameworks/Java/hserver-business/src/main/java/com/test/hserver/bean/World.java new file mode 100644 index 00000000000..206846abcc3 --- /dev/null +++ b/frameworks/Java/hserver-business/src/main/java/com/test/hserver/bean/World.java @@ -0,0 +1,24 @@ +package com.test.hserver.bean; + +public class World implements Comparable { + private final int id; + + private final int randomNumber; + + public World(int id, int randomNumber) { + this.id = id; + this.randomNumber = randomNumber; + } + + public int getId() { + return id; + } + + public int getRandomNumber() { + return randomNumber; + } + + @Override public int compareTo(World o) { + return id - o.id; + } +} \ No newline at end of file diff --git a/frameworks/Java/hserver-business/src/main/java/com/test/hserver/controller/TestController.java b/frameworks/Java/hserver-business/src/main/java/com/test/hserver/controller/TestController.java new file mode 100644 index 00000000000..c4bc3b4b0fb --- /dev/null +++ b/frameworks/Java/hserver-business/src/main/java/com/test/hserver/controller/TestController.java @@ -0,0 +1,136 @@ +package com.test.hserver.controller; + +import cn.hserver.core.ioc.annotation.Autowired; +import cn.hserver.plugin.web.annotation.Controller; +import cn.hserver.plugin.web.annotation.GET; +import cn.hserver.plugin.web.interfaces.HttpRequest; +import cn.hserver.plugin.web.interfaces.HttpResponse; +import com.test.hserver.bean.Fortune; +import com.test.hserver.bean.Message; +import com.test.hserver.bean.World; +import com.test.hserver.util.DateUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; + +import static com.test.hserver.util.Util.getQueries; +import static com.test.hserver.util.Util.randomWorld; + +/** + * @author hxm + */ +@Controller +public class TestController { + private static final String HELLO = "Hello, World!"; + private static final String SELECT_WORLD = "select * from world where id=?"; + + @Autowired + private DataSource dataSource; + + @GET("/json") + public Message json(HttpResponse response) { + response.setHeader("Date", DateUtil.getTime()); + return new Message(); + } + + @GET("/plaintext") + public String plaintext(HttpResponse response) { + response.setHeader("Date", DateUtil.getTime()); + return HELLO; + } + + @GET("/db") + public void db(HttpResponse response) throws SQLException { + World result; + try (Connection conn = dataSource.getConnection()) { + try (final PreparedStatement statement = conn.prepareStatement(SELECT_WORLD)) { + statement.setInt(1, randomWorld()); + try (ResultSet rs = statement.executeQuery()) { + rs.next(); + result = new World(rs.getInt("id"), rs.getInt("randomNumber")); + } + } + } + response.setHeader("Date", DateUtil.getTime()); + response.sendJson(result); + } + + @GET("/queries") + public void queries(HttpRequest request,HttpResponse response) throws Exception { + World[] result = new World[getQueries(request.query("queries"))]; + try (Connection conn = dataSource.getConnection()) { + for (int i = 0; i < result.length; i++) { + try (final PreparedStatement statement = conn.prepareStatement(SELECT_WORLD)) { + statement.setInt(1, randomWorld()); + try (ResultSet rs = statement.executeQuery()) { + rs.next(); + result[i] = new World(rs.getInt("id"), rs.getInt("randomNumber")); + } + } + } + } + response.setHeader("Date", DateUtil.getTime()); + response.sendJson(result); + } + + + @GET("/updates") + public void updates(HttpRequest request,HttpResponse response) throws Exception { + World[] result = new World[getQueries(request.query("queries"))]; + StringJoiner updateSql = new StringJoiner( + ", ", + "UPDATE world SET randomNumber = temp.randomNumber FROM (VALUES ", + " ORDER BY 1) AS temp(id, randomNumber) WHERE temp.id = world.id"); + + try (Connection connection = dataSource.getConnection()) { + try (PreparedStatement statement = connection.prepareStatement(SELECT_WORLD)) { + for (int i = 0; i < result.length; i++) { + statement.setInt(1, randomWorld()); + try (ResultSet rs = statement.executeQuery()) { + rs.next(); + result[i] = new World(rs.getInt("id"), randomWorld()); + } + // prepare update query + updateSql.add("(?, ?)"); + } + } + + try (PreparedStatement statement = connection.prepareStatement(updateSql.toString())) { + int i = 0; + for (World world : result) { + statement.setInt(++i, world.getRandomNumber()); + statement.setInt(++i, world.getRandomNumber()); + } + statement.executeUpdate(); + } + } + response.setHeader("Date", DateUtil.getTime()); + response.sendJson(result); + } + + @GET("/fortunes") + public void fortunes(HttpResponse response) throws Exception { + List fortunes = new ArrayList<>(); + try (Connection connection = dataSource.getConnection()) { + try (PreparedStatement stt = connection.prepareStatement("select * from fortune")) { + try (ResultSet rs = stt.executeQuery()) { + while (rs.next()) { + fortunes.add(new Fortune(rs.getInt("id"), rs.getString("message"))); + } + } + } + } + fortunes.add(new Fortune(0, "Additional fortune added at request time.")); + Collections.sort(fortunes); + response.setHeader("Date", DateUtil.getTime()); + Map data=new HashMap<>(); + data.put("data",fortunes); + response.sendTemplate("fortunes.ftl",data); + } +} diff --git a/frameworks/Java/hserver-business/src/main/java/com/test/hserver/db/DataSourceConfig.java b/frameworks/Java/hserver-business/src/main/java/com/test/hserver/db/DataSourceConfig.java new file mode 100644 index 00000000000..0fee7b56b1c --- /dev/null +++ b/frameworks/Java/hserver-business/src/main/java/com/test/hserver/db/DataSourceConfig.java @@ -0,0 +1,25 @@ +package com.test.hserver.db; + +import cn.hserver.core.ioc.annotation.Autowired; +import cn.hserver.core.ioc.annotation.Bean; +import cn.hserver.core.ioc.annotation.Configuration; +import com.zaxxer.hikari.HikariDataSource; + +import javax.sql.DataSource; + +@Configuration +public class DataSourceConfig { + + @Autowired + private PostgresConfig postgresConfig; + + @Bean + public DataSource initDataSource() { + HikariDataSource ds = new HikariDataSource(); + ds.setJdbcUrl(postgresConfig.getJdbcUrl()); + ds.setUsername(postgresConfig.getUsername()); + ds.setPassword(postgresConfig.getPassword()); + ds.setMaximumPoolSize(postgresConfig.getMaximumPoolSize()); + return ds; + } +} diff --git a/frameworks/Java/hserver-business/src/main/java/com/test/hserver/db/PostgresConfig.java b/frameworks/Java/hserver-business/src/main/java/com/test/hserver/db/PostgresConfig.java new file mode 100644 index 00000000000..57c08ea4b25 --- /dev/null +++ b/frameworks/Java/hserver-business/src/main/java/com/test/hserver/db/PostgresConfig.java @@ -0,0 +1,57 @@ +package com.test.hserver.db; + + +import cn.hserver.core.ioc.annotation.ConfigurationProperties; + +@ConfigurationProperties +public class PostgresConfig { + private String jdbcUrl; + private String username; + private String password; + private int maximumPoolSize; + + public PostgresConfig() { + } + + public String getJdbcUrl() { + return jdbcUrl; + } + + public void setJdbcUrl(String jdbcUrl) { + this.jdbcUrl = jdbcUrl; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public int getMaximumPoolSize() { + return maximumPoolSize; + } + + public void setMaximumPoolSize(int maximumPoolSize) { + this.maximumPoolSize = maximumPoolSize; + } + + @Override + public String toString() { + return "PostgresConfig{" + + "jdbcUrl='" + jdbcUrl + '\'' + + ", username='" + username + '\'' + + ", password='" + password + '\'' + + ", maximumPoolSize=" + maximumPoolSize + + '}'; + } +} \ No newline at end of file diff --git a/frameworks/Java/hserver-business/src/main/java/com/test/hserver/task/TimeAdd.java b/frameworks/Java/hserver-business/src/main/java/com/test/hserver/task/TimeAdd.java new file mode 100644 index 00000000000..a1f866b2d89 --- /dev/null +++ b/frameworks/Java/hserver-business/src/main/java/com/test/hserver/task/TimeAdd.java @@ -0,0 +1,15 @@ +package com.test.hserver.task; + +import cn.hserver.core.ioc.annotation.Bean; +import cn.hserver.core.ioc.annotation.Task; +import com.test.hserver.util.DateUtil; + +@Bean +public class TimeAdd { + + @Task(name = "时间计算", time = "1000") + public void add() { + DateUtil.time = DateUtil.getNow(); + } + +} diff --git a/frameworks/Java/hserver-business/src/main/java/com/test/hserver/util/DateUtil.java b/frameworks/Java/hserver-business/src/main/java/com/test/hserver/util/DateUtil.java new file mode 100644 index 00000000000..0874b9bd9eb --- /dev/null +++ b/frameworks/Java/hserver-business/src/main/java/com/test/hserver/util/DateUtil.java @@ -0,0 +1,25 @@ +package com.test.hserver.util; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.Locale; + +/** + * @author hxm + */ +public class DateUtil { + private static final DateTimeFormatter GMT_FMT = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US); + private static final ZoneId zoneId = ZoneId.of("GMT"); + public static String getNow() { + return GMT_FMT.format(LocalDateTime.now().atZone(zoneId)); + } + public static String time; + public static String getTime(){ + if (time==null){ + time=getNow(); + return time; + } + return time; + } +} diff --git a/frameworks/Java/hserver-business/src/main/java/com/test/hserver/util/Util.java b/frameworks/Java/hserver-business/src/main/java/com/test/hserver/util/Util.java new file mode 100644 index 00000000000..775f4f8f0f6 --- /dev/null +++ b/frameworks/Java/hserver-business/src/main/java/com/test/hserver/util/Util.java @@ -0,0 +1,18 @@ +package com.test.hserver.util; + +import java.util.concurrent.ThreadLocalRandom; + +public class Util { + public static int randomWorld() { + return 1 + ThreadLocalRandom.current().nextInt(10000); + } + + public static int getQueries(String queries) { + try { + int count = Integer.parseInt(queries); + return Math.min(500, Math.max(1, count)); + } catch (Exception e) { + return 1; + } + } +} \ No newline at end of file diff --git a/frameworks/Java/hserver-business/src/main/resources/app.properties b/frameworks/Java/hserver-business/src/main/resources/app.properties new file mode 100644 index 00000000000..11b8d560772 --- /dev/null +++ b/frameworks/Java/hserver-business/src/main/resources/app.properties @@ -0,0 +1,6 @@ +jdbcUrl= jdbc:postgresql://tfb-database:5432/hello_world +username= benchmarkdbuser +password= benchmarkdbpass +maximumPoolSize= 256 + +log=info diff --git a/frameworks/Java/hserver-business/src/main/resources/template/fortunes.ftl b/frameworks/Java/hserver-business/src/main/resources/template/fortunes.ftl new file mode 100644 index 00000000000..233927248fa --- /dev/null +++ b/frameworks/Java/hserver-business/src/main/resources/template/fortunes.ftl @@ -0,0 +1,20 @@ + + + + Fortunes + + + + + + + + <#list data as fortune> + + + + + +
idmessage
${fortune.id?html}${fortune.message?html}
+ + \ No newline at end of file diff --git a/frameworks/Java/hserver/benchmark_config.json b/frameworks/Java/hserver/benchmark_config.json index 87397f30d93..035eae68c9c 100644 --- a/frameworks/Java/hserver/benchmark_config.json +++ b/frameworks/Java/hserver/benchmark_config.json @@ -5,11 +5,11 @@ { "default": { "db_url": "/db", - "query_url": "/queries?queries=", "fortune_url": "/fortunes", "plaintext_url": "/plaintext", - "update_url": "/updates?queries=", "json_url": "/json", + "query_url": "/queries?queries=", + "update_url": "/updates?queries=", "port": 8888, "approach": "Realistic", "classification": "Fullstack", @@ -18,8 +18,8 @@ "language": "Java", "flavor": "None", "orm": "Full", - "platform": "None", - "webserver": "None", + "platform": "hserver", + "webserver": "hserver", "os": "Linux", "database_os": "Linux", "display_name": "hserver", @@ -28,4 +28,4 @@ } } ] -} \ No newline at end of file +} diff --git a/frameworks/Java/hserver/config.toml b/frameworks/Java/hserver/config.toml index db5b6ff47a1..6979faa03a0 100644 --- a/frameworks/Java/hserver/config.toml +++ b/frameworks/Java/hserver/config.toml @@ -12,6 +12,6 @@ approach = "Realistic" classification = "Fullstack" os = "Linux" orm = "Full" -platform = "None" -webserver = "None" -versus = "hserver" \ No newline at end of file +platform = "hserver" +webserver = "hserver" +versus = "hserver" diff --git a/frameworks/Java/hserver/hserver.dockerfile b/frameworks/Java/hserver/hserver.dockerfile index 483485da30f..67095536752 100644 --- a/frameworks/Java/hserver/hserver.dockerfile +++ b/frameworks/Java/hserver/hserver.dockerfile @@ -1,13 +1,13 @@ -FROM maven:3.6.3-openjdk-8-slim as maven +FROM maven:3.6.1-jdk-11-slim as maven WORKDIR /hserver COPY pom.xml pom.xml COPY src src -RUN mvn package +RUN mvn package --quiet -FROM openjdk:8u275-jdk-slim +FROM openjdk:11.0.3-jdk-slim WORKDIR /hserver COPY --from=maven /hserver/target/hserver-1.0.jar app.jar EXPOSE 8888 -CMD ["java", "-jar", "app.jar"] \ No newline at end of file +CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-XX:+AggressiveOpts", "-Dio.netty.buffer.checkBounds=false", "-Dio.netty.buffer.checkAccessible=false", "-Dio.netty.iouring.iosqeAsyncThreshold=32000", "-jar", "app.jar"] diff --git a/frameworks/Java/hserver/pom.xml b/frameworks/Java/hserver/pom.xml index b841cbd60ab..1183e3a8567 100644 --- a/frameworks/Java/hserver/pom.xml +++ b/frameworks/Java/hserver/pom.xml @@ -11,7 +11,7 @@ hserver-parent cn.hserver - 3.5.M2 + 3.6.M3 UTF-8 diff --git a/frameworks/Java/hserver/src/main/java/com/test/hserver/controller/TestController.java b/frameworks/Java/hserver/src/main/java/com/test/hserver/controller/TestController.java index 9ab5d958039..8055d476a55 100644 --- a/frameworks/Java/hserver/src/main/java/com/test/hserver/controller/TestController.java +++ b/frameworks/Java/hserver/src/main/java/com/test/hserver/controller/TestController.java @@ -3,11 +3,14 @@ import cn.hserver.core.ioc.annotation.Autowired; import cn.hserver.plugin.web.annotation.Controller; import cn.hserver.plugin.web.annotation.GET; +import cn.hserver.plugin.web.interfaces.HttpRequest; import cn.hserver.plugin.web.interfaces.HttpResponse; import com.test.hserver.bean.Fortune; import com.test.hserver.bean.Message; import com.test.hserver.bean.World; import com.test.hserver.util.DateUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.sql.DataSource; import java.sql.Connection; @@ -30,6 +33,13 @@ public class TestController { @Autowired private DataSource dataSource; + + @GET("/jso2n") + private Message jso2n(long ud,int a,HttpResponse response) { + response.setHeader("Date", DateUtil.getTime()); + return new Message(); + } + @GET("/json") public Message json(HttpResponse response) { response.setHeader("Date", DateUtil.getTime()); @@ -59,8 +69,8 @@ public void db(HttpResponse response) throws SQLException { } @GET("/queries") - public void queries(String queries,HttpResponse response) throws Exception { - World[] result = new World[getQueries(queries)]; + public void queries(HttpRequest request,HttpResponse response) throws Exception { + World[] result = new World[getQueries(request.query("queries"))]; try (Connection conn = dataSource.getConnection()) { for (int i = 0; i < result.length; i++) { try (final PreparedStatement statement = conn.prepareStatement(SELECT_WORLD)) { @@ -78,8 +88,8 @@ public void queries(String queries,HttpResponse response) throws Exception { @GET("/updates") - public void updates(String queries,HttpResponse response) throws Exception { - World[] result = new World[getQueries(queries)]; + public void updates(HttpRequest request,HttpResponse response) throws Exception { + World[] result = new World[getQueries(request.query("queries"))]; StringJoiner updateSql = new StringJoiner( ", ", "UPDATE world SET randomNumber = temp.randomNumber FROM (VALUES ", diff --git a/frameworks/Java/light-java/pom.xml b/frameworks/Java/light-java/pom.xml index 787dc1253bb..bead4b9f0e9 100644 --- a/frameworks/Java/light-java/pom.xml +++ b/frameworks/Java/light-java/pom.xml @@ -25,7 +25,7 @@ 11 2.0.1 1.3.12 - 2.3.15.Final + 2.3.17.Final 3.3.1 8.0.28 42.7.2 diff --git a/frameworks/Java/micronaut/buildSrc/build.gradle b/frameworks/Java/micronaut/buildSrc/build.gradle index 386638337fe..c30089f453e 100644 --- a/frameworks/Java/micronaut/buildSrc/build.gradle +++ b/frameworks/Java/micronaut/buildSrc/build.gradle @@ -8,6 +8,6 @@ repositories { } dependencies { - implementation "io.micronaut.gradle:micronaut-gradle-plugin:4.4.2" + implementation "io.micronaut.gradle:micronaut-gradle-plugin:4.4.4" implementation "com.github.johnrengelman.shadow:com.github.johnrengelman.shadow.gradle.plugin:8.1.1" } \ No newline at end of file diff --git a/frameworks/Java/micronaut/common/build.gradle b/frameworks/Java/micronaut/common/build.gradle index 8153fd5d8ea..61c68255503 100644 --- a/frameworks/Java/micronaut/common/build.gradle +++ b/frameworks/Java/micronaut/common/build.gradle @@ -34,8 +34,7 @@ dependencies { transitive = false } - // Switch to BOM version after https://github.com/micronaut-projects/micronaut-views/issues/876 - implementation("gg.jte:jte-runtime:3.1.12") + implementation("io.micronaut.views:micronaut-views-jte") runtimeOnly("ch.qos.logback:logback-classic") runtimeOnly("org.yaml:snakeyaml") diff --git a/frameworks/Java/micronaut/gradle.properties b/frameworks/Java/micronaut/gradle.properties index 8ce6479ffe6..264330f5525 100644 --- a/frameworks/Java/micronaut/gradle.properties +++ b/frameworks/Java/micronaut/gradle.properties @@ -1 +1 @@ -micronautVersion=4.6.1 +micronautVersion=4.7.1 diff --git a/frameworks/Java/micronaut/micronaut-data-jdbc-graalvm.dockerfile b/frameworks/Java/micronaut/micronaut-data-jdbc-graalvm.dockerfile index 8d7377b166d..f0b0a8a1cd7 100644 --- a/frameworks/Java/micronaut/micronaut-data-jdbc-graalvm.dockerfile +++ b/frameworks/Java/micronaut/micronaut-data-jdbc-graalvm.dockerfile @@ -1,4 +1,4 @@ -FROM container-registry.oracle.com/graalvm/native-image:21 +FROM container-registry.oracle.com/graalvm/native-image:23 RUN microdnf install findutils # Gradle 8.7 requires xargs COPY . /home/gradle/src WORKDIR /home/gradle/src diff --git a/frameworks/Java/micronaut/micronaut-data-mongodb-graalvm.dockerfile b/frameworks/Java/micronaut/micronaut-data-mongodb-graalvm.dockerfile index f0b28e5bcf3..003ace5e920 100644 --- a/frameworks/Java/micronaut/micronaut-data-mongodb-graalvm.dockerfile +++ b/frameworks/Java/micronaut/micronaut-data-mongodb-graalvm.dockerfile @@ -1,4 +1,4 @@ -FROM container-registry.oracle.com/graalvm/native-image:21 +FROM container-registry.oracle.com/graalvm/native-image:23 RUN microdnf install findutils # Gradle 8.7 requires xargs COPY . /home/gradle/src WORKDIR /home/gradle/src diff --git a/frameworks/Java/micronaut/micronaut-data-r2dbc-graalvm.dockerfile b/frameworks/Java/micronaut/micronaut-data-r2dbc-graalvm.dockerfile index 614967b3379..740abd93847 100644 --- a/frameworks/Java/micronaut/micronaut-data-r2dbc-graalvm.dockerfile +++ b/frameworks/Java/micronaut/micronaut-data-r2dbc-graalvm.dockerfile @@ -1,4 +1,4 @@ -FROM container-registry.oracle.com/graalvm/native-image:21 +FROM container-registry.oracle.com/graalvm/native-image:23 RUN microdnf install findutils # Gradle 8.7 requires xargs COPY . /home/gradle/src WORKDIR /home/gradle/src diff --git a/frameworks/Java/ninja-standalone/pom.xml b/frameworks/Java/ninja-standalone/pom.xml index b4af584038f..1c3aaafb7ee 100644 --- a/frameworks/Java/ninja-standalone/pom.xml +++ b/frameworks/Java/ninja-standalone/pom.xml @@ -20,7 +20,7 @@ 2.2.220 5.4.24.Final - 6.0.20.Final + 6.2.0.Final 2.3.0 9.4.18.v20190429 8.0.28 diff --git a/frameworks/Java/redkale/redkale-pgclient.dockerfile b/frameworks/Java/redkale/redkale-pgclient.dockerfile index af47b952c72..761368ba604 100644 --- a/frameworks/Java/redkale/redkale-pgclient.dockerfile +++ b/frameworks/Java/redkale/redkale-pgclient.dockerfile @@ -12,4 +12,4 @@ COPY --from=maven /redkale/target/redkale-benchmark-1.0.0.jar redkale-benchmark. EXPOSE 8080 -CMD ["java", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-DAPP_HOME=./", "-jar", "redkale-benchmark.jar"] \ No newline at end of file +CMD ["java", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-Dio.netty.buffer.checkBounds=false", "-Dio.netty.buffer.checkAccessible=false", "-Dvertx.disableURIValidation=true", "-Dvertx.threadChecks=false", "-Dvertx.disableContextTimings=true", "-DAPP_HOME=./", "-jar", "redkale-benchmark.jar"] \ No newline at end of file diff --git a/frameworks/Java/smart-socket/benchmark_config.json b/frameworks/Java/smart-socket/benchmark_config.json index f69d7a43eb7..7a26d71ed56 100755 --- a/frameworks/Java/smart-socket/benchmark_config.json +++ b/frameworks/Java/smart-socket/benchmark_config.json @@ -5,9 +5,6 @@ "default": { "json_url": "/json", "plaintext_url": "/plaintext", - "db_url": "/db", - "query_url": "/queries?queries=", - "update_url": "/updates?queries=", "port": 8080, "approach": "Realistic", "classification": "Platform", diff --git a/frameworks/Java/smart-socket/pom.xml b/frameworks/Java/smart-socket/pom.xml index 239721f8cba..a4107b3d3e8 100644 --- a/frameworks/Java/smart-socket/pom.xml +++ b/frameworks/Java/smart-socket/pom.xml @@ -11,7 +11,7 @@ 21 21 2.17.1 - 1.5-SNAPSHOT + 2.5 5.0.0 0.9.23 @@ -23,7 +23,7 @@ 0.1-SNAPSHOT - org.smartboot.servlet + tech.smartboot.servlet servlet-core ${smartservlet.version} diff --git a/frameworks/Java/smart-socket/src/main/java/org/smartboot/http/Bootstrap.java b/frameworks/Java/smart-socket/src/main/java/org/smartboot/http/Bootstrap.java index 06753113440..9dda965e0be 100755 --- a/frameworks/Java/smart-socket/src/main/java/org/smartboot/http/Bootstrap.java +++ b/frameworks/Java/smart-socket/src/main/java/org/smartboot/http/Bootstrap.java @@ -18,34 +18,11 @@ import org.smartboot.http.server.handler.HttpRouteHandler; import javax.sql.DataSource; -import java.io.IOException; public class Bootstrap { static byte[] body = "Hello, World!".getBytes(); public static void main(String[] args) { - HttpRouteHandler routeHandle = new HttpRouteHandler(); - routeHandle - .route("/plaintext", new HttpServerHandler() { - - - @Override - public void handle(HttpRequest request, HttpResponse response) throws IOException { - response.setContentLength(body.length); - response.setContentType("text/plain; charset=UTF-8"); - response.write(body); - } - }) - .route("/json", new HttpServerHandler() { - - @Override - public void handle(HttpRequest request, HttpResponse response) throws IOException { - - response.setContentType("application/json"); - JsonUtil.writeJsonBytes(response, new Message("Hello, World!")); - } - }); - initDB(routeHandle); int cpuNum = Runtime.getRuntime().availableProcessors(); // 定义服务器接受的消息类型以及各类消息对应的处理器 HttpBootstrap bootstrap = new HttpBootstrap(); @@ -53,10 +30,20 @@ public void handle(HttpRequest request, HttpResponse response) throws IOExceptio .threadNum(cpuNum) .headerLimiter(0) .readBufferSize(1024 * 4) - .writeBufferSize(1024 * 4) - .readMemoryPool(16384 * 1024 * 4) - .writeMemoryPool(10 * 1024 * 1024 * cpuNum, cpuNum); - bootstrap.httpHandler(routeHandle).setPort(8080).start(); + .writeBufferSize(1024 * 4); + bootstrap.httpHandler(new HttpServerHandler() { + @Override + public void handle(HttpRequest request, HttpResponse response) throws Throwable { + if ("/plaintext".equals(request.getRequestURI())) { + response.setContentLength(body.length); + response.setContentType("text/plain; charset=UTF-8"); + response.write(body); + } else if ("/json".equals(request.getRequestURI())) { + response.setContentType("application/json"); + JsonUtil.writeJsonBytes(response, new Message("Hello, World!")); + } + } + }).setPort(8080).start(); } private static void initDB(HttpRouteHandler routeHandle) { diff --git a/frameworks/Java/smart-socket/src/main/java/org/smartboot/http/JsonUtil.java b/frameworks/Java/smart-socket/src/main/java/org/smartboot/http/JsonUtil.java index 5c46d0ca5ef..b20525ec571 100644 --- a/frameworks/Java/smart-socket/src/main/java/org/smartboot/http/JsonUtil.java +++ b/frameworks/Java/smart-socket/src/main/java/org/smartboot/http/JsonUtil.java @@ -4,9 +4,9 @@ import com.jsoniter.output.JsonStreamPool; import com.jsoniter.spi.JsonException; import com.jsoniter.spi.Slice; +import jakarta.servlet.http.HttpServletResponse; import org.smartboot.http.server.HttpResponse; -import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** diff --git a/frameworks/Java/smart-socket/src/main/java/org/smartboot/servlet/Bootstrap.java b/frameworks/Java/smart-socket/src/main/java/org/smartboot/servlet/Bootstrap.java index b6691bfd359..f3caa038cbe 100644 --- a/frameworks/Java/smart-socket/src/main/java/org/smartboot/servlet/Bootstrap.java +++ b/frameworks/Java/smart-socket/src/main/java/org/smartboot/servlet/Bootstrap.java @@ -1,12 +1,10 @@ package org.smartboot.servlet; -import org.smartboot.http.server.HttpBootstrap; -import org.smartboot.http.server.HttpRequest; -import org.smartboot.http.server.HttpResponse; -import org.smartboot.http.server.HttpServerHandler; -import org.smartboot.servlet.conf.ServletInfo; -import java.util.concurrent.CompletableFuture; +import tech.smartboot.servlet.Container; +import tech.smartboot.servlet.ServletContextRuntime; +import tech.smartboot.servlet.conf.ServletInfo; +import tech.smartboot.servlet.conf.ServletMappingInfo; /** * @author 三刀(zhengjunweimail@163.com) @@ -15,7 +13,8 @@ public class Bootstrap { public static void main(String[] args) throws Throwable { - ContainerRuntime containerRuntime = new ContainerRuntime(); + System.setProperty("smart-servlet-spring-boot-starter","true"); + Container containerRuntime = new Container(); // plaintext ServletContextRuntime applicationRuntime = new ServletContextRuntime(null, Thread.currentThread().getContextClassLoader(), "/"); applicationRuntime.setVendorProvider(response -> { @@ -24,36 +23,23 @@ public static void main(String[] args) throws Throwable { ServletInfo plainTextServletInfo = new ServletInfo(); plainTextServletInfo.setServletName("plaintext"); plainTextServletInfo.setServletClass(HelloWorldServlet.class.getName()); - plainTextServletInfo.addMapping("/plaintext"); + applicationRuntime.getDeploymentInfo().addServletMapping(new ServletMappingInfo(plainTextServletInfo.getServletName(), "/plaintext")); applicationRuntime.getDeploymentInfo().addServlet(plainTextServletInfo); // json ServletInfo jsonServletInfo = new ServletInfo(); jsonServletInfo.setServletName("json"); jsonServletInfo.setServletClass(JsonServlet.class.getName()); - jsonServletInfo.addMapping("/json"); applicationRuntime.getDeploymentInfo().addServlet(jsonServletInfo); + applicationRuntime.getDeploymentInfo().addServletMapping(new ServletMappingInfo(jsonServletInfo.getServletName(), "/json")); containerRuntime.addRuntime(applicationRuntime); - int cpuNum = Runtime.getRuntime().availableProcessors(); // 定义服务器接受的消息类型以及各类消息对应的处理器 - HttpBootstrap bootstrap = new HttpBootstrap(); - bootstrap.configuration() - .threadNum(cpuNum) - .bannerEnabled(false) - .headerLimiter(0) - .readBufferSize(1024 * 4) - .writeBufferSize(1024 * 4) - .readMemoryPool(16384 * 1024 * 4) - .writeMemoryPool(10 * 1024 * 1024 * cpuNum, cpuNum); - containerRuntime.start(bootstrap.configuration()); - bootstrap.setPort(8080) - .httpHandler(new HttpServerHandler() { - @Override - public void handle(HttpRequest request, HttpResponse response, CompletableFuture completableFuture) throws Throwable { - containerRuntime.doHandle(request, response, completableFuture); - } - }) - .start(); + containerRuntime.getConfiguration() + .setThreadNum(cpuNum) + .setReadBufferSize(1024 * 4); + containerRuntime.initialize(); + containerRuntime.start(); + } } diff --git a/frameworks/Java/smart-socket/src/main/java/org/smartboot/servlet/HelloWorldServlet.java b/frameworks/Java/smart-socket/src/main/java/org/smartboot/servlet/HelloWorldServlet.java index 6ac97cc7606..3d0d8bd1d2a 100644 --- a/frameworks/Java/smart-socket/src/main/java/org/smartboot/servlet/HelloWorldServlet.java +++ b/frameworks/Java/smart-socket/src/main/java/org/smartboot/servlet/HelloWorldServlet.java @@ -1,10 +1,11 @@ package org.smartboot.servlet; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + import java.io.IOException; /** diff --git a/frameworks/Java/smart-socket/src/main/java/org/smartboot/servlet/JsonServlet.java b/frameworks/Java/smart-socket/src/main/java/org/smartboot/servlet/JsonServlet.java index e484da19fc3..064bc80556d 100644 --- a/frameworks/Java/smart-socket/src/main/java/org/smartboot/servlet/JsonServlet.java +++ b/frameworks/Java/smart-socket/src/main/java/org/smartboot/servlet/JsonServlet.java @@ -1,12 +1,12 @@ package org.smartboot.servlet; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.smartboot.Message; import org.smartboot.http.JsonUtil; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** diff --git a/frameworks/Java/smart-socket/src/main/resources/smart-servlet/License.shield b/frameworks/Java/smart-socket/src/main/resources/smart-servlet/License.shield new file mode 100644 index 00000000000..f970f023915 Binary files /dev/null and b/frameworks/Java/smart-socket/src/main/resources/smart-servlet/License.shield differ diff --git a/frameworks/Java/smart-socket/src/main/resources/smart-servlet/smart-servlet.pem b/frameworks/Java/smart-socket/src/main/resources/smart-servlet/smart-servlet.pem new file mode 100644 index 00000000000..38a0a7c0ec9 --- /dev/null +++ b/frameworks/Java/smart-socket/src/main/resources/smart-servlet/smart-servlet.pem @@ -0,0 +1,55 @@ +-----BEGIN CERTIFICATE----- +MIIEhTCCAu2gAwIBAgIQF5C4/s1tNk9arohyD4J2UDANBgkqhkiG9w0BAQsFADCB +nzEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMTowOAYDVQQLDDF6aGVu +Z2p3MjJtYWMxMjNAemhlbmdqdzIyTWFjMTIzLmxvY2FsICh6aGVuZ2p3MjIpMUEw +PwYDVQQDDDhta2NlcnQgemhlbmdqdzIybWFjMTIzQHpoZW5nancyMk1hYzEyMy5s +b2NhbCAoemhlbmdqdzIyKTAeFw0yNDAyMTUwNzU3NTNaFw0yNjA1MTUwNzU3NTNa +MGUxJzAlBgNVBAoTHm1rY2VydCBkZXZlbG9wbWVudCBjZXJ0aWZpY2F0ZTE6MDgG +A1UECwwxemhlbmdqdzIybWFjMTIzQHpoZW5nancyMk1hYzEyMy5sb2NhbCAoemhl +bmdqdzIyKTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALvMsE1RIalU +Kzy+4U+Ixw+Z8FL3KHiKLk1513qsn40Po4bsE8zgsrd4lLx1lHpEkmKK3dpHREP1 +a1goi8Jqypmiz4oBUzrBMKBVo60wom3Al/XslgiZdzrhtFMOhwcjfGhkthq3ps5q +wHyqRneijfeB/YICfF+e0K6fnbiSPd8rBJP6F7de2oafawIV9jNtIpqUQpfk+b+F +Y3oDE2xCiz1chx/AfY6CaSICHYoHR9beugnm9RO45mjw/84Fs5AnneTUkZbeozYo +I0FyqplhfExglU3k/S0Fvkbd+GT5RPkhwwC3SQGldORHR9/+yoAsNjKlrLMD+aTo +5G618AEldr0CAwEAAaN2MHQwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsG +AQUFBwMBMB8GA1UdIwQYMBaAFDnyay2H0T65/ghxa7Ap7kKXyEefMCwGA1UdEQQl +MCOCCWxvY2FsaG9zdIcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0B +AQsFAAOCAYEAu31uRYtB+5dYbtOTVJDNw94nRXNkkh/u1Gv/WcEaauwsgoaeyy6P +kIavx1RiNrCkAO5aYIVCF407OadMzixRurvc4hVatAJVaj6zNfSs4HCViB7BCfLu +oYmqEYwjaPdqLq04uA/ZEeklqeKRJ0gzKXToaoQ8xcxIJcCjojaSi4usqSI5bi4y +Md79AtB3fxv+RipaHOPAqs2i2Eo7wCENEnUs8Uqu/VI3hZEljVOOFHbak33iFdwM +Gg4NfE3QSxVOctseB+2lcNjuk8Wxee1CIvH/imskgZl25dg4B2nHG9TEiYbabJ7/ +K5MOWg81lNAF+pmUJ01OaoFcXyDPc5hh5Unv3zHJrhRc86JwxqwhThkXRwAgh8fA +gjQIfE9byUD9o/HTeTdC7B8Tb3EnvYxkj83fi0fDj90022sjsRD/JMLfigx+T4rA +C4FKqpXgqfry0QUfibX6sxRWw7QwWtf9AInAEVgukx2ollxLGoVeK6hYG26d94YE +QtxEn3cOVqU+ +-----END CERTIFICATE----- +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC7zLBNUSGpVCs8 +vuFPiMcPmfBS9yh4ii5Nedd6rJ+ND6OG7BPM4LK3eJS8dZR6RJJiit3aR0RD9WtY +KIvCasqZos+KAVM6wTCgVaOtMKJtwJf17JYImXc64bRTDocHI3xoZLYat6bOasB8 +qkZ3oo33gf2CAnxfntCun524kj3fKwST+he3XtqGn2sCFfYzbSKalEKX5Pm/hWN6 +AxNsQos9XIcfwH2OgmkiAh2KB0fW3roJ5vUTuOZo8P/OBbOQJ53k1JGW3qM2KCNB +cqqZYXxMYJVN5P0tBb5G3fhk+UT5IcMAt0kBpXTkR0ff/sqALDYypayzA/mk6ORu +tfABJXa9AgMBAAECggEAHGOV5yozj3hUzOsB/lbr2JTpunD4YjhpRXb8tuOvftB1 +ZOj9GUSCX6/PtCmGF3GUO2dIoD2TuT45SuteLTadh9oPy4nlvgUER8iKZJzsgPDT +R+7Kw2QHnRQPgVq52L9piBJpYOKQSbXjgTTwUBd3pIm2+9dKW94TJ8KjQgqBZeHF +jBFrqqAVLvgRm5nOeNx98H6bhLd/GoB1RSj61jIcNNpDRnPxzq3IYLgv2zjXWasJ ++IvVCTlxapbVBgzvp6burmJ8eYBruW3FAR0tGBJi3imySDLs3EbB1F2ox0pOJjOw +c2bdZVgPdKY2wbmk+/2pyG4le5/64ByLxqX7Gux9dQKBgQDT/25SzZF9sVGkOo0u +5WUYqBA0ni2O7mFJR9KdHKrEeN6IcvPL+ttp92ESBAZf/rxN4KZs1OPeg7MABN/3 +caQQIqQNaFBdPrX0E5fs+gTKb38r1hXxpsCEhDjxMXroBen2RaTf7Q7AyxQFKFXZ +wjFyOWHR0tL1pO+O7dt3YgMB7wKBgQDix3pisWzUAeXOVVXRzfe9H9n5KmY0rnmC +raXa00Aq2RnBxtdIDf2whX6wOBOz3cHxsFMSDOVy36DfzGeBjMJjhvwq4Mdzu9HV +fYqUJHQBGdK/wGOMOto32E/hbIVhWwxsPZvpeb3h8lhOHVTJN0HbqrZbDLPObjlE +Dlx67ILOEwKBgB4CUV6dRNQTDqh9tVCHHllwKOMZ5P8PlWvnI9Qjo7SuG2obQ5GD +UB3e67m+IhzilUs82rIbLKpp4CPHjOCdEIlMLgbL1lxsrRsAzwe3mIgDYnAVHQQZ +A7V+dgUGaQyBEc5Pq3gbOXRnCs10GTr69z7hCozGGCC3mUWVO/TZRe23AoGBAOFI +y0LaAUPHstS8H2oyU8a0qqSFQ01YemugN+Bf9iHa1GSVNO5mv7upkkZbHu+TAAUq +ZgvLdfEdSUKqW7Tt8XpP8Zhi/qDxV63fblhmsjsZvSwyYnI/UOMjZ4+IcCRb/8ZT +mdxhzYl1Z9YJ+119IFapi0h+IO2UwBzkq2iOJg+zAoGBALJSi+nD7/lhA/HKu8Nr +sh4G6tczLMOHN+jvngf5WoGEnBM1ENUtmMiH0YvBIpyHSHgyKwq+H29fgA5f42Eb +xe7HfOye3a3OmmJvzkckEs5Ms1J4ahErwRUvM5+xsCG5bdpA/p61sy14W3eYyA/r +tSSrxN4ernLg7k/vNHB0NIjK +-----END PRIVATE KEY----- diff --git a/frameworks/Java/smart-socket/src/main/resources/smart-servlet/smart-servlet.properties b/frameworks/Java/smart-socket/src/main/resources/smart-servlet/smart-servlet.properties new file mode 100644 index 00000000000..2df513cd3ae --- /dev/null +++ b/frameworks/Java/smart-socket/src/main/resources/smart-servlet/smart-servlet.properties @@ -0,0 +1,15 @@ +# +# Copyright (c) 2017-2020, org.smartboot. All rights reserved. +# project name: smart-servlet +# file name: smart-servlet.properties +# Date: 2020-12-10 +# Author: sandao (zhengjunweimail@163.com) +# +# +http.port=8080 +http.readBufferSize=8192 +#http.threadNum=10 +root.context=ROOT +## tls/ssl +ssl.enable=false +ssl.port=443 diff --git a/frameworks/Java/solon/benchmark_config.json b/frameworks/Java/solon/benchmark_config.json index 70a0c1cfc10..52c9fd8be8c 100644 --- a/frameworks/Java/solon/benchmark_config.json +++ b/frameworks/Java/solon/benchmark_config.json @@ -3,16 +3,16 @@ "tests": [ { "default": { - "json_url": "/json", "plaintext_url": "/plaintext", + "json_url": "/json", "port": 8080, "approach": "Realistic", "classification": "Fullstack", - "database": "None", + "database": "Postgres", "framework": "solon", "language": "Java", "flavor": "None", - "orm": "Full", + "orm": "Micro", "platform": "solon", "webserver": "smarthttp", "os": "Linux", diff --git a/frameworks/Java/solon/config.toml b/frameworks/Java/solon/config.toml index c806ddfb0bf..83bd6e72946 100644 --- a/frameworks/Java/solon/config.toml +++ b/frameworks/Java/solon/config.toml @@ -6,10 +6,10 @@ urls.plaintext = "/plaintext" urls.json = "/json" approach = "Realistic" classification = "Platform" -database = "None" +database = "Postgres" database_os = "Linux" os = "Linux" -orm = "Raw" +orm = "Micro" platform = "solon" webserver = "smarthttp" versus = "None" diff --git a/frameworks/Java/solon/pom.xml b/frameworks/Java/solon/pom.xml index 0c9d8d570ce..d483a51b397 100644 --- a/frameworks/Java/solon/pom.xml +++ b/frameworks/Java/solon/pom.xml @@ -5,7 +5,7 @@ org.noear solon-parent - 3.0.2 + 3.0.4 hello @@ -15,17 +15,49 @@ 21 + 1.3.6 org.noear - solon-boot-smarthttp + solon-web org.noear - solon-serialization-snack3 + solon-serialization-jackson + + + + org.noear + solon-data-sqlutils + + + + io.jstach + jstachio + ${jstachio.version} + + + + io.jstach + jstachio-apt + ${jstachio.version} + provided + true + + + + com.zaxxer + HikariCP + 6.0.0 + + + + org.postgresql + postgresql + 42.7.4 @@ -33,6 +65,20 @@ ${project.artifactId} + + org.apache.maven.plugins + maven-compiler-plugin + + + + io.jstach + jstachio-apt + ${jstachio.version} + + + + + org.apache.maven.plugins maven-assembly-plugin diff --git a/frameworks/Java/solon/src/main/java/hello/Main.java b/frameworks/Java/solon/src/main/java/hello/Main.java index 0746f86be45..75fd11bfc36 100644 --- a/frameworks/Java/solon/src/main/java/hello/Main.java +++ b/frameworks/Java/solon/src/main/java/hello/Main.java @@ -2,10 +2,6 @@ import org.noear.solon.Solon; -/** - * @author pmg1991 - * @version V1.0 - */ public class Main { public static void main(String[] args) { Solon.start(Main.class, args); diff --git a/frameworks/Java/solon/src/main/java/hello/Utils.java b/frameworks/Java/solon/src/main/java/hello/Utils.java new file mode 100644 index 00000000000..162f89cc79c --- /dev/null +++ b/frameworks/Java/solon/src/main/java/hello/Utils.java @@ -0,0 +1,17 @@ +package hello; + +import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.IntStream; + +abstract public class Utils { + private static final int MIN_WORLD_NUMBER = 1; + private static final int MAX_WORLD_NUMBER_PLUS_ONE = 10_001; + + public static int randomWorldNumber() { + return ThreadLocalRandom.current().nextInt(MIN_WORLD_NUMBER, MAX_WORLD_NUMBER_PLUS_ONE); + } + + public static IntStream randomWorldNumbers() { + return ThreadLocalRandom.current().ints(MIN_WORLD_NUMBER, MAX_WORLD_NUMBER_PLUS_ONE).distinct(); + } +} diff --git a/frameworks/Java/solon/src/main/java/hello/controller/HelloController.java b/frameworks/Java/solon/src/main/java/hello/controller/HelloController.java deleted file mode 100644 index 77049043e97..00000000000 --- a/frameworks/Java/solon/src/main/java/hello/controller/HelloController.java +++ /dev/null @@ -1,25 +0,0 @@ -package hello.controller; - -import org.noear.solon.annotation.Controller; -import org.noear.solon.annotation.Get; -import org.noear.solon.annotation.Mapping; -import hello.model.Message; - -/** - * @author noear - * @version V1.0 - */ -@Controller -public class HelloController { - @Get - @Mapping("plaintext") - public String plaintext() { - return "Hello, World!"; - } - - @Get - @Mapping("json") - public Message json() { - return new Message("Hello, World!"); - } -} diff --git a/frameworks/Java/solon/src/main/java/hello/model/Fortune.java b/frameworks/Java/solon/src/main/java/hello/model/Fortune.java new file mode 100644 index 00000000000..f7b5769ef28 --- /dev/null +++ b/frameworks/Java/solon/src/main/java/hello/model/Fortune.java @@ -0,0 +1,16 @@ +package hello.model; + +public final class Fortune implements Comparable{ + public final int id; + public final String message; + + public Fortune(int id, String message) { + this.id = id; + this.message = message; + } + + @Override + public int compareTo(final Fortune other) { + return message.compareTo(other.message); + } +} diff --git a/frameworks/Java/solon/src/main/java/hello/model/World.java b/frameworks/Java/solon/src/main/java/hello/model/World.java new file mode 100644 index 00000000000..ec6fc5b3abf --- /dev/null +++ b/frameworks/Java/solon/src/main/java/hello/model/World.java @@ -0,0 +1,12 @@ +package hello.model; + + +public final class World { + public int id; + public int randomNumber; + public World(int id, int randomNumber) { + this.id = id; + this.randomNumber = randomNumber; + } + +} \ No newline at end of file diff --git a/frameworks/Java/solon/src/main/java/hello/repository/DbRepository.java b/frameworks/Java/solon/src/main/java/hello/repository/DbRepository.java new file mode 100644 index 00000000000..e5445184077 --- /dev/null +++ b/frameworks/Java/solon/src/main/java/hello/repository/DbRepository.java @@ -0,0 +1,15 @@ +package hello.repository; + +import hello.model.Fortune; +import hello.model.World; + +import java.util.List; + +public interface DbRepository { + + World getWorld(int id) throws Exception; + + void updateWorlds(List worlds) throws Exception; + + List fortunes() throws Exception; +} diff --git a/frameworks/Java/solon/src/main/java/hello/repository/JdbcDbRepository.java b/frameworks/Java/solon/src/main/java/hello/repository/JdbcDbRepository.java new file mode 100644 index 00000000000..5fb83f9a68e --- /dev/null +++ b/frameworks/Java/solon/src/main/java/hello/repository/JdbcDbRepository.java @@ -0,0 +1,41 @@ +package hello.repository; + +import hello.model.Fortune; +import hello.model.World; +import org.noear.solon.annotation.Component; +import org.noear.solon.annotation.Inject; +import org.noear.solon.data.sql.SqlUtils; + +import java.sql.SQLException; +import java.util.List; + +@Component +public class JdbcDbRepository implements DbRepository { + @Inject + SqlUtils sqlUtils; + + @Override + public World getWorld(int id) { + try { + return sqlUtils.sql("SELECT id, randomnumber FROM world WHERE id = ?", id) + .queryRow((rs) -> new World(rs.getInt(1), rs.getInt(2))); + } catch (Exception e) { + return null; + } + } + + @Override + public void updateWorlds(List worlds) throws SQLException { + sqlUtils.sql("UPDATE world SET randomnumber = ? WHERE id = ?") + .updateBatch(worlds, (ps, w) -> { + ps.setInt(1, w.randomNumber); + ps.setInt(2, w.id); + }); + } + + @Override + public List fortunes() throws SQLException { + return sqlUtils.sql("SELECT id, message FROM fortune") + .queryRowList((r) -> new Fortune(r.getInt(1), r.getString(2))); + } +} diff --git a/frameworks/Java/solon/src/main/java/hello/web/DbHandler.java b/frameworks/Java/solon/src/main/java/hello/web/DbHandler.java new file mode 100644 index 00000000000..9163c94e3c9 --- /dev/null +++ b/frameworks/Java/solon/src/main/java/hello/web/DbHandler.java @@ -0,0 +1,61 @@ +package hello.web; + +import hello.Utils; +import hello.model.Fortune; +import hello.model.World; +import hello.repository.JdbcDbRepository; +import org.noear.solon.annotation.Component; +import org.noear.solon.annotation.Inject; +import org.noear.solon.core.handle.Context; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +@Component +public class DbHandler { + @Inject + JdbcDbRepository dbRepository; + + public void db(Context ctx) throws Throwable { + ctx.render(dbRepository.getWorld(Utils.randomWorldNumber())); + } + + public void queries(Context ctx) throws Throwable { + int queries = ctx.paramAsInt("queries", 0); + + World[] worlds = Utils.randomWorldNumbers() + .mapToObj(dbRepository::getWorld).limit(queries) + .toArray(World[]::new); + + ctx.render(worlds); + } + + public void updates(Context ctx) throws Throwable { + int queries = ctx.paramAsInt("queries", 0); + + List worlds = Utils.randomWorldNumbers() + .mapToObj(id -> { + World world = dbRepository.getWorld(id); + int randomNumber; + do { + randomNumber = Utils.randomWorldNumber(); + } while (randomNumber == world.randomNumber); + world.randomNumber = randomNumber; + return world; + }).limit(queries) + .sorted(Comparator.comparingInt(w -> w.id)) + .toList(); + dbRepository.updateWorlds(worlds); + + ctx.render(worlds); + } + + public void fortunes(Context ctx) throws Throwable { + List fortunes = dbRepository.fortunes(); + fortunes.add(new Fortune(0, "Additional fortune added at request time.")); + Collections.sort(fortunes); + + ctx.render(new Fortunes(fortunes)); + } +} diff --git a/frameworks/Java/solon/src/main/java/hello/web/Fortunes.java b/frameworks/Java/solon/src/main/java/hello/web/Fortunes.java new file mode 100644 index 00000000000..a5416ec07bc --- /dev/null +++ b/frameworks/Java/solon/src/main/java/hello/web/Fortunes.java @@ -0,0 +1,10 @@ +package hello.web; + +import hello.model.Fortune; +import io.jstach.jstache.JStache; + +import java.util.List; + +@JStache(path = "fortunes.mustache") +public record Fortunes(List fortunes) { +} diff --git a/frameworks/Java/solon/src/main/java/hello/web/JsonHandler.java b/frameworks/Java/solon/src/main/java/hello/web/JsonHandler.java new file mode 100644 index 00000000000..c65bb73ab74 --- /dev/null +++ b/frameworks/Java/solon/src/main/java/hello/web/JsonHandler.java @@ -0,0 +1,27 @@ +package hello.web; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; +import org.noear.solon.annotation.Component; +import org.noear.solon.boot.web.MimeType; +import org.noear.solon.core.handle.Context; +import org.noear.solon.core.handle.Handler; + +import java.util.Map; + +@Component +public class JsonHandler implements Handler { + private final ObjectWriter writer; + + public JsonHandler() { + this.writer = new ObjectMapper().writerFor(Map.class); + } + + @Override + public void handle(Context ctx) throws Throwable { + byte[] body = this.writer.writeValueAsBytes(Map.of("message", "Hello, world!")); + ctx.contentLength(body.length); + ctx.contentType(MimeType.APPLICATION_JSON_VALUE); + ctx.output(body); + } +} diff --git a/frameworks/Java/solon/src/main/java/hello/web/TextHandler.java b/frameworks/Java/solon/src/main/java/hello/web/TextHandler.java new file mode 100644 index 00000000000..96d68e6e7df --- /dev/null +++ b/frameworks/Java/solon/src/main/java/hello/web/TextHandler.java @@ -0,0 +1,25 @@ +package hello.web; + +import org.noear.solon.annotation.Component; +import org.noear.solon.boot.web.MimeType; +import org.noear.solon.core.handle.Context; +import org.noear.solon.core.handle.Handler; + +import java.nio.charset.StandardCharsets; + +@Component +public class TextHandler implements Handler { + private static final byte[] TEXT_BODY = "Hello, World!".getBytes(StandardCharsets.UTF_8); + + private static final String TEXT_BODY_LENGTH = String.valueOf(TEXT_BODY.length); + private static final String CONTENT_LENGTH = "Content-Length"; + private static final String CONTENT_TYPE = "Content-Type"; + + @Override + public void handle(Context ctx) throws Throwable { + ctx.headerSet(CONTENT_LENGTH, TEXT_BODY_LENGTH); + ctx.headerSet(CONTENT_TYPE, MimeType.TEXT_PLAIN_VALUE); + ctx.output("Hello, World!".getBytes()); + } +} + diff --git a/frameworks/Java/solon/src/main/java/hello/web/WebmvcRouter.java b/frameworks/Java/solon/src/main/java/hello/web/WebmvcRouter.java new file mode 100644 index 00000000000..6024ddcc548 --- /dev/null +++ b/frameworks/Java/solon/src/main/java/hello/web/WebmvcRouter.java @@ -0,0 +1,29 @@ +package hello.web; + +import org.noear.solon.SolonApp; +import org.noear.solon.annotation.Bean; +import org.noear.solon.annotation.Configuration; + +@Configuration +public class WebmvcRouter { + @Bean + public void initRouter(SolonApp app, + DbHandler dbHandler, + JsonHandler jsonHandler, + TextHandler textHandler) { + app.handler().prev(ctx -> { + ctx.setHandled(true); + ctx.headerSet("Server", "Solon"); + + switch (ctx.path()) { + case "/plaintext" -> textHandler.handle(ctx); + case "/json" -> jsonHandler.handle(ctx); + case "/db" -> dbHandler.db(ctx); + case "/queries" -> dbHandler.queries(ctx); + case "/updates" -> dbHandler.updates(ctx); + case "/fortunes" -> dbHandler.fortunes(ctx); + default -> ctx.status(404); + } + }); + } +} diff --git a/frameworks/Java/solon/src/main/resources/app.properties b/frameworks/Java/solon/src/main/resources/app.properties deleted file mode 100644 index 26a5df70351..00000000000 --- a/frameworks/Java/solon/src/main/resources/app.properties +++ /dev/null @@ -1 +0,0 @@ -server.http.ioBound=false \ No newline at end of file diff --git a/frameworks/Java/solon/src/main/resources/app.yml b/frameworks/Java/solon/src/main/resources/app.yml new file mode 100644 index 00000000000..cbd9e850625 --- /dev/null +++ b/frameworks/Java/solon/src/main/resources/app.yml @@ -0,0 +1,23 @@ +database: + name: hello_world + host: tfb-database + port: 5432 + username: benchmarkdbuser + password: benchmarkdbpass + +server.http: + ioBound: false + +solon.threads: + virtual: + enabled: true + +solon.dataSources: + test!: + class: "com.zaxxer.hikari.HikariDataSource" + driverClassName: "org.postgresql.Driver" + url: jdbc:postgresql://${database.host}:${database.port}/${database.name}?loggerLevel=OFF&disableColumnSanitiser=true&assumeMinServerVersion=16&sslmode=disable + username: ${database.username} + password: ${database.password} + hikari: + maximum-pool-size: 256 \ No newline at end of file diff --git a/frameworks/Java/solon/src/main/resources/fortunes.mustache b/frameworks/Java/solon/src/main/resources/fortunes.mustache new file mode 100644 index 00000000000..3043546205b --- /dev/null +++ b/frameworks/Java/solon/src/main/resources/fortunes.mustache @@ -0,0 +1,20 @@ + + + + Fortunes + + + + + + + + {{#fortunes}} + + + + + {{/fortunes}} +
idmessage
{{id}}{{message}}
+ + diff --git a/frameworks/Java/spring-webflux/pom.xml b/frameworks/Java/spring-webflux/pom.xml index 7e1b6865d2d..af60e1f7c7e 100644 --- a/frameworks/Java/spring-webflux/pom.xml +++ b/frameworks/Java/spring-webflux/pom.xml @@ -13,14 +13,12 @@ org.springframework.boot spring-boot-starter-parent - 3.3.4 + 3.3.5 21 1.3.6 - 1.0.2.RELEASE - 1.0.7.RELEASE diff --git a/frameworks/Java/spring-webflux/spring-webflux-mongo.dockerfile b/frameworks/Java/spring-webflux/spring-webflux-mongo.dockerfile index d565d1556c3..514a1db96df 100644 --- a/frameworks/Java/spring-webflux/spring-webflux-mongo.dockerfile +++ b/frameworks/Java/spring-webflux/spring-webflux-mongo.dockerfile @@ -4,7 +4,7 @@ COPY src src COPY pom.xml pom.xml RUN mvn package -q -FROM bellsoft/liberica-openjre-debian:21 +FROM bellsoft/liberica-openjre-debian:23 WORKDIR /spring COPY --from=maven /spring/target/spring-webflux-benchmark.jar app.jar # See https://docs.spring.io/spring-boot/reference/packaging/efficient.html diff --git a/frameworks/Java/spring-webflux/spring-webflux.dockerfile b/frameworks/Java/spring-webflux/spring-webflux.dockerfile index e1cee08ff31..60c2229b795 100644 --- a/frameworks/Java/spring-webflux/spring-webflux.dockerfile +++ b/frameworks/Java/spring-webflux/spring-webflux.dockerfile @@ -4,7 +4,7 @@ COPY src src COPY pom.xml pom.xml RUN mvn package -q -FROM bellsoft/liberica-openjre-debian:21 +FROM bellsoft/liberica-openjre-debian:23 WORKDIR /spring COPY --from=maven /spring/target/spring-webflux-benchmark.jar app.jar # See https://docs.spring.io/spring-boot/reference/packaging/efficient.html diff --git a/frameworks/Java/spring-webflux/src/main/java/benchmark/repository/DbRepository.java b/frameworks/Java/spring-webflux/src/main/java/benchmark/repository/DbRepository.java index 54b6d0d9d02..2eea6d41049 100644 --- a/frameworks/Java/spring-webflux/src/main/java/benchmark/repository/DbRepository.java +++ b/frameworks/Java/spring-webflux/src/main/java/benchmark/repository/DbRepository.java @@ -1,5 +1,7 @@ package benchmark.repository; +import java.util.List; + import benchmark.model.Fortune; import benchmark.model.World; import reactor.core.publisher.Flux; @@ -9,7 +11,7 @@ public interface DbRepository { Mono getWorld(int id); - Mono findAndUpdateWorld(int id, int randomNumber); + Mono updateWorlds(List worlds); Flux fortunes(); } \ No newline at end of file diff --git a/frameworks/Java/spring-webflux/src/main/java/benchmark/repository/MongoDbRepository.java b/frameworks/Java/spring-webflux/src/main/java/benchmark/repository/MongoDbRepository.java index df777dd2d8e..4fff0eb2c5d 100644 --- a/frameworks/Java/spring-webflux/src/main/java/benchmark/repository/MongoDbRepository.java +++ b/frameworks/Java/spring-webflux/src/main/java/benchmark/repository/MongoDbRepository.java @@ -1,16 +1,16 @@ package benchmark.repository; +import java.util.List; + import benchmark.model.Fortune; import benchmark.model.World; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; + import org.springframework.context.annotation.Profile; import org.springframework.data.mongodb.core.ReactiveMongoOperations; import org.springframework.stereotype.Component; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import static org.springframework.data.mongodb.core.FindAndModifyOptions.options; import static org.springframework.data.mongodb.core.query.Criteria.where; import static org.springframework.data.mongodb.core.query.Query.query; import static org.springframework.data.mongodb.core.query.Update.update; @@ -31,12 +31,11 @@ public Mono getWorld(int id) { } @Override - public Mono findAndUpdateWorld(int id, int randomNumber) { - return operations.findAndModify( - query(where("id").is(id)), - update("randomNumber", randomNumber), - options().returnNew(true), - World.class); + public Mono updateWorlds(List worlds) { + return Flux.fromIterable(worlds).flatMap(world -> operations.findAndModify( + query(where("id").is(world.id)), + update("randomNumber", world.randomnumber), + World.class)).then(); } @Override diff --git a/frameworks/Java/spring-webflux/src/main/java/benchmark/repository/R2dbcDbRepository.java b/frameworks/Java/spring-webflux/src/main/java/benchmark/repository/R2dbcDbRepository.java index c2524f07862..c8f1cdbc0c2 100644 --- a/frameworks/Java/spring-webflux/src/main/java/benchmark/repository/R2dbcDbRepository.java +++ b/frameworks/Java/spring-webflux/src/main/java/benchmark/repository/R2dbcDbRepository.java @@ -1,11 +1,17 @@ package benchmark.repository; +import java.util.List; + import org.springframework.context.annotation.Profile; import org.springframework.r2dbc.core.DatabaseClient; import org.springframework.stereotype.Component; import benchmark.model.Fortune; import benchmark.model.World; +import io.r2dbc.spi.Connection; +import io.r2dbc.spi.ConnectionFactory; +import io.r2dbc.spi.Result; +import io.r2dbc.spi.Statement; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -23,28 +29,25 @@ public R2dbcDbRepository(DatabaseClient databaseClient) { public Mono getWorld(int id) { return databaseClient .sql("SELECT id, randomnumber FROM world WHERE id = $1") - .bind("$1", id) + .bind(0, id) .mapProperties(World.class) .first(); } - private Mono updateWorld(World world) { - return databaseClient - .sql("UPDATE world SET randomnumber=$2 WHERE id = $1") - .bind("$1", world.id) - .bind("$2", world.randomnumber) - .fetch() - .rowsUpdated() - .map(count -> world); - } - - @Override - public Mono findAndUpdateWorld(int id, int randomNumber) { - return getWorld(id).flatMap(world -> { - world.randomnumber = randomNumber; - return updateWorld(world); - }); + public Mono updateWorlds(List worlds) { + return databaseClient.inConnectionMany(con -> { + Statement statement = con.createStatement("UPDATE world SET randomnumber=$2 WHERE id = $1"); + for (int i = 0; i < worlds.size(); i++) { + World world = worlds.get(i); + statement.bind(0, world.randomnumber) + .bind(1, world.id); + if (i < worlds.size() - 1) { + statement.add(); + } + } + return Flux.from(statement.execute()); + }).flatMap(Result::getRowsUpdated).then(); } @Override diff --git a/frameworks/Java/spring-webflux/src/main/java/benchmark/web/DbHandler.java b/frameworks/Java/spring-webflux/src/main/java/benchmark/web/DbHandler.java index e85d9c5be15..03f90652f69 100644 --- a/frameworks/Java/spring-webflux/src/main/java/benchmark/web/DbHandler.java +++ b/frameworks/Java/spring-webflux/src/main/java/benchmark/web/DbHandler.java @@ -1,5 +1,6 @@ package benchmark.web; +import java.util.Comparator; import java.util.List; import benchmark.Utils; @@ -68,8 +69,16 @@ public Mono updates(ServerRequest request) { int queries = parseQueryCount(request.queryParams().getFirst("queries")); Mono> worlds = Flux.fromStream(Utils.randomWorldNumbers().limit(queries).boxed()) - .flatMap(i -> dbRepository.findAndUpdateWorld(i, Utils.randomWorldNumber())) - .collectList(); + .flatMap(id -> dbRepository.getWorld(id).map(world -> { + int randomNumber; + do { + randomNumber = Utils.randomWorldNumber(); + } while (randomNumber == world.randomnumber); + world.randomnumber = randomNumber; + return world; + })) + .collectSortedList(Comparator.comparingInt(w -> w.id)) + .flatMap(list -> dbRepository.updateWorlds(list).thenReturn(list)); return ServerResponse.ok() .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) diff --git a/frameworks/Java/spring/pom.xml b/frameworks/Java/spring/pom.xml index 6c9341fed54..3271ab04f03 100644 --- a/frameworks/Java/spring/pom.xml +++ b/frameworks/Java/spring/pom.xml @@ -11,7 +11,7 @@ org.springframework.boot spring-boot-starter-parent - 3.3.4 + 3.3.5 diff --git a/frameworks/Java/spring/spring-mongo.dockerfile b/frameworks/Java/spring/spring-mongo.dockerfile index e700a21cabd..cc51f6c844c 100644 --- a/frameworks/Java/spring/spring-mongo.dockerfile +++ b/frameworks/Java/spring/spring-mongo.dockerfile @@ -4,7 +4,7 @@ COPY src src COPY pom.xml pom.xml RUN mvn package -q -FROM bellsoft/liberica-openjre-debian:21 +FROM bellsoft/liberica-openjre-debian:23 WORKDIR /spring COPY --from=maven /spring/target/hello-spring-1.0-SNAPSHOT.jar app.jar # See https://docs.spring.io/spring-boot/reference/packaging/efficient.html diff --git a/frameworks/Java/spring/spring.dockerfile b/frameworks/Java/spring/spring.dockerfile index f52c1f36df1..1f102057b99 100644 --- a/frameworks/Java/spring/spring.dockerfile +++ b/frameworks/Java/spring/spring.dockerfile @@ -4,7 +4,7 @@ COPY src src COPY pom.xml pom.xml RUN mvn package -q -FROM bellsoft/liberica-openjre-debian:21 +FROM bellsoft/liberica-openjre-debian:23 WORKDIR /spring COPY --from=maven /spring/target/hello-spring-1.0-SNAPSHOT.jar app.jar # See https://docs.spring.io/spring-boot/reference/packaging/efficient.html diff --git a/frameworks/JavaScript/express/app.js b/frameworks/JavaScript/express/app.js index c8614691135..9f1039e2179 100755 --- a/frameworks/JavaScript/express/app.js +++ b/frameworks/JavaScript/express/app.js @@ -41,5 +41,6 @@ if (cluster.isPrimary) { app.get('/plaintext', (req, res) => res.header('Content-Type', 'text/plain').send('Hello, World!')); - app.listen(8080); + const server = app.listen(8080); + server.keepAliveTimeout = 0; } diff --git a/frameworks/JavaScript/express/benchmark_config.json b/frameworks/JavaScript/express/benchmark_config.json index 0c8a91678cc..9f04d0dd56e 100644 --- a/frameworks/JavaScript/express/benchmark_config.json +++ b/frameworks/JavaScript/express/benchmark_config.json @@ -17,22 +17,6 @@ "display_name": "express", "versus": "nodejs" }, - "chakra": { - "json_url": "/json", - "plaintext_url": "/plaintext", - "port": 8080, - "approach": "Realistic", - "classification": "Micro", - "framework": "express", - "language": "JavaScript", - "flavor": "node-chakracore", - "platform": "nodejs", - "webserver": "None", - "os": "Linux", - "database_os": "Linux", - "display_name": "express", - "versus": "nodejs" - }, "mongodb": { "db_url": "/mongoose", "query_url": "/mongooseq?queries=", diff --git a/frameworks/JavaScript/express/config.toml b/frameworks/JavaScript/express/config.toml index d5c8a111c67..a18c6244628 100644 --- a/frameworks/JavaScript/express/config.toml +++ b/frameworks/JavaScript/express/config.toml @@ -56,14 +56,3 @@ orm = "Full" platform = "nodejs" webserver = "None" versus = "nodejs" - -[chakra] -urls.plaintext = "/plaintext" -urls.json = "/json" -approach = "Realistic" -classification = "Micro" -database_os = "Linux" -os = "Linux" -platform = "nodejs" -webserver = "None" -versus = "nodejs" diff --git a/frameworks/JavaScript/express/express-chakra.dockerfile b/frameworks/JavaScript/express/express-chakra.dockerfile deleted file mode 100644 index 7d179c4fc6a..00000000000 --- a/frameworks/JavaScript/express/express-chakra.dockerfile +++ /dev/null @@ -1,11 +0,0 @@ -FROM node:chakracore - -COPY ./ ./ - -RUN npm install - -ENV NODE_ENV production - -EXPOSE 8080 - -CMD ["node", "app.js"] diff --git a/frameworks/JavaScript/express/express-mongodb.dockerfile b/frameworks/JavaScript/express/express-mongodb.dockerfile index b9f22ed1162..fdd130d3195 100644 --- a/frameworks/JavaScript/express/express-mongodb.dockerfile +++ b/frameworks/JavaScript/express/express-mongodb.dockerfile @@ -1,4 +1,4 @@ -FROM node:20.12.2-slim +FROM node:20.16-slim COPY ./ ./ diff --git a/frameworks/JavaScript/express/express-mysql.dockerfile b/frameworks/JavaScript/express/express-mysql.dockerfile index c34740fb009..f446c93d2f4 100644 --- a/frameworks/JavaScript/express/express-mysql.dockerfile +++ b/frameworks/JavaScript/express/express-mysql.dockerfile @@ -1,4 +1,4 @@ -FROM node:20.12.2-slim +FROM node:20.16-slim COPY ./ ./ diff --git a/frameworks/JavaScript/express/express-postgres.dockerfile b/frameworks/JavaScript/express/express-postgres.dockerfile index 7f6367401fc..46cf427d0a2 100644 --- a/frameworks/JavaScript/express/express-postgres.dockerfile +++ b/frameworks/JavaScript/express/express-postgres.dockerfile @@ -1,4 +1,4 @@ -FROM node:20.12.2-slim +FROM node:20.16-slim COPY ./ ./ diff --git a/frameworks/JavaScript/express/express-postgresjs.dockerfile b/frameworks/JavaScript/express/express-postgresjs.dockerfile index d9a5e1b35ba..bc570d57faf 100644 --- a/frameworks/JavaScript/express/express-postgresjs.dockerfile +++ b/frameworks/JavaScript/express/express-postgresjs.dockerfile @@ -1,4 +1,4 @@ -FROM node:21.1.0-slim +FROM node:20.16-slim COPY ./ ./ diff --git a/frameworks/JavaScript/express/express.dockerfile b/frameworks/JavaScript/express/express.dockerfile index c78d8648283..ec726dd2800 100644 --- a/frameworks/JavaScript/express/express.dockerfile +++ b/frameworks/JavaScript/express/express.dockerfile @@ -1,4 +1,4 @@ -FROM node:20.12.2-slim +FROM node:20.16-slim COPY ./ ./ diff --git a/frameworks/JavaScript/express/package.json b/frameworks/JavaScript/express/package.json index 09723c74dd8..5655ae91033 100644 --- a/frameworks/JavaScript/express/package.json +++ b/frameworks/JavaScript/express/package.json @@ -7,7 +7,7 @@ "dateformat": "3.0.3", "escape-html": "1.0.3", "express": "4.18.2", - "mongoose": "8.3.2", + "mongoose": "8.8.3", "mysql2": "3.9.8", "pg": "8.5.0", "pg-promise": "10.7.3", diff --git a/frameworks/JavaScript/fastify/README.md b/frameworks/JavaScript/fastify/README.md index 705c282aee8..fe63567a9d5 100644 --- a/frameworks/JavaScript/fastify/README.md +++ b/frameworks/JavaScript/fastify/README.md @@ -19,33 +19,3 @@ Information about Fastify can be found at https://github.com/fastify/fastify ### JSON Encoding Test http://TFB-server:8080/json - -### Data-Store/Database Mapping Test - -MongoDB: -http://TFB-server:8080/mongoose/ - -MySQL: -http://TFB-server:8080/mysql-orm/ - -### Variable Query Test - -MongoDB: -http://TFB-server:8080/mongoose/2 - -MySQL: -http://TFB-server:8080/mysql-orm/2 - -### Fortune Test - -MySQL: -http://TFB-server:8080/fortune - -### Database Update Test - -MongoDB: -http://TFB-server:8080/mongoose-update/2 - -MySQL: -http://TFB-server:8080/mysql-orm-update/2 - diff --git a/frameworks/JavaScript/fastify/benchmark_config.json b/frameworks/JavaScript/fastify/benchmark_config.json index 944ae63a3ed..b4203ea43b9 100644 --- a/frameworks/JavaScript/fastify/benchmark_config.json +++ b/frameworks/JavaScript/fastify/benchmark_config.json @@ -5,14 +5,9 @@ "default": { "json_url": "/json", "plaintext_url": "/plaintext", - "db_url": "/db", - "query_url": "/queries?queries=", - "fortune_url": "/fortunes", - "update_url": "/updates?queries=", "port": 8080, "approach": "Realistic", "classification": "Micro", - "database": "MongoDB", "framework": "fastify", "language": "JavaScript", "flavor": "NodeJS", @@ -42,7 +37,7 @@ "webserver": "None", "os": "Linux", "database_os": "Linux", - "display_name": "fastify", + "display_name": "fastify [mysql]", "notes": "", "versus": "nodejs" }, @@ -63,7 +58,7 @@ "webserver": "None", "os": "Linux", "database_os": "Linux", - "display_name": "fastify", + "display_name": "fastify [postgres]", "notes": "", "versus": "nodejs" } diff --git a/frameworks/JavaScript/fastify/config.toml b/frameworks/JavaScript/fastify/config.toml index c512a025989..d47b5822fa4 100644 --- a/frameworks/JavaScript/fastify/config.toml +++ b/frameworks/JavaScript/fastify/config.toml @@ -4,13 +4,8 @@ name = "fastify" [main] urls.plaintext = "/plaintext" urls.json = "/json" -urls.db = "/db" -urls.query = "/queries?queries=" -urls.update = "/updates?queries=" -urls.fortune = "/fortunes" approach = "Realistic" classification = "Micro" -database = "MongoDB" database_os = "Linux" os = "Linux" orm = "Raw" diff --git a/frameworks/JavaScript/fastify/create-server.js b/frameworks/JavaScript/fastify/create-server.js index 5680ef31f04..a20ba97e6e8 100644 --- a/frameworks/JavaScript/fastify/create-server.js +++ b/frameworks/JavaScript/fastify/create-server.js @@ -1,6 +1,11 @@ -const fastify = require("fastify")(); +const fastify = require("fastify")({ logger: false, keepAliveTimeout: 0 }); const handlers = require("./handlers"); +fastify.setErrorHandler((error, request, reply) => { + console.log(error) + reply.status(500).send({ ok: false }) +}) + fastify.register(require("@fastify/view"), { engine: { handlebars: require("handlebars") diff --git a/frameworks/JavaScript/fastify/db/mongo.js b/frameworks/JavaScript/fastify/db/mongo.js deleted file mode 100644 index 666dd49d412..00000000000 --- a/frameworks/JavaScript/fastify/db/mongo.js +++ /dev/null @@ -1,47 +0,0 @@ -const { MongoClient } = require("mongodb"); - -const mongoUrl = "mongodb://tfb-database:27017"; -const dbName = "hello_world"; - -let client; - -async function getCollection(name) { - if (!client) { - client = await new MongoClient(mongoUrl).connect(); - } - - const db = client.db(dbName); - - return db.collection(name); -} - -async function allFortunes() { - const collection = await getCollection("fortune"); - const fortunes = await collection.find({}, { projection: { _id: 0 } }); - - return fortunes.toArray(); -} - -async function getWorld(id) { - const collection = await getCollection("world"); - - return collection.findOne({ id }, { projection: { _id: 0 } }); -} - -async function saveWorlds(worlds) { - const collection = await getCollection("world"); - - const bulk = collection.initializeUnorderedBulkOp(); - - worlds.forEach(world => { - bulk.find({ id: world.id }).updateOne(world); - }); - - return bulk.execute(); -} - -module.exports = { - getWorld, - saveWorlds, - allFortunes -}; diff --git a/frameworks/JavaScript/fastify/db/mysql.js b/frameworks/JavaScript/fastify/db/mysql.js index 3887cc20dd1..ed2c26b44a4 100644 --- a/frameworks/JavaScript/fastify/db/mysql.js +++ b/frameworks/JavaScript/fastify/db/mysql.js @@ -1,41 +1,35 @@ -const knex = require("knex")({ - client: "mysql2", - connection: { - host: "tfb-database", - user: "benchmarkdbuser", - password: "benchmarkdbpass", - database: "hello_world" - } -}); +const { createPool } = require("mariadb"); + +const clientOpts = { + host: process.env.MYSQL_HOST, + user: process.env.MYSQL_USER, + password: process.env.MYSQL_PSWD, + database: process.env.MYSQL_DBNAME, +}; + +const pool = createPool({ ...clientOpts, connectionLimit: 1 }); +const execute = (text, values) => pool.execute(text, values || undefined); async function allFortunes() { - return knex("Fortune").select("*"); + return execute("select id, message from fortune", []); } async function getWorld(id) { - return knex("World") - .first() - .where({ id }); + return execute("select id, randomNumber from world where id = ?", [id]).then( + (arr) => arr[0] + ); } -async function saveWorlds(worlds) { - const updates = []; - - worlds.forEach(world => { - const { id, randomNumber } = world; - - updates.push( - knex("World") - .update({ randomNumber }) - .where({ id }) - ); - }); - - return Promise.all(updates); +async function bulkUpdate(worlds) { + const sql = "update world set randomNumber = ? where id = ?"; + const values = worlds + .map((world) => [world.randomnumber, world.id]) + .sort((a, b) => (a[0] < b[0] ? -1 : 1)); + return pool.batch(sql, values); } module.exports = { getWorld, - saveWorlds, - allFortunes + bulkUpdate, + allFortunes, }; diff --git a/frameworks/JavaScript/fastify/db/postgres.js b/frameworks/JavaScript/fastify/db/postgres.js index 1d5820eeeac..45f62c5eee4 100644 --- a/frameworks/JavaScript/fastify/db/postgres.js +++ b/frameworks/JavaScript/fastify/db/postgres.js @@ -1,41 +1,38 @@ -const knex = require("knex")({ - client: "pg", - connection: { - host: "tfb-database", - user: "benchmarkdbuser", - password: "benchmarkdbpass", - database: "hello_world" - } -}); +const postgres = require("postgres"); + +const clientOpts = { + host: process.env.PG_HOST, + user: process.env.PG_USER, + password: process.env.PG_PSWD, + database: process.env.PG_DBNAME, +}; + +const sql = postgres({ ...clientOpts, max: 1 }); async function allFortunes() { - return knex("Fortune").select("*"); + return sql`select id, message from fortune`; } async function getWorld(id) { - return knex("World") - .first() - .where({ id }); + return sql`select id, randomNumber from world where id = ${id}`.then( + (arr) => arr[0] + ); } -async function saveWorlds(worlds) { - const updates = []; - - worlds.forEach(world => { - const { id, randomNumber } = world; - - updates.push( - knex("World") - .update({ randomnumber: randomNumber }) - .where({ id }) - ); - }); +async function bulkUpdate(worlds) { + const values = sql( + worlds + .map((world) => [world.id, world.randomnumber]) + .sort((a, b) => (a[0] < b[0] ? -1 : 1)) + ); - return Promise.all(updates); + return sql`update world set randomNumber = (update_data.randomNumber)::int + from (values ${values}) as update_data (id, randomNumber) + where world.id = (update_data.id)::int`; } module.exports = { getWorld, - saveWorlds, - allFortunes + bulkUpdate, + allFortunes, }; diff --git a/frameworks/JavaScript/fastify/fastify-mysql.dockerfile b/frameworks/JavaScript/fastify/fastify-mysql.dockerfile index a77917aceb0..8da0cc484d0 100644 --- a/frameworks/JavaScript/fastify/fastify-mysql.dockerfile +++ b/frameworks/JavaScript/fastify/fastify-mysql.dockerfile @@ -1,4 +1,4 @@ -FROM node:20.12.2-alpine +FROM node:20.16-slim COPY ./ ./ @@ -6,6 +6,10 @@ RUN npm install ENV NODE_ENV production ENV DATABASE mysql +ENV MYSQL_HOST tfb-database +ENV MYSQL_USER benchmarkdbuser +ENV MYSQL_PSWD benchmarkdbpass +ENV MYSQL_DBNAME hello_world EXPOSE 8080 diff --git a/frameworks/JavaScript/fastify/fastify-postgres.dockerfile b/frameworks/JavaScript/fastify/fastify-postgres.dockerfile index 069b66c250a..e5b63795e13 100644 --- a/frameworks/JavaScript/fastify/fastify-postgres.dockerfile +++ b/frameworks/JavaScript/fastify/fastify-postgres.dockerfile @@ -1,4 +1,4 @@ -FROM node:20.12.2-alpine +FROM node:20.16-slim COPY ./ ./ @@ -6,6 +6,10 @@ RUN npm install ENV NODE_ENV production ENV DATABASE postgres +ENV PG_HOST tfb-database +ENV PG_USER benchmarkdbuser +ENV PG_PSWD benchmarkdbpass +ENV PG_DBNAME hello_world EXPOSE 8080 diff --git a/frameworks/JavaScript/fastify/fastify.dockerfile b/frameworks/JavaScript/fastify/fastify.dockerfile index 48a7611a848..ec726dd2800 100644 --- a/frameworks/JavaScript/fastify/fastify.dockerfile +++ b/frameworks/JavaScript/fastify/fastify.dockerfile @@ -1,11 +1,10 @@ -FROM node:20.12.2-alpine +FROM node:20.16-slim COPY ./ ./ RUN npm install ENV NODE_ENV production -ENV DATABASE mongo EXPOSE 8080 diff --git a/frameworks/JavaScript/fastify/handlers.js b/frameworks/JavaScript/fastify/handlers.js index df2d37fc378..5032b4b829d 100644 --- a/frameworks/JavaScript/fastify/handlers.js +++ b/frameworks/JavaScript/fastify/handlers.js @@ -4,7 +4,7 @@ const h = require("./helper"); * @param databaseLayer * @returns {{singleQuery: function(*), multipleQueries: function(*), fortunes: function(*), updates: function(*)}} */ -module.exports = databaseLayer => ({ +module.exports = (databaseLayer) => ({ singleQuery: async (req, reply) => { const world = await databaseLayer.getWorld(h.randomTfbNumber()); @@ -34,29 +34,29 @@ module.exports = databaseLayer => ({ }, updates: async (req, reply) => { - const queries = h.getQueries(req.query.queries); + const num = h.getQueries(req.query.queries); const worldPromises = []; - for (let i = 0; i < queries; i++) { - worldPromises.push(databaseLayer.getWorld(h.randomTfbNumber())); + for (let i = 0; i < num; i++) { + const id = h.randomTfbNumber(); + worldPromises.push(databaseLayer.getWorld(id)); } const worlds = await Promise.all(worldPromises); - const worldsToUpdate = worlds.map(world => { - world.randomNumber = h.randomTfbNumber(); + const worldsToUpdate = worlds.map((world) => { + world.randomnumber = h.randomTfbNumber(); return world; }); - await databaseLayer.saveWorlds(worldsToUpdate); - + await databaseLayer.bulkUpdate(worldsToUpdate); reply.send(worldsToUpdate); - } + }, }); // faster than localeCompare function compare(a, b) { - if(a.message < b.message){ + if (a.message < b.message) { return -1; } else if (a.message > b.message) { return 1; diff --git a/frameworks/JavaScript/fastify/helper.js b/frameworks/JavaScript/fastify/helper.js index 8ed4447dbc4..6369564b902 100644 --- a/frameworks/JavaScript/fastify/helper.js +++ b/frameworks/JavaScript/fastify/helper.js @@ -1,12 +1,12 @@ module.exports = { randomTfbNumber: () => Math.floor(Math.random() * 10000) + 1, - getQueries: queries => { + getQueries: (queries) => { return Math.min(Math.max(parseInt(queries) || 1, 1), 500); }, additionalFortune: { id: 0, - message: "Additional fortune added at request time." - } + message: "Additional fortune added at request time.", + }, }; diff --git a/frameworks/JavaScript/fastify/package.json b/frameworks/JavaScript/fastify/package.json index c1a261a4328..4de1056bfe7 100644 --- a/frameworks/JavaScript/fastify/package.json +++ b/frameworks/JavaScript/fastify/package.json @@ -5,12 +5,10 @@ "main": "app.js", "private": true, "dependencies": { - "@fastify/view": "7.4.1", - "fastify": "4.13.0", - "handlebars": "4.7.6", - "knex": "2.4.2", - "mongodb": "6.5.0", - "mysql2": "3.9.7", - "pg": "8.5.1" + "@fastify/view": "^10.0.1", + "fastify": "^5.1.0", + "handlebars": "4.7.8", + "mariadb": "^3.4.0", + "postgres": "^3.4.5" } } diff --git a/frameworks/JavaScript/hapi/create-server.js b/frameworks/JavaScript/hapi/create-server.js index 6286264fd2e..db3c86b3f19 100644 --- a/frameworks/JavaScript/hapi/create-server.js +++ b/frameworks/JavaScript/hapi/create-server.js @@ -13,6 +13,7 @@ const options = { }; const server = new Hapi.server(options); +server.listener.keepAliveTimeout = 0; const provision = async () => { diff --git a/frameworks/JavaScript/hapi/hapi-mysql.dockerfile b/frameworks/JavaScript/hapi/hapi-mysql.dockerfile index 695bacf9e1e..f902c839a26 100644 --- a/frameworks/JavaScript/hapi/hapi-mysql.dockerfile +++ b/frameworks/JavaScript/hapi/hapi-mysql.dockerfile @@ -1,4 +1,4 @@ -FROM node:18.12.1-slim +FROM node:20.16-slim COPY ./ ./ diff --git a/frameworks/JavaScript/hapi/hapi-postgres.dockerfile b/frameworks/JavaScript/hapi/hapi-postgres.dockerfile index ee4e9251ee8..e9c8fb0e9a9 100644 --- a/frameworks/JavaScript/hapi/hapi-postgres.dockerfile +++ b/frameworks/JavaScript/hapi/hapi-postgres.dockerfile @@ -1,4 +1,4 @@ -FROM node:18.12.1-slim +FROM node:20.16-slim COPY ./ ./ diff --git a/frameworks/JavaScript/hapi/hapi.dockerfile b/frameworks/JavaScript/hapi/hapi.dockerfile index 23b09a88995..233cd3c53b0 100644 --- a/frameworks/JavaScript/hapi/hapi.dockerfile +++ b/frameworks/JavaScript/hapi/hapi.dockerfile @@ -1,4 +1,4 @@ -FROM node:18.12.1-slim +FROM node:20.16-slim COPY ./ ./ diff --git a/frameworks/JavaScript/hapi/package.json b/frameworks/JavaScript/hapi/package.json index 6a6c23a897b..d38a23b49c9 100644 --- a/frameworks/JavaScript/hapi/package.json +++ b/frameworks/JavaScript/hapi/package.json @@ -8,7 +8,7 @@ "async": "2.1.5", "bluebird": "3.4.7", "handlebars": "4.3.0", - "mongoose": "5.13.20", + "mongoose": "8.8.3", "mysql": "2.16.0", "mysql2": "3.9.8", "pg": "8.5.1", diff --git a/frameworks/JavaScript/hyperexpress/package-lock.json b/frameworks/JavaScript/hyperexpress/package-lock.json index cbaea996902..7e4af63b97f 100644 --- a/frameworks/JavaScript/hyperexpress/package-lock.json +++ b/frameworks/JavaScript/hyperexpress/package-lock.json @@ -9,120 +9,17 @@ "version": "0.0.1", "license": "MIT", "dependencies": { - "hyper-express": "^6.8.5", + "hyper-express": "^6.17.3", "lru-cache": "^10.0.1", "mariadb": "^3.2.0", "postgres": "^3.3.5" } }, - "node_modules/@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/busboy": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@types/busboy/-/busboy-0.3.2.tgz", - "integrity": "sha512-iEvdm9Z9KdSs/ozuh1Z7ZsXrOl8F4M/CLMXPZHr3QuJ4d6Bjn+HBMC5EMKpwpAo8oi8iK9GZfFoHaIMrrZgwVw==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/express": { - "version": "4.17.17", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", - "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.17.35", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz", - "integrity": "sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, "node_modules/@types/geojson": { "version": "7946.0.10", "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz", "integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==" }, - "node_modules/@types/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ==" - }, - "node_modules/@types/mime": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" - }, - "node_modules/@types/node": { - "version": "16.18.38", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.38.tgz", - "integrity": "sha512-6sfo1qTulpVbkxECP+AVrHV9OoJqhzCsfTNp5NIG+enM4HyM3HvZCO798WShIXBN0+QtDIcutJCjsVYnQP5rIQ==" - }, - "node_modules/@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" - }, - "node_modules/@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" - }, - "node_modules/@types/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz", - "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==", - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.2.tgz", - "integrity": "sha512-J2LqtvFYCzaj8pVYKw8klQXrLLk7TBZmQ4ShlcdkELFKGwGMfevMLneMMRkMgZxotOD9wg497LpC7O8PcvAmfw==", - "dependencies": { - "@types/http-errors": "*", - "@types/mime": "*", - "@types/node": "*" - } - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -135,11 +32,11 @@ } }, "node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", "engines": { - "node": ">= 0.6" + "node": ">=18" } }, "node_modules/cookie-signature": { @@ -159,22 +56,19 @@ } }, "node_modules/hyper-express": { - "version": "6.8.5", - "resolved": "https://registry.npmjs.org/hyper-express/-/hyper-express-6.8.5.tgz", - "integrity": "sha512-tlHCGg0xKRHRheqY3Fgb8FvuvluCqIf6tJXiiyadsQh108g/givMqKt0XBYV+Rw0vstmmROuJ3cHXiUkQoddgg==", + "version": "6.17.3", + "resolved": "https://registry.npmjs.org/hyper-express/-/hyper-express-6.17.3.tgz", + "integrity": "sha512-fNFrRVEAIqjADtAq2F5wFRBlpo3G63QgK9PsNklsKLpM2S3fNlFaojafv926mlEW+C4zZ1VI8Tm5PCJOaIWJMQ==", "dependencies": { - "@types/busboy": "^0.3.1", - "@types/express": "^4.17.13", - "@types/node": "^16.11.6", - "accepts": "^1.3.7", - "busboy": "^1.0.0", - "cookie": "^0.4.1", - "cookie-signature": "^1.1.0", - "mime-types": "^2.1.33", + "busboy": "^1.6.0", + "cookie": "^1.0.1", + "cookie-signature": "^1.2.1", + "mime-types": "^2.1.35", + "negotiator": "^1.0.0", "range-parser": "^1.2.1", "type-is": "^1.6.18", "typed-emitter": "^2.1.0", - "uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.31.0" + "uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.51.0" } }, "node_modules/iconv-lite": { @@ -252,9 +146,9 @@ } }, "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "engines": { "node": ">= 0.6" } @@ -330,111 +224,11 @@ } }, "dependencies": { - "@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", - "requires": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "@types/busboy": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@types/busboy/-/busboy-0.3.2.tgz", - "integrity": "sha512-iEvdm9Z9KdSs/ozuh1Z7ZsXrOl8F4M/CLMXPZHr3QuJ4d6Bjn+HBMC5EMKpwpAo8oi8iK9GZfFoHaIMrrZgwVw==", - "requires": { - "@types/node": "*" - } - }, - "@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "requires": { - "@types/node": "*" - } - }, - "@types/express": { - "version": "4.17.17", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", - "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", - "requires": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "@types/express-serve-static-core": { - "version": "4.17.35", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz", - "integrity": "sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==", - "requires": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, "@types/geojson": { "version": "7946.0.10", "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz", "integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==" }, - "@types/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ==" - }, - "@types/mime": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" - }, - "@types/node": { - "version": "16.18.38", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.38.tgz", - "integrity": "sha512-6sfo1qTulpVbkxECP+AVrHV9OoJqhzCsfTNp5NIG+enM4HyM3HvZCO798WShIXBN0+QtDIcutJCjsVYnQP5rIQ==" - }, - "@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" - }, - "@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" - }, - "@types/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz", - "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==", - "requires": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "@types/serve-static": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.2.tgz", - "integrity": "sha512-J2LqtvFYCzaj8pVYKw8klQXrLLk7TBZmQ4ShlcdkELFKGwGMfevMLneMMRkMgZxotOD9wg497LpC7O8PcvAmfw==", - "requires": { - "@types/http-errors": "*", - "@types/mime": "*", - "@types/node": "*" - } - }, - "accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "requires": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - } - }, "busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -444,9 +238,9 @@ } }, "cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==" }, "cookie-signature": { "version": "1.2.1", @@ -459,22 +253,19 @@ "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==" }, "hyper-express": { - "version": "6.8.5", - "resolved": "https://registry.npmjs.org/hyper-express/-/hyper-express-6.8.5.tgz", - "integrity": "sha512-tlHCGg0xKRHRheqY3Fgb8FvuvluCqIf6tJXiiyadsQh108g/givMqKt0XBYV+Rw0vstmmROuJ3cHXiUkQoddgg==", + "version": "6.17.3", + "resolved": "https://registry.npmjs.org/hyper-express/-/hyper-express-6.17.3.tgz", + "integrity": "sha512-fNFrRVEAIqjADtAq2F5wFRBlpo3G63QgK9PsNklsKLpM2S3fNlFaojafv926mlEW+C4zZ1VI8Tm5PCJOaIWJMQ==", "requires": { - "@types/busboy": "^0.3.1", - "@types/express": "^4.17.13", - "@types/node": "^16.11.6", - "accepts": "^1.3.7", - "busboy": "^1.0.0", - "cookie": "^0.4.1", - "cookie-signature": "^1.1.0", - "mime-types": "^2.1.33", + "busboy": "^1.6.0", + "cookie": "^1.0.1", + "cookie-signature": "^1.2.1", + "mime-types": "^2.1.35", + "negotiator": "^1.0.0", "range-parser": "^1.2.1", "type-is": "^1.6.18", "typed-emitter": "^2.1.0", - "uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.31.0" + "uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.51.0" } }, "iconv-lite": { @@ -533,9 +324,9 @@ } }, "negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==" }, "postgres": { "version": "3.3.5", @@ -591,7 +382,7 @@ }, "uWebSockets.js": { "version": "git+ssh://git@github.com/uNetworking/uWebSockets.js.git#e6ecc2102d68d99dc35969b0898fbd201e0f252b", - "from": "uWebSockets.js@github:uNetworking/uWebSockets.js#v20.31.0" + "from": "uWebSockets.js@github:uNetworking/uWebSockets.js#v20.51.0" } } } diff --git a/frameworks/JavaScript/hyperexpress/package.json b/frameworks/JavaScript/hyperexpress/package.json index ba3cacbd893..a2500fe07a5 100644 --- a/frameworks/JavaScript/hyperexpress/package.json +++ b/frameworks/JavaScript/hyperexpress/package.json @@ -8,7 +8,7 @@ "author": "", "license": "MIT", "dependencies": { - "hyper-express": "^6.8.5", + "hyper-express": "^6.17.3", "lru-cache": "^10.0.1", "mariadb": "^3.2.0", "postgres": "^3.3.5" diff --git a/frameworks/JavaScript/ultimate-express/README.md b/frameworks/JavaScript/ultimate-express/README.md new file mode 100644 index 00000000000..5580806833d --- /dev/null +++ b/frameworks/JavaScript/ultimate-express/README.md @@ -0,0 +1,60 @@ +# UltimateExpress Benchmarking Test + +The Ultimate Express. Fastest http server with full Express compatibility, based on [µWebSockets](https://github.com/uNetworking/uWebSockets.js). + +## Important Libraries + +The tests were run with: + +- [ultimate-express](https://github.com/dimdenGD/ultimate-express) +- [postgres](https://github.com/porsager/postgres) +- [mariadb](https://github.com/mariadb-corporation/mariadb-connector-nodejs) +- [lru-cache](https://github.com/isaacs/node-lru-cache) + +## Database + +There are individual handlers for each DB approach. The logic for each of them are found here: + +- [Postgres](database/postgres.js) +- [MySQL](database/mysql.js) + +There are **no database endpoints** or drivers attached by default. + +To initialize the application with one of these, run any _one_ of the following commands: + +```sh +$ DATABASE=postgres npm start +$ DATABASE=mysql npm start +``` + +## Test Endpoints + +> Visit the test requirements [here](https://github.com/TechEmpower/FrameworkBenchmarks/wiki/Project-Information-Framework-Tests-Overview) + +```sh +$ curl localhost:8080/json +$ curl localhost:8080/plaintext + +# The following are only available with the DATABASE env var + +$ curl localhost:8080/db +$ curl localhost:8080/fortunes + +$ curl localhost:8080/queries?queries=2 +$ curl localhost:8080/queries?queries=0 +$ curl localhost:8080/queries?queries=foo +$ curl localhost:8080/queries?queries=501 +$ curl localhost:8080/queries?queries= + +$ curl localhost:8080/updates?queries=2 +$ curl localhost:8080/updates?queries=0 +$ curl localhost:8080/updates?queries=foo +$ curl localhost:8080/updates?queries=501 +$ curl localhost:8080/updates?queries= + +$ curl localhost:8080/cached-worlds?count=2 +$ curl localhost:8080/cached-worlds?count=0 +$ curl localhost:8080/cached-worlds?count=foo +$ curl localhost:8080/cached-worlds?count=501 +$ curl localhost:8080/cached-worlds?count= +``` diff --git a/frameworks/JavaScript/ultimate-express/app.js b/frameworks/JavaScript/ultimate-express/app.js new file mode 100755 index 00000000000..1914abf8d17 --- /dev/null +++ b/frameworks/JavaScript/ultimate-express/app.js @@ -0,0 +1,157 @@ +import express from 'ultimate-express'; +import { LRUCache } from 'lru-cache'; +import cluster, { isWorker } from 'node:cluster'; +import { maxQuery, maxRows } from './config.js'; +import fjs from 'fast-json-stringify'; + +const { DATABASE } = process.env; +const db = DATABASE ? await import(`./database/${DATABASE}.js`) : null; + +const jsonSerializer = fjs({ + type: 'object', + properties: { + message: { + type: 'string', + format: 'unsafe', + } + } +}); + +const generateRandomNumber = () => Math.floor(Math.random() * maxRows) + 1; + +const parseQueries = (i) => Math.min(parseInt(i) || 1, maxQuery); + +const escapeHTMLRules = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', '/': '/' }; + +const unsafeHTMLMatcher = /[&<>"'\/]/g; + +const escapeHTMLCode = (text) => unsafeHTMLMatcher.test(text) ? text.replace(unsafeHTMLMatcher, function (m) { return escapeHTMLRules[m] || m; }) : text; + +const cache = new LRUCache({ + max: maxRows +}); + +const app = express(); +app.set("etag", false); +app.set("x-powered-by", false); + +app.get('/plaintext', (req, res) => { + res.setHeader('Server', 'UltimateExpress'); + res.setHeader('Content-Type', 'text/plain'); + res.end('Hello, World!'); +}); + +app.get('/json', (req, res) => { + res.setHeader('Server', 'UltimateExpress'); + res.setHeader('Content-Type', 'application/json'); + res.end(jsonSerializer({ message: "Hello, World!" })); +}); + +if (db) { + app.get('/db', async (req, res) => { + res.setHeader('Server', 'UltimateExpress'); + + try { + const world = await db.find(generateRandomNumber()); + res.json(world); + } catch (error) { + throw error; + } + }); + + app.get('/queries', async (req, res) => { + res.setHeader('Server', 'UltimateExpress'); + + try { + const queries = parseQueries(req.query.queries); + const worldPromises = new Array(queries); + + for (let i = 0; i < queries; i++) { + worldPromises[i] = db.find(generateRandomNumber()); + } + + const worlds = await Promise.all(worldPromises); + + res.json(worlds); + } catch (error) { + throw error; + } + }) + + app.get('/updates', async (req, res) => { + res.setHeader('Server', 'UltimateExpress'); + + try { + const queries = parseQueries(req.query.queries); + const worldPromises = new Array(queries); + + for (let i = 0; i < queries; i++) { + worldPromises[i] = db.find(generateRandomNumber()); + } + + const worlds = await Promise.all(worldPromises); + + for (let i = 0; i < queries; i++) { + worlds[i].randomNumber = generateRandomNumber(); + } + + await db.bulkUpdate(worlds); + + res.json(worlds); + } catch (error) { + throw error; + } + }) + + app.get('/fortunes', async (req, res) => { + res.setHeader('Server', 'UltimateExpress'); + + try { + const fortunes = await db.fortunes() + + fortunes.push({ id: 0, message: 'Additional fortune added at request time.' }); + + fortunes.sort((a, b) => (a.message < b.message) ? -1 : 1); + + const n = fortunes.length + + let i = 0, html = '' + for (; i < n; i++) html += `${fortunes[i].id}${escapeHTMLCode(fortunes[i].message)}` + + res + .header('Content-Type', 'text/html; charset=utf-8') + .end(`Fortunes${html}
idmessage
`); + } catch (error) { + throw error; + } + }) + + let isCachePopulated = false + app.get('/cached-worlds', async (req, res) => { + res.setHeader('Server', 'UltimateExpress'); + + try { + if (!isCachePopulated) { + const worlds = await db.getAllWorlds(); + for (let i = 0; i < worlds.length; i++) { + cache.set(worlds[i].id, worlds[i]); + } + isCachePopulated = true; + } + const count = parseQueries(req.query.count); + const worlds = new Array(count); + + for (let i = 0; i < count; i++) { + worlds[i] = cache.get(generateRandomNumber()); + } + + res.json(worlds); + } catch (error) { + throw error; + } + }); +} + +app.listen(8080, () => { + console.log(`${isWorker ? `${cluster.worker.id}: ` : ''}Successfully bound to http://0.0.0.0:8080`); +}); \ No newline at end of file diff --git a/frameworks/JavaScript/ultimate-express/benchmark_config.json b/frameworks/JavaScript/ultimate-express/benchmark_config.json new file mode 100644 index 00000000000..c477aa24007 --- /dev/null +++ b/frameworks/JavaScript/ultimate-express/benchmark_config.json @@ -0,0 +1,70 @@ +{ + "framework": "ultimate-express", + "tests": [ + { + "default": { + "json_url": "/json", + "plaintext_url": "/plaintext", + "port": 8080, + "approach": "Realistic", + "classification": "Micro", + "database": "None", + "framework": "ultimate-express", + "language": "JavaScript", + "flavor": "None", + "orm": "Raw", + "platform": "NodeJS", + "webserver": "µws", + "os": "Linux", + "database_os": "Linux", + "display_name": "ultimate-express", + "notes": "", + "versus": "nodejs" + }, + "postgres": { + "db_url": "/db", + "fortune_url": "/fortunes", + "query_url": "/queries?queries=", + "update_url": "/updates?queries=", + "cached_query_url": "/cached-worlds?count=", + "port": 8080, + "approach": "Realistic", + "classification": "Micro", + "database": "Postgres", + "framework": "ultimate-express", + "language": "JavaScript", + "flavor": "None", + "orm": "Raw", + "platform": "NodeJS", + "webserver": "µws", + "os": "Linux", + "database_os": "Linux", + "display_name": "ultimate-express-postgres", + "notes": "", + "versus": "nodejs" + }, + "mysql": { + "db_url": "/db", + "fortune_url": "/fortunes", + "query_url": "/queries?queries=", + "update_url": "/updates?queries=", + "cached_query_url": "/cached-worlds?count=", + "port": 8080, + "approach": "Realistic", + "classification": "Micro", + "database": "MySQL", + "framework": "ultimate-express", + "language": "JavaScript", + "flavor": "None", + "orm": "Raw", + "platform": "NodeJS", + "webserver": "µws", + "os": "Linux", + "database_os": "Linux", + "display_name": "ultimate-express-mysql", + "notes": "", + "versus": "nodejs" + } + } + ] +} diff --git a/frameworks/JavaScript/ultimate-express/clustered.js b/frameworks/JavaScript/ultimate-express/clustered.js new file mode 100644 index 00000000000..6bf96c8a57f --- /dev/null +++ b/frameworks/JavaScript/ultimate-express/clustered.js @@ -0,0 +1,15 @@ +import cluster, { isPrimary, setupPrimary, fork } from 'node:cluster' +import { cpus } from 'node:os' + +if (isPrimary) { + setupPrimary({ + exec: 'app.js', + }) + cluster.on('exit', (worker) => { + console.log(`worker ${worker.process.pid} died`) + process.exit(1) + }) + for (let i = 0; i < cpus().length; i++) { + fork() + } +} \ No newline at end of file diff --git a/frameworks/JavaScript/ultimate-express/config.js b/frameworks/JavaScript/ultimate-express/config.js new file mode 100644 index 00000000000..b85d510b405 --- /dev/null +++ b/frameworks/JavaScript/ultimate-express/config.js @@ -0,0 +1,8 @@ +export const maxQuery = 500 +export const maxRows = 10000 +export const clientOpts = { + host: 'tfb-database', + user: 'benchmarkdbuser', + password: 'benchmarkdbpass', + database: 'hello_world', +} \ No newline at end of file diff --git a/frameworks/JavaScript/ultimate-express/database/mysql.js b/frameworks/JavaScript/ultimate-express/database/mysql.js new file mode 100644 index 00000000000..5e7949997e7 --- /dev/null +++ b/frameworks/JavaScript/ultimate-express/database/mysql.js @@ -0,0 +1,15 @@ +import { createPool } from 'mariadb' +import { cpus } from 'node:os' +import { clientOpts } from '../config.js' + +const pool = createPool({ ...clientOpts, connectionLimit: cpus().length }) + +const execute = (text, values) => pool.execute(text, values || undefined) + +export const fortunes = () => execute('SELECT id, message FROM fortune') + +export const find = (id) => execute('SELECT id, randomNumber FROM world WHERE id = ?', [id]).then(arr => arr[0]) + +export const getAllWorlds = () => execute('SELECT id, randomNumber FROM world') + +export const bulkUpdate = (worlds) => pool.batch('UPDATE world SET randomNumber = ? WHERE id = ?', worlds.map(world => [world.randomNumber, world.id]).sort((a, b) => (a[1] < b[1]) ? -1 : 1)) \ No newline at end of file diff --git a/frameworks/JavaScript/ultimate-express/database/postgres.js b/frameworks/JavaScript/ultimate-express/database/postgres.js new file mode 100644 index 00000000000..5bfad8e13ad --- /dev/null +++ b/frameworks/JavaScript/ultimate-express/database/postgres.js @@ -0,0 +1,14 @@ +import postgres from 'postgres' +import { clientOpts } from '../config.js' + +const sql = postgres({ ...clientOpts, max: 1 }) + +export const fortunes = async () => sql`SELECT id, message FROM fortune` + +export const find = async (id) => sql`SELECT id, randomNumber FROM world WHERE id = ${id}`.then((arr) => arr[0]) + +export const getAllWorlds = async () => sql`SELECT id, randomNumber FROM world` + +export const bulkUpdate = async (worlds) => await sql`UPDATE world SET randomNumber = (update_data.randomNumber)::int + FROM (VALUES ${sql(worlds.map(world => [world.id, world.randomNumber]).sort((a, b) => (a[0] < b[0]) ? -1 : 1))}) AS update_data (id, randomNumber) + WHERE world.id = (update_data.id)::int`; \ No newline at end of file diff --git a/frameworks/JavaScript/ultimate-express/package-lock.json b/frameworks/JavaScript/ultimate-express/package-lock.json new file mode 100644 index 00000000000..f8bd80e2dd1 --- /dev/null +++ b/frameworks/JavaScript/ultimate-express/package-lock.json @@ -0,0 +1,766 @@ +{ + "name": "ultimate-express", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "ultimate-express", + "version": "0.0.1", + "license": "MIT", + "dependencies": { + "fast-json-stringify": "^6.0.0", + "lru-cache": "^10.0.1", + "mariadb": "^3.2.0", + "postgres": "^3.3.5", + "ultimate-express": "^1.3.9" + } + }, + "node_modules/@fastify/merge-json-schemas": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.1.1.tgz", + "integrity": "sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/geojson": { + "version": "7946.0.14", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", + "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==", + "license": "MIT" + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "license": "MIT" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", + "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.8" + } + }, + "node_modules/@types/qs": { + "version": "6.9.17", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.17.tgz", + "integrity": "sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==", + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv/node_modules/fast-uri": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", + "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", + "license": "BSD-3-Clause" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/cookie": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.1.tgz", + "integrity": "sha512-Xd8lFX4LM9QEEwxQpF9J9NTUh8pmdJO0cyRJhFiDoLTk2eH8FXlRv2IFGYVadZpqI3j8fhNrSdKCeYPxiAhLXw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fast-decode-uri-component": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", + "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-json-stringify": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-6.0.0.tgz", + "integrity": "sha512-FGMKZwniMTgZh7zQp9b6XnBVxUmKVahQLQeRQHqwYmPDqDhcEKZ3BaQsxelFFI5PY7nN71OEeiL47/zUWcYe1A==", + "license": "MIT", + "dependencies": { + "@fastify/merge-json-schemas": "^0.1.1", + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^2.3.0", + "json-schema-ref-resolver": "^1.0.1", + "rfdc": "^1.2.0" + } + }, + "node_modules/fast-querystring": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz", + "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==", + "license": "MIT", + "dependencies": { + "fast-decode-uri-component": "^1.0.1" + } + }, + "node_modules/fast-uri": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.4.0.tgz", + "integrity": "sha512-ypuAmmMKInk5q7XcepxlnUWDLWv4GFtaJqAzWKqn62IpQ3pejtr5dTVbt3vwqVaMKmkNR55sTT+CqUKIaT21BA==", + "license": "MIT" + }, + "node_modules/fast-zlib": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-zlib/-/fast-zlib-2.0.1.tgz", + "integrity": "sha512-DCoYgNagM2Bt1VIpXpdGnRx4LzqJeYG0oh6Nf/7cWo6elTXkFGMw9CrRCYYUIapYNrozYMoyDRflx9mgT3Awyw==", + "license": "MIT", + "funding": { + "type": "patreon", + "url": "https://patreon.com/timotejroiko" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/json-schema-ref-resolver": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-1.0.1.tgz", + "integrity": "sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/mariadb": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/mariadb/-/mariadb-3.4.0.tgz", + "integrity": "sha512-hdRPcAzs+MTxK5VG1thBW18gGTlw6yWBe9YnLB65GLo7q0fO5DWsgomIevV/pXSaWRmD3qi6ka4oSFRTExRiEQ==", + "license": "LGPL-2.1-or-later", + "dependencies": { + "@types/geojson": "^7946.0.14", + "@types/node": "^22.5.4", + "denque": "^2.1.0", + "iconv-lite": "^0.6.3", + "lru-cache": "^10.3.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/postgres": { + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.4.5.tgz", + "integrity": "sha512-cDWgoah1Gez9rN3H4165peY9qfpEo+SA61oQv65O3cRUE1pOEoJWwddwcqKE8XZYjbblOJlYDlLV4h67HrEVDg==", + "license": "Unlicense", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/porsager" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/tseep": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tseep/-/tseep-1.3.1.tgz", + "integrity": "sha512-ZPtfk1tQnZVyr7BPtbJ93qaAh2lZuIOpTMjhrYa4XctT8xe7t4SAW9LIxrySDuYMsfNNayE51E/WNGrNVgVicQ==", + "license": "MIT" + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ultimate-express": { + "version": "1.3.9", + "resolved": "https://registry.npmjs.org/ultimate-express/-/ultimate-express-1.3.9.tgz", + "integrity": "sha512-jr4TMDsQM2nlG/1VMkesy70kTLIVecUeX8dzkh0JoSQ2wilhUaVbCM3I6qvKCNHD+UsYXI6QFhgKmSum1k19hw==", + "license": "Apache-2.0", + "dependencies": { + "@types/express": "^4.0.0", + "accepts": "^1.3.8", + "acorn": "^8.12.1", + "bytes": "^3.1.2", + "cookie": "^1.0.1", + "cookie-signature": "^1.2.1", + "encodeurl": "^2.0.0", + "etag": "^1.8.1", + "fast-querystring": "^1.1.2", + "fast-zlib": "^2.0.1", + "fresh": "^0.5.2", + "mime-types": "^2.1.35", + "ms": "^2.1.3", + "proxy-addr": "^2.0.7", + "qs": "^6.13.0", + "range-parser": "^1.2.1", + "statuses": "^2.0.1", + "tseep": "^1.2.2", + "type-is": "^1.6.18", + "uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.49.0", + "vary": "^1.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "license": "MIT" + }, + "node_modules/uWebSockets.js": { + "version": "20.49.0", + "resolved": "git+ssh://git@github.com/uNetworking/uWebSockets.js.git#442087c0a01bf146acb7386910739ec81df06700", + "license": "Apache-2.0" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + } + } +} diff --git a/frameworks/JavaScript/ultimate-express/package.json b/frameworks/JavaScript/ultimate-express/package.json new file mode 100644 index 00000000000..552f0bbf287 --- /dev/null +++ b/frameworks/JavaScript/ultimate-express/package.json @@ -0,0 +1,18 @@ +{ + "name": "ultimate-express", + "version": "0.0.1", + "main": "app.js", + "scripts": { + "start": "node clustered.js" + }, + "author": "", + "license": "MIT", + "dependencies": { + "fast-json-stringify": "^6.0.0", + "lru-cache": "^10.0.1", + "mariadb": "^3.2.0", + "postgres": "^3.3.5", + "ultimate-express": "^1.3.9" + }, + "type": "module" +} diff --git a/frameworks/JavaScript/ultimate-express/ultimate-express-mysql.dockerfile b/frameworks/JavaScript/ultimate-express/ultimate-express-mysql.dockerfile new file mode 100644 index 00000000000..30c2079f2ab --- /dev/null +++ b/frameworks/JavaScript/ultimate-express/ultimate-express-mysql.dockerfile @@ -0,0 +1,18 @@ +# syntax=docker/dockerfile:1 +FROM node:20-slim + +WORKDIR /app + +COPY --chown=node:node . . + +ENV NODE_ENV production + +RUN npm install + +ENV DATABASE mysql + +USER node + +EXPOSE 8080 + +CMD ["node", "clustered.js"] \ No newline at end of file diff --git a/frameworks/JavaScript/ultimate-express/ultimate-express-postgres.dockerfile b/frameworks/JavaScript/ultimate-express/ultimate-express-postgres.dockerfile new file mode 100644 index 00000000000..ebf6591a379 --- /dev/null +++ b/frameworks/JavaScript/ultimate-express/ultimate-express-postgres.dockerfile @@ -0,0 +1,18 @@ +# syntax=docker/dockerfile:1 +FROM node:20-slim + +WORKDIR /app + +COPY --chown=node:node . . + +ENV NODE_ENV production + +RUN npm install + +ENV DATABASE postgres + +USER node + +EXPOSE 8080 + +CMD ["node", "clustered.js"] \ No newline at end of file diff --git a/frameworks/JavaScript/ultimate-express/ultimate-express.dockerfile b/frameworks/JavaScript/ultimate-express/ultimate-express.dockerfile new file mode 100644 index 00000000000..3defb8423fb --- /dev/null +++ b/frameworks/JavaScript/ultimate-express/ultimate-express.dockerfile @@ -0,0 +1,16 @@ +# syntax=docker/dockerfile:1 +FROM node:20-slim + +WORKDIR /app + +COPY --chown=node:node . . + +ENV NODE_ENV production + +RUN npm install + +USER node + +EXPOSE 8080 + +CMD ["node", "clustered.js"] \ No newline at end of file diff --git a/frameworks/Kotlin/ktor/ktor-exposed-dao.dockerfile b/frameworks/Kotlin/ktor/ktor-exposed-dao.dockerfile index e12a664f8dc..417c0358735 100644 --- a/frameworks/Kotlin/ktor/ktor-exposed-dao.dockerfile +++ b/frameworks/Kotlin/ktor/ktor-exposed-dao.dockerfile @@ -1,9 +1,9 @@ -FROM gradle:jdk17 +FROM gradle:jdk21 WORKDIR /ktor-exposed COPY ktor-exposed/settings.gradle.kts settings.gradle.kts COPY ktor-exposed/app app -RUN gradle shadowJar +RUN gradle --no-daemon shadowJar EXPOSE 8080 diff --git a/frameworks/Kotlin/ktor/ktor-exposed-dsl.dockerfile b/frameworks/Kotlin/ktor/ktor-exposed-dsl.dockerfile index f65069e6b78..495b8b6c0e6 100644 --- a/frameworks/Kotlin/ktor/ktor-exposed-dsl.dockerfile +++ b/frameworks/Kotlin/ktor/ktor-exposed-dsl.dockerfile @@ -1,9 +1,9 @@ -FROM gradle:jdk17 +FROM gradle:jdk21 WORKDIR /ktor-exposed COPY ktor-exposed/settings.gradle.kts settings.gradle.kts COPY ktor-exposed/app app -RUN gradle shadowJar +RUN gradle --no-daemon shadowJar EXPOSE 8080 diff --git a/frameworks/Kotlin/ktor/ktor-exposed/app/build.gradle.kts b/frameworks/Kotlin/ktor/ktor-exposed/app/build.gradle.kts index 60f4dab3b17..80a9debfa16 100644 --- a/frameworks/Kotlin/ktor/ktor-exposed/app/build.gradle.kts +++ b/frameworks/Kotlin/ktor/ktor-exposed/app/build.gradle.kts @@ -1,6 +1,6 @@ plugins { application - kotlin("jvm") version "1.9.22" + kotlin("jvm") version "2.0.21" kotlin("plugin.serialization") version "2.0.0" id("com.github.johnrengelman.shadow") version "8.1.0" } @@ -9,9 +9,9 @@ repositories { mavenCentral() } -val ktorVersion = "2.3.12" -val kotlinxSerializationVersion = "1.6.3" -val exposedVersion = "0.52.0" +val ktorVersion = "3.0.1" +val kotlinxSerializationVersion = "1.7.3" +val exposedVersion = "0.56.0" dependencies { implementation("io.ktor:ktor-server-core:$ktorVersion") @@ -25,8 +25,8 @@ dependencies { implementation("org.jetbrains.exposed:exposed-dao:$exposedVersion") implementation("org.jetbrains.exposed:exposed-jdbc:$exposedVersion") - implementation("org.postgresql:postgresql:42.5.4") - implementation("com.zaxxer:HikariCP:5.0.1") + implementation("org.postgresql:postgresql:42.7.4") + implementation("com.zaxxer:HikariCP:5.1.0") runtimeOnly("org.slf4j:slf4j-simple:1.7.36") } diff --git a/frameworks/Kotlin/ktor/ktor-exposed/app/src/main/kotlin/App.kt b/frameworks/Kotlin/ktor/ktor-exposed/app/src/main/kotlin/App.kt index c284ecd7b17..513a20b7ab7 100644 --- a/frameworks/Kotlin/ktor/ktor-exposed/app/src/main/kotlin/App.kt +++ b/frameworks/Kotlin/ktor/ktor-exposed/app/src/main/kotlin/App.kt @@ -19,9 +19,12 @@ import org.jetbrains.exposed.dao.IntEntity import org.jetbrains.exposed.dao.IntEntityClass import org.jetbrains.exposed.dao.id.EntityID import org.jetbrains.exposed.dao.id.IdTable -import org.jetbrains.exposed.sql.* +import org.jetbrains.exposed.sql.Database +import org.jetbrains.exposed.sql.ResultRow import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq +import org.jetbrains.exposed.sql.Transaction import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.sql.update import java.util.concurrent.ThreadLocalRandom @Serializable @@ -82,7 +85,7 @@ fun Application.module(exposedMode: ExposedMode) { routing { fun selectWorldsWithIdQuery(id: Int) = - WorldTable.slice(WorldTable.id, WorldTable.randomNumber).select(WorldTable.id eq id) + WorldTable.select(WorldTable.id, WorldTable.randomNumber).where(WorldTable.id eq id) fun ResultRow.toWorld() = World(this[WorldTable.id].value, this[WorldTable.randomNumber]) @@ -129,7 +132,7 @@ fun Application.module(exposedMode: ExposedMode) { get("/fortunes") { val result = withDatabaseContextAndTransaction { when (exposedMode) { - Dsl -> FortuneTable.slice(FortuneTable.id, FortuneTable.message).selectAll() + Dsl -> FortuneTable.select(FortuneTable.id, FortuneTable.message) .asSequence().map { it.toFortune() } Dao -> FortuneDao.all().asSequence().map { it.toFortune() } diff --git a/frameworks/Kotlin/ktor/ktor.dockerfile b/frameworks/Kotlin/ktor/ktor.dockerfile index 1558d312736..076a51a1416 100644 --- a/frameworks/Kotlin/ktor/ktor.dockerfile +++ b/frameworks/Kotlin/ktor/ktor.dockerfile @@ -1,10 +1,10 @@ -FROM maven:3.9.7-amazoncorretto-17-debian as maven +FROM maven:3.9.9-amazoncorretto-21-debian-bookworm as maven WORKDIR /ktor COPY ktor/pom.xml pom.xml COPY ktor/src src RUN mvn clean package -q -FROM amazoncorretto:17.0.11-al2023-headless +FROM amazoncorretto:21-al2023-headless WORKDIR /ktor COPY --from=maven /ktor/target/tech-empower-framework-benchmark-1.0-SNAPSHOT-netty-bundle.jar app.jar diff --git a/frameworks/Kotlin/ktor/ktor/pom.xml b/frameworks/Kotlin/ktor/ktor/pom.xml index a794bb59fe3..74d725ca6fa 100644 --- a/frameworks/Kotlin/ktor/ktor/pom.xml +++ b/frameworks/Kotlin/ktor/ktor/pom.xml @@ -12,15 +12,15 @@ org.jetbrains.ktor tech-empower-framework-benchmark - 1.9.22 - 2.3.11 - 1.6.3 + 2.0.21 + 3.0.2 + 1.7.3 0.11.0 UTF-8 - 5.0.0 + 5.1.0 1.2.13 - 8.0.28 - 42.7.2 + 8.0.33 + 42.7.4 diff --git a/frameworks/Kotlin/ktor/ktor/src/main/kotlin/org/jetbrains/ktor/benchmarks/Hello.kt b/frameworks/Kotlin/ktor/ktor/src/main/kotlin/org/jetbrains/ktor/benchmarks/Hello.kt index 82bdd34bc66..b44808fba40 100644 --- a/frameworks/Kotlin/ktor/ktor/src/main/kotlin/org/jetbrains/ktor/benchmarks/Hello.kt +++ b/frameworks/Kotlin/ktor/ktor/src/main/kotlin/org/jetbrains/ktor/benchmarks/Hello.kt @@ -19,6 +19,7 @@ import org.jetbrains.ktor.benchmarks.Constants.UPDATE_QUERY import org.jetbrains.ktor.benchmarks.Constants.WORLD_QUERY import java.sql.Connection import java.util.concurrent.ThreadLocalRandom +import kotlin.random.Random @Serializable data class Message(val message: String) @@ -49,7 +50,7 @@ fun Application.main() { } get("/db") { - val random = ThreadLocalRandom.current() + val random = Random.Default val world = withContext(databaseDispatcher) { pool.connection.use { connection -> @@ -67,7 +68,7 @@ fun Application.main() { call.respondText(Json.encodeToString(world), ContentType.Application.Json) } - fun Connection.selectWorlds(queries: Int, random: ThreadLocalRandom): List { + fun Connection.selectWorlds(queries: Int, random: Random): List { val result = ArrayList(queries) prepareStatement(WORLD_QUERY).use { statement -> repeat(queries) { @@ -85,7 +86,7 @@ fun Application.main() { get("/queries") { val queries = call.queries() - val random = ThreadLocalRandom.current() + val random = Random.Default val result = withContext(databaseDispatcher) { pool.connection.use { it.selectWorlds(queries, random) } @@ -130,7 +131,7 @@ fun Application.main() { get("/updates") { val queries = call.queries() - val random = ThreadLocalRandom.current() + val random = Random.Default val result: List withContext(databaseDispatcher) { @@ -139,14 +140,14 @@ fun Application.main() { result.forEach { it.randomNumber = random.nextInt(dbRows) + 1 } - connection.prepareStatement(UPDATE_QUERY) - .use { updateStatement -> + connection.prepareStatement(UPDATE_QUERY).use { updateStatement -> for ((id, randomNumber) in result) { updateStatement.setInt(1, randomNumber) updateStatement.setInt(2, id) - - updateStatement.executeUpdate() + updateStatement.addBatch() } + + updateStatement.executeBatch() } } } diff --git a/frameworks/Kotlin/vertx-web-kotlin-coroutines/benchmark_config.json b/frameworks/Kotlin/vertx-web-kotlin-coroutines/benchmark_config.json index d2bab14abb7..2b8ebd26c83 100644 --- a/frameworks/Kotlin/vertx-web-kotlin-coroutines/benchmark_config.json +++ b/frameworks/Kotlin/vertx-web-kotlin-coroutines/benchmark_config.json @@ -18,8 +18,7 @@ "database_os": "Linux", "display_name": "vertx-web-kotlin-coroutines", "notes": "", - "versus": "vertx-web", - "tags": ["broken"] + "versus": "vertx-web" }, "postgres": { "db_url": "/db", diff --git a/frameworks/Kotlin/vertx-web-kotlin-coroutines/build.gradle.kts b/frameworks/Kotlin/vertx-web-kotlin-coroutines/build.gradle.kts index 584f6102956..973bf6d8116 100644 --- a/frameworks/Kotlin/vertx-web-kotlin-coroutines/build.gradle.kts +++ b/frameworks/Kotlin/vertx-web-kotlin-coroutines/build.gradle.kts @@ -1,34 +1,37 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar -import org.jetbrains.kotlin.gradle.dsl.JvmTarget -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile tasks.wrapper { distributionType = Wrapper.DistributionType.ALL } plugins { - kotlin("jvm") version "1.8.10" + kotlin("jvm") version "2.0.21" application id("nu.studer.rocker") version "3.0.4" - id("com.github.johnrengelman.shadow") version "7.1.2" + id("com.gradleup.shadow") version "8.3.4" } group = "io.vertx" -version = "4.3.8" +version = "4.3.8" // 4.5.10 is available, but this is not updated to be kept consistent with the "vert-web" portion repositories { mavenCentral() } dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0") implementation(platform("io.vertx:vertx-stack-depchain:$version")) implementation("io.vertx:vertx-core") - implementation("com.fasterxml.jackson.module:jackson-module-blackbird:2.14.2") + implementation("com.fasterxml.jackson.module:jackson-module-blackbird:2.14.2") // 2.18.1 is available, but this is not updated to be kept consistent with the "vert-web" portion implementation("io.vertx:vertx-web") implementation("io.vertx:vertx-pg-client") implementation("io.vertx:vertx-web-templ-rocker") - implementation("io.netty", "netty-transport-native-epoll", classifier = "linux-x86_64") + runtimeOnly("io.netty", "netty-transport-native-epoll", classifier = "linux-x86_64") + // not used to make the project consistent with the "vert-web" portion + /* + runtimeOnly("io.vertx:vertx-io_uring-incubator") + runtimeOnly("io.netty.incubator:netty-incubator-transport-native-io_uring:0.0.25.Final:linux-x86_64") + */ implementation("io.vertx:vertx-lang-kotlin") implementation("io.vertx:vertx-lang-kotlin-coroutines") } @@ -38,14 +41,12 @@ rocker { create("main") { templateDir.set(file("src/main/resources")) optimize.set(true) - javaVersion.set("17") + javaVersion.set("17") // kept consistent with the "vert-web" portion } } } -tasks.withType { - compilerOptions.jvmTarget.set(JvmTarget.JVM_17) -} +kotlin.jvmToolchain(17) // kept consistent with the "vert-web" portion // content below copied from the project generated by the app generator diff --git a/frameworks/Kotlin/vertx-web-kotlin-coroutines/gradle/wrapper/gradle-wrapper.jar b/frameworks/Kotlin/vertx-web-kotlin-coroutines/gradle/wrapper/gradle-wrapper.jar index 943f0cbfa75..a4b76b9530d 100644 Binary files a/frameworks/Kotlin/vertx-web-kotlin-coroutines/gradle/wrapper/gradle-wrapper.jar and b/frameworks/Kotlin/vertx-web-kotlin-coroutines/gradle/wrapper/gradle-wrapper.jar differ diff --git a/frameworks/Kotlin/vertx-web-kotlin-coroutines/gradle/wrapper/gradle-wrapper.properties b/frameworks/Kotlin/vertx-web-kotlin-coroutines/gradle/wrapper/gradle-wrapper.properties index 2b22d057a07..79eb9d003fe 100644 --- a/frameworks/Kotlin/vertx-web-kotlin-coroutines/gradle/wrapper/gradle-wrapper.properties +++ b/frameworks/Kotlin/vertx-web-kotlin-coroutines/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/frameworks/Kotlin/vertx-web-kotlin-coroutines/gradlew b/frameworks/Kotlin/vertx-web-kotlin-coroutines/gradlew old mode 100644 new mode 100755 index 65dcd68d65c..f5feea6d6b1 --- a/frameworks/Kotlin/vertx-web-kotlin-coroutines/gradlew +++ b/frameworks/Kotlin/vertx-web-kotlin-coroutines/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -83,10 +85,9 @@ done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,10 +134,13 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. @@ -144,7 +148,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -152,7 +156,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -197,11 +201,15 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ diff --git a/frameworks/Kotlin/vertx-web-kotlin-coroutines/gradlew.bat b/frameworks/Kotlin/vertx-web-kotlin-coroutines/gradlew.bat index 93e3f59f135..9d21a21834d 100644 --- a/frameworks/Kotlin/vertx-web-kotlin-coroutines/gradlew.bat +++ b/frameworks/Kotlin/vertx-web-kotlin-coroutines/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -43,11 +45,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail diff --git a/frameworks/Kotlin/vertx-web-kotlin-coroutines/vertx-web-kotlin-coroutines-postgres.dockerfile b/frameworks/Kotlin/vertx-web-kotlin-coroutines/vertx-web-kotlin-coroutines-postgres.dockerfile index 0703722b599..dfcc87cb4bb 100644 --- a/frameworks/Kotlin/vertx-web-kotlin-coroutines/vertx-web-kotlin-coroutines-postgres.dockerfile +++ b/frameworks/Kotlin/vertx-web-kotlin-coroutines/vertx-web-kotlin-coroutines-postgres.dockerfile @@ -1,10 +1,8 @@ -FROM gradle:7.6-jdk17 as gradle +FROM gradle:8.10.2-jdk17 as gradle WORKDIR /vertx-web-kotlin-coroutines -COPY gradle gradle COPY src src COPY build.gradle.kts build.gradle.kts COPY gradle.properties gradle.properties -COPY gradlew gradlew COPY settings.gradle.kts settings.gradle.kts RUN gradle shadowJar diff --git a/frameworks/Kotlin/vertx-web-kotlin-coroutines/vertx-web-kotlin-coroutines.dockerfile b/frameworks/Kotlin/vertx-web-kotlin-coroutines/vertx-web-kotlin-coroutines.dockerfile index 5c9e6378404..dfcc87cb4bb 100644 --- a/frameworks/Kotlin/vertx-web-kotlin-coroutines/vertx-web-kotlin-coroutines.dockerfile +++ b/frameworks/Kotlin/vertx-web-kotlin-coroutines/vertx-web-kotlin-coroutines.dockerfile @@ -1,4 +1,4 @@ -FROM gradle:7.6-jdk17 as gradle +FROM gradle:8.10.2-jdk17 as gradle WORKDIR /vertx-web-kotlin-coroutines COPY src src COPY build.gradle.kts build.gradle.kts diff --git a/frameworks/Kotlin/vertx-web-kotlinx/README.md b/frameworks/Kotlin/vertx-web-kotlinx/README.md index 6c1d6caa252..b9fe8d87eef 100755 --- a/frameworks/Kotlin/vertx-web-kotlinx/README.md +++ b/frameworks/Kotlin/vertx-web-kotlinx/README.md @@ -2,7 +2,7 @@ Vert.x-Web in Kotlin with request handling implemented as much with official kotlinx libraries as possible. -Code is written from scratch to be as concise as possible with common code extracted into common (possibly inline) functions. SQL client implementation details and JVM Options are adapted referring to [the vertx-web portion](../../Java/vertx-web) and [the vertx portion](../../Java/vertx). All requests are handled in coroutines and suspend `await`s are used instead of future compositions. Compared to [the vertx-web-kotlin-coroutines portion](../vertx-web-kotlin-coroutines), besides adopting the Kotlinx libraries, this project simplifies the code by using more built-in Coroutine functions and avoids mutability as much as possible. JSON serialization is implemented with kotlinx.serialization and Fortunes with kotlinx.html. The benchmark is run on the latest LTS version of JVM, 17. +Code is written from scratch to be as concise as possible with common code extracted into common (possibly inline) functions. SQL client implementation details and JVM Options are adapted referring to [the vertx-web portion](../../Java/vertx-web) and [the vertx portion](../../Java/vertx). All requests are handled in coroutines and suspend `await`s are used instead of future compositions. Compared to [the vertx-web-kotlin-coroutines portion](../vertx-web-kotlin-coroutines), besides adopting the Kotlinx libraries, this project simplifies the code by using more built-in Coroutine functions and avoids mutability as much as possible. JSON serialization is implemented with kotlinx.serialization and Fortunes with kotlinx.html. The benchmark is run on the latest LTS version of JVM, 21. ## Test Type Implementation Source Code @@ -27,6 +27,7 @@ The tests were run with: * [Vert.x Reactive PostgreSQL Client](https://vertx.io/docs/vertx-pg-client/java/) * [kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) * [kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization) +* [kotlinx-io](https://github.com/Kotlin/kotlinx-io) * [kotlinx.html](https://github.com/Kotlin/kotlinx.html) ## Test URLs diff --git a/frameworks/Kotlin/vertx-web-kotlinx/build.gradle.kts b/frameworks/Kotlin/vertx-web-kotlinx/build.gradle.kts index 232eadaf821..2e0035ab3e7 100644 --- a/frameworks/Kotlin/vertx-web-kotlinx/build.gradle.kts +++ b/frameworks/Kotlin/vertx-web-kotlinx/build.gradle.kts @@ -1,12 +1,9 @@ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile - tasks.wrapper { distributionType = Wrapper.DistributionType.ALL } plugins { - val kotlinVersion = "1.8.10" + val kotlinVersion = "2.0.21" kotlin("jvm") version kotlinVersion kotlin("plugin.serialization") version kotlinVersion application @@ -16,7 +13,8 @@ repositories { mavenCentral() } -val vertxVersion = "4.3.8" +val vertxVersion = "4.5.10" +val kotlinxSerializationVersion = "1.7.3" dependencies { implementation(platform("io.vertx:vertx-stack-depchain:$vertxVersion")) implementation("io.vertx:vertx-web") @@ -24,15 +22,20 @@ dependencies { implementation("io.netty", "netty-transport-native-epoll", classifier = "linux-x86_64") implementation("io.vertx:vertx-lang-kotlin") implementation("io.vertx:vertx-lang-kotlin-coroutines") + runtimeOnly("io.vertx:vertx-io_uring-incubator") + // This dependency has to be added for io_uring to work. + runtimeOnly("io.netty.incubator:netty-incubator-transport-native-io_uring:0.0.25.Final:linux-x86_64") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4") - implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1") - implementation("org.jetbrains.kotlinx:kotlinx-html:0.8.0") - //implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0") -} + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0") -tasks.withType { - compilerOptions.jvmTarget.set(JvmTarget.JVM_17) + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinxSerializationVersion") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json-io:$kotlinxSerializationVersion") + implementation("org.jetbrains.kotlinx:kotlinx-io-core:0.5.4") + + implementation("org.jetbrains.kotlinx:kotlinx-html:0.11.0") + //implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0") // the latest version is 0.6.1 } +kotlin.jvmToolchain(21) + application.mainClass.set("MainKt") diff --git a/frameworks/Kotlin/vertx-web-kotlinx/gradle/wrapper/gradle-wrapper.jar b/frameworks/Kotlin/vertx-web-kotlinx/gradle/wrapper/gradle-wrapper.jar index ccebba7710d..a4b76b9530d 100644 Binary files a/frameworks/Kotlin/vertx-web-kotlinx/gradle/wrapper/gradle-wrapper.jar and b/frameworks/Kotlin/vertx-web-kotlinx/gradle/wrapper/gradle-wrapper.jar differ diff --git a/frameworks/Kotlin/vertx-web-kotlinx/gradle/wrapper/gradle-wrapper.properties b/frameworks/Kotlin/vertx-web-kotlinx/gradle/wrapper/gradle-wrapper.properties index 284897427bc..79eb9d003fe 100644 --- a/frameworks/Kotlin/vertx-web-kotlinx/gradle/wrapper/gradle-wrapper.properties +++ b/frameworks/Kotlin/vertx-web-kotlinx/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/frameworks/Kotlin/vertx-web-kotlinx/gradlew b/frameworks/Kotlin/vertx-web-kotlinx/gradlew index 79a61d421cc..f5feea6d6b1 100755 --- a/frameworks/Kotlin/vertx-web-kotlinx/gradlew +++ b/frameworks/Kotlin/vertx-web-kotlinx/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -83,10 +85,9 @@ done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,10 +134,13 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. @@ -144,7 +148,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -152,7 +156,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -197,11 +201,15 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ diff --git a/frameworks/Kotlin/vertx-web-kotlinx/gradlew.bat b/frameworks/Kotlin/vertx-web-kotlinx/gradlew.bat index 93e3f59f135..9d21a21834d 100644 --- a/frameworks/Kotlin/vertx-web-kotlinx/gradlew.bat +++ b/frameworks/Kotlin/vertx-web-kotlinx/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -43,11 +45,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail diff --git a/frameworks/Kotlin/vertx-web-kotlinx/src/main/kotlin/KotlinxIo.kt b/frameworks/Kotlin/vertx-web-kotlinx/src/main/kotlin/KotlinxIo.kt new file mode 100644 index 00000000000..6427b91e85e --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlinx/src/main/kotlin/KotlinxIo.kt @@ -0,0 +1,47 @@ +import io.vertx.core.streams.WriteStream +import io.vertx.kotlin.coroutines.coAwait +import kotlinx.coroutines.runBlocking +import kotlinx.io.RawSink +import kotlinx.io.readByteArray +import io.vertx.core.buffer.Buffer as VertxBuffer +import kotlinx.io.Buffer as KotlinxIoBuffer + +@Suppress("NOTHING_TO_INLINE") +private inline fun Long.toIntOrThrow(): Int { + require(this in Int.MIN_VALUE.toLong()..Int.MAX_VALUE.toLong()) + return toInt() +} + +@JvmInline +value class VertxBufferWriteStreamRawSink(val writeStream: WriteStream) : RawSink { + override fun write(source: KotlinxIoBuffer, byteCount: Long) { + runBlocking { + writeStream.write(VertxBuffer.buffer(source.readByteArray(byteCount.toIntOrThrow()))).coAwait() + } + } + + override fun flush() {} + + override fun close() { + writeStream.end() + } +} + +// not used currently +fun WriteStream.toRawSink(): RawSink = + VertxBufferWriteStreamRawSink(this) + + +@JvmInline +value class VertxBufferRawSink(val vertxBuffer: VertxBuffer) : RawSink { + override fun write(source: KotlinxIoBuffer, byteCount: Long) { + vertxBuffer.appendBytes(source.readByteArray(byteCount.toIntOrThrow())) + } + + override fun flush() {} + + override fun close() {} +} + +fun VertxBuffer.toRawSink(): RawSink = + VertxBufferRawSink(this) diff --git a/frameworks/Kotlin/vertx-web-kotlinx/src/main/kotlin/Main.kt b/frameworks/Kotlin/vertx-web-kotlinx/src/main/kotlin/Main.kt index 75cbd79e3cd..987c22162b3 100644 --- a/frameworks/Kotlin/vertx-web-kotlinx/src/main/kotlin/Main.kt +++ b/frameworks/Kotlin/vertx-web-kotlinx/src/main/kotlin/Main.kt @@ -2,10 +2,10 @@ import io.vertx.core.Vertx import io.vertx.core.impl.cpu.CpuCoreSensor import io.vertx.kotlin.core.deploymentOptionsOf import io.vertx.kotlin.core.vertxOptionsOf -import io.vertx.kotlin.coroutines.await +import io.vertx.kotlin.coroutines.coAwait import java.util.logging.Logger -const val SERVER_NAME = "Vert.x-Web Kotlinx Benchmark" +const val SERVER_NAME = "Vert.x-Web Kotlinx Benchmark server" val logger = Logger.getLogger("Vert.x-Web Kotlinx Benchmark") suspend fun main(args: Array) { @@ -19,6 +19,6 @@ suspend fun main(args: Array) { it.printStackTrace() } vertx.deployVerticle({ MainVerticle(hasDb) }, deploymentOptionsOf(instances = CpuCoreSensor.availableProcessors())) - .await() + .coAwait() logger.info("$SERVER_NAME started.") } diff --git a/frameworks/Kotlin/vertx-web-kotlinx/src/main/kotlin/MainVerticle.kt b/frameworks/Kotlin/vertx-web-kotlinx/src/main/kotlin/MainVerticle.kt index 9d16710709e..2bd701fa083 100644 --- a/frameworks/Kotlin/vertx-web-kotlinx/src/main/kotlin/MainVerticle.kt +++ b/frameworks/Kotlin/vertx-web-kotlinx/src/main/kotlin/MainVerticle.kt @@ -1,4 +1,5 @@ import io.netty.channel.unix.Errors.NativeIoException +import io.vertx.core.buffer.Buffer import io.vertx.core.http.HttpHeaders import io.vertx.core.http.HttpServer import io.vertx.core.http.HttpServerRequest @@ -7,39 +8,28 @@ import io.vertx.ext.web.Route import io.vertx.ext.web.Router import io.vertx.ext.web.RoutingContext import io.vertx.kotlin.core.http.httpServerOptionsOf +import io.vertx.kotlin.coroutines.CoroutineRouterSupport import io.vertx.kotlin.coroutines.CoroutineVerticle -import io.vertx.kotlin.coroutines.await +import io.vertx.kotlin.coroutines.coAwait import io.vertx.kotlin.pgclient.pgConnectOptionsOf import io.vertx.pgclient.PgConnection import io.vertx.sqlclient.PreparedQuery import io.vertx.sqlclient.Row import io.vertx.sqlclient.RowSet import io.vertx.sqlclient.Tuple -import kotlinx.coroutines.* +import kotlinx.coroutines.Dispatchers import kotlinx.html.* import kotlinx.html.stream.appendHTML +import kotlinx.io.buffered +import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.Serializable -import kotlinx.serialization.encodeToString +import kotlinx.serialization.SerializationStrategy import kotlinx.serialization.json.Json -import java.net.SocketException +import kotlinx.serialization.json.io.encodeToSink import java.time.ZonedDateTime import java.time.format.DateTimeFormatter -class MainVerticle(val hasDb: Boolean) : CoroutineVerticle() { - inline fun Route.checkedCoroutineHandlerUnconfined(crossinline requestHandler: suspend (RoutingContext) -> Unit): Route = - handler { ctx -> - /* Some conclusions from the Plaintext test results with trailing `await()`s: - 1. `launch { /*...*/ }` < `launch(start = CoroutineStart.UNDISPATCHED) { /*...*/ }` < `launch(Dispatchers.Unconfined) { /*...*/ }`. - 1. `launch { /*...*/ }` without `context` or `start` lead to `io.netty.channel.StacklessClosedChannelException` and `io.netty.channel.unix.Errors$NativeIoException: sendAddress(..) failed: Connection reset by peer`. */ - launch(Dispatchers.Unconfined) { - try { - requestHandler(ctx) - } catch (t: Throwable) { - ctx.fail(t) - } - } - } - +class MainVerticle(val hasDb: Boolean) : CoroutineVerticle(), CoroutineRouterSupport { // `PgConnection`s as used in the "vertx" portion offers better performance than `PgPool`s. lateinit var pgConnection: PgConnection lateinit var date: String @@ -68,7 +58,7 @@ class MainVerticle(val hasDb: Boolean) : CoroutineVerticle() { cachePreparedStatements = true, pipeliningLimit = 100000 ) - ).await() + ).coAwait() selectWorldQuery = pgConnection.preparedQuery(SELECT_WORLD_SQL) selectFortuneQuery = pgConnection.preparedQuery(SELECT_FORTUNE_SQL) @@ -81,15 +71,19 @@ class MainVerticle(val hasDb: Boolean) : CoroutineVerticle() { .requestHandler(Router.router(vertx).apply { routes() }) .exceptionHandler { // wrk resets the connections when benchmarking is finished. - if ((it is NativeIoException && it.message == "recvAddress(..) failed: Connection reset by peer") - || (it is SocketException && it.message == "Connection reset") + if ( + // for epoll + /*(it is NativeIoException && it.message == "recvAddress(..) failed: Connection reset by peer") + || (it is SocketException && it.message == "Connection reset")*/ + // for io_uring + it is NativeIoException && it.message == "io_uring read(..) failed: Connection reset by peer" ) return@exceptionHandler logger.info("Exception in HttpServer: $it") it.printStackTrace() } - .listen().await() + .listen().coAwait() } @@ -110,14 +104,46 @@ class MainVerticle(val hasDb: Boolean) : CoroutineVerticle() { putHeader(HttpHeaders.CONTENT_TYPE, "application/json") } - inline fun Route.jsonResponseHandler(crossinline requestHandler: suspend (RoutingContext) -> @Serializable T) = - checkedCoroutineHandlerUnconfined { + + fun Route.coHandlerUnconfined(requestHandler: suspend (RoutingContext) -> Unit): Route = + /* Some conclusions from the Plaintext test results with trailing `await()`s: + 1. `launch { /*...*/ }` < `launch(start = CoroutineStart.UNDISPATCHED) { /*...*/ }` < `launch(Dispatchers.Unconfined) { /*...*/ }`. + 1. `launch { /*...*/ }` without `context` or `start` lead to `io.netty.channel.StacklessClosedChannelException` and `io.netty.channel.unix.Errors$NativeIoException: sendAddress(..) failed: Connection reset by peer`. */ + coHandler(Dispatchers.Unconfined, requestHandler) + + inline fun Route.jsonResponseCoHandler( + serializer: SerializationStrategy, + crossinline requestHandler: suspend (RoutingContext) -> @Serializable T + ) = + coHandlerUnconfined { it.response().run { putJsonResponseHeader() - end(Json.encodeToString(requestHandler(it)))/*.await()*/ + + /* + // approach 1 + end(Json.encodeToString(serializer, requestHandler(it)))/*.coAwait()*/ + */ + + /* + // approach 2 + // java.lang.IllegalStateException: You must set the Content-Length header to be the total size of the message body BEFORE sending any data if you are not using HTTP chunked encoding. + toRawSink().buffered().use { bufferedSink -> + @OptIn(ExperimentalSerializationApi::class) + Json.encodeToSink(serializer, requestHandler(it), bufferedSink) + } + */ + + // approach 3 + end(Buffer.buffer().apply { + toRawSink().buffered().use { bufferedSink -> + @OptIn(ExperimentalSerializationApi::class) + Json.encodeToSink(serializer, requestHandler(it), bufferedSink) + } + }) } } + suspend fun selectRandomWorlds(queries: Int): List { val rowSets = List(queries) { selectWorldQuery.execute(Tuple.of(randomIntBetween1And10000())) @@ -126,23 +152,23 @@ class MainVerticle(val hasDb: Boolean) : CoroutineVerticle() { } fun Router.routes() { - get("/json").jsonResponseHandler { + get("/json").jsonResponseCoHandler(Serializers.message) { jsonSerializationMessage } - get("/db").jsonResponseHandler { - val rowSet = selectWorldQuery.execute(Tuple.of(randomIntBetween1And10000())).await() + get("/db").jsonResponseCoHandler(Serializers.world) { + val rowSet = selectWorldQuery.execute(Tuple.of(randomIntBetween1And10000())).coAwait() rowSet.single().toWorld() } - get("/queries").jsonResponseHandler { + get("/queries").jsonResponseCoHandler(Serializers.worlds) { val queries = it.request().getQueries() selectRandomWorlds(queries) } - get("/fortunes").checkedCoroutineHandlerUnconfined { + get("/fortunes").coHandlerUnconfined { val fortunes = mutableListOf() - selectFortuneQuery.execute().await() + selectFortuneQuery.execute().coAwait() .mapTo(fortunes) { it.toFortune() } fortunes.add(Fortune(0, "Additional fortune added at request time.")) @@ -173,11 +199,11 @@ class MainVerticle(val hasDb: Boolean) : CoroutineVerticle() { it.response().run { putCommonHeaders() putHeader(HttpHeaders.CONTENT_TYPE, "text/html; charset=utf-8") - end(htmlString)/*.await()*/ + end(htmlString)/*.coAwait()*/ } } - get("/updates").jsonResponseHandler { + get("/updates").jsonResponseCoHandler(Serializers.worlds) { val queries = it.request().getQueries() val worlds = selectRandomWorlds(queries) val updatedWorlds = worlds.map { it.copy(randomNumber = randomIntBetween1And10000()) } @@ -185,7 +211,7 @@ class MainVerticle(val hasDb: Boolean) : CoroutineVerticle() { // Approach 1 // The updated worlds need to be sorted first to avoid deadlocks. updateWordQuery - .executeBatch(updatedWorlds.sortedBy { it.id }.map { Tuple.of(it.randomNumber, it.id) }).await() + .executeBatch(updatedWorlds.sortedBy { it.id }.map { Tuple.of(it.randomNumber, it.id) }).coAwait() /* // Approach 2, worse performance @@ -197,11 +223,11 @@ class MainVerticle(val hasDb: Boolean) : CoroutineVerticle() { updatedWorlds } - get("/plaintext").checkedCoroutineHandlerUnconfined { + get("/plaintext").coHandlerUnconfined { it.response().run { putCommonHeaders() putHeader(HttpHeaders.CONTENT_TYPE, "text/plain") - end("Hello, World!")/*.await()*/ + end("Hello, World!")/*.coAwait()*/ } } } diff --git a/frameworks/Kotlin/vertx-web-kotlinx/src/main/kotlin/Serializers.kt b/frameworks/Kotlin/vertx-web-kotlinx/src/main/kotlin/Serializers.kt new file mode 100644 index 00000000000..c975fc07fdd --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlinx/src/main/kotlin/Serializers.kt @@ -0,0 +1,7 @@ +import kotlinx.serialization.serializer + +object Serializers { + val message = serializer() + val world = serializer() + val worlds = serializer>() +} \ No newline at end of file diff --git a/frameworks/Kotlin/vertx-web-kotlinx/src/main/kotlin/VertxCoroutine.kt b/frameworks/Kotlin/vertx-web-kotlinx/src/main/kotlin/VertxCoroutine.kt index bb8d419d399..d6230fef7f7 100644 --- a/frameworks/Kotlin/vertx-web-kotlinx/src/main/kotlin/VertxCoroutine.kt +++ b/frameworks/Kotlin/vertx-web-kotlinx/src/main/kotlin/VertxCoroutine.kt @@ -1,6 +1,5 @@ -import io.vertx.core.CompositeFuture import io.vertx.core.Future -import io.vertx.kotlin.coroutines.await +import io.vertx.kotlin.coroutines.coAwait suspend fun List>.awaitAll(): List = - CompositeFuture.all(this).await().list() + Future.all(this).coAwait().list() diff --git a/frameworks/Kotlin/vertx-web-kotlinx/vertx-web-kotlinx-postgresql.dockerfile b/frameworks/Kotlin/vertx-web-kotlinx/vertx-web-kotlinx-postgresql.dockerfile index 55c7c2c6803..17b43ebcb5c 100644 --- a/frameworks/Kotlin/vertx-web-kotlinx/vertx-web-kotlinx-postgresql.dockerfile +++ b/frameworks/Kotlin/vertx-web-kotlinx/vertx-web-kotlinx-postgresql.dockerfile @@ -1,12 +1,11 @@ -FROM gradle:8.0-jdk17 +FROM gradle:8.10.2-jdk21 WORKDIR /vertx-web-kotlinx COPY build.gradle.kts build.gradle.kts COPY settings.gradle.kts settings.gradle.kts COPY gradle.properties gradle.properties COPY src src -RUN gradle assembleDist -RUN tar -xf build/distributions/vertx-web-kotlinx-benchmark.tar +RUN gradle --no-daemon installDist EXPOSE 8080 @@ -25,4 +24,4 @@ CMD export JAVA_OPTS=" \ -Dio.netty.buffer.checkBounds=false \ -Dio.netty.buffer.checkAccessible=false \ " && \ - vertx-web-kotlinx-benchmark/bin/vertx-web-kotlinx-benchmark true + build/install/vertx-web-kotlinx-benchmark/bin/vertx-web-kotlinx-benchmark true diff --git a/frameworks/Kotlin/vertx-web-kotlinx/vertx-web-kotlinx.dockerfile b/frameworks/Kotlin/vertx-web-kotlinx/vertx-web-kotlinx.dockerfile index 63d2d4a68ad..4bdc993707d 100644 --- a/frameworks/Kotlin/vertx-web-kotlinx/vertx-web-kotlinx.dockerfile +++ b/frameworks/Kotlin/vertx-web-kotlinx/vertx-web-kotlinx.dockerfile @@ -1,12 +1,11 @@ -FROM gradle:8.0-jdk17 +FROM gradle:8.10.2-jdk21 WORKDIR /vertx-web-kotlinx COPY build.gradle.kts build.gradle.kts COPY settings.gradle.kts settings.gradle.kts COPY gradle.properties gradle.properties COPY src src -RUN gradle assembleDist -RUN tar -xf build/distributions/vertx-web-kotlinx-benchmark.tar +RUN gradle --no-daemon installDist EXPOSE 8080 @@ -25,4 +24,4 @@ CMD export JAVA_OPTS=" \ -Dio.netty.buffer.checkBounds=false \ -Dio.netty.buffer.checkAccessible=false \ " && \ - vertx-web-kotlinx-benchmark/bin/vertx-web-kotlinx-benchmark false + build/install/vertx-web-kotlinx-benchmark/bin/vertx-web-kotlinx-benchmark false diff --git a/frameworks/PHP/cakephp/cakephp.dockerfile b/frameworks/PHP/cakephp/cakephp.dockerfile index 438d467b820..3ebafcb9d8c 100644 --- a/frameworks/PHP/cakephp/cakephp.dockerfile +++ b/frameworks/PHP/cakephp/cakephp.dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:22.04 +FROM ubuntu:24.04 ARG DEBIAN_FRONTEND=noninteractive @@ -7,22 +7,22 @@ RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php > /dev/null && \ apt-get update -yqq > /dev/null && apt-get upgrade -yqq > /dev/null RUN apt-get install -yqq nginx git unzip \ - php8.3-fpm php8.3-mysql php8.3-xml php8.3-mbstring php8.3-intl php8.3-dev php8.3-curl php-xdebug > /dev/null + php8.4-fpm php8.4-mysql php8.4-xml php8.4-mbstring php8.4-intl php8.4-dev php8.4-curl php-xdebug > /dev/null COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer -COPY deploy/conf/* /etc/php/8.3/fpm/ -COPY deploy/conf/* /etc/php/8.3/cli/ +COPY deploy/conf/* /etc/php/8.4/fpm/ +COPY deploy/conf/* /etc/php/8.4/cli/ ADD ./ /cakephp WORKDIR /cakephp -RUN if [ $(nproc) = 2 ]; then sed -i "s|pm.max_children = 1024|pm.max_children = 512|g" /etc/php/8.3/fpm/php-fpm.conf ; fi; +RUN if [ $(nproc) = 2 ]; then sed -i "s|pm.max_children = 1024|pm.max_children = 512|g" /etc/php/8.4/fpm/php-fpm.conf ; fi; -RUN composer install --optimize-autoloader --classmap-authoritative --no-dev +RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet RUN chmod -R 777 /cakephp -CMD service php8.3-fpm start && \ +CMD service php8.4-fpm start && \ nginx -c /cakephp/deploy/nginx.conf diff --git a/frameworks/PHP/comet/comet.dockerfile b/frameworks/PHP/comet/comet.dockerfile index b210108832e..2cfbc0935c6 100644 --- a/frameworks/PHP/comet/comet.dockerfile +++ b/frameworks/PHP/comet/comet.dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:22.04 +FROM ubuntu:24.04 ARG DEBIAN_FRONTEND=noninteractive @@ -6,14 +6,14 @@ RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /de RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php > /dev/null && \ apt-get update -yqq > /dev/null && apt-get upgrade -yqq > /dev/null -RUN apt-get install -yqq php8.3-cli php8.3-pgsql php8.3-xml php8.3-mbstring > /dev/null +RUN apt-get install -yqq php8.4-cli php8.4-pgsql php8.4-xml php8.4-mbstring > /dev/null COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer -RUN apt-get install -y php-pear php8.3-dev libevent-dev git > /dev/null -RUN pecl install event-3.1.3 > /dev/null && echo "extension=event.so" > /etc/php/8.3/cli/conf.d/event.ini +RUN apt-get install -y php-pear php8.4-dev libevent-dev git > /dev/null +RUN pecl install event-3.1.3 > /dev/null && echo "extension=event.so" > /etc/php/8.4/cli/conf.d/event.ini -COPY php.ini /etc/php/8.3/cli/php.ini +COPY php.ini /etc/php/8.4/cli/php.ini ADD ./ /comet WORKDIR /comet diff --git a/frameworks/PHP/duckphp/duckphp.dockerfile b/frameworks/PHP/duckphp/duckphp.dockerfile index f81fcdb546f..ae97dfc8cf6 100644 --- a/frameworks/PHP/duckphp/duckphp.dockerfile +++ b/frameworks/PHP/duckphp/duckphp.dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:22.04 +FROM ubuntu:24.04 ARG DEBIAN_FRONTEND=noninteractive @@ -6,18 +6,18 @@ RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /de RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php RUN apt-get update -yqq > /dev/null && \ apt-get install -yqq nginx git unzip \ - php8.3-cli php8.3-fpm php8.3-mysql php8.3-dev > /dev/null + php8.4-cli php8.4-fpm php8.4-mysql php8.4-dev > /dev/null COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer -COPY deploy/conf/* /etc/php/8.3/fpm/ +COPY deploy/conf/* /etc/php/8.4/fpm/ -ADD ./ /duckphp WORKDIR /duckphp +COPY --link . . -RUN if [ $(nproc) = 2 ]; then sed -i "s|pm.max_children = 1024|pm.max_children = 512|g" /etc/php/8.3/fpm/php-fpm.conf ; fi; +RUN if [ $(nproc) = 2 ]; then sed -i "s|pm.max_children = 1024|pm.max_children = 512|g" /etc/php/8.4/fpm/php-fpm.conf ; fi; RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet -CMD service php8.3-fpm start && \ +CMD service php8.4-fpm start && \ nginx -c /duckphp/deploy/nginx.conf diff --git a/frameworks/PHP/flight/flight.dockerfile b/frameworks/PHP/flight/flight.dockerfile index 07dbf7670c9..60b2b3ebc7a 100644 --- a/frameworks/PHP/flight/flight.dockerfile +++ b/frameworks/PHP/flight/flight.dockerfile @@ -1,16 +1,16 @@ -FROM ubuntu:22.04 +FROM ubuntu:24.04 ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /dev/null RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php RUN apt-get update -yqq > /dev/null && \ - apt-get install -yqq nginx git unzip php8.3 php8.3-common php8.3-cli php8.3-fpm php8.3-mysql > /dev/null + apt-get install -yqq nginx git unzip php8.4 php8.4-common php8.4-cli php8.4-fpm php8.4-mysql > /dev/null -COPY deploy/conf/* /etc/php/8.3/fpm/ +COPY deploy/conf/* /etc/php/8.4/fpm/ -ADD ./ /flight WORKDIR /flight +COPY --link . . ENV FLIGHT_DIR="/flight/src" @@ -18,11 +18,11 @@ COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet -RUN if [ $(nproc) = 2 ]; then sed -i "s|pm.max_children = 1024|pm.max_children = 512|g" /etc/php/8.3/fpm/php-fpm.conf ; fi; +RUN if [ $(nproc) = 2 ]; then sed -i "s|pm.max_children = 1024|pm.max_children = 512|g" /etc/php/8.4/fpm/php-fpm.conf ; fi; RUN chmod -R 777 /flight EXPOSE 8080 -CMD service php8.3-fpm start && \ +CMD service php8.4-fpm start && \ nginx -c /flight/deploy/nginx.conf diff --git a/frameworks/PHP/mark/mark.dockerfile b/frameworks/PHP/mark/mark.dockerfile index 1785231f315..7f6b8180969 100644 --- a/frameworks/PHP/mark/mark.dockerfile +++ b/frameworks/PHP/mark/mark.dockerfile @@ -1,24 +1,24 @@ -FROM ubuntu:22.04 +FROM ubuntu:24.04 ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /dev/null RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php RUN apt-get update -yqq > /dev/null && \ - apt-get install -yqq php8.3-cli php8.3-pgsql php8.3-xml > /dev/null + apt-get install -yqq php8.4-cli php8.4-pgsql php8.4-xml > /dev/null COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer -RUN apt-get install -y php-pear php8.3-dev libevent-dev git > /dev/null -RUN pecl install event-3.1.3 > /dev/null && echo "extension=event.so" > /etc/php/8.3/cli/conf.d/event.ini +RUN apt-get install -y php-pear php8.4-dev libevent-dev git > /dev/null +RUN pecl install event-3.1.3 > /dev/null && echo "extension=event.so" > /etc/php/8.4/cli/conf.d/event.ini -COPY php.ini /etc/php/8.3/cli/php.ini +COPY php.ini /etc/php/8.4/cli/php.ini ADD ./ /mark WORKDIR /mark RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet -RUN sed -i "s|opcache.jit=off|opcache.jit=tracing|g" /etc/php/8.3/cli/conf.d/10-opcache.ini +RUN sed -i "s|opcache.jit=off|opcache.jit=tracing|g" /etc/php/8.4/cli/conf.d/10-opcache.ini EXPOSE 8080 diff --git a/frameworks/PHP/php/php-caddy.dockerfile b/frameworks/PHP/php/php-caddy.dockerfile index e148b16570f..0f75378cccf 100644 --- a/frameworks/PHP/php/php-caddy.dockerfile +++ b/frameworks/PHP/php/php-caddy.dockerfile @@ -1,15 +1,15 @@ -FROM ubuntu:22.04 +FROM ubuntu:24.04 ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /dev/null RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php > /dev/null && \ - apt-get update -yqq > /dev/null && apt-get upgrade -yqq + apt-get update -yqq > /dev/null && apt-get upgrade -yqq RUN apt-get install -yqq git unzip curl \ - php8.3 php8.3-common php8.3-cli php8.3-fpm php8.3-mysql > /dev/null + php8.4 php8.4-common php8.4-cli php8.4-fpm php8.4-mysql > /dev/null -COPY deploy/conf/* /etc/php/8.3/fpm/ +COPY deploy/conf/* /etc/php/8.4/fpm/ # Install Caddyserver RUN apt-get install -y debian-keyring debian-archive-keyring apt-transport-https > /dev/null \ @@ -20,11 +20,11 @@ RUN apt-get install -y debian-keyring debian-archive-keyring apt-transport-https ADD ./ /php WORKDIR /php -RUN if [ $(nproc) = 2 ]; then sed -i "s|pm.max_children = 1024|pm.max_children = 512|g" /etc/php/8.3/fpm/php-fpm.conf ; fi; +RUN if [ $(nproc) = 2 ]; then sed -i "s|pm.max_children = 1024|pm.max_children = 512|g" /etc/php/8.4/fpm/php-fpm.conf ; fi; RUN chmod -R 777 /php EXPOSE 8080 -CMD service php8.3-fpm start && \ +CMD service php8.4-fpm start && \ caddy run --config deploy/caddy/Caddyfile diff --git a/frameworks/PHP/php/php-eloquent.dockerfile b/frameworks/PHP/php/php-eloquent.dockerfile index 615f0a10072..ea2e74e1c9a 100644 --- a/frameworks/PHP/php/php-eloquent.dockerfile +++ b/frameworks/PHP/php/php-eloquent.dockerfile @@ -1,20 +1,20 @@ -FROM ubuntu:22.04 +FROM ubuntu:24.04 ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /dev/null RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php RUN apt-get update -yqq > /dev/null && \ - apt-get install -yqq nginx git unzip php8.3 php8.3-common php8.3-cli php8.3-fpm php8.3-mysql php8.3-mbstring php8.3-dev > /dev/null + apt-get install -yqq nginx git unzip php8.4 php8.4-common php8.4-cli php8.4-fpm php8.4-mysql php8.4-mbstring php8.4-dev > /dev/null COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer -COPY deploy/conf/* /etc/php/8.3/fpm/ +COPY deploy/conf/* /etc/php/8.4/fpm/ ADD ./ /php WORKDIR /php -RUN if [ $(nproc) = 2 ]; then sed -i "s|pm.max_children = 1024|pm.max_children = 512|g" /etc/php/8.3/fpm/php-fpm.conf ; fi; +RUN if [ $(nproc) = 2 ]; then sed -i "s|pm.max_children = 1024|pm.max_children = 512|g" /etc/php/8.4/fpm/php-fpm.conf ; fi; COPY deploy/eloquent/composer* ./ RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet @@ -23,6 +23,5 @@ RUN chmod -R 777 /php EXPOSE 8080 -CMD service php8.3-fpm start && \ +CMD service php8.4-fpm start && \ nginx -c /php/deploy/nginx7.conf -g "daemon off;" - diff --git a/frameworks/PHP/php/php-h2o.dockerfile b/frameworks/PHP/php/php-h2o.dockerfile index 3de194e526b..f61bbdbcea6 100644 --- a/frameworks/PHP/php/php-h2o.dockerfile +++ b/frameworks/PHP/php/php-h2o.dockerfile @@ -44,7 +44,7 @@ RUN apt-get -yqq update && \ FROM "ubuntu:${UBUNTU_VERSION}" -ARG PHP_VERSION=8.3 +ARG PHP_VERSION=8.4 ENV TZ=America/Los_Angeles @@ -75,5 +75,5 @@ ARG TFB_TEST_DATABASE ARG TFB_TEST_NAME CMD sed -i "s/num-threads: x/num-threads: $((2 * $(nproc)))/g" /opt/h2o/etc/h2o.conf && \ - service php8.3-fpm start && \ + service php8.4-fpm start && \ /opt/h2o/bin/h2o -c /opt/h2o/etc/h2o.conf diff --git a/frameworks/PHP/php/php-laravel-query-builder.dockerfile b/frameworks/PHP/php/php-laravel-query-builder.dockerfile index 2ca94a15881..ea2e74e1c9a 100644 --- a/frameworks/PHP/php/php-laravel-query-builder.dockerfile +++ b/frameworks/PHP/php/php-laravel-query-builder.dockerfile @@ -1,20 +1,20 @@ -FROM ubuntu:22.04 +FROM ubuntu:24.04 ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /dev/null RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php RUN apt-get update -yqq > /dev/null && \ - apt-get install -yqq nginx git unzip php8.3 php8.3-common php8.3-cli php8.3-fpm php8.3-mysql php8.3-mbstring php8.3-dev > /dev/null + apt-get install -yqq nginx git unzip php8.4 php8.4-common php8.4-cli php8.4-fpm php8.4-mysql php8.4-mbstring php8.4-dev > /dev/null COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer -COPY deploy/conf/* /etc/php/8.3/fpm/ +COPY deploy/conf/* /etc/php/8.4/fpm/ ADD ./ /php WORKDIR /php -RUN if [ $(nproc) = 2 ]; then sed -i "s|pm.max_children = 1024|pm.max_children = 512|g" /etc/php/8.3/fpm/php-fpm.conf ; fi; +RUN if [ $(nproc) = 2 ]; then sed -i "s|pm.max_children = 1024|pm.max_children = 512|g" /etc/php/8.4/fpm/php-fpm.conf ; fi; COPY deploy/eloquent/composer* ./ RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet @@ -23,5 +23,5 @@ RUN chmod -R 777 /php EXPOSE 8080 -CMD service php8.3-fpm start && \ +CMD service php8.4-fpm start && \ nginx -c /php/deploy/nginx7.conf -g "daemon off;" diff --git a/frameworks/PHP/php/php-pgsql-raw.dockerfile b/frameworks/PHP/php/php-pgsql-raw.dockerfile index 374656d40b9..c3d3c922718 100644 --- a/frameworks/PHP/php/php-pgsql-raw.dockerfile +++ b/frameworks/PHP/php/php-pgsql-raw.dockerfile @@ -1,13 +1,13 @@ -FROM ubuntu:22.04 +FROM ubuntu:24.04 ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /dev/null RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php RUN apt-get update -yqq > /dev/null && \ - apt-get install -yqq nginx git unzip php8.3 php8.3-common php8.3-cli php8.3-fpm php8.3-pgsql > /dev/null + apt-get install -yqq nginx git unzip php8.4 php8.4-common php8.4-cli php8.4-fpm php8.4-pgsql > /dev/null -COPY deploy/conf/* /etc/php/8.3/fpm/ +COPY deploy/conf/* /etc/php/8.4/fpm/ ADD ./ /php WORKDIR /php @@ -17,11 +17,11 @@ RUN sed -i "s|PDO('mysql:|PDO('pgsql:|g" dbquery.php RUN sed -i "s|PDO('mysql:|PDO('pgsql:|g" fortune.php RUN sed -i "s|PDO('mysql:|PDO('pgsql:|g" updateraw.php -RUN if [ $(nproc) = 2 ]; then sed -i "s|pm.max_children = 1024|pm.max_children = 512|g" /etc/php/8.3/fpm/php-fpm.conf ; fi; +RUN if [ $(nproc) = 2 ]; then sed -i "s|pm.max_children = 1024|pm.max_children = 512|g" /etc/php/8.4/fpm/php-fpm.conf ; fi; RUN chmod -R 777 /php EXPOSE 8080 -CMD service php8.3-fpm start && \ +CMD service php8.4-fpm start && \ nginx -c /php/deploy/nginx7.conf -g "daemon off;" diff --git a/frameworks/PHP/php/php-pools.dockerfile b/frameworks/PHP/php/php-pools.dockerfile index b62917e0ec3..e217f448e88 100644 --- a/frameworks/PHP/php/php-pools.dockerfile +++ b/frameworks/PHP/php/php-pools.dockerfile @@ -1,23 +1,23 @@ -FROM ubuntu:22.04 +FROM ubuntu:24.04 ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /dev/null RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php RUN apt-get update -yqq > /dev/null && \ - apt-get install -yqq nginx git unzip php8.3 php8.3-common php8.3-cli php8.3-fpm php8.3-mysql > /dev/null + apt-get install -yqq nginx git unzip php8.4 php8.4-common php8.4-cli php8.4-fpm php8.4-mysql > /dev/null -COPY deploy/conf/* /etc/php/8.3/fpm/ +COPY deploy/conf/* /etc/php/8.4/fpm/ ADD ./ /php WORKDIR /php -COPY deploy/conf/php-fpm-pools.conf /etc/php/8.3/fpm/php-fpm.conf -RUN if [ $(nproc) = 2 ]; then sed -i "s|pm.max_children = 512|pm.max_children = 256|g" /etc/php/8.3/fpm/php-fpm.conf ; fi; +COPY deploy/conf/php-fpm-pools.conf /etc/php/8.4/fpm/php-fpm.conf +RUN if [ $(nproc) = 2 ]; then sed -i "s|pm.max_children = 512|pm.max_children = 256|g" /etc/php/8.4/fpm/php-fpm.conf ; fi; RUN chmod -R 777 /php EXPOSE 8080 -CMD service php8.3-fpm start && \ +CMD service php8.4-fpm start && \ nginx -c /php/deploy/nginx-pools.conf -g "daemon off;" diff --git a/frameworks/PHP/php/php-raw7-tcp.dockerfile b/frameworks/PHP/php/php-raw7-tcp.dockerfile index c8cd5eb855c..2dde7c65ee0 100644 --- a/frameworks/PHP/php/php-raw7-tcp.dockerfile +++ b/frameworks/PHP/php/php-raw7-tcp.dockerfile @@ -1,25 +1,25 @@ -FROM ubuntu:22.04 +FROM ubuntu:24.04 ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /dev/null RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php RUN apt-get update -yqq > /dev/null && \ - apt-get install -yqq nginx git unzip php8.3 php8.3-common php8.3-cli php8.3-fpm php8.3-mysql > /dev/null + apt-get install -yqq nginx git unzip php8.4 php8.4-common php8.4-cli php8.4-fpm php8.4-mysql > /dev/null -COPY deploy/conf/* /etc/php/8.3/fpm/ +COPY deploy/conf/* /etc/php/8.4/fpm/ ADD ./ /php WORKDIR /php -RUN sed -i "s|listen = /run/php/php-fpm.sock|listen = 127.0.0.1:9001|g" /etc/php/8.3/fpm/php-fpm.conf +RUN sed -i "s|listen = /run/php/php-fpm.sock|listen = 127.0.0.1:9001|g" /etc/php/8.4/fpm/php-fpm.conf RUN sed -i "s|server unix:/var/run/php/php-fpm.sock;|server 127.0.0.1:9001;|g" deploy/nginx7.conf -RUN if [ $(nproc) = 2 ]; then sed -i "s|pm.max_children = 1024|pm.max_children = 512|g" /etc/php/8.3/fpm/php-fpm.conf ; fi; +RUN if [ $(nproc) = 2 ]; then sed -i "s|pm.max_children = 1024|pm.max_children = 512|g" /etc/php/8.4/fpm/php-fpm.conf ; fi; RUN chmod -R 777 /php EXPOSE 8080 -CMD service php8.3-fpm start && \ - nginx -c /php/deploy/nginx7.conf -g "daemon off;" \ No newline at end of file +CMD service php8.4-fpm start && \ + nginx -c /php/deploy/nginx7.conf -g "daemon off;" diff --git a/frameworks/PHP/php/php.dockerfile b/frameworks/PHP/php/php.dockerfile index 5ad4298427f..495e54b5631 100644 --- a/frameworks/PHP/php/php.dockerfile +++ b/frameworks/PHP/php/php.dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:22.04 +FROM ubuntu:24.04 ARG DEBIAN_FRONTEND=noninteractive @@ -6,19 +6,19 @@ RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /de RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php > /dev/null && \ apt-get update -yqq > /dev/null && apt-get upgrade -yqq RUN apt-get install -yqq nginx git unzip \ - php8.3 php8.3-common php8.3-cli php8.3-fpm php8.3-mysql > /dev/null + php8.4 php8.4-common php8.4-cli php8.4-fpm php8.4-mysql > /dev/null -COPY deploy/conf/* /etc/php/8.3/fpm/ +COPY deploy/conf/* /etc/php/8.4/fpm/ ADD ./ /php WORKDIR /php -RUN if [ $(nproc) = 2 ]; then sed -i "s|pm.max_children = 1024|pm.max_children = 512|g" /etc/php/8.3/fpm/php-fpm.conf ; fi; -RUN sed -i "s|opcache.jit=off|;opcache.jit=off|g" /etc/php/8.3/fpm/conf.d/10-opcache.ini +RUN if [ $(nproc) = 2 ]; then sed -i "s|pm.max_children = 1024|pm.max_children = 512|g" /etc/php/8.4/fpm/php-fpm.conf ; fi; +RUN sed -i "s|opcache.jit=off|;opcache.jit=off|g" /etc/php/8.4/fpm/conf.d/10-opcache.ini RUN chmod -R 777 /php EXPOSE 8080 -CMD service php8.3-fpm start && \ +CMD service php8.4-fpm start && \ nginx -c /php/deploy/nginx7.conf -g "daemon off;" diff --git a/frameworks/PHP/ripple/README.md b/frameworks/PHP/ripple/README.md new file mode 100644 index 00000000000..ce4d89d0bc2 --- /dev/null +++ b/frameworks/PHP/ripple/README.md @@ -0,0 +1,75 @@ +

+Logo +

+

+Build Status +Download statistics +Stable version +License +

+

+Ripple is a modern, high-performance native PHP coroutine engine designed to solve PHP's challenges in high concurrency, complex network communication and data operations. +The engine uses an innovative architecture and efficient programming model to provide powerful and flexible backend support for modern web and web applications. +By using ripple, you will experience the advantages of managing tasks from a global view of the system and efficiently handling network traffic and data.

+ +## Install + +````bash +composer require cloudtay/ripple +```` + +## Latest documentation + +You can visit `ripple`’s [documentation](https://ripple.cloudtay.com/) to start reading + +We recommend that you start with [Manual Installation](https://ripple.cloudtay.com/docs/install/professional) to better +understand the workflow of ripple + +If you want to quickly deploy and use `ripple` services, you can directly +visit [Quick Deployment](https://ripple.cloudtay.com/docs/install/server) + +## Appendix + +### Applicable component library + +> We allow users to choose applicable component libraries by themselves. All components can be used as described in the +> document without additional configuration. + +**🚀 [Guzzle](https://docs.guzzlephp.org/en/stable/)** +PHP is the most widely used HTTP client + +**🔥 [AmPHP](https://amphp.org/)** +Provides rich PHP asynchronous components for users to encapsulate by themselves + +**🚀 [Driver](https://github.com/cloudtay/ripple-driver)** +The official high-performance driver library provides seamless access to your traditional applications. + +**🚀 [Webman-coroutine](https://github.com/workbunny/webman-coroutine)** +The workbunny team's integrated webman coroutine extension provides coroutine support for Webman. + +**🟢[ripple](https://github.com/cloudtay/ripple)** +Provides standard coroutine architecture and tools for rapid development or packaging of traditional applications + +### Event Library Guide + +| Extension Types | Recommended Use | Compatibility | Description | +|:---------------:|:---------------:|:-------------:|:--------------------------------------------------------------------------------------------------------------------:| +| `libev` | 🏅️ | 🟢️ | `Ev` is a more efficient event extension that performs consistently in various systems and is recommended to be used | +| `Native` | ️ | 🟢 | Support the use of PHP's built-in select mechanism | +| `event` | | 🌗 | The event characteristics under different systems are not uniform, and their use is not recommended | + +## Special thanks + + + jetbrains + + +[Jetbrains](https://www.jetbrains.com/?from=ripple) provides free development tools for this project + +### Contact information + +`Email` jingnigg@gmail.com + +`WeChat` jingnigg + +--- diff --git a/frameworks/PHP/ripple/benchmark_config.json b/frameworks/PHP/ripple/benchmark_config.json new file mode 100644 index 00000000000..4c625c29bbe --- /dev/null +++ b/frameworks/PHP/ripple/benchmark_config.json @@ -0,0 +1,29 @@ +{ + "framework": "ripple", + "tests": [ + { + "default": { + "json_url": "/json", + "db_url": "/db", + "query_url": "/queries?queries=", + "fortune_url": "/fortunes", + "update_url": "/updates?queries=", + "plaintext_url": "/plaintext", + "port": 8080, + "approach": "Realistic", + "classification": "Fullstack", + "database": "MySQL", + "framework": "ripple", + "language": "PHP", + "flavor": "PHP8.3", + "orm": "Raw", + "platform": "ripple", + "os": "Linux", + "database_os": "Linux", + "display_name": "ripple", + "notes": "", + "versus": "php" + } + } + ] +} diff --git a/frameworks/PHP/ripple/composer.json b/frameworks/PHP/ripple/composer.json new file mode 100644 index 00000000000..679f58c8cd2 --- /dev/null +++ b/frameworks/PHP/ripple/composer.json @@ -0,0 +1,9 @@ +{ + "require": { + "ext-pdo": "*", + "cloudtay/ripple-http": "^1.0", + "cloudtay/ripple": "^1.0" + }, + "minimum-stability": "beta", + "prefer-stable": true +} diff --git a/frameworks/PHP/ripple/fortunes.php b/frameworks/PHP/ripple/fortunes.php new file mode 100644 index 00000000000..32f765eabb6 --- /dev/null +++ b/frameworks/PHP/ripple/fortunes.php @@ -0,0 +1,18 @@ + + +Fortunes + + + + + + + + + + + + +
idmessage
+ + diff --git a/frameworks/PHP/ripple/ripple.dockerfile b/frameworks/PHP/ripple/ripple.dockerfile new file mode 100644 index 00000000000..fb521c874f4 --- /dev/null +++ b/frameworks/PHP/ripple/ripple.dockerfile @@ -0,0 +1,32 @@ +FROM php:8.3-cli + +RUN apt-get update -yqq >> /dev/null +RUN apt-get install -y libevent-dev \ + libssl-dev \ + pkg-config \ + build-essential \ + unzip >> /dev/null + +RUN docker-php-ext-install pdo_mysql \ + opcache \ + posix \ + pcntl \ + sockets >> /dev/null + +RUN pecl install event >> /dev/null + +RUN docker-php-ext-enable posix pcntl sockets +RUN docker-php-ext-enable --ini-name zz-event.ini event + +COPY --from=composer --link /usr/bin/composer /usr/local/bin/composer + +# Initialize +WORKDIR /ripple +COPY --link . . + +# Configure +RUN composer install --quiet + +# Start +EXPOSE 8080 +ENTRYPOINT ["php","server.php"] diff --git a/frameworks/PHP/ripple/server.php b/frameworks/PHP/ripple/server.php new file mode 100644 index 00000000000..8a615fb91c5 --- /dev/null +++ b/frameworks/PHP/ripple/server.php @@ -0,0 +1,200 @@ +format('D, d M Y H:i:s T'); + } + + + /** + * @return int + */ + public static function randomInt(): int + { + try { + return \random_int(1, 10000); + } catch (Throwable $e) { + return mt_rand(1, 10000); + } + } + + /** + * @param mixed $value + * + * @return int + */ + public static function clamp(mixed $value): int + { + if (!\is_numeric($value) || $value < 1) { + return 1; + } + if ($value > 500) { + return 500; + } + return \intval($value); + } + + /** + * @param string $template + * @param array $data + * + * @return string + */ + public static function render(string $template, array $data = []): string + { + foreach ($data as $key => $value) { + $$key = $value; + } + + \ob_start(); + include $template; + return \ob_get_clean(); + } +} + +$manager = new Manager(); +$worker = new class() extends \Ripple\Worker { + /*** @var \Ripple\Http\Server */ + public Server $server; + + /** + * @param \Ripple\Worker\Manager $manager + * + * @return void + */ + public function register(Manager $manager): void + { + $this->count = 64; + $this->server = new Server('http://0.0.0.0:8080'); + } + + /** + * @return void + */ + public function boot(): void + { + Setup::dateRefresh(); + repeat(static fn () => Setup::dateRefresh(), 1); + + Setup::$pdo = new \PDO( + 'mysql:host=tfb-database;port=3306;dbname=hello_world', + 'benchmarkdbuser', + 'benchmarkdbpass', + [ + \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC, + \PDO::ATTR_EMULATE_PREPARES => false, + \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, + ] + ); + + Setup::$queryWorldWhereID = Setup::$pdo->prepare('SELECT id, randomNumber FROM World WHERE id = ?'); + Setup::$updateWorldRandomNumber = Setup::$pdo->prepare('UPDATE World SET randomNumber = ? WHERE id = ?'); + Setup::$queryFortune = Setup::$pdo->prepare('SELECT * FROM `Fortune`'); + $this->server->onRequest(fn (Server\Request $request) => $this->onRequest($request)); + $this->server->listen(); + } + + /** + * @param \Ripple\Http\Server\Request $request + * + * @return void + */ + public function onRequest(Server\Request $request): void + { + switch ($request->SERVER['REQUEST_URI']) { + case '/json': + $request->respondJson( + ['message' => 'Hello, World!'], + ['Date' => Setup::$dateFormatted] + ); + break; + + case '/db': + $statement = Setup::$queryWorldWhereID; + $statement->execute([Setup::randomInt()]); + $request->respondJson($statement->fetch(), ['Date' => Setup::$dateFormatted]); + break; + + case '/queries': + $queries = Setup::clamp($request->GET['queries'] ?? 1); + $results = []; + $statement = Setup::$queryWorldWhereID; + while ($queries--) { + $statement->execute([Setup::randomInt()]); + $results[] = $statement->fetch(); + } + $request->respondJson($results, ['Date' => Setup::$dateFormatted]); + + break; + case '/fortunes': + $rows = Setup::$pdo->query('SELECT * FROM `Fortune`')?->fetchAll(); + $rows[] = ['id' => 0, 'message' => 'Additional fortune added at request time.']; + \usort($rows, function ($a, $b) { + return $a['message'] <=> $b['message']; + }); + + $request->respondHtml( + Setup::render('fortunes.php', ['rows' => $rows]), + [ + 'Date' => Setup::$dateFormatted, + 'Content-Type' => 'text/html; charset=UTF-8' + ] + ); + break; + + case '/updates': + $queries = Setup::clamp($request->GET['queries'] ?? 1); + $results = []; + $statement = Setup::$queryWorldWhereID; + $update = Setup::$updateWorldRandomNumber; + while ($queries--) { + $statement->execute([Setup::randomInt()]); + $row = $statement->fetch(); + $row['randomNumber'] = Setup::randomInt(); + $results[] = $row; + $update->execute([$row['randomNumber'], $row['id']]); + } + $request->respondJson($results, ['Date' => Setup::$dateFormatted]); + break; + + case '/plaintext': + $request->respond( + 'Hello, World!', + [ + 'Content-Type' => 'text/plain; charset=utf-8', + 'Date' => Setup::$dateFormatted + ] + ); + break; + + default: + $request->respond('Not Found', [], 404); + } + } +}; + +$manager->addWorker($worker); +$manager->run(); +wait(); diff --git a/frameworks/PHP/swoole/swoole-async-mysql.dockerfile b/frameworks/PHP/swoole/swoole-async-mysql.dockerfile index 786f80c88cd..494b15844d3 100644 --- a/frameworks/PHP/swoole/swoole-async-mysql.dockerfile +++ b/frameworks/PHP/swoole/swoole-async-mysql.dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:24.04 -ENV SWOOLE_VERSION 5.1.5 +ENV SWOOLE_VERSION 6.0.0 ENV ENABLE_COROUTINE 1 ENV CPU_MULTIPLES 1 ENV DATABASE_DRIVER mysql @@ -11,22 +11,22 @@ RUN apt update -yqq > /dev/null \ && apt install -yqq software-properties-common > /dev/null \ && LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php > /dev/null \ && apt update -yqq > /dev/null \ - && apt install libbrotli-dev php8.3-cli php8.3-pdo-mysql php8.3-dev -y > /dev/null \ + && apt install libbrotli-dev php8.4-cli php8.4-pdo-mysql php8.4-dev -y > /dev/null \ && cd /tmp && curl -sSL "https://github.com/swoole/swoole-src/archive/v${SWOOLE_VERSION}.tar.gz" | tar xzf - \ && cd /tmp/swoole-src-${SWOOLE_VERSION} \ && phpize > /dev/null \ && ./configure > /dev/null \ && make -j "$(nproc)" > /dev/null \ && make install > /dev/null \ - && echo "extension=swoole.so" > /etc/php/8.3/cli/conf.d/50-swoole.ini \ - && echo "memory_limit=1024M" >> /etc/php/8.3/cli/php.ini + && echo "extension=swoole.so" > /etc/php/8.4/cli/conf.d/50-swoole.ini \ + && echo "memory_limit=1024M" >> /etc/php/8.4/cli/php.ini WORKDIR /swoole ADD ./swoole-server.php /swoole ADD ./database.php /swoole -COPY 10-opcache.ini /etc/php/8.3/cli/conf.d/10-opcache.ini +COPY 10-opcache.ini /etc/php/8.4/cli/conf.d/10-opcache.ini EXPOSE 8080 CMD php /swoole/swoole-server.php diff --git a/frameworks/PHP/swoole/swoole-async-postgres.dockerfile b/frameworks/PHP/swoole/swoole-async-postgres.dockerfile index b658f569f0e..7050668fe56 100644 --- a/frameworks/PHP/swoole/swoole-async-postgres.dockerfile +++ b/frameworks/PHP/swoole/swoole-async-postgres.dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:24.04 -ENV SWOOLE_VERSION 5.1.5 +ENV SWOOLE_VERSION 6.0.0 ENV ENABLE_COROUTINE 1 ENV CPU_MULTIPLES 1 ENV DATABASE_DRIVER pgsql @@ -11,22 +11,22 @@ RUN apt update -yqq > /dev/null \ && apt install -yqq software-properties-common > /dev/null \ && LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php > /dev/null \ && apt update -yqq > /dev/null \ - && apt install libbrotli-dev php8.3-cli php8.3-pdo-pgsql php8.3-dev libpq-dev -y > /dev/null \ + && apt install libbrotli-dev php8.4-cli php8.4-pdo-pgsql php8.4-dev libpq-dev -y > /dev/null \ && cd /tmp && curl -sSL "https://github.com/swoole/swoole-src/archive/v${SWOOLE_VERSION}.tar.gz" | tar xzf - \ && cd /tmp/swoole-src-${SWOOLE_VERSION} \ && phpize > /dev/null \ && ./configure --enable-swoole-pgsql > /dev/null \ && make -j "$(nproc)" > /dev/null \ && make install > /dev/null \ - && echo "extension=swoole.so" > /etc/php/8.3/cli/conf.d/50-swoole.ini \ - && echo "memory_limit=1024M" >> /etc/php/8.3/cli/php.ini + && echo "extension=swoole.so" > /etc/php/8.4/cli/conf.d/50-swoole.ini \ + && echo "memory_limit=1024M" >> /etc/php/8.4/cli/php.ini WORKDIR /swoole ADD ./swoole-server.php /swoole ADD ./database.php /swoole -COPY 10-opcache.ini /etc/php/8.3/cli/conf.d/10-opcache.ini +COPY 10-opcache.ini /etc/php/8.4/cli/conf.d/10-opcache.ini EXPOSE 8080 CMD php /swoole/swoole-server.php diff --git a/frameworks/PHP/swoole/swoole-sync-mysql.dockerfile b/frameworks/PHP/swoole/swoole-sync-mysql.dockerfile index 86304d151fc..a97871cea8c 100644 --- a/frameworks/PHP/swoole/swoole-sync-mysql.dockerfile +++ b/frameworks/PHP/swoole/swoole-sync-mysql.dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:24.04 -ENV SWOOLE_VERSION 5.1.5 +ENV SWOOLE_VERSION 6.0.0 ENV ENABLE_COROUTINE 0 ENV CPU_MULTIPLES 1 ENV DATABASE_DRIVER mysql @@ -11,22 +11,22 @@ RUN apt update -yqq > /dev/null \ && apt install -yqq software-properties-common > /dev/null \ && LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php > /dev/null \ && apt update -yqq > /dev/null \ - && apt install libbrotli-dev php8.3-cli php8.3-pdo-mysql php8.3-dev -y > /dev/null \ + && apt install libbrotli-dev php8.4-cli php8.4-pdo-mysql php8.4-dev -y > /dev/null \ && cd /tmp && curl -sSL "https://github.com/swoole/swoole-src/archive/v${SWOOLE_VERSION}.tar.gz" | tar xzf - \ && cd /tmp/swoole-src-${SWOOLE_VERSION} \ && phpize > /dev/null \ && ./configure > /dev/null \ && make -j "$(nproc)" > /dev/null \ && make install > /dev/null \ - && echo "extension=swoole.so" > /etc/php/8.3/cli/conf.d/50-swoole.ini \ - && echo "memory_limit=1024M" >> /etc/php/8.3/cli/php.ini + && echo "extension=swoole.so" > /etc/php/8.4/cli/conf.d/50-swoole.ini \ + && echo "memory_limit=1024M" >> /etc/php/8.4/cli/php.ini WORKDIR /swoole ADD ./swoole-server.php /swoole ADD ./database.php /swoole -COPY 10-opcache.ini /etc/php/8.3/cli/conf.d/10-opcache.ini +COPY 10-opcache.ini /etc/php/8.4/cli/conf.d/10-opcache.ini EXPOSE 8080 CMD php /swoole/swoole-server.php diff --git a/frameworks/PHP/swoole/swoole-sync-postgres.dockerfile b/frameworks/PHP/swoole/swoole-sync-postgres.dockerfile index 8054b66912b..e45b90683df 100644 --- a/frameworks/PHP/swoole/swoole-sync-postgres.dockerfile +++ b/frameworks/PHP/swoole/swoole-sync-postgres.dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:24.04 -ENV SWOOLE_VERSION 5.1.5 +ENV SWOOLE_VERSION 6.0.0 ENV ENABLE_COROUTINE 0 ENV CPU_MULTIPLES 4 ENV DATABASE_DRIVER pgsql @@ -11,22 +11,22 @@ RUN apt update -yqq > /dev/null \ && apt install -yqq software-properties-common > /dev/null \ && LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php > /dev/null \ && apt update -yqq > /dev/null \ - && apt install libbrotli-dev php8.3-cli php8.3-pdo-pgsql php8.3-dev libpq-dev -y > /dev/null \ + && apt install libbrotli-dev php8.4-cli php8.4-pdo-pgsql php8.4-dev libpq-dev -y > /dev/null \ && cd /tmp && curl -sSL "https://github.com/swoole/swoole-src/archive/v${SWOOLE_VERSION}.tar.gz" | tar xzf - \ && cd /tmp/swoole-src-${SWOOLE_VERSION} \ && phpize > /dev/null \ && ./configure > /dev/null \ && make -j "$(nproc)" > /dev/null \ && make install > /dev/null \ - && echo "extension=swoole.so" > /etc/php/8.3/cli/conf.d/50-swoole.ini \ - && echo "memory_limit=1024M" >> /etc/php/8.3/cli/php.ini + && echo "extension=swoole.so" > /etc/php/8.4/cli/conf.d/50-swoole.ini \ + && echo "memory_limit=1024M" >> /etc/php/8.4/cli/php.ini WORKDIR /swoole ADD ./swoole-server.php /swoole ADD ./database.php /swoole -COPY 10-opcache.ini /etc/php/8.3/cli/conf.d/10-opcache.ini +COPY 10-opcache.ini /etc/php/8.4/cli/conf.d/10-opcache.ini EXPOSE 8080 CMD php /swoole/swoole-server.php diff --git a/frameworks/PHP/webman/app/controller/Index.php b/frameworks/PHP/webman/app/controller/Index.php index 947e85a0d0b..1b27503762f 100644 --- a/frameworks/PHP/webman/app/controller/Index.php +++ b/frameworks/PHP/webman/app/controller/Index.php @@ -9,7 +9,7 @@ class Index { - public function plaintext($request) + public function plaintext() { return new Response(200, [ 'Content-Type' => 'text/plain', @@ -18,7 +18,7 @@ public function plaintext($request) } - public function json($request) + public function json() { return new Response(200, [ 'Content-Type' => 'application/json', @@ -26,7 +26,7 @@ public function json($request) ], json_encode(['message' => 'Hello, World!'])); } - public function db($request) + public function db() { $statement = Db::$random; $statement->execute([\mt_rand(1, 10000)]); @@ -37,7 +37,7 @@ public function db($request) ], json_encode($statement->fetch())); } - public function fortunes($request) + public function fortunes() { $fortune = Db::$fortune; @@ -59,7 +59,7 @@ public function fortunes($request) ); } - public function queries($request, $q = 1) + public function queries(Request $request, $q = 1) { $statement = Db::$random; @@ -80,7 +80,7 @@ public function queries($request, $q = 1) ], json_encode($arr)); } - public function updates($request, $q = 1) + public function updates(Request $request, $q = 1) { $random = Db::$random; diff --git a/frameworks/PHP/webman/benchmark_config.json b/frameworks/PHP/webman/benchmark_config.json index 7e185ce1b24..5c2a7f4d4bf 100644 --- a/frameworks/PHP/webman/benchmark_config.json +++ b/frameworks/PHP/webman/benchmark_config.json @@ -1,6 +1,5 @@ { "framework": "webman", - "maintainers": ["walkor"], "tests": [{ "default": { "dockerfile": "webman.dockerfile", diff --git a/frameworks/PHP/webman/composer.json b/frameworks/PHP/webman/composer.json index 73af23714b2..317e6598381 100644 --- a/frameworks/PHP/webman/composer.json +++ b/frameworks/PHP/webman/composer.json @@ -25,9 +25,10 @@ }, "require": { "php": ">=7.2", - "workerman/webman-framework": "^1.0", + "workerman/webman-framework": "dev-master", "monolog/monolog": "^2.0", - "vlucas/phpdotenv": ">=4.1,<6.0" + "vlucas/phpdotenv": ">=4.1,<6.0", + "workerman/workerman": "dev-master" }, "suggest": { "ext-event": "For better performance. " @@ -51,5 +52,6 @@ "pre-package-uninstall": [ "support\\Plugin::uninstall" ] - } -} + }, + "minimum-stability": "dev" +} \ No newline at end of file diff --git a/frameworks/PHP/webman/webman-pgsql.dockerfile b/frameworks/PHP/webman/webman-pgsql.dockerfile index 491396785ea..2fe12ba3529 100644 --- a/frameworks/PHP/webman/webman-pgsql.dockerfile +++ b/frameworks/PHP/webman/webman-pgsql.dockerfile @@ -8,19 +8,19 @@ RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /de RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php > /dev/null && \ apt-get update -yqq > /dev/null && apt-get upgrade -yqq > /dev/null -RUN apt-get install -yqq php8.3-cli php8.3-pgsql php8.3-xml > /dev/null +RUN apt-get install -yqq php8.4-cli php8.4-pgsql php8.4-xml > /dev/null COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer -RUN apt-get update -yqq && apt-get install -y php-pear php8.3-dev libevent-dev git > /dev/null -RUN pecl install event-3.1.4 > /dev/null && echo "extension=event.so" > /etc/php/8.3/cli/conf.d/30-event.ini +RUN apt-get update -yqq && apt-get install -y php-pear php8.4-dev libevent-dev git > /dev/null +RUN pecl install event-3.1.4 > /dev/null && echo "extension=event.so" > /etc/php/8.4/cli/conf.d/30-event.ini -ADD ./ /webman WORKDIR /webman +COPY --link . . RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet -COPY php.ini /etc/php/8.3/cli/conf.d/10-opcache.ini +COPY php.ini /etc/php/8.4/cli/conf.d/10-opcache.ini EXPOSE 8080 -CMD php /webman/start.php start +CMD php /webman/start.php start \ No newline at end of file diff --git a/frameworks/PHP/webman/webman.dockerfile b/frameworks/PHP/webman/webman.dockerfile index 20ef9d61064..6020fed75ed 100644 --- a/frameworks/PHP/webman/webman.dockerfile +++ b/frameworks/PHP/webman/webman.dockerfile @@ -8,19 +8,19 @@ RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /de RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php > /dev/null && \ apt-get update -yqq > /dev/null && apt-get upgrade -yqq > /dev/null -RUN apt-get install -yqq php8.3-cli php8.3-pgsql php8.3-xml > /dev/null +RUN apt-get install -yqq php8.4-cli php8.4-pgsql php8.4-xml > /dev/null COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer -RUN apt-get update -yqq && apt-get install -y php-pear php8.3-dev libevent-dev git > /dev/null -RUN pecl install event-3.1.4 > /dev/null && echo "extension=event.so" > /etc/php/8.3/cli/conf.d/30-event.ini +RUN apt-get update -yqq && apt-get install -y php-pear php8.4-dev libevent-dev git > /dev/null +RUN pecl install event-3.1.4 > /dev/null && echo "extension=event.so" > /etc/php/8.4/cli/conf.d/30-event.ini -ADD ./ /webman WORKDIR /webman +COPY --link . . RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet -COPY php.ini /etc/php/8.3/cli/conf.d/10-opcache.ini +COPY php.ini /etc/php/8.4/cli/conf.d/10-opcache.ini EXPOSE 8080 -CMD php /webman/start.php start +CMD php /webman/start.php start \ No newline at end of file diff --git a/frameworks/PHP/wolff/wolff.dockerfile b/frameworks/PHP/wolff/wolff.dockerfile index a22a0ceb76c..d3dfc84bfad 100644 --- a/frameworks/PHP/wolff/wolff.dockerfile +++ b/frameworks/PHP/wolff/wolff.dockerfile @@ -1,26 +1,26 @@ -FROM ubuntu:22.04 +FROM ubuntu:24.04 ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /dev/null RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php RUN apt-get install -yqq nginx git unzip \ - php8.3-fpm php8.3-mysql php8.3-xml php8.3-mbstring php8.3-intl php8.3-dev php8.3-curl > /dev/null + php8.4-fpm php8.4-mysql php8.4-xml php8.4-mbstring php8.4-intl php8.4-dev php8.4-curl > /dev/null -COPY deploy/conf/* /etc/php/8.3/fpm/ +COPY deploy/conf/* /etc/php/8.4/fpm/ -ADD ./ /wolff WORKDIR /wolff +COPY --link . . COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet -RUN if [ $(nproc) = 2 ]; then sed -i "s|pm.max_children = 1024|pm.max_children = 512|g" /etc/php/8.3/fpm/php-fpm.conf ; fi; +RUN if [ $(nproc) = 2 ]; then sed -i "s|pm.max_children = 1024|pm.max_children = 512|g" /etc/php/8.4/fpm/php-fpm.conf ; fi; RUN chmod -R 777 /wolff EXPOSE 8080 -CMD service php8.3-fpm start && \ +CMD service php8.4-fpm start && \ nginx -c /wolff/deploy/nginx.conf 2>&1 > /dev/stderr diff --git a/frameworks/PHP/workerman/Date.php b/frameworks/PHP/workerman/Date.php new file mode 100644 index 00000000000..d09638da713 --- /dev/null +++ b/frameworks/PHP/workerman/Date.php @@ -0,0 +1,15 @@ +date = gmdate('D, d M Y H:i:s').' GMT'; + Timer::add(1, function() { + $this->date = gmdate('D, d M Y H:i:s').' GMT'; + }); + } +} \ No newline at end of file diff --git a/frameworks/PHP/workerman/Mysql.php b/frameworks/PHP/workerman/Mysql.php new file mode 100644 index 00000000000..c8dcd0ff40a --- /dev/null +++ b/frameworks/PHP/workerman/Mysql.php @@ -0,0 +1,74 @@ +pdo = new PDO( + 'mysql:host=tfb-database;dbname=hello_world', + 'benchmarkdbuser', + 'benchmarkdbpass', + [ + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + PDO::ATTR_EMULATE_PREPARES => false + ] + ); + $this->world = $this->pdo->prepare('SELECT id,randomNumber FROM World WHERE id=?'); + $this->fortune = $this->pdo->prepare('SELECT id,message FROM Fortune'); + $this->update = $this->pdo->prepare('UPDATE World SET randomNumber=? WHERE id=?'); + } + + function db(): array + { + $this->world->execute([mt_rand(1, 10000)]); + return $this->world->fetch(); + } + + function query($request): array + { + $count = min(max((int) $request->get('q'), 1), 500); + $arr = []; + while ($count--) { + $this->world->execute([mt_rand(1, 10000)]); + $arr[] = $this->world->fetch(); + } + return $arr; + } + + function update($request): array + { + $count = min(max((int) $request->get('q'), 1), 500); + $arr = []; + while ($count--) { + $id = mt_rand(1, 10000); + $this->world->execute([$id]); + $item = $this->world->fetch(); + $this->update->execute( + [$item['randomNumber'] = mt_rand(1, 10000), $id] + ); + $arr[] = $item; + } + return $arr; + } + + function fortune(): string + { + $this->fortune->execute(); + $arr = $this->fortune->fetchAll(PDO::FETCH_KEY_PAIR); + $arr[0] = 'Additional fortune added at request time.'; + asort($arr); + $html = ''; + foreach ($arr as $id => $message) { + $message = htmlspecialchars($message, ENT_QUOTES, 'UTF-8'); + $html .= "$id$message"; + } + return "Fortunes$html
idmessage
"; + } + +} \ No newline at end of file diff --git a/frameworks/PHP/workerman/MysqlSwoole.php b/frameworks/PHP/workerman/MysqlSwoole.php new file mode 100644 index 00000000000..d294afae62d --- /dev/null +++ b/frameworks/PHP/workerman/MysqlSwoole.php @@ -0,0 +1,83 @@ +withDriver('mysql') + ->withHost('tfb-database') + ->withPort(3306) + ->withDbName('hello_world') + ->withUsername('benchmarkdbuser') + ->withPassword('benchmarkdbpass'); + $this->pool = new PDOPool($config, $size); + } + + function db(): array + { + $pdo = $this->pool->get(); + $stmt = $pdo->prepare('SELECT id,randomNumber FROM World WHERE id=?'); + $stmt->execute([mt_rand(1, 10000)]); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $this->pool->put($pdo); + return $result; + } + + function query($request): array + { + $count = min(max((int) $request->get('q'), 1), 500); + $pdo = $this->pool->get(); + $stmt = $pdo->prepare('SELECT id,randomNumber FROM World WHERE id=?'); + $arr = []; + while ($count--) { + $stmt->execute([mt_rand(1, 10000)]); + $arr[] = $stmt->fetch(PDO::FETCH_ASSOC); + } + $this->pool->put($pdo); + return $arr; + } + + function update($request): array + { + $count = min(max((int) $request->get('q'), 1), 500); + $arr = []; + $pdo = $this->pool->get(); + $world = $pdo->prepare('SELECT id,randomNumber FROM World WHERE id=?'); + $update = $pdo->prepare('UPDATE World SET randomNumber=? WHERE id=?'); + while ($count--) { + $id = mt_rand(1, 10000); + $world->execute([$id]); + $item = $world->fetch(PDO::FETCH_ASSOC); + $update->execute( + [$item['randomNumber'] = mt_rand(1, 10000), $id] + ); + $arr[] = $item; + } + $this->pool->put($pdo); + return $arr; + } + + function fortune(): string + { + $pdo = $this->pool->get(); + $stmt = $pdo->prepare('SELECT id,message FROM Fortune'); + $stmt->execute(); + $arr = $stmt->fetchAll(PDO::FETCH_KEY_PAIR); + $this->pool->put($pdo); + $arr[0] = 'Additional fortune added at request time.'; + asort($arr); + $html = ''; + foreach ($arr as $id => $message) { + $message = htmlspecialchars($message, ENT_QUOTES, 'UTF-8'); + $html .= "$id$message"; + } + return "Fortunes$html
idmessage
"; + } + +} \ No newline at end of file diff --git a/frameworks/PHP/workerman/MysqlSwow.php b/frameworks/PHP/workerman/MysqlSwow.php new file mode 100644 index 00000000000..81cb8534c2a --- /dev/null +++ b/frameworks/PHP/workerman/MysqlSwow.php @@ -0,0 +1,12 @@ +pool = new Pool("mysql:host=tfb-database;dbname=hello_world", 'benchmarkdbuser', 'benchmarkdbpass', $size); + } + +} \ No newline at end of file diff --git a/frameworks/PHP/workerman/Pgsql.php b/frameworks/PHP/workerman/Pgsql.php new file mode 100644 index 00000000000..4f4be58fe44 --- /dev/null +++ b/frameworks/PHP/workerman/Pgsql.php @@ -0,0 +1,49 @@ +pdo = new PDO( + 'pgsql:host=tfb-database;dbname=hello_world', + 'benchmarkdbuser', + 'benchmarkdbpass', + [ + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_EMULATE_PREPARES => false + ] + ); + $this->world = $this->random = $this->pdo->prepare('SELECT id,randomNumber FROM World WHERE id=?'); + $this->fortune = $this->pdo->prepare('SELECT id,message FROM Fortune'); + $this->update = $this->pdo->prepare('UPDATE World SET randomNumber=? WHERE id=?'); + } + + function update($request): array + { + $queries = $request->get('q'); + $worlds = $keys = $values = []; + $count = min(max((int) $queries, 1), 500); + for ($i = 0; $i < $count; ++ $i) { + $values[] = $keys[] = $id = mt_rand(1, 10000); + $this->random->execute([$id]); + $row = $this->random->fetch(); + $values[] = $row['randomNumber'] = mt_rand(1, 10000); + $worlds[] = $row; + } + if (!isset($this->updates[$count])) { + $sql = 'UPDATE World SET randomNumber = CASE id' . str_repeat(' WHEN ?::INTEGER THEN ?::INTEGER ', $count) . 'END WHERE id IN (' . str_repeat('?::INTEGER,', $count - 1) . '?::INTEGER)'; + $this->updates[$count] = $this->pdo->prepare($sql); + } + $this->updates[$count]->execute([...$values, ...$keys]); + return $worlds; + } + +} \ No newline at end of file diff --git a/frameworks/PHP/workerman/PgsqlSwoole.php b/frameworks/PHP/workerman/PgsqlSwoole.php new file mode 100644 index 00000000000..0a8b99dfc18 --- /dev/null +++ b/frameworks/PHP/workerman/PgsqlSwoole.php @@ -0,0 +1,57 @@ +withDriver('pgsql') + ->withHost('tfb-database') + ->withPort(5432) + ->withDbName('hello_world') + ->withUsername('benchmarkdbuser') + ->withPassword('benchmarkdbpass'); + + $this->pool = new PDOPool($config, $size); + } + + + function update($request): array + { + $count = min(max((int) $request->get('q'), 1), 500); + $worlds = []; + $pdo = $this->pool->get(); + $random = $pdo->prepare('SELECT id,randomNumber FROM World WHERE id=?'); + while ($count--) { + $random->execute([mt_rand(1, 10000)]); + $world = $random->fetch(PDO::FETCH_ASSOC); + $world['randomNumber'] = mt_rand(1, 10000); + $worlds[] = $world; + } + $rows = count($worlds); + + $sql = 'UPDATE world SET randomNumber = CASE id' + . str_repeat(' WHEN ?::INTEGER THEN ?::INTEGER ', $rows) + . 'END WHERE id IN (' + . str_repeat('?::INTEGER,', $rows - 1) . '?::INTEGER)'; + + $update = $pdo->prepare($sql); + + $val = []; + $keys = []; + foreach ($worlds as $world) { + $val[] = $keys[] = $world['id']; + $val[] = $world['randomNumber']; + } + + $update->execute([...$val, ...$keys]); + $this->pool->put($pdo); + return $worlds; + } + +} \ No newline at end of file diff --git a/frameworks/PHP/workerman/PgsqlSwow.php b/frameworks/PHP/workerman/PgsqlSwow.php new file mode 100644 index 00000000000..c2a2d3cc4aa --- /dev/null +++ b/frameworks/PHP/workerman/PgsqlSwow.php @@ -0,0 +1,12 @@ +pool = new Pool("pgsql:host=tfb-database;dbname=hello_world", 'benchmarkdbuser', 'benchmarkdbpass', $size); + } + +} \ No newline at end of file diff --git a/frameworks/PHP/workerman/Pool.php b/frameworks/PHP/workerman/Pool.php new file mode 100644 index 00000000000..522ad7afed2 --- /dev/null +++ b/frameworks/PHP/workerman/Pool.php @@ -0,0 +1,29 @@ +push(new PDO($dsn, $username, $password,[ + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC + ])); + } + } + + public function get(): PDO + { + return static::$channel->pop(); + } + + public function put(PDO $pdo) + { + return static::$channel->push($pdo); + } +} \ No newline at end of file diff --git a/frameworks/PHP/workerman/app-pg.php b/frameworks/PHP/workerman/app-pg.php deleted file mode 100644 index b92809d2787..00000000000 --- a/frameworks/PHP/workerman/app-pg.php +++ /dev/null @@ -1,122 +0,0 @@ -path()) { - '/plaintext' => text(), - '/json' => json(), - '/db' => db(), - '/fortunes' => fortune(), - '/query' => query($request), - '/update' => updateraw($request), - // '/info' => info(), - default => new Response(404, [], 'Error 404'), - }; -} - -function text() -{ - return new Response(200, [ - 'Content-Type' => 'text/plain', - 'Date' => Header::$date - ], 'Hello, World!'); -} - -function json() -{ - return new Response(200, [ - 'Content-Type' => 'application/json', - 'Date' => Header::$date - ], json_encode(['message' => 'Hello, World!'])); -} - -function db() -{ - DbRaw::$random->execute([mt_rand(1, 10000)]); - - return new Response(200, [ - 'Content-Type' => 'application/json', - 'Date' => Header::$date - ], json_encode(DbRaw::$random->fetch())); -} - -function query($request) -{ - $random = DbRaw::$random; - - $query_count = 1; - $q = (int) $request->get('q'); - if ($q > 1) { - $query_count = min($q, 500); - } - - while ($query_count--) { - $random->execute([mt_rand(1, 10000)]); - $arr[] = $random->fetch(); - } - - return new Response(200, [ - 'Content-Type' => 'application/json', - 'Date' => Header::$date - ], json_encode($arr)); -} - -function updateraw($request) -{ - $random = DbRaw::$random; - - $query_count = 1; - $q = (int) $request->get('q'); - if ($q > 1) { - $query_count = min($q, 500); - } - - while ($query_count--) { - - $random->execute([mt_rand(1, 10000)]); - $row = $random->fetch(); - $row['randomNumber'] = mt_rand(1, 10000); - - $worlds[] = $row; - } - - DbRaw::update($worlds); - - return new Response(200, [ - 'Content-Type' => 'application/json', - 'Date' => Header::$date - ], json_encode($worlds)); -} - -function fortune() -{ - DbRaw::$fortune->execute(); - - $arr = DbRaw::$fortune->fetchAll(PDO::FETCH_KEY_PAIR); - $arr[0] = 'Additional fortune added at request time.'; - asort($arr); - - $html = ''; - foreach ($arr as $id => $message) { - $message = htmlspecialchars($message, ENT_QUOTES, 'UTF-8'); - $html .= "$id$message"; - } - - return new Response(200, [ - 'Date' => Header::$date - ], "Fortunes$html
idmessage
" - ); -} - -/* function info() -{ - ob_start(); - phpinfo(); - return new Response(200, ['Content-Type' => 'text/plain'], ob_get_clean()); -} - */ diff --git a/frameworks/PHP/workerman/app.php b/frameworks/PHP/workerman/app.php deleted file mode 100644 index ad6b7997efc..00000000000 --- a/frameworks/PHP/workerman/app.php +++ /dev/null @@ -1,145 +0,0 @@ - PDO::FETCH_ASSOC, - PDO::ATTR_EMULATE_PREPARES => false - ] - ); - $world = $pdo->prepare('SELECT id,randomNumber FROM World WHERE id=?'); - $fortune = $pdo->prepare('SELECT id,message FROM Fortune'); - $update = $pdo->prepare('UPDATE World SET randomNumber=? WHERE id=?'); -} - -function router(Request $request) -{ - return match($request->path()) { - '/plaintext' => text(), - '/json' => json(), - '/db' => db(), - '/fortunes' => fortune(), - '/query' => query($request), - '/update' => updateraw($request), - // '/info' => info(), - default => new Response(404, [], 'Error 404'), - }; -} - -function text() -{ - return new Response(200, [ - 'Content-Type' => 'text/plain', - 'Date' => Header::$date - ], 'Hello, World!'); -} - -function json() -{ - return new Response(200, [ - 'Content-Type' => 'application/json', - 'Date' => Header::$date - ], json_encode(['message' => 'Hello, World!'])); -} - -function db() -{ - global $world; - - $world->execute([mt_rand(1, 10000)]); - - return new Response(200, [ - 'Content-Type' => 'application/json', - 'Date' => Header::$date - ], json_encode($world->fetch())); -} - -function query($request) -{ - global $world; - - $query_count = 1; - $q = (int) $request->get('q'); - if ($q > 1) { - $query_count = min($q, 500); - } - - while ($query_count--) { - $world->execute([mt_rand(1, 10000)]); - $arr[] = $world->fetch(); - } - - return new Response(200, [ - 'Content-Type' => 'application/json', - 'Date' => Header::$date - ], json_encode($arr)); -} - -function updateraw($request) -{ - global $world, $update; - - $query_count = 1; - $q = (int) $request->get('q'); - if ($q > 1) { - $query_count = min($q, 500); - } - - while ($query_count--) { - $id = mt_rand(1, 10000); - $world->execute([$id]); - $item = $world->fetch(); - $update->execute( - [$item['randomNumber'] = mt_rand(1, 10000), $id] - ); - - $arr[] = $item; - } - - // $pdo->beginTransaction(); - // foreach($arr as $world) { - // $update->execute([$world['randomNumber'], $world['id']]); - // } - // $pdo->commit(); - return new Response(200, [ - 'Content-Type' => 'application/json', - 'Date' => Header::$date - ], json_encode($arr)); -} - -function fortune() -{ - global $fortune; - - $fortune->execute(); - - $arr = $fortune->fetchAll(PDO::FETCH_KEY_PAIR); - $arr[0] = 'Additional fortune added at request time.'; - asort($arr); - - $html = ''; - foreach ($arr as $id => $message) { - $message = htmlspecialchars($message, ENT_QUOTES, 'UTF-8'); - $html .= "$id$message"; - } - - return new Response(200, [ - 'Date' => Header::$date - ], "Fortunes$html
idmessage
" - ); -} - -/* function info() -{ - ob_start(); - phpinfo(); - return new Response(200, ['Content-Type' => 'text/plain'], ob_get_clean()); -} - */ \ No newline at end of file diff --git a/frameworks/PHP/workerman/benchmark_config.json b/frameworks/PHP/workerman/benchmark_config.json index bcb286db6d8..8e79f1b124c 100644 --- a/frameworks/PHP/workerman/benchmark_config.json +++ b/frameworks/PHP/workerman/benchmark_config.json @@ -107,6 +107,50 @@ "display_name": "workerman [pgsql]", "notes": "", "versus": "php" + }, + "mysql-swow": { + "dockerfile": "workerman-mysql-swow-jit.dockerfile", + "db_url": "/db", + "query_url": "/query?q=", + "update_url": "/update?q=", + "fortune_url": "/fortunes", + "port": 8080, + "approach": "Realistic", + "classification": "Platform", + "database": "MySQL", + "framework": "workerman", + "language": "PHP", + "flavor": "PHP8", + "orm": "Raw", + "platform": "workerman", + "webserver": "None", + "os": "Linux", + "database_os": "Linux", + "display_name": "workerman [jit, mysql, swow, async]", + "notes": "", + "versus": "php" + }, + "mysql-swoole": { + "dockerfile": "workerman-mysql-swoole-jit.dockerfile", + "db_url": "/db", + "query_url": "/query?q=", + "update_url": "/update?q=", + "fortune_url": "/fortunes", + "port": 8080, + "approach": "Realistic", + "classification": "Platform", + "database": "MySQL", + "framework": "workerman", + "language": "PHP", + "flavor": "PHP8", + "orm": "Raw", + "platform": "workerman", + "webserver": "None", + "os": "Linux", + "database_os": "Linux", + "display_name": "workerman [jit, mysql, swoole, async]", + "notes": "", + "versus": "php" } }] } diff --git a/frameworks/PHP/workerman/composer.json b/frameworks/PHP/workerman/composer.json index 7e6c8b1f088..d0c8ca7e678 100644 --- a/frameworks/PHP/workerman/composer.json +++ b/frameworks/PHP/workerman/composer.json @@ -1,5 +1,10 @@ { "require": { "workerman/workerman": "dev-master" + }, + "autoload": { + "psr-4": { + "": "./" + } } } diff --git a/frameworks/PHP/workerman/config.toml b/frameworks/PHP/workerman/config.toml index b2f50bda1a0..791e9502688 100644 --- a/frameworks/PHP/workerman/config.toml +++ b/frameworks/PHP/workerman/config.toml @@ -82,4 +82,64 @@ os = "Linux" orm = "Raw" platform = "workerman" webserver = "None" +versus = "php" + +[pgsql-swow] +urls.db = "/db" +urls.query = "/query?q=" +urls.update = "/update?q=" +urls.fortune = "/fortunes" +approach = "Realistic" +classification = "Platform" +database = "Postgres" +database_os = "Linux" +os = "Linux" +orm = "Raw" +platform = "workerman" +webserver = "None" +versus = "php" + +[mysql-swow] +urls.db = "/db" +urls.query = "/query?q=" +urls.update = "/update?q=" +urls.fortune = "/fortunes" +approach = "Realistic" +classification = "Platform" +database = "MySQL" +database_os = "Linux" +os = "Linux" +orm = "Raw" +platform = "workerman" +webserver = "None" +versus = "php" + +[pgsql-swoole] +urls.db = "/db" +urls.query = "/query?q=" +urls.update = "/update?q=" +urls.fortune = "/fortunes" +approach = "Realistic" +classification = "Platform" +database = "Postgres" +database_os = "Linux" +os = "Linux" +orm = "Raw" +platform = "workerman" +webserver = "None" +versus = "php" + +[mysql-swoole] +urls.db = "/db" +urls.query = "/query?q=" +urls.update = "/update?q=" +urls.fortune = "/fortunes" +approach = "Realistic" +classification = "Platform" +database = "MySQL" +database_os = "Linux" +os = "Linux" +orm = "Raw" +platform = "workerman" +webserver = "None" versus = "php" \ No newline at end of file diff --git a/frameworks/PHP/workerman/dbraw.php b/frameworks/PHP/workerman/dbraw.php deleted file mode 100644 index 2a7c8d355c1..00000000000 --- a/frameworks/PHP/workerman/dbraw.php +++ /dev/null @@ -1,88 +0,0 @@ - PDO::FETCH_ASSOC, - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - PDO::ATTR_EMULATE_PREPARES => false - ] - ); - - self::$fortune = $pdo->prepare('SELECT id,message FROM Fortune'); - self::$random = $pdo->prepare('SELECT id,randomNumber FROM World WHERE id = ?'); - self::$instance = $pdo; - } - - /** - * Postgres bulk update - * - * @param array $worlds - * @return void - */ - public static function update(array $worlds) - { - $rows = count($worlds); - - if (!isset(self::$update[$rows])) { - $sql = 'UPDATE world SET randomNumber = CASE id' - . str_repeat(' WHEN ?::INTEGER THEN ?::INTEGER ', $rows) - . 'END WHERE id IN (' - . str_repeat('?::INTEGER,', $rows - 1) . '?::INTEGER)'; - - self::$update[$rows] = self::$instance->prepare($sql); - } - - $val = []; - $keys = []; - foreach ($worlds as $world) { - $val[] = $keys[] = $world['id']; - $val[] = $world['randomNumber']; - } - - self::$update[$rows]->execute([...$val, ...$keys]); - } - - /** - * Alternative bulk update in Postgres - * - * @param array $worlds - * @return void - */ - public static function update2(array $worlds) - { - $rows = count($worlds); - - if (!isset(self::$update[$rows])) { - $sql = 'UPDATE world SET randomNumber = temp.randomNumber FROM (VALUES ' - . implode(', ', array_fill(0, $rows, '(?::INTEGER, ?::INTEGER)')) . - ' ORDER BY 1) AS temp(id, randomNumber) WHERE temp.id = world.id'; - - self::$update[$rows] = self::$instance->prepare($sql); - } - - $val = []; - foreach ($worlds as $world) { - $val[] = $world['id']; - $val[] = $world['randomNumber']; - //$update->bindParam(++$i, $world['id'], PDO::PARAM_INT); - } - - self::$update[$rows]->execute($val); - } -} diff --git a/frameworks/PHP/workerman/server.php b/frameworks/PHP/workerman/server.php index f856790efa9..8f845329bf2 100644 --- a/frameworks/PHP/workerman/server.php +++ b/frameworks/PHP/workerman/server.php @@ -1,38 +1,83 @@ reusePort = true; -$http_worker->count = $process_count; -$http_worker->onWorkerStart = static function () use ($test_type) { - Header::$date = gmdate('D, d M Y H:i:s').' GMT'; - Timer::add(1, function() { - Header::$date = gmdate('D, d M Y H:i:s').' GMT'; - }); - if ($test_type === 'pgsql') { - DbRaw::init(); - } else { - init(); - } +$http_worker->count = $process_count; +$http_worker->onWorkerStart = static function () use ($test_type, $pool_size, &$db, &$date) { + $db = match ($test_type) { + 'pgsql' => new Pgsql(), + 'mysql' => new Mysql(), + 'pgsql-swow' => new PgsqlSwow($pool_size), + 'mysql-swow' => new MysqlSwow($pool_size), + 'pgsql-swoole' => new PgsqlSwoole($pool_size), + 'mysql-swoole' => new MysqlSwoole($pool_size), + 'default' => new Mysql(), + }; + $date = new Date(); }; -$http_worker->onMessage = static function ($connection, $request) { - $connection->send(router($request)); +Worker::$eventLoopClass = "Workerman\\Events\\$event_loop"; +if ($event_loop === 'Swoole') { + Coroutine::set(['hook_flags' => SWOOLE_HOOK_ALL]); +} + +$http_worker->onMessage = static function ($connection, $request) use (&$db, &$date) { + switch ($request->path()) { + case '/plaintext': + $connection->headers = [ + 'Content-Type' => 'text/plain', + 'Date' => $date->date + ]; + return $connection->send('Hello, World!'); + case '/json': + $connection->headers = [ + 'Content-Type' => 'application/json', + 'Date' => $date->date + ]; + return $connection->send(json_encode(['message' => 'Hello, World!'])); + case '/db': + $connection->headers = [ + 'Content-Type' => 'application/json', + 'Date' => $date->date + ]; + return $connection->send(json_encode($db->db())); + case '/fortunes': + $connection->headers = [ + 'Date' => $date->date + ]; + return $connection->send($db->fortune()); + case '/query': + $connection->headers = [ + 'Content-Type' => 'application/json', + 'Date' => $date->date + ]; + return $connection->send(json_encode($db->query($request))); + case '/update': + $connection->headers = [ + 'Content-Type' => 'application/json', + 'Date' => $date->date + ]; + return $connection->send(json_encode($db->update($request))); + } + return $connection->send(new Response(404, [ + 'Content-Type' => 'text/plain', + 'Date' => $date->date + ], '404 Not Found')); }; Worker::runAll(); - -class Header { - public static $date = null; -} diff --git a/frameworks/PHP/workerman/workerman-jit.dockerfile b/frameworks/PHP/workerman/workerman-jit.dockerfile index c90794ab9be..47ae11800d2 100644 --- a/frameworks/PHP/workerman/workerman-jit.dockerfile +++ b/frameworks/PHP/workerman/workerman-jit.dockerfile @@ -1,6 +1,8 @@ FROM ubuntu:24.04 ENV TEST_TYPE default +ENV PROCESS_MULTIPLIER 1 +ENV EVENT_LOOP Select ARG DEBIAN_FRONTEND=noninteractive @@ -8,18 +10,18 @@ RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /de RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php > /dev/null && \ apt-get update -yqq > /dev/null && apt-get upgrade -yqq > /dev/null -RUN apt-get install -yqq php8.3-cli php8.3-mysql php8.3-xml > /dev/null +RUN apt-get install -yqq php8.4-cli php8.4-mysql php8.4-xml > /dev/null COPY --from=composer/composer:latest-bin --link /composer /usr/local/bin/composer -RUN apt-get install -y php-pear php8.3-dev libevent-dev git > /dev/null && \ - pecl install event-3.1.4 > /dev/null && echo "extension=event.so" > /etc/php/8.3/cli/conf.d/30-event.ini +RUN apt-get install -y php-pear php8.4-dev libevent-dev git > /dev/null && \ + pecl install event-3.1.4 > /dev/null && echo "extension=event.so" > /etc/php/8.4/cli/conf.d/30-event.ini WORKDIR /workerman COPY --link . . RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet -COPY php-jit.ini /etc/php/8.3/cli/conf.d/10-opcache.ini +COPY php-jit.ini /etc/php/8.4/cli/conf.d/10-opcache.ini EXPOSE 8080 diff --git a/frameworks/PHP/workerman/workerman-mysql-jit.dockerfile b/frameworks/PHP/workerman/workerman-mysql-jit.dockerfile index 7d8fd9e238d..8992f918a0d 100644 --- a/frameworks/PHP/workerman/workerman-mysql-jit.dockerfile +++ b/frameworks/PHP/workerman/workerman-mysql-jit.dockerfile @@ -1,6 +1,8 @@ FROM ubuntu:24.04 ENV TEST_TYPE mysql +ENV PROCESS_MULTIPLIER 4 +ENV EVENT_LOOP Event ARG DEBIAN_FRONTEND=noninteractive @@ -8,18 +10,18 @@ RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /de RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php > /dev/null && \ apt-get update -yqq > /dev/null && apt-get upgrade -yqq > /dev/null -RUN apt-get install -yqq php8.3-cli php8.3-mysql php8.3-xml > /dev/null +RUN apt-get install -yqq php8.4-cli php8.4-mysql php8.4-xml > /dev/null COPY --from=composer/composer:latest-bin --link /composer /usr/local/bin/composer -RUN apt-get install -y php-pear php8.3-dev libevent-dev git > /dev/null && \ - pecl install event-3.1.4 > /dev/null && echo "extension=event.so" > /etc/php/8.3/cli/conf.d/30-event.ini +RUN apt-get install -y php-pear php8.4-dev libevent-dev git > /dev/null && \ + pecl install event-3.1.4 > /dev/null && echo "extension=event.so" > /etc/php/8.4/cli/conf.d/30-event.ini WORKDIR /workerman COPY --link . . RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet -COPY php-jit.ini /etc/php/8.3/cli/conf.d/10-opcache.ini +COPY php-jit.ini /etc/php/8.4/cli/conf.d/10-opcache.ini EXPOSE 8080 diff --git a/frameworks/PHP/workerman/workerman-mysql-swoole-jit.dockerfile b/frameworks/PHP/workerman/workerman-mysql-swoole-jit.dockerfile new file mode 100644 index 00000000000..54440a5a047 --- /dev/null +++ b/frameworks/PHP/workerman/workerman-mysql-swoole-jit.dockerfile @@ -0,0 +1,35 @@ +FROM ubuntu:24.04 + +ENV TEST_TYPE mysql-swoole +ENV SWOOLE_VERSION 5.1.5 +ENV PROCESS_MULTIPLIER 1 +ENV POOL_SIZE 4 +ENV EVENT_LOOP Swoole + +ARG DEBIAN_FRONTEND=noninteractive + +RUN apt update -yqq > /dev/null \ + && apt install -yqq software-properties-common > /dev/null \ + && LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php > /dev/null \ + && apt update -yqq > /dev/null \ + && apt install libbrotli-dev php8.3-cli php8.3-pdo-mysql php8.3-dev php8.3-mbstring git -y > /dev/null \ + && cd /tmp && curl -sSL "https://github.com/swoole/swoole-src/archive/v${SWOOLE_VERSION}.tar.gz" | tar xzf - \ + && cd /tmp/swoole-src-${SWOOLE_VERSION} \ + && phpize > /dev/null \ + && ./configure > /dev/null \ + && make -j "$(nproc)" > /dev/null \ + && make install > /dev/null \ + && echo "extension=swoole.so" > /etc/php/8.3/cli/conf.d/50-swoole.ini \ + && echo "memory_limit=1024M" >> /etc/php/8.3/cli/php.ini + +COPY --from=composer/composer:latest-bin --link /composer /usr/local/bin/composer + +WORKDIR /workerman +COPY --link . . + +RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet +COPY php-jit.ini /etc/php/8.3/cli/conf.d/10-opcache.ini + +EXPOSE 8080 + +CMD php /workerman/server.php start diff --git a/frameworks/PHP/workerman/workerman-mysql-swow-jit.dockerfile b/frameworks/PHP/workerman/workerman-mysql-swow-jit.dockerfile new file mode 100644 index 00000000000..ffab87c078d --- /dev/null +++ b/frameworks/PHP/workerman/workerman-mysql-swow-jit.dockerfile @@ -0,0 +1,34 @@ +FROM ubuntu:24.04 + +ENV TEST_TYPE mysql-swow +ENV PROCESS_MULTIPLIER 1 +ENV POOL_SIZE 4 +ENV EVENT_LOOP Swow + +ARG DEBIAN_FRONTEND=noninteractive + +RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /dev/null +RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php > /dev/null && \ + apt-get update -yqq > /dev/null && apt-get upgrade -yqq > /dev/null + +RUN apt-get install -yqq php8.3-cli php8.3-mysql php8.3-xml > /dev/null + +COPY --from=composer/composer:latest-bin --link /composer /usr/local/bin/composer + +RUN apt-get install -y php-pear php8.3-dev git > /dev/null + + +WORKDIR /workerman +COPY --link . . + + +RUN composer require swow/swow > /dev/null +RUN ./vendor/bin/swow-builder --install > /dev/null +RUN echo extension=swow.so > /etc/php/8.3/cli/conf.d/20-swow.ini +RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet +COPY php-jit.ini /etc/php/8.3/cli/conf.d/10-opcache.ini + +EXPOSE 8080 + + +CMD php /workerman/server.php start diff --git a/frameworks/PHP/workerman/workerman-pgsql-jit.dockerfile b/frameworks/PHP/workerman/workerman-pgsql-jit.dockerfile index 6c4af626cc0..f1e84f54011 100644 --- a/frameworks/PHP/workerman/workerman-pgsql-jit.dockerfile +++ b/frameworks/PHP/workerman/workerman-pgsql-jit.dockerfile @@ -1,6 +1,8 @@ FROM ubuntu:24.04 ENV TEST_TYPE pgsql +ENV PROCESS_MULTIPLIER 4 +ENV EVENT_LOOP Event ARG DEBIAN_FRONTEND=noninteractive @@ -8,18 +10,18 @@ RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /de RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php > /dev/null && \ apt-get update -yqq > /dev/null && apt-get upgrade -yqq > /dev/null -RUN apt-get install -yqq php8.3-cli php8.3-pgsql php8.3-xml > /dev/null +RUN apt-get install -yqq php8.4-cli php8.4-pgsql php8.4-xml > /dev/null COPY --from=composer/composer:latest-bin --link /composer /usr/local/bin/composer -RUN apt-get install -y php-pear php8.3-dev libevent-dev git > /dev/null && \ - pecl install event-3.1.4 > /dev/null && echo "extension=event.so" > /etc/php/8.3/cli/conf.d/30-event.ini +RUN apt-get install -y php-pear php8.4-dev libevent-dev git > /dev/null && \ + pecl install event-3.1.4 > /dev/null && echo "extension=event.so" > /etc/php/8.4/cli/conf.d/30-event.ini WORKDIR /workerman COPY --link . . RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet -COPY php-jit.ini /etc/php/8.3/cli/conf.d/10-opcache.ini +COPY php-jit.ini /etc/php/8.4/cli/conf.d/10-opcache.ini EXPOSE 8080 diff --git a/frameworks/PHP/workerman/workerman-pgsql-swoole-jit.dockerfile b/frameworks/PHP/workerman/workerman-pgsql-swoole-jit.dockerfile new file mode 100644 index 00000000000..09a6026a853 --- /dev/null +++ b/frameworks/PHP/workerman/workerman-pgsql-swoole-jit.dockerfile @@ -0,0 +1,35 @@ +FROM ubuntu:24.04 + +ENV TEST_TYPE pgsql-swoole +ENV SWOOLE_VERSION 5.1.5 +ENV PROCESS_MULTIPLIER 2 +ENV POOL_SIZE 16 +ENV EVENT_LOOP Swoole + +ARG DEBIAN_FRONTEND=noninteractive + +RUN apt update -yqq > /dev/null \ + && apt install -yqq software-properties-common > /dev/null \ + && LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php > /dev/null \ + && apt update -yqq > /dev/null \ + && apt install libbrotli-dev php8.3-cli php8.3-pdo-pgsql php8.3-dev libpq-dev php8.3-mbstring git -y > /dev/null \ + && cd /tmp && curl -sSL "https://github.com/swoole/swoole-src/archive/v${SWOOLE_VERSION}.tar.gz" | tar xzf - \ + && cd /tmp/swoole-src-${SWOOLE_VERSION} \ + && phpize > /dev/null \ + && ./configure --enable-swoole-pgsql > /dev/null \ + && make -j "$(nproc)" > /dev/null \ + && make install > /dev/null \ + && echo "extension=swoole.so" > /etc/php/8.3/cli/conf.d/50-swoole.ini \ + && echo "memory_limit=1024M" >> /etc/php/8.3/cli/php.ini + +COPY --from=composer/composer:latest-bin --link /composer /usr/local/bin/composer + +WORKDIR /workerman +COPY --link . . + +RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet +COPY php-jit.ini /etc/php/8.3/cli/conf.d/10-opcache.ini + +EXPOSE 8080 + +CMD php /workerman/server.php start diff --git a/frameworks/PHP/workerman/workerman-pgsql-swow-jit.dockerfile b/frameworks/PHP/workerman/workerman-pgsql-swow-jit.dockerfile new file mode 100644 index 00000000000..72557d22306 --- /dev/null +++ b/frameworks/PHP/workerman/workerman-pgsql-swow-jit.dockerfile @@ -0,0 +1,34 @@ +FROM ubuntu:24.04 + +ENV TEST_TYPE pgsql-swow +ENV PROCESS_MULTIPLIER 2 +ENV POOL_SIZE 16 +ENV EVENT_LOOP Swow + +ARG DEBIAN_FRONTEND=noninteractive + +RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /dev/null +RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php > /dev/null && \ + apt-get update -yqq > /dev/null && apt-get upgrade -yqq > /dev/null + +RUN apt-get install -yqq php8.3-cli php8.3-pgsql php8.3-xml > /dev/null + +COPY --from=composer/composer:latest-bin --link /composer /usr/local/bin/composer + +RUN apt-get install -y php-pear php8.3-dev git > /dev/null + + +WORKDIR /workerman +COPY --link . . + + +RUN composer require swow/swow > /dev/null +RUN ./vendor/bin/swow-builder --install > /dev/null +RUN echo extension=swow.so > /etc/php/8.3/cli/conf.d/20-swow.ini +RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet +COPY php-jit.ini /etc/php/8.3/cli/conf.d/10-opcache.ini + +EXPOSE 8080 + + +CMD php /workerman/server.php start diff --git a/frameworks/PHP/workerman/workerman-pgsql.dockerfile b/frameworks/PHP/workerman/workerman-pgsql.dockerfile index 9b1552d354e..f8209fb7d00 100644 --- a/frameworks/PHP/workerman/workerman-pgsql.dockerfile +++ b/frameworks/PHP/workerman/workerman-pgsql.dockerfile @@ -1,6 +1,8 @@ FROM ubuntu:24.04 ENV TEST_TYPE pgsql +ENV PROCESS_MULTIPLIER 4 +ENV EVENT_LOOP Event ARG DEBIAN_FRONTEND=noninteractive @@ -8,18 +10,18 @@ RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /de RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php > /dev/null && \ apt-get update -yqq > /dev/null && apt-get upgrade -yqq > /dev/null -RUN apt-get install -yqq php8.3-cli php8.3-pgsql php8.3-xml > /dev/null +RUN apt-get install -yqq php8.4-cli php8.4-pgsql php8.4-xml > /dev/null COPY --from=composer/composer:latest-bin --link /composer /usr/local/bin/composer -RUN apt-get install -y php-pear php8.3-dev libevent-dev git > /dev/null && \ - pecl install event-3.1.4 > /dev/null && echo "extension=event.so" > /etc/php/8.3/cli/conf.d/30-event.ini +RUN apt-get install -y php-pear php8.4-dev libevent-dev git > /dev/null && \ + pecl install event-3.1.4 > /dev/null && echo "extension=event.so" > /etc/php/8.4/cli/conf.d/30-event.ini WORKDIR /workerman COPY --link . . RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet -COPY php.ini /etc/php/8.3/cli/php.ini +COPY php.ini /etc/php/8.4/cli/php.ini EXPOSE 8080 diff --git a/frameworks/PHP/workerman/workerman.dockerfile b/frameworks/PHP/workerman/workerman.dockerfile index a4d9602019e..8d93101166f 100644 --- a/frameworks/PHP/workerman/workerman.dockerfile +++ b/frameworks/PHP/workerman/workerman.dockerfile @@ -1,6 +1,8 @@ FROM ubuntu:24.04 ENV TEST_TYPE default +ENV PROCESS_MULTIPLIER 1 +ENV EVENT_LOOP Select ARG DEBIAN_FRONTEND=noninteractive @@ -8,18 +10,18 @@ RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /de RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php > /dev/null && \ apt-get update -yqq > /dev/null && apt-get upgrade -yqq > /dev/null -RUN apt-get install -yqq php8.3-cli php8.3-mysql php8.3-xml > /dev/null +RUN apt-get install -yqq php8.4-cli php8.4-mysql php8.4-xml > /dev/null COPY --from=composer/composer:latest-bin --link /composer /usr/local/bin/composer -RUN apt-get install -y php-pear php8.3-dev libevent-dev git > /dev/null && \ - pecl install event-3.1.4 > /dev/null && echo "extension=event.so" > /etc/php/8.3/cli/conf.d/30-event.ini +RUN apt-get install -y php-pear php8.4-dev libevent-dev git > /dev/null && \ + pecl install event-3.1.4 > /dev/null && echo "extension=event.so" > /etc/php/8.4/cli/conf.d/30-event.ini WORKDIR /workerman COPY --link . . RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet -COPY php.ini /etc/php/8.3/cli/php.ini +COPY php.ini /etc/php/8.4/cli/php.ini EXPOSE 8080 diff --git a/frameworks/Pascal/mormot/benchmark_config.json b/frameworks/Pascal/mormot/benchmark_config.json index def2b2c2b17..378b0a6d9a0 100644 --- a/frameworks/Pascal/mormot/benchmark_config.json +++ b/frameworks/Pascal/mormot/benchmark_config.json @@ -71,28 +71,6 @@ "display_name": "mormot [async]", "notes": "", "versus": "None" - }, - "postgres-async2": { - "dockerfile": "mormot.dockerfile", - "db_url": "/asyncdb", - "query_url": "/asyncqueries?queries=", - "fortune_url": "/asyncfortunes", - "update_url": "/asyncupdates?queries=", - "port": 8080, - "approach": "Realistic", - "classification": "Fullstack", - "database": "postgres", - "framework": "mormot", - "language": "Pascal", - "flavor": "None", - "orm": "Raw", - "platform": "None", - "webserver": "None", - "os": "Linux", - "database_os": "Linux", - "display_name": "mormot [async,nopin]", - "notes": "", - "versus": "None" } } ] diff --git a/frameworks/Pascal/mormot/setup_and_build.sh b/frameworks/Pascal/mormot/setup_and_build.sh index 62bd68746f8..c19884f9e1b 100755 --- a/frameworks/Pascal/mormot/setup_and_build.sh +++ b/frameworks/Pascal/mormot/setup_and_build.sh @@ -35,8 +35,8 @@ echo "Download statics from $URL ..." wget -qO- "$URL" | tar -xz -C ./libs/mORMot/static # uncomment for fixed commit URL -#URL=https://github.com/synopse/mORMot2/tarball/6dc09ceca456931384857b383ed61b63f11f3be7 -URL="https://api.github.com/repos/synopse/mORMot2/tarball/$USED_TAG" +URL=https://github.com/synopse/mORMot2/tarball/2017bddac17a838c9584763e4bd7538aa6f8a529 +#URL="https://api.github.com/repos/synopse/mORMot2/tarball/$USED_TAG" echo "Download and unpacking mORMot sources from $URL ..." wget -qO- "$URL" | tar -xz -C ./libs/mORMot --strip-components=1 diff --git a/frameworks/Pascal/mormot/src/raw.pas b/frameworks/Pascal/mormot/src/raw.pas index fa53c7fd038..1f24df7173d 100644 --- a/frameworks/Pascal/mormot/src/raw.pas +++ b/frameworks/Pascal/mormot/src/raw.pas @@ -85,14 +85,14 @@ TRawAsyncServer = class(TSynPersistent) fModel: TOrmModel; fStore: TRestServerDB; fTemplate: TSynMustache; - fCachedWorldsTable: POrmCacheTable; + fOrmCache: POrmCacheTable; fRawCache: TOrmWorlds; fDbPool: TSqlDBPostgresConnectionProperties; - procedure OnAsyncDb(Statement: TSqlDBPostgresAsyncStatement; Context: TObject); - procedure OnAsyncFortunes(Statement: TSqlDBPostgresAsyncStatement; Context: TObject); + procedure OnAsyncDb(Statement: TSqlDBPostgresAsyncStatement; Context: PtrInt); + procedure OnAsyncFortunes(Statement: TSqlDBPostgresAsyncStatement; Context: PtrInt); // pipelined reading as used by /rawqueries and /rawupdates function GetRawRandomWorlds(cnt: PtrInt; out res: TWorlds): boolean; - function ComputeRawFortunes(stmt: TSqlDBStatement; ctxt: THttpServerRequest): integer; + function ComputeRawFortunes(stmt: TSqlDBStatement): RawUtf8; public constructor Create(threadCount: integer; flags: THttpServerOptions; pin2Core: integer); reintroduce; @@ -170,7 +170,7 @@ constructor TRawAsyncServer.Create( inherited Create; fDbPool := TSqlDBPostgresConnectionProperties.Create( 'tfb-database:5432', 'hello_world', 'benchmarkdbuser', 'benchmarkdbpass'); - fDbPool.ArrayParamsAsBinary := true; + // fDbPool.ArrayParamsAsBinary := true; // seems not really faster // customize JSON serialization for TFB expectations TOrmWorld.OrmProps.Fields.JsonRenameProperties([ 'ID', 'id', @@ -188,10 +188,10 @@ constructor TRawAsyncServer.Create( fStore := TRestServerDB.Create(fModel, SQLITE_MEMORY_DATABASE_NAME); fStore.NoAjaxJson := true; fStore.Server.CreateMissingTables; // create SQlite3 virtual tables - // pre-fill the ORM + // ORM and raw caches warmup if fStore.Server.Cache.SetCache(TOrmCachedWorld) then fStore.Server.Cache.FillFromQuery(TOrmCachedWorld, '', []); - fCachedWorldsTable := fStore.Orm.Cache.Table(TOrmCachedWorld); + fOrmCache := fStore.Orm.Cache.Table(TOrmCachedWorld); fStore.Orm.RetrieveListObjArray(fRawCache, TOrmCachedWorld, 'order by id', []); // initialize the mustache template for /fortunes fTemplate := TSynMustache.Parse(FORTUNES_TPL); @@ -277,8 +277,7 @@ function FortuneCompareByMessage(const A, B): integer; result := StrComp(pointer(TFortune(A).message), pointer(TFortune(B).message)); end; -function TRawAsyncServer.ComputeRawFortunes( - stmt: TSqlDBStatement; ctxt: THttpServerRequest): integer; +function TRawAsyncServer.ComputeRawFortunes(stmt: TSqlDBStatement): RawUtf8; var list: TFortunes; arr: TDynArray; @@ -286,7 +285,7 @@ function TRawAsyncServer.ComputeRawFortunes( f: ^TFortune; mus: TSynMustacheContextData; begin - result := HTTP_BADREQUEST; + result := ''; if stmt = nil then exit; arr.Init(TypeInfo(TFortunes), list, @n); @@ -303,9 +302,7 @@ function TRawAsyncServer.ComputeRawFortunes( mus := stmt.Connection.GetThreadOwned(TSynMustacheContextData); if mus = nil then mus := stmt.Connection.SetThreadOwned(fTemplate.NewMustacheContextData); - ctxt.OutContent := mus.RenderArray(arr); - ctxt.OutContentType := HTML_CONTENT_TYPE; - result := HTTP_SUCCESS; + result := mus.RenderArray(arr); end; // following methods implement the server endpoints @@ -363,7 +360,7 @@ function TRawAsyncServer.cached_queries(ctxt: THttpServerRequest): cardinal; SetLength(res, GetQueriesParamValue(ctxt, 'COUNT=')); gen := Lecuyer; for i := 0 to length(res) - 1 do - res[i] := fCachedWorldsTable.Get(ComputeRandomWorld(gen)); + res[i] := fOrmCache.Get(ComputeRandomWorld(gen)); ctxt.SetOutJson(@res, TypeInfo(TOrmWorlds)); result := HTTP_SUCCESS; end; @@ -479,7 +476,9 @@ function TRawAsyncServer.rawfortunes(ctxt: THttpServerRequest): cardinal; conn := fDbPool.ThreadSafeConnection; stmt := conn.NewStatementPrepared(FORTUNES_SQL, true, true); stmt.ExecutePrepared; - result := ComputeRawFortunes(stmt.Instance, ctxt); + ctxt.OutContent := ComputeRawFortunes(stmt.Instance); + ctxt.OutContentType := HTML_CONTENT_TYPE; + result := HTTP_SUCCESS; end; var @@ -519,7 +518,7 @@ function TRawAsyncServer.rawupdates(ctxt: THttpServerRequest): cardinal; var cnt, i: PtrInt; res: TWorlds; - ids, nums: TInt64DynArray; + params: TInt64DynArray; gen: PLecuyer; conn: TSqlDBConnection; stmt: ISqlDBStatement; @@ -536,16 +535,14 @@ function TRawAsyncServer.rawupdates(ctxt: THttpServerRequest): cardinal; if cnt > 20 then begin // fill parameters arrays for update with nested select (PostgreSQL only) - setLength(ids{%H-}, cnt); - setLength(nums{%H-}, cnt); - for i := 0 to cnt - 1 do - begin - ids[i] := res[i].id; - nums[i] := res[i].randomNumber; - end; stmt := conn.NewStatementPrepared(WORLD_UPDATE_SQLN, false, true); - stmt.BindArray(1, ids); - stmt.BindArray(2, nums); + SetLength(params{%H-}, cnt); + for i := 0 to cnt - 1 do + params[i] := res[i].id; + stmt.BindArray(1, params); + for i := 0 to cnt - 1 do + params[i] := res[i].randomNumber; + stmt.BindArray(2, params); end else begin @@ -573,47 +570,43 @@ function TRawAsyncServer.asyncdb(ctxt: THttpServerRequest): cardinal; with fDbPool.Async.PrepareLocked(WORLD_READ_SQL, {res=}true, ASYNC_OPT) do try Bind(1, ComputeRandomWorld(Lecuyer)); - ExecuteAsync(ctxt, OnAsyncDb); + ExecuteAsync(ctxt.AsyncHandle, OnAsyncDb); finally UnLock; end; - result := ctxt.SetAsyncResponse; + result := HTTP_ASYNCRESPONSE; end; procedure TRawAsyncServer.OnAsyncDb(Statement: TSqlDBPostgresAsyncStatement; - Context: TObject); -var - ctxt: THttpServerRequest absolute Context; + Context: PtrInt); begin if (Statement = nil) or not Statement.Step then - ctxt.ErrorMessage := 'asyncdb failed' + fHttpServer.AsyncResponseError(Context, 'asyncdb failed') else - ctxt.SetOutJson('{"id":%,"randomNumber":%}', + fHttpServer.AsyncResponseFmt(Context, '{"id":%,"randomNumber":%}', [Statement.ColumnInt(0), Statement.ColumnInt(1)]); - ctxt.OnAsyncResponse(ctxt); end; function TRawAsyncServer.asyncfortunes(ctxt: THttpServerRequest): cardinal; begin fDbPool.Async.PrepareLocked(FORTUNES_SQL, {res=}true, ASYNC_OPT). - ExecuteAsyncNoParam(ctxt, OnAsyncFortunes); - result := ctxt.SetAsyncResponse; + ExecuteAsyncNoParam(ctxt.AsyncHandle, OnAsyncFortunes); + result := HTTP_ASYNCRESPONSE; end; procedure TRawAsyncServer.OnAsyncFortunes(Statement: TSqlDBPostgresAsyncStatement; - Context: TObject); -var - ctxt: THttpServerRequest absolute Context; + Context: PtrInt); begin - ctxt.OnAsyncResponse(ctxt, ComputeRawFortunes(Statement, ctxt)); + fHttpServer.AsyncResponse(Context, ComputeRawFortunes(Statement), HTML_CONTENT_TYPE); end; type // simple state machine used for /asyncqueries and /asyncupdates TAsyncWorld = class public - request: THttpServerRequest; + http: THttpAsyncServer; + connection: TConnectionAsyncHandle; res: TWorlds; count, current: integer; update: TSqlDBPostgresAsyncStatement; // prepared before any callback @@ -621,8 +614,8 @@ TAsyncWorld = class function Queries(server: TRawAsyncServer; ctxt: THttpServerRequest): cardinal; function Updates(server: TRawAsyncServer; ctxt: THttpServerRequest): cardinal; procedure DoUpdates; - procedure OnQueries(Statement: TSqlDBPostgresAsyncStatement; Context: TObject); - procedure OnRes({%H-}Statement: TSqlDBPostgresAsyncStatement; Context: TObject); + procedure OnQueries(Statement: TSqlDBPostgresAsyncStatement; Context: PtrInt); + procedure OnRes({%H-}Statement: TSqlDBPostgresAsyncStatement; Context: PtrInt); end; function TRawAsyncServer.asyncqueries(ctxt: THttpServerRequest): cardinal; @@ -645,7 +638,8 @@ function TAsyncWorld.Queries(server: TRawAsyncServer; ctxt: THttpServerRequest): gen: PLecuyer; select: TSqlDBPostgresAsyncStatement; begin - request := ctxt; + http := server.fHttpServer; + connection := ctxt.AsyncHandle; if async = nil then async := server.fDbPool.Async; if count = 0 then @@ -656,26 +650,26 @@ function TAsyncWorld.Queries(server: TRawAsyncServer; ctxt: THttpServerRequest): n := count; gen := Lecuyer; repeat - dec(n); select.Bind(1, ComputeRandomWorld(gen)); + dec(n); if n = 0 then // last item should include asoForceConnectionFlush (if set) opt := ASYNC_OPT; - select.ExecuteAsync(ctxt, OnQueries, @opt); + select.ExecuteAsync(connection, OnQueries, @opt); until n = 0; select.UnLock; - result := ctxt.SetAsyncResponse; + result := HTTP_ASYNCRESPONSE; end; function TAsyncWorld.Updates(server: TRawAsyncServer; ctxt: THttpServerRequest): cardinal; begin async := server.fDbPool.Async; count := getQueriesParamValue(ctxt); - update := async.Prepare(WORLD_UPDATE_SQLN, false, ASYNC_OPT); - result := Queries(server, ctxt); + update := async.Prepare(WORLD_UPDATE_SQLN, false, ASYNC_OPT); // to trigger DoUpdates + result := Queries(server, ctxt); // will set http and connection fields end; procedure TAsyncWorld.OnQueries(Statement: TSqlDBPostgresAsyncStatement; - Context: TObject); + Context: PtrInt); begin if (Statement <> nil) and Statement.Step then @@ -708,14 +702,13 @@ procedure TAsyncWorld.DoUpdates; for i := 0 to count - 1 do params[i] := res[i].randomNumber; update.BindArrayInt32(2, params); - update.ExecuteAsync(request, OnRes); + update.ExecuteAsync(connection, OnRes); end; procedure TAsyncWorld.OnRes(Statement: TSqlDBPostgresAsyncStatement; - Context: TObject); + Context: PtrInt); begin - request.SetOutJson(@res, TypeInfo(TWorlds)); - request.OnAsyncResponse(Context as THttpServerRequest); + http.AsyncResponseJson(Context, @res, TypeInfo(TWorlds)); Free; // we don't need this state machine any more end; @@ -733,6 +726,9 @@ procedure TAsyncWorld.OnRes(Statement: TSqlDBPostgresAsyncStatement; TSynLog.Family.HighResolutionTimestamp := true; TSynLog.Family.PerThreadLog := ptIdentifiedInOneFile; TSynLog.Family.AutoFlushTimeOut := 1; + TSynLog.Family.RotateFileCount := 10; + TSynLog.Family.RotateFileSizeKB := 500000; + LogCompressAlgo := nil; // keep 10 x 512MB uncompressed files {$else} SynDBLog := nil; // slightly faster: no need to check log level {$endif WITH_LOGS} @@ -745,27 +741,20 @@ procedure TAsyncWorld.OnRes(Statement: TSqlDBPostgresAsyncStatement; // compute default execution context from HW information cpuCount := CurrentCpuSet(cpuMask); // may run from a "taskset" command - if cpuCount >= 6 then + if GetEnvironmentVariable('TFB_TEST_NAME') = 'mormot-postgres-async' then + begin + // asynchronous tests do not require several listeners + servers := 1; + threads := cpucount * 4; + pinServers2Cores := false; + end + else if cpuCount >= 6 then begin // high-end CPU would scale better using several listeners (one per core) // see https://synopse.info/forum/viewtopic.php?pid=39263#p39263 servers := cpuCount; threads := 8; pinServers2Cores := true; - if GetEnvironmentVariable('TFB_TEST_NAME') = 'mormot-postgres-async' then - begin - // asynchronus test - servers := cpuCount; - threads := 8; - end - else - if GetEnvironmentVariable('TFB_TEST_NAME') = 'mormot-postgres-async2' then - begin - // asynchronus test with single listener socket and no CPU pinning - servers := 1; - threads := cpuCount * 4; - pinServers2Cores := false; - end; end else begin @@ -778,13 +767,12 @@ procedure TAsyncWorld.OnRes(Statement: TSqlDBPostgresAsyncStatement; // parse command line parameters with Executable.Command do begin - ExeDescription := 'TFB Server using mORMot 2'; - if Option(['p', 'pin'], 'pin each server to a CPU') then + if Option('&pin', 'pin each server to a CPU') then pinServers2Cores := true; if Option('nopin', 'disable the CPU pinning') then pinServers2Cores := false; // no option would keep the default boolean - Get(['s', 'servers'], servers, '#count of servers (listener sockets)', servers); - Get(['t', 'threads'], threads, 'per-server thread pool #size', threads); + Get('&servers', servers, '#count of servers (listener sockets)', servers); + Get('&threads', threads, 'per-server thread pool #size', threads); if ConsoleHelpFailed('TFB Server using mORMot 2') then exit; end; @@ -809,42 +797,41 @@ procedure TAsyncWorld.OnRes(Statement: TSqlDBPostgresAsyncStatement; if GetBit(cpuMask, cpuIdx) then dec(k); until k = -1; - writeln('Pin #', i, ' server to #', cpuIdx, ' CPU'); + ConsoleWrite(['Pin #', i, ' server to #', cpuIdx, ' CPU']); end; rawServers[i] := TRawAsyncServer.Create(threads, flags, cpuIdx) end; try // display some information and wait for SIGTERM - writeln; - writeln(rawServers[0].fHttpServer.ClassName, - ' running on localhost:', rawServers[0].fHttpServer.SockPort); - writeln(' num servers=', servers, - ', threads per server=', threads, - ', total threads=', threads * servers, - ', total CPU=', SystemInfo.dwNumberOfProcessors, - ', accessible CPU=', cpuCount, - ', pinned=', pinServers2Cores, - ', db=', rawServers[0].fDbPool.DbmsEngineName); - writeln(' options=', GetSetName(TypeInfo(THttpServerOptions), flags)); - writeln('Press [Enter] or Ctrl+C or send SIGTERM to terminate'); + ConsoleWrite([CRLF, rawServers[0].fHttpServer.ClassName, + ' running on localhost:', rawServers[0].fHttpServer.SockPort], ccWhite); + ConsoleWrite([' num servers=', servers, + ', threads per server=', threads, + ', total threads=', threads * servers, + ', total CPU=', SystemInfo.dwNumberOfProcessors, + ', accessible CPU=', cpuCount, + ', pinned=', pinServers2Cores, + ', db=', rawServers[0].fDbPool.DbmsEngineName, CRLF, + ' options=', GetSetName(TypeInfo(THttpServerOptions), flags), CRLF]); + ConsoleWrite('Press [Enter] or Ctrl+C or send SIGTERM to terminate', ccWhite); ConsoleWaitForEnterKey; //TSynLog.Family.Level := LOG_VERBOSE; // enable shutdown logs for debug if servers = 1 then - writeln(ObjectToJsonDebug(rawServers[0].fHttpServer)) + ConsoleObject(rawServers[0].fHttpServer) else begin - writeln('Per-server accepted connections:'); + ConsoleWrite('Per-server accepted connections:'); for i := 0 to servers - 1 do - write(' ', rawServers[i].fHttpServer.Async.Accepted); - writeln(#10'Please wait: Shutdown ', servers, ' servers and ', - threads * servers, ' threads'); + ConsoleWrite([' ', rawServers[i].fHttpServer.Async.Accepted], ccLightGray, true); + ConsoleWrite([#10'Please wait: Shutdown ', servers, ' servers and ', + threads * servers, ' threads']); end; finally // clear all server instance(s) ObjArrayClear(rawServers); end; - write('Shutdown complete'#10); + ConsoleWrite('Shutdown complete'#10); {$ifdef FPC_X64MM} WriteHeapStatus(' ', 16, 8, {compileflags=}true); {$endif FPC_X64MM} diff --git a/frameworks/Python/api_hour/requirements.txt b/frameworks/Python/api_hour/requirements.txt index 19dcab407ab..9045239a633 100644 --- a/frameworks/Python/api_hour/requirements.txt +++ b/frameworks/Python/api_hour/requirements.txt @@ -1,4 +1,4 @@ -aiohttp==3.10.2 +aiohttp==3.10.11 -e git+https://github.com/Eyepea/aiohttp_jinja2.git@c9675e5c1e1ee7741b30aea8d8fbffcde016c7a0#egg=aiohttp_jinja2-master aiopg==0.7.0 -e git+https://github.com/Eyepea/API-Hour.git@577abbdcbb8cc2810dad46e260b338b15db4d0e3#egg=api_hour-master diff --git a/frameworks/Python/bottle/requirements-pypy.txt b/frameworks/Python/bottle/requirements-pypy.txt index c42dadb2ef0..7cfbf108e30 100644 --- a/frameworks/Python/bottle/requirements-pypy.txt +++ b/frameworks/Python/bottle/requirements-pypy.txt @@ -3,4 +3,4 @@ bottle-sqlalchemy==0.4.3 gunicorn==19.9.0 PyMySQL==0.8.0 SQLAlchemy==1.3.0 -tornado==4.5.3 +tornado==6.4.2 diff --git a/frameworks/Python/granian/granian-rsgi.dockerfile b/frameworks/Python/granian/granian-rsgi.dockerfile index d84b713044b..315493dc7ca 100644 --- a/frameworks/Python/granian/granian-rsgi.dockerfile +++ b/frameworks/Python/granian/granian-rsgi.dockerfile @@ -1,4 +1,4 @@ -FROM python:3.11-slim +FROM python:3.10-slim ADD ./ /granian diff --git a/frameworks/Python/granian/granian-wrk.dockerfile b/frameworks/Python/granian/granian-wrk.dockerfile index 2f56af92d53..533a5bc722a 100644 --- a/frameworks/Python/granian/granian-wrk.dockerfile +++ b/frameworks/Python/granian/granian-wrk.dockerfile @@ -1,4 +1,4 @@ -FROM python:3.11-slim +FROM python:3.10-slim ADD ./ /granian diff --git a/frameworks/Python/granian/granian-wsgi.dockerfile b/frameworks/Python/granian/granian-wsgi.dockerfile index f178eb4b618..9ddcee211ff 100644 --- a/frameworks/Python/granian/granian-wsgi.dockerfile +++ b/frameworks/Python/granian/granian-wsgi.dockerfile @@ -1,4 +1,4 @@ -FROM python:3.11-slim +FROM python:3.10-slim ADD ./ /granian diff --git a/frameworks/Python/granian/granian.dockerfile b/frameworks/Python/granian/granian.dockerfile index 883c433f3f0..828808b1009 100644 --- a/frameworks/Python/granian/granian.dockerfile +++ b/frameworks/Python/granian/granian.dockerfile @@ -1,4 +1,4 @@ -FROM python:3.11-slim +FROM python:3.10-slim ADD ./ /granian diff --git a/frameworks/Python/granian/requirements.txt b/frameworks/Python/granian/requirements.txt index 90c02f2c8d4..1d8e4322ee2 100644 --- a/frameworks/Python/granian/requirements.txt +++ b/frameworks/Python/granian/requirements.txt @@ -1,4 +1,4 @@ asyncpg==0.29.0 -granian>=1.6.0,<1.7.0 +granian>=1.7.0,<1.8.0 jinja2==3.1.4 orjson==3.10.2 diff --git a/frameworks/Python/granian/run.py b/frameworks/Python/granian/run.py index ef0724fad2f..11bdee8d453 100644 --- a/frameworks/Python/granian/run.py +++ b/frameworks/Python/granian/run.py @@ -10,8 +10,8 @@ workers = multiprocessing.cpu_count() if interface == "rsgi": - #: split cores between the two loops - workers = round(workers / 2) + #: leave 25% cpu to the Rust runtime + workers = round(workers * 0.75) blocking_threads = None if interface == "wsgi": diff --git a/frameworks/Ruby/rack-sequel/Gemfile b/frameworks/Ruby/rack-sequel/Gemfile index 22e0dd418b8..d427bd4ef4e 100644 --- a/frameworks/Ruby/rack-sequel/Gemfile +++ b/frameworks/Ruby/rack-sequel/Gemfile @@ -1,20 +1,29 @@ source 'https://rubygems.org' -gem 'json', '~> 2.0' -gem 'oj', '~> 3.14', platforms: %i[ruby mswin] -gem 'passenger', '~> 6.0', :platforms=>[:ruby, :mswin], :require=>false -gem 'puma', '~> 6.4', :require=>false +gem 'json', '~> 2.8' gem 'sequel', '~> 5.0' gem 'rack', '~> 3.0' -gem 'unicorn', '~> 6.1', :platforms=>[:ruby, :mswin], :require=>false group :mysql do - gem 'jdbc-mysql', '~> 5.1', :platforms=>:jruby, :require=>'jdbc/mysql' - gem 'mysql2', '~> 0.5', :platforms=>[:ruby, :mswin] + gem 'jdbc-mysql', '~> 5.1', platforms: :jruby, require: 'jdbc/mysql' + gem 'mysql2', '~> 0.4', platforms: [:ruby, :mswin] end group :postgresql do - gem 'jdbc-postgres', '~> 9.4', :platforms=>:jruby, :require=>'jdbc/postgres' - gem 'pg', '~> 1.5', :platforms=>[:ruby, :mswin] - gem 'sequel_pg', '~> 1.6', :platforms=>:ruby, :require=>false + gem 'jdbc-postgres', '~> 9.4', platforms: :jruby, require: 'jdbc/postgres' + gem 'pg', '~> 1.5', platforms: [:ruby, :mswin] + gem 'sequel_pg', '~> 1.6', platforms: :ruby, require: false +end + +group :passenger do + gem 'base64' # required by passenger on Ruby 3.4 + gem 'passenger', '~> 6.0', platforms: [:ruby, :mswin], require: false +end + +group :puma do + gem 'puma', '~> 6.4', require: false +end + +group :unicorn do + gem 'unicorn', '~> 6.1', platforms: [:ruby, :mswin], require: false end diff --git a/frameworks/Ruby/rack-sequel/Gemfile.lock b/frameworks/Ruby/rack-sequel/Gemfile.lock new file mode 100644 index 00000000000..72c1f6c0d47 --- /dev/null +++ b/frameworks/Ruby/rack-sequel/Gemfile.lock @@ -0,0 +1,57 @@ +GEM + remote: https://rubygems.org/ + specs: + base64 (0.2.0) + bigdecimal (3.1.8) + json (2.8.2) + kgio (2.11.4) + mysql2 (0.5.6) + nio4r (2.7.4) + oj (3.16.7) + bigdecimal (>= 3.0) + ostruct (>= 0.2) + ostruct (0.6.1) + passenger (6.0.23) + rack (>= 1.6.13) + rackup + rake (>= 12.3.3) + pg (1.5.8) + puma (6.5.0) + nio4r (~> 2.0) + rack (3.1.8) + rackup (2.1.0) + rack (>= 3) + webrick (~> 1.8) + raindrops (0.20.1) + rake (13.2.1) + sequel (5.85.0) + bigdecimal + sequel_pg (1.17.1) + pg (>= 0.18.0, != 1.2.0) + sequel (>= 4.38.0) + unicorn (6.1.0) + kgio (~> 2.6) + raindrops (~> 0.7) + webrick (1.8.2) + +PLATFORMS + ruby + x86_64-darwin-22 + +DEPENDENCIES + base64 + jdbc-mysql (~> 5.1) + jdbc-postgres (~> 9.4) + json (~> 2.0) + mysql2 (~> 0.4) + oj (~> 3.14) + passenger (~> 6.0) + pg (~> 1.5) + puma (~> 6.4) + rack (~> 3.0) + sequel (~> 5.0) + sequel_pg (~> 1.6) + unicorn (~> 6.1) + +BUNDLED WITH + 2.5.10 diff --git a/frameworks/Ruby/rack-sequel/README.md b/frameworks/Ruby/rack-sequel/README.md index 11563d8ffeb..b3a02d90b91 100644 --- a/frameworks/Ruby/rack-sequel/README.md +++ b/frameworks/Ruby/rack-sequel/README.md @@ -14,7 +14,7 @@ The tests will be run with: * [Ruby 3.3](http://www.ruby-lang.org) * [Puma 6](http://puma.io) -* [Passenger 5](https://www.phusionpassenger.com) +* [Passenger 6](https://www.phusionpassenger.com) * [Unicorn 5](https://bogomips.org/unicorn/) * [Rack 2](http://rack.rubyforge.org) * [Sequel 5](http://sequel.jeremyevans.net) diff --git a/frameworks/Ruby/rack-sequel/hello_world.rb b/frameworks/Ruby/rack-sequel/hello_world.rb index 6fc3a787975..dbda8902f90 100644 --- a/frameworks/Ruby/rack-sequel/hello_world.rb +++ b/frameworks/Ruby/rack-sequel/hello_world.rb @@ -1,8 +1,5 @@ # frozen_string_literal: true -require 'oj' -Oj.mimic_JSON - # Our Rack application to be executed by rackup class HelloWorld DEFAULT_HEADERS = {}.tap do |h| diff --git a/frameworks/Ruby/rack-sequel/rack-sequel-passenger-mri.dockerfile b/frameworks/Ruby/rack-sequel/rack-sequel-passenger-mri.dockerfile index 9482a004540..d451ea776d6 100644 --- a/frameworks/Ruby/rack-sequel/rack-sequel-passenger-mri.dockerfile +++ b/frameworks/Ruby/rack-sequel/rack-sequel-passenger-mri.dockerfile @@ -1,4 +1,4 @@ -FROM ruby:3.3 +FROM ruby:3.4-rc ADD ./ /rack-sequel @@ -11,6 +11,7 @@ RUN apt-get update && \ apt-get install -y --no-install-recommends libjemalloc2 ENV LD_PRELOAD=libjemalloc.so.2 +RUN bundle config set without 'postgresql puma unicorn' RUN bundle install --jobs=4 --gemfile=/rack-sequel/Gemfile # TODO: https://github.com/phusion/passenger/issues/1916 diff --git a/frameworks/Ruby/rack-sequel/rack-sequel-postgres-passenger-mri.dockerfile b/frameworks/Ruby/rack-sequel/rack-sequel-postgres-passenger-mri.dockerfile index 2765c0c4f89..c201cad91e1 100644 --- a/frameworks/Ruby/rack-sequel/rack-sequel-postgres-passenger-mri.dockerfile +++ b/frameworks/Ruby/rack-sequel/rack-sequel-postgres-passenger-mri.dockerfile @@ -1,4 +1,4 @@ -FROM ruby:3.3 +FROM ruby:3.4-rc ADD ./ /rack-sequel @@ -11,6 +11,7 @@ RUN apt-get update && \ apt-get install -y --no-install-recommends libjemalloc2 ENV LD_PRELOAD=libjemalloc.so.2 +RUN bundle config set without 'mysql puma unicorn' RUN bundle install --jobs=4 --gemfile=/rack-sequel/Gemfile # TODO: https://github.com/phusion/passenger/issues/1916 diff --git a/frameworks/Ruby/rack-sequel/rack-sequel-postgres-unicorn-mri.dockerfile b/frameworks/Ruby/rack-sequel/rack-sequel-postgres-unicorn-mri.dockerfile index 37ce2662e7b..35cc8145a10 100644 --- a/frameworks/Ruby/rack-sequel/rack-sequel-postgres-unicorn-mri.dockerfile +++ b/frameworks/Ruby/rack-sequel/rack-sequel-postgres-unicorn-mri.dockerfile @@ -11,6 +11,7 @@ RUN apt-get update && \ apt-get install -y --no-install-recommends libjemalloc2 ENV LD_PRELOAD=libjemalloc.so.2 +RUN bundle config set without 'mysql passenger puma' RUN bundle install --jobs=4 --gemfile=/rack-sequel/Gemfile ENV DBTYPE=postgresql diff --git a/frameworks/Ruby/rack-sequel/rack-sequel-postgres.dockerfile b/frameworks/Ruby/rack-sequel/rack-sequel-postgres.dockerfile index 37634239c37..f0ab2e5981b 100644 --- a/frameworks/Ruby/rack-sequel/rack-sequel-postgres.dockerfile +++ b/frameworks/Ruby/rack-sequel/rack-sequel-postgres.dockerfile @@ -11,6 +11,7 @@ RUN apt-get update && \ apt-get install -y --no-install-recommends libjemalloc2 ENV LD_PRELOAD=libjemalloc.so.2 +RUN bundle config set without 'mysql passenger unicorn' RUN bundle install --jobs=4 --gemfile=/rack-sequel/Gemfile ENV DBTYPE=postgresql diff --git a/frameworks/Ruby/rack-sequel/rack-sequel-unicorn-mri.dockerfile b/frameworks/Ruby/rack-sequel/rack-sequel-unicorn-mri.dockerfile index 67ef3768e71..f8385fbb8df 100644 --- a/frameworks/Ruby/rack-sequel/rack-sequel-unicorn-mri.dockerfile +++ b/frameworks/Ruby/rack-sequel/rack-sequel-unicorn-mri.dockerfile @@ -11,6 +11,7 @@ RUN apt-get update && \ apt-get install -y --no-install-recommends libjemalloc2 ENV LD_PRELOAD=libjemalloc.so.2 +RUN bundle config set without 'postgresql passenger puma' RUN bundle install --jobs=4 --gemfile=/rack-sequel/Gemfile ENV DBTYPE=mysql diff --git a/frameworks/Ruby/rack-sequel/rack-sequel.dockerfile b/frameworks/Ruby/rack-sequel/rack-sequel.dockerfile index 2c7ee155a56..b83995deda1 100644 --- a/frameworks/Ruby/rack-sequel/rack-sequel.dockerfile +++ b/frameworks/Ruby/rack-sequel/rack-sequel.dockerfile @@ -11,6 +11,7 @@ RUN apt-get update && \ apt-get install -y --no-install-recommends libjemalloc2 ENV LD_PRELOAD=libjemalloc.so.2 +RUN bundle config set without 'postgresql passenger unicorn' RUN bundle install --jobs=4 --gemfile=/rack-sequel/Gemfile ENV DBTYPE=mysql diff --git a/frameworks/Ruby/rack/Gemfile b/frameworks/Ruby/rack/Gemfile index ab7f29130b7..5b34822d7e0 100644 --- a/frameworks/Ruby/rack/Gemfile +++ b/frameworks/Ruby/rack/Gemfile @@ -5,8 +5,7 @@ source 'https://rubygems.org' gem 'rack', '~> 3.0' gem 'connection_pool', '~> 2.4' gem 'jdbc-postgres', '~> 42.2', platforms: :jruby, require: 'jdbc/postgres' -gem 'json', '~> 2.6', platforms: :jruby -gem 'oj', '~> 3.14', platforms: %i[ruby mswin] +gem 'json', '~> 2.8' gem 'pg', '~> 1.5', platforms: %i[ruby mswin] gem 'sequel' gem 'sequel_pg', platforms: %i[ruby mswin] diff --git a/frameworks/Ruby/rack/Gemfile.lock b/frameworks/Ruby/rack/Gemfile.lock index ee579e13085..b3ef3cbda2d 100644 --- a/frameworks/Ruby/rack/Gemfile.lock +++ b/frameworks/Ruby/rack/Gemfile.lock @@ -2,134 +2,133 @@ GEM remote: https://rubygems.org/ specs: ast (2.4.2) - async (2.12.1) - console (~> 1.25, >= 1.25.2) + async (2.20.0) + console (~> 1.29) fiber-annotation io-event (~> 1.6, >= 1.6.5) - async-container (0.18.2) + async-container (0.18.3) async (~> 2.10) - async-http (0.69.0) + async-http (0.83.1) async (>= 2.10.2) - async-pool (~> 0.7) - io-endpoint (~> 0.11) - io-stream (~> 0.4) - protocol-http (~> 0.26) - protocol-http1 (~> 0.19) - protocol-http2 (~> 0.18) - traces (>= 0.10) - async-http-cache (0.4.3) + async-pool (~> 0.9) + io-endpoint (~> 0.14) + io-stream (~> 0.6) + metrics (~> 0.12) + protocol-http (~> 0.43) + protocol-http1 (>= 0.28.1) + protocol-http2 (~> 0.19) + traces (~> 0.10) + async-http-cache (0.4.4) async-http (~> 0.56) - async-pool (0.7.0) + async-pool (0.10.1) async (>= 1.25) + traces async-service (0.12.0) async async-container (~> 0.16) bigdecimal (3.1.8) - concurrent-ruby (1.3.3) + concurrent-ruby (1.3.4) connection_pool (2.4.1) - console (1.25.2) + console (1.29.0) fiber-annotation fiber-local (~> 1.1) json - falcon (0.47.7) + falcon (0.48.3) async async-container (~> 0.18) - async-http (~> 0.66, >= 0.66.3) - async-http-cache (~> 0.4.0) + async-http (~> 0.75) + async-http-cache (~> 0.4) async-service (~> 0.10) bundler localhost (~> 1.1) openssl (~> 3.0) - process-metrics (~> 0.2.0) - protocol-rack (~> 0.5) + process-metrics (~> 0.2) + protocol-http (~> 0.31) + protocol-rack (~> 0.7) samovar (~> 2.3) fiber-annotation (0.2.0) fiber-local (1.1.0) fiber-storage - fiber-storage (0.1.2) - io-endpoint (0.11.0) - io-event (1.6.5) - io-stream (0.4.0) - json (2.7.2) + fiber-storage (1.0.0) + io-endpoint (0.14.0) + io-event (1.7.3) + io-stream (0.6.1) + json (2.8.2) kgio (2.11.4) language_server-protocol (3.17.0.3) localhost (1.3.1) mapping (1.1.1) - nio4r (2.7.3) - oj (3.16.4) - bigdecimal (>= 3.0) + metrics (0.12.1) + nio4r (2.7.4) openssl (3.2.0) - parallel (1.25.1) - parser (3.3.3.0) + parallel (1.26.3) + parser (3.3.6.0) ast (~> 2.4.1) racc - pg (1.5.6) - process-metrics (0.2.1) + pg (1.5.9) + process-metrics (0.3.0) console (~> 1.8) + json (~> 2) samovar (~> 2.1) - protocol-hpack (1.4.3) - protocol-http (0.26.6) - protocol-http1 (0.19.1) + protocol-hpack (1.5.1) + protocol-http (0.43.0) + protocol-http1 (0.28.1) protocol-http (~> 0.22) - protocol-http2 (0.18.0) + protocol-http2 (0.20.0) protocol-hpack (~> 1.4) protocol-http (~> 0.18) - protocol-rack (0.6.0) - protocol-http (~> 0.23) + protocol-rack (0.11.0) + protocol-http (~> 0.43) rack (>= 1.0) - puma (6.4.3) + puma (6.5.0) nio4r (~> 2.0) - racc (1.8.0) - rack (3.1.6) + racc (1.8.1) + rack (3.1.8) rack-test (2.1.0) rack (>= 1.3) rainbow (3.1.1) raindrops (0.20.1) regexp_parser (2.9.2) - rexml (3.3.9) - rubocop (1.64.1) + rubocop (1.68.0) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 1.8, < 3.0) - rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.31.1, < 2.0) + regexp_parser (>= 2.4, < 3.0) + rubocop-ast (>= 1.32.2, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.31.3) + rubocop-ast (1.35.0) parser (>= 3.3.1.0) ruby-progressbar (1.13.0) samovar (2.3.0) console (~> 1.0) mapping (~> 1.0) - sequel (5.82.0) + sequel (5.86.0) bigdecimal sequel_pg (1.17.1) pg (>= 0.18.0, != 1.2.0) sequel (>= 4.38.0) - traces (0.11.1) + traces (0.14.1) tzinfo (2.0.6) concurrent-ruby (~> 1.0) tzinfo-data (1.2023.3) tzinfo (>= 1.0.0) - unicode-display_width (2.5.0) + unicode-display_width (2.6.0) unicorn (6.1.0) kgio (~> 2.6) raindrops (~> 0.7) PLATFORMS - x86_64-darwin-20 - x86_64-darwin-22 + x86_64-darwin-23 x86_64-linux DEPENDENCIES connection_pool (~> 2.4) falcon (~> 0.47) jdbc-postgres (~> 42.2) - json (~> 2.6) - oj (~> 3.14) + json (~> 2.8) pg (~> 1.5) puma (~> 6.4) rack (~> 3.0) diff --git a/frameworks/Ruby/rack-sequel/config/java_tune.sh b/frameworks/Ruby/rack/config/java_tune.sh similarity index 100% rename from frameworks/Ruby/rack-sequel/config/java_tune.sh rename to frameworks/Ruby/rack/config/java_tune.sh diff --git a/frameworks/Ruby/rack/hello_world.rb b/frameworks/Ruby/rack/hello_world.rb index 050b6eac801..f2884f3ad1b 100644 --- a/frameworks/Ruby/rack/hello_world.rb +++ b/frameworks/Ruby/rack/hello_world.rb @@ -5,13 +5,11 @@ require_relative 'pg_db' require_relative 'config/auto_tune' require 'rack' +require 'json' if RUBY_PLATFORM == 'java' - require 'json' DEFAULT_DATABASE_URL = 'jdbc:postgresql://tfb-database/hello_world?user=benchmarkdbuser&password=benchmarkdbpass' else - require 'oj' - Oj.mimic_JSON DEFAULT_DATABASE_URL = 'postgresql://tfb-database/hello_world?user=benchmarkdbuser&password=benchmarkdbpass' end diff --git a/frameworks/Ruby/rack/pg_db.rb b/frameworks/Ruby/rack/pg_db.rb index e8c8634c60c..0851ee2acfe 100644 --- a/frameworks/Ruby/rack/pg_db.rb +++ b/frameworks/Ruby/rack/pg_db.rb @@ -19,11 +19,7 @@ class PgDb def initialize(connection_string = nil, max_connections = 512) @connection = Sequel.connect(connection_string, max_connections: max_connections, sql_log_level: :warning) - if defined?(Falcon) - Sequel.extension :fiber_concurrency if defined?(Falcon) - else - @connection.extension :async_thread_pool - end + Sequel.extension :fiber_concurrency if defined?(Falcon) prepare_statements end diff --git a/frameworks/Ruby/rack/rack-jruby.dockerfile b/frameworks/Ruby/rack/rack-jruby.dockerfile index ab06ae132ed..c280cca6ac8 100644 --- a/frameworks/Ruby/rack/rack-jruby.dockerfile +++ b/frameworks/Ruby/rack/rack-jruby.dockerfile @@ -13,4 +13,6 @@ COPY . . EXPOSE 8080 +CMD config/java_tune.sh + CMD bundle exec puma -C config/puma.rb -b tcp://0.0.0.0:8080 -e production diff --git a/frameworks/Ruby/rails/Gemfile b/frameworks/Ruby/rails/Gemfile index b188913327b..9b7458ae417 100644 --- a/frameworks/Ruby/rails/Gemfile +++ b/frameworks/Ruby/rails/Gemfile @@ -1,7 +1,6 @@ source 'https://rubygems.org' -gem 'oj', '~> 3.16' -gem 'rails', '~> 7.2.0' +gem 'rails', '~> 8.0.0' gem 'redis', '~> 5.0' gem 'tzinfo-data' @@ -21,10 +20,6 @@ group :puma do gem 'puma', '~> 6.4', require: false end -group :unicorn do - gem 'unicorn', '~> 6.1', require: false -end - group :agoo do gem 'agoo', require: false gem 'rackup' diff --git a/frameworks/Ruby/rails/Gemfile.lock b/frameworks/Ruby/rails/Gemfile.lock index 9773df3e123..010270b9617 100644 --- a/frameworks/Ruby/rails/Gemfile.lock +++ b/frameworks/Ruby/rails/Gemfile.lock @@ -1,67 +1,67 @@ GEM remote: https://rubygems.org/ specs: - actioncable (7.2.1.1) - actionpack (= 7.2.1.1) - activesupport (= 7.2.1.1) + actioncable (8.0.0) + actionpack (= 8.0.0) + activesupport (= 8.0.0) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (7.2.1.1) - actionpack (= 7.2.1.1) - activejob (= 7.2.1.1) - activerecord (= 7.2.1.1) - activestorage (= 7.2.1.1) - activesupport (= 7.2.1.1) + actionmailbox (8.0.0) + actionpack (= 8.0.0) + activejob (= 8.0.0) + activerecord (= 8.0.0) + activestorage (= 8.0.0) + activesupport (= 8.0.0) mail (>= 2.8.0) - actionmailer (7.2.1.1) - actionpack (= 7.2.1.1) - actionview (= 7.2.1.1) - activejob (= 7.2.1.1) - activesupport (= 7.2.1.1) + actionmailer (8.0.0) + actionpack (= 8.0.0) + actionview (= 8.0.0) + activejob (= 8.0.0) + activesupport (= 8.0.0) mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (7.2.1.1) - actionview (= 7.2.1.1) - activesupport (= 7.2.1.1) + actionpack (8.0.0) + actionview (= 8.0.0) + activesupport (= 8.0.0) nokogiri (>= 1.8.5) - racc - rack (>= 2.2.4, < 3.2) + rack (>= 2.2.4) rack-session (>= 1.0.1) rack-test (>= 0.6.3) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) useragent (~> 0.16) - actiontext (7.2.1.1) - actionpack (= 7.2.1.1) - activerecord (= 7.2.1.1) - activestorage (= 7.2.1.1) - activesupport (= 7.2.1.1) + actiontext (8.0.0) + actionpack (= 8.0.0) + activerecord (= 8.0.0) + activestorage (= 8.0.0) + activesupport (= 8.0.0) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.2.1.1) - activesupport (= 7.2.1.1) + actionview (8.0.0) + activesupport (= 8.0.0) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - activejob (7.2.1.1) - activesupport (= 7.2.1.1) + activejob (8.0.0) + activesupport (= 8.0.0) globalid (>= 0.3.6) - activemodel (7.2.1.1) - activesupport (= 7.2.1.1) - activerecord (7.2.1.1) - activemodel (= 7.2.1.1) - activesupport (= 7.2.1.1) + activemodel (8.0.0) + activesupport (= 8.0.0) + activerecord (8.0.0) + activemodel (= 8.0.0) + activesupport (= 8.0.0) timeout (>= 0.4.0) - activestorage (7.2.1.1) - actionpack (= 7.2.1.1) - activejob (= 7.2.1.1) - activerecord (= 7.2.1.1) - activesupport (= 7.2.1.1) + activestorage (8.0.0) + actionpack (= 8.0.0) + activejob (= 8.0.0) + activerecord (= 8.0.0) + activesupport (= 8.0.0) marcel (~> 1.0) - activesupport (7.2.1.1) + activesupport (8.0.0) base64 + benchmark (>= 0.3) bigdecimal concurrent-ruby (~> 1.0, >= 1.3.1) connection_pool (>= 2.2.5) @@ -71,14 +71,15 @@ GEM minitest (>= 5.1) securerandom (>= 0.3) tzinfo (~> 2.0, >= 2.0.5) + uri (>= 0.13.1) agoo (2.15.13) - async (2.17.0) - console (~> 1.26) + async (2.19.0) + console (~> 1.29) fiber-annotation io-event (~> 1.6, >= 1.6.5) async-container (0.18.3) async (~> 2.10) - async-http (0.82.1) + async-http (0.82.3) async (>= 2.10.2) async-pool (~> 0.9) io-endpoint (~> 0.14) @@ -97,16 +98,17 @@ GEM async async-container (~> 0.16) base64 (0.2.0) + benchmark (0.4.0) bigdecimal (3.1.8) builder (3.3.0) concurrent-ruby (1.3.4) connection_pool (2.4.1) - console (1.27.0) + console (1.29.0) fiber-annotation fiber-local (~> 1.1) json crass (1.0.6) - date (3.3.4) + date (3.4.1) drb (2.2.1) erubi (1.13.0) falcon (0.48.3) @@ -130,18 +132,17 @@ GEM activesupport (>= 6.1) i18n (1.14.6) concurrent-ruby (~> 1.0) - io-console (0.7.2) + io-console (0.8.0) io-endpoint (0.14.0) - io-event (1.7.2) - io-stream (0.6.0) + io-event (1.7.3) + io-stream (0.6.1) irb (1.14.1) rdoc (>= 4.0.0) reline (>= 0.4.2) - json (2.7.2) - kgio (2.11.4) + json (2.8.1) localhost (1.3.1) - logger (1.6.1) - loofah (2.22.0) + logger (1.6.2) + loofah (2.23.1) crass (~> 1.0.2) nokogiri (>= 1.12.0) mail (2.8.1) @@ -151,11 +152,11 @@ GEM net-smtp mapping (1.1.1) marcel (1.0.4) - metrics (0.12.0) + metrics (0.12.1) mini_mime (1.1.5) - mini_portile2 (2.8.7) - minitest (5.25.1) - net-imap (0.4.17) + mini_portile2 (2.8.8) + minitest (5.25.4) + net-imap (0.5.1) date net-protocol net-pop (0.1.2) @@ -164,37 +165,35 @@ GEM timeout net-smtp (0.5.0) net-protocol - nio4r (2.7.3) - nokogiri (1.16.7) + nio4r (2.7.4) + nokogiri (1.16.8) mini_portile2 (~> 2.8.2) racc (~> 1.4) - nokogiri (1.16.7-arm64-darwin) + nokogiri (1.16.8-x86_64-darwin) racc (~> 1.4) - nokogiri (1.16.7-x86_64-linux) + nokogiri (1.16.8-x86_64-linux) racc (~> 1.4) - oj (3.16.6) - bigdecimal (>= 3.0) - ostruct (>= 0.2) openssl (3.2.0) - ostruct (0.6.0) - pg (1.5.8) + pg (1.5.9) process-metrics (0.3.0) console (~> 1.8) json (~> 2) samovar (~> 2.1) protocol-hpack (1.5.1) - protocol-http (0.40.0) + protocol-http (0.42.0) protocol-http1 (0.28.1) protocol-http (~> 0.22) - protocol-http2 (0.19.3) + protocol-http2 (0.19.4) protocol-hpack (~> 1.4) protocol-http (~> 0.18) + traces protocol-rack (0.10.1) protocol-http (~> 0.37) rack (>= 1.0) - psych (5.1.2) + psych (5.2.1) + date stringio - puma (6.4.3) + puma (6.5.0) nio4r (~> 2.0) racc (1.8.1) rack (3.1.8) @@ -202,88 +201,81 @@ GEM rack (>= 3.0.0) rack-test (2.1.0) rack (>= 1.3) - rackup (2.1.0) + rackup (2.2.1) rack (>= 3) - webrick (~> 1.8) - rails (7.2.1.1) - actioncable (= 7.2.1.1) - actionmailbox (= 7.2.1.1) - actionmailer (= 7.2.1.1) - actionpack (= 7.2.1.1) - actiontext (= 7.2.1.1) - actionview (= 7.2.1.1) - activejob (= 7.2.1.1) - activemodel (= 7.2.1.1) - activerecord (= 7.2.1.1) - activestorage (= 7.2.1.1) - activesupport (= 7.2.1.1) + rails (8.0.0) + actioncable (= 8.0.0) + actionmailbox (= 8.0.0) + actionmailer (= 8.0.0) + actionpack (= 8.0.0) + actiontext (= 8.0.0) + actionview (= 8.0.0) + activejob (= 8.0.0) + activemodel (= 8.0.0) + activerecord (= 8.0.0) + activestorage (= 8.0.0) + activesupport (= 8.0.0) bundler (>= 1.15.0) - railties (= 7.2.1.1) + railties (= 8.0.0) rails-dom-testing (2.2.0) activesupport (>= 5.0.0) minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.6.0) + rails-html-sanitizer (1.6.1) loofah (~> 2.21) - nokogiri (~> 1.14) - railties (7.2.1.1) - actionpack (= 7.2.1.1) - activesupport (= 7.2.1.1) + nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) + railties (8.0.0) + actionpack (= 8.0.0) + activesupport (= 8.0.0) irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) thor (~> 1.0, >= 1.2.2) zeitwerk (~> 2.6) - raindrops (0.20.1) rake (13.2.1) - rdoc (6.7.0) + rdoc (6.8.1) psych (>= 4.0.0) redis (5.3.0) redis-client (>= 0.22.0) redis-client (0.22.2) connection_pool - reline (0.5.10) + reline (0.5.12) io-console (~> 0.5) samovar (2.3.0) console (~> 1.0) mapping (~> 1.0) - securerandom (0.3.1) - stringio (3.1.1) + securerandom (0.4.0) + stringio (3.1.2) thor (1.3.2) - timeout (0.4.1) - traces (0.13.1) + timeout (0.4.2) + traces (0.14.1) trilogy (2.8.1) tzinfo (2.0.6) concurrent-ruby (~> 1.0) tzinfo-data (1.2024.2) tzinfo (>= 1.0.0) - unicorn (6.1.0) - kgio (~> 2.6) - raindrops (~> 0.7) - useragent (0.16.10) - webrick (1.8.2) + uri (1.0.2) + useragent (0.16.11) websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) - zeitwerk (2.7.0) + zeitwerk (2.7.1) PLATFORMS - arm64-darwin-20 ruby + x86_64-darwin-22 x86_64-linux DEPENDENCIES agoo falcon (~> 0.47) - oj (~> 3.16) pg (~> 1.5) puma (~> 6.4) rackup - rails (~> 7.2.0) + rails (~> 8.0.0) redis (~> 5.0) trilogy (~> 2.8.1) tzinfo-data - unicorn (~> 6.1) BUNDLED WITH - 2.3.3 + 2.5.16 diff --git a/frameworks/Ruby/rails/README.md b/frameworks/Ruby/rails/README.md index 564df6d6479..de1ba2aacf0 100644 --- a/frameworks/Ruby/rails/README.md +++ b/frameworks/Ruby/rails/README.md @@ -13,7 +13,7 @@ comparing a variety of web platforms. The tests were run with: - [Ruby 3.3](http://www.ruby-lang.org/) -- [Rails 7.1](http://rubyonrails.org/) +- [Rails 7.2](http://rubyonrails.org/) - [Puma 6.4](http://puma.io/) - [MySQL](https://dev.mysql.com/) - [PostgreSQL](https://www.postgresql.org/) diff --git a/frameworks/Ruby/rails/app/controllers/json_controller.rb b/frameworks/Ruby/rails/app/controllers/json_controller.rb index e7234f692f3..1a87495f93c 100644 --- a/frameworks/Ruby/rails/app/controllers/json_controller.rb +++ b/frameworks/Ruby/rails/app/controllers/json_controller.rb @@ -4,6 +4,6 @@ class JsonController < ApplicationControllerMetal def index add_headers self.content_type = 'application/json' - self.response_body = Oj.dump({ 'message' => 'Hello, World!' }) + self.response_body = { 'message' => 'Hello, World!' }.to_json end end diff --git a/frameworks/Ruby/rails/config/environments/development.rb b/frameworks/Ruby/rails/config/environments/development.rb index 6a2306dbabc..ab2b0a86d2f 100644 --- a/frameworks/Ruby/rails/config/environments/development.rb +++ b/frameworks/Ruby/rails/config/environments/development.rb @@ -3,9 +3,7 @@ Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. - # In the development environment your application's code is reloaded any time - # it changes. This slows down response time but is perfect for development - # since you don't have to restart the web server when you make code changes. + # Make code changes take effect immediately without server restart. config.enable_reloading = true # Do not eager load code on boot. @@ -17,35 +15,31 @@ # Enable server timing. config.server_timing = true - # Enable/disable caching. By default caching is disabled. - # Run rails dev:cache to toggle caching. + # Enable/disable Action Controller caching. By default Action Controller caching is disabled. + # Run rails dev:cache to toggle Action Controller caching. if Rails.root.join("tmp/caching-dev.txt").exist? config.action_controller.perform_caching = true config.action_controller.enable_fragment_cache_logging = true - - config.cache_store = :memory_store - config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{2.days.to_i}" } + config.public_file_server.headers = { "cache-control" => "public, max-age=#{2.days.to_i}" } else config.action_controller.perform_caching = false - - config.cache_store = :null_store end + # Change to :null_store to avoid any caching. + config.cache_store = :memory_store + # Print deprecation notices to the Rails logger. config.active_support.deprecation = :log - # Raise exceptions for disallowed deprecations. - config.active_support.disallowed_deprecation = :raise - - # Tell Active Support which deprecation messages to disallow. - config.active_support.disallowed_deprecation_warnings = [] - # Raise an error on page load if there are pending migrations. config.active_record.migration_error = :page_load # Highlight code that triggered database queries in logs. config.active_record.verbose_query_logs = true + # Append comments with runtime information tags to SQL queries in logs. + config.active_record.query_log_tags_enabled = true + # Raises error for missing translations. # config.i18n.raise_on_missing_translations = true @@ -54,7 +48,4 @@ # Raise error when a before_action's only/except options reference missing actions. config.action_controller.raise_on_missing_callback_actions = true - - # Apply autocorrection by RuboCop to files generated by `bin/rails generate`. - # config.generators.apply_rubocop_autocorrect_after_generate! end diff --git a/frameworks/Ruby/rails/config/environments/production.rb b/frameworks/Ruby/rails/config/environments/production.rb index c6594e5d17d..331a2687e8f 100644 --- a/frameworks/Ruby/rails/config/environments/production.rb +++ b/frameworks/Ruby/rails/config/environments/production.rb @@ -6,19 +6,17 @@ # Code is not reloaded between requests. config.enable_reloading = false - # Eager load code on boot. This eager loads most of Rails and - # your application in memory, allowing both threaded web servers - # and those relying on copy on write to perform better. - # Rake tasks automatically ignore this option for performance. + # Eager load code on boot for better performance and memory savings (ignored by Rake tasks). config.eager_load = true - # Full error reports are disabled and caching is turned on. + # Full error reports are disabled. config.consider_all_requests_local = false + + # Turn on fragment caching in view templates. config.action_controller.perform_caching = true - # Ensures that a master key has been made available in ENV["RAILS_MASTER_KEY"], config/master.key, or an environment - # key such as config/credentials/production.key. This key is used to decrypt credentials (and other encrypted files). - # config.require_master_key = true + # Cache assets for far-future expiry since they are all digest stamped. + config.public_file_server.headers = { "cache-control" => "public, max-age=#{1.year.to_i}" } # Disable serving static files from `public/`, relying on NGINX/Apache to do so instead. # This disables the ActionDispatch::Static middleware. @@ -27,12 +25,7 @@ # Enable serving of images, stylesheets, and JavaScripts from an asset server. # config.asset_host = "http://assets.example.com" - # Specifies the header that your server uses for sending files. - # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache - # config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" # for NGINX - # Assume all access to the app is happening through a SSL-terminating reverse proxy. - # Can be used together with config.force_ssl for Strict-Transport-Security and secure cookies. # config.assume_ssl = true # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. @@ -41,14 +34,19 @@ # Skip http-to-https redirect for the default health check endpoint. # config.ssl_options = { redirect: { exclude: ->(request) { request.path == "/up" } } } - # Prepend all log lines with the following tags. - config.log_tags = [ :request_id ] + # Log to STDOUT with the current request id as a default log tag. + # config.log_tags = [ :request_id ] + config.logger = ActiveSupport::TaggedLogging.logger(STDOUT) - # "info" includes generic and useful information about system operation, but avoids logging too much - # information to avoid inadvertent exposure of personally identifiable information (PII). If you - # want to log everything, set the level to "debug". + # Change to "debug" to log everything (including potentially personally-identifiable information!) config.log_level = :fatal + # Prevent health checks from clogging up the logs. + config.silence_healthcheck_path = "/up" + + # Don't log any deprecations. + config.active_support.report_deprecations = false + # Use a different cache store in production. config.cache_store = :redis_cache_store, { url: ENV['REDIS_URL'], @@ -66,17 +64,18 @@ # the I18n.default_locale when a translation cannot be found). config.i18n.fallbacks = true - # Don't log any deprecations. - config.active_support.report_deprecations = false - # Do not dump schema after migrations. config.active_record.dump_schema_after_migration = false + # Only use :id for inspections in production. + config.active_record.attributes_for_inspect = [ :id ] + # Enable DNS rebinding protection and other `Host` header attacks. # config.hosts = [ # "example.com", # Allow requests from example.com # /.*\.example\.com/ # Allow requests from subdomains like `www.example.com` # ] + # # Skip DNS rebinding protection for the default health check endpoint. # config.host_authorization = { exclude: ->(request) { request.path == "/up" } } end diff --git a/frameworks/Ruby/rails/config/environments/test.rb b/frameworks/Ruby/rails/config/environments/test.rb index 999db7091ef..14bc29e0659 100644 --- a/frameworks/Ruby/rails/config/environments/test.rb +++ b/frameworks/Ruby/rails/config/environments/test.rb @@ -1,5 +1,3 @@ -require "active_support/core_ext/integer/time" - # The test environment is used exclusively to run your application's # test suite. You never need to work with it otherwise. Remember that # your test database is "scratch space" for the test suite and is wiped @@ -17,12 +15,11 @@ # loading is working properly before deploying your code. config.eager_load = ENV["CI"].present? - # Configure public file server for tests with Cache-Control for performance. - config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{1.hour.to_i}" } + # Configure public file server for tests with cache-control for performance. + config.public_file_server.headers = { "cache-control" => "public, max-age=3600" } - # Show full error reports and disable caching. + # Show full error reports. config.consider_all_requests_local = true - config.action_controller.perform_caching = false config.cache_store = :null_store # Render exception templates for rescuable exceptions and raise for other exceptions. @@ -34,12 +31,6 @@ # Print deprecation notices to the stderr. config.active_support.deprecation = :stderr - # Raise exceptions for disallowed deprecations. - config.active_support.disallowed_deprecation = :raise - - # Tell Active Support which deprecation messages to disallow. - config.active_support.disallowed_deprecation_warnings = [] - # Raises error for missing translations. # config.i18n.raise_on_missing_translations = true diff --git a/frameworks/Ruby/rails/config/initializers/filter_parameter_logging.rb b/frameworks/Ruby/rails/config/initializers/filter_parameter_logging.rb index 4b34a036689..c0b717f7ecb 100644 --- a/frameworks/Ruby/rails/config/initializers/filter_parameter_logging.rb +++ b/frameworks/Ruby/rails/config/initializers/filter_parameter_logging.rb @@ -1,6 +1,8 @@ # Be sure to restart your server when you modify this file. -# Configure sensitive parameters which will be filtered from the log file. +# Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file. +# Use this to limit dissemination of sensitive information. +# See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors. Rails.application.config.filter_parameters += [ - :passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn + :passw, :email, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn, :cvv, :cvc ] diff --git a/frameworks/Ruby/rails/config/initializers/oj.rb b/frameworks/Ruby/rails/config/initializers/oj.rb deleted file mode 100644 index 8c00f0456ff..00000000000 --- a/frameworks/Ruby/rails/config/initializers/oj.rb +++ /dev/null @@ -1 +0,0 @@ -Oj.optimize_rails diff --git a/frameworks/Ruby/rails/rails-agoo.dockerfile b/frameworks/Ruby/rails/rails-agoo.dockerfile index 7160fe32f84..507077565bb 100644 --- a/frameworks/Ruby/rails/rails-agoo.dockerfile +++ b/frameworks/Ruby/rails/rails-agoo.dockerfile @@ -15,7 +15,7 @@ ENV LD_PRELOAD=libjemalloc.so.2 COPY ./Gemfile* /rails/ ENV BUNDLE_FORCE_RUBY_PLATFORM=true -ENV BUNDLE_WITHOUT=trilogy +ENV BUNDLE_WITHOUT=mysql:falcon:puma RUN bundle install --jobs=8 COPY . /rails/ @@ -23,5 +23,5 @@ COPY . /rails/ ENV RAILS_ENV=production_postgresql ENV PORT=8080 ENV REDIS_URL=redis://localhost:6379/0 -CMD service redis-server start -CMD RACK_ENV=production bundle exec rackup -r agoo -s agoo -p 8080 -q -O workers=$(ruby config/auto_tune.rb | grep -Eo '[0-9]+' | head -n 1) +CMD service redis-server start && \ + RACK_ENV=production bundle exec rackup -r agoo -s agoo -p 8080 -q -O workers=$(ruby config/auto_tune.rb | grep -Eo '[0-9]+' | head -n 1) diff --git a/frameworks/Ruby/rails/rails-falcon.dockerfile b/frameworks/Ruby/rails/rails-falcon.dockerfile index 3244b73aa02..2e0c659e34b 100644 --- a/frameworks/Ruby/rails/rails-falcon.dockerfile +++ b/frameworks/Ruby/rails/rails-falcon.dockerfile @@ -15,7 +15,7 @@ ENV LD_PRELOAD=libjemalloc.so.2 COPY ./Gemfile* /rails/ ENV BUNDLE_FORCE_RUBY_PLATFORM=true -ENV BUNDLE_WITHOUT=mysql +ENV BUNDLE_WITHOUT=mysql:agoo:puma RUN bundle install --jobs=8 COPY . /rails/ @@ -23,4 +23,5 @@ COPY . /rails/ ENV RAILS_ENV=production_postgresql ENV PORT=8080 ENV REDIS_URL=redis://localhost:6379/0 -CMD bundle exec falcon host +CMD service redis-server start && \ + bundle exec falcon host diff --git a/frameworks/Ruby/rails/rails-mysql.dockerfile b/frameworks/Ruby/rails/rails-mysql.dockerfile index b14a3b75af6..9e7083a1f4d 100644 --- a/frameworks/Ruby/rails/rails-mysql.dockerfile +++ b/frameworks/Ruby/rails/rails-mysql.dockerfile @@ -15,7 +15,7 @@ ENV LD_PRELOAD=libjemalloc.so.2 COPY ./Gemfile* /rails/ ENV BUNDLE_FORCE_RUBY_PLATFORM=true -ENV BUNDLE_WITHOUT=postgresql +ENV BUNDLE_WITHOUT=postgresql:agoo:falcon RUN bundle install --jobs=8 COPY . /rails/ @@ -23,4 +23,5 @@ COPY . /rails/ ENV RAILS_ENV=production_mysql ENV PORT=8080 ENV REDIS_URL=redis://localhost:6379/0 -CMD ./run-with-redis.sh +CMD service redis-server start && \ + rails server diff --git a/frameworks/Ruby/rails/rails.dockerfile b/frameworks/Ruby/rails/rails.dockerfile index 8eb53c4dbd2..451a4e5bfac 100644 --- a/frameworks/Ruby/rails/rails.dockerfile +++ b/frameworks/Ruby/rails/rails.dockerfile @@ -15,7 +15,7 @@ ENV LD_PRELOAD=libjemalloc.so.2 COPY ./Gemfile* /rails/ ENV BUNDLE_FORCE_RUBY_PLATFORM=true -ENV BUNDLE_WITHOUT=mysql +ENV BUNDLE_WITHOUT=mysql:agoo:falcon RUN bundle install --jobs=8 COPY . /rails/ @@ -23,4 +23,5 @@ COPY . /rails/ ENV RAILS_ENV=production_postgresql ENV PORT=8080 ENV REDIS_URL=redis://localhost:6379/0 -CMD ./run-with-redis.sh +CMD service redis-server start && \ + rails server diff --git a/frameworks/Ruby/rails/run-with-redis.sh b/frameworks/Ruby/rails/run-with-redis.sh deleted file mode 100755 index 036224f0df5..00000000000 --- a/frameworks/Ruby/rails/run-with-redis.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -service redis-server start -bundle exec falcon host diff --git a/frameworks/Ruby/roda-sequel/Gemfile b/frameworks/Ruby/roda-sequel/Gemfile index e511bb3d848..73b6ee99edc 100644 --- a/frameworks/Ruby/roda-sequel/Gemfile +++ b/frameworks/Ruby/roda-sequel/Gemfile @@ -1,13 +1,14 @@ source "https://rubygems.org" +gem 'base64' # required by passenger on Ruby 3.4 gem "erubi", "~> 1.12" +gem "json", "~> 2.8" gem "passenger", "~> 6.0", platforms: %i[ruby mswin], require: false gem "puma", "~> 6.2", require: false gem "sequel", "~> 5.67" gem "roda", "~> 3.66" gem "tilt", "~> 2.1", require: "tilt/erb" gem "unicorn", "~> 6.1", platforms: %i[ruby mswin], require: false -gem "rapidjson" group :mysql do gem "mysql2", "~> 0.5", platforms: %i[ruby mswin] diff --git a/frameworks/Ruby/roda-sequel/Gemfile.lock b/frameworks/Ruby/roda-sequel/Gemfile.lock new file mode 100644 index 00000000000..c701d602394 --- /dev/null +++ b/frameworks/Ruby/roda-sequel/Gemfile.lock @@ -0,0 +1,56 @@ +GEM + remote: https://rubygems.org/ + specs: + base64 (0.2.0) + bigdecimal (3.1.8) + erubi (1.13.0) + json (2.8.1) + kgio (2.11.4) + mysql2 (0.5.6) + nio4r (2.7.4) + passenger (6.0.23) + rack (>= 1.6.13) + rackup + rake (>= 12.3.3) + pg (1.5.8) + puma (6.5.0) + nio4r (~> 2.0) + rack (3.1.8) + rackup (2.1.0) + rack (>= 3) + webrick (~> 1.8) + raindrops (0.20.1) + rake (13.2.1) + roda (3.85.0) + rack + sequel (5.85.0) + bigdecimal + sequel_pg (1.17.1) + pg (>= 0.18.0, != 1.2.0) + sequel (>= 4.38.0) + tilt (2.4.0) + unicorn (6.1.0) + kgio (~> 2.6) + raindrops (~> 0.7) + webrick (1.8.2) + +PLATFORMS + ruby + x86_64-darwin-22 + +DEPENDENCIES + base64 + erubi (~> 1.12) + json (~> 2.8) + mysql2 (~> 0.5) + passenger (~> 6.0) + pg (~> 1.4) + puma (~> 6.2) + roda (~> 3.66) + sequel (~> 5.67) + sequel_pg (~> 1.17) + tilt (~> 2.1) + unicorn (~> 6.1) + +BUNDLED WITH + 2.5.10 diff --git a/frameworks/Ruby/roda-sequel/README.md b/frameworks/Ruby/roda-sequel/README.md index 1ca9c7b55a9..a409974a10f 100644 --- a/frameworks/Ruby/roda-sequel/README.md +++ b/frameworks/Ruby/roda-sequel/README.md @@ -14,7 +14,7 @@ The tests will be run with: * [Ruby 3.3](http://www.ruby-lang.org) * [Puma 6](http://puma.io) -* [Passenger 5](https://www.phusionpassenger.com) +* [Passenger 6](https://www.phusionpassenger.com) * [Unicorn 5](https://bogomips.org/unicorn/) * [Roda 3](http://roda.jeremyevans.net) * [Sequel 5](http://sequel.jeremyevans.net) diff --git a/frameworks/Ruby/roda-sequel/boot.rb b/frameworks/Ruby/roda-sequel/boot.rb index bc7277e2362..5d852e9e80c 100644 --- a/frameworks/Ruby/roda-sequel/boot.rb +++ b/frameworks/Ruby/roda-sequel/boot.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require "bundler/setup" require "time" -require "rapidjson" MAX_PK = 10_000 QUERY_RANGE = (1..MAX_PK).freeze ALL_IDS = QUERY_RANGE.to_a diff --git a/frameworks/Ruby/roda-sequel/hello_world.rb b/frameworks/Ruby/roda-sequel/hello_world.rb index 458e1a7c687..c05354e2097 100644 --- a/frameworks/Ruby/roda-sequel/hello_world.rb +++ b/frameworks/Ruby/roda-sequel/hello_world.rb @@ -22,13 +22,13 @@ def rand1 # Test type 1: JSON serialization r.is "json" do response[CONTENT_TYPE] = JSON_TYPE - RapidJSON.encode({ message: "Hello, World!" }) + { message: "Hello, World!" }.to_json end # Test type 2: Single database query r.is "db" do response[CONTENT_TYPE] = JSON_TYPE - RapidJSON.encode(World.with_pk(rand1).values) + World.with_pk(rand1).values.to_json end # Test type 3: Multiple database queries @@ -40,7 +40,7 @@ def rand1 World.with_pk(id).values end end - RapidJSON.encode(worlds) + worlds.to_json end # Test type 4: Fortunes @@ -70,7 +70,7 @@ def rand1 end World.batch_update(worlds) end - RapidJSON.encode(worlds.map!(&:values)) + worlds.map!(&:values).to_json end # Test type 6: Plaintext diff --git a/frameworks/Ruby/roda-sequel/roda-sequel-postgres-passenger-mri.dockerfile b/frameworks/Ruby/roda-sequel/roda-sequel-postgres-passenger-mri.dockerfile index b3f87e9c742..e060497ea57 100644 --- a/frameworks/Ruby/roda-sequel/roda-sequel-postgres-passenger-mri.dockerfile +++ b/frameworks/Ruby/roda-sequel/roda-sequel-postgres-passenger-mri.dockerfile @@ -1,4 +1,4 @@ -FROM ruby:3.3 +FROM ruby:3.4-rc ADD ./ /roda-sequel WORKDIR /roda-sequel diff --git a/frameworks/Ruby/sinatra-sequel/Gemfile b/frameworks/Ruby/sinatra-sequel/Gemfile index 4aace641277..23f21663480 100644 --- a/frameworks/Ruby/sinatra-sequel/Gemfile +++ b/frameworks/Ruby/sinatra-sequel/Gemfile @@ -1,11 +1,8 @@ source 'https://rubygems.org' -gem 'oj' -gem 'passenger', '~> 6.0', :platforms=>[:ruby, :mswin], :require=>false -gem 'puma', '~> 6.4', :require=>false +gem 'json', '~> 2.8' gem 'sequel', '~> 5.0' gem 'sinatra', '~> 4.0', :require=>'sinatra/base' -gem 'unicorn', '~> 6.1', :platforms=>[:ruby, :mswin], :require=>false group :mysql do gem 'mysql2', '~> 0.5', :platforms=>[:ruby, :mswin] @@ -15,3 +12,15 @@ group :postgresql do gem 'pg', '~> 1.5', :platforms=>[:ruby, :mswin] gem 'sequel_pg', '~> 1.6', :platforms=>:ruby, :require=>false end + +group :passenger do + gem 'passenger', '~> 6.0', platforms: [:ruby, :mswin], require: false +end + +group :puma do + gem 'puma', '~> 6.4', require: false +end + +group :unicorn do + gem 'unicorn', '~> 6.1', platforms: [:ruby, :mswin], require: false +end diff --git a/frameworks/Ruby/sinatra-sequel/Gemfile.lock b/frameworks/Ruby/sinatra-sequel/Gemfile.lock new file mode 100644 index 00000000000..8f868632e82 --- /dev/null +++ b/frameworks/Ruby/sinatra-sequel/Gemfile.lock @@ -0,0 +1,64 @@ +GEM + remote: https://rubygems.org/ + specs: + base64 (0.2.0) + bigdecimal (3.1.8) + json (2.8.2) + kgio (2.11.4) + mustermann (3.0.3) + ruby2_keywords (~> 0.0.1) + mysql2 (0.5.6) + nio4r (2.7.4) + passenger (6.0.23) + rack (>= 1.6.13) + rackup + rake (>= 12.3.3) + pg (1.5.8) + puma (6.5.0) + nio4r (~> 2.0) + rack (3.1.8) + rack-protection (4.0.0) + base64 (>= 0.1.0) + rack (>= 3.0.0, < 4) + rack-session (2.0.0) + rack (>= 3.0.0) + rackup (2.1.0) + rack (>= 3) + webrick (~> 1.8) + raindrops (0.20.1) + rake (13.2.1) + ruby2_keywords (0.0.5) + sequel (5.85.0) + bigdecimal + sequel_pg (1.17.1) + pg (>= 0.18.0, != 1.2.0) + sequel (>= 4.38.0) + sinatra (4.0.0) + mustermann (~> 3.0) + rack (>= 3.0.0, < 4) + rack-protection (= 4.0.0) + rack-session (>= 2.0.0, < 3) + tilt (~> 2.0) + tilt (2.4.0) + unicorn (6.1.0) + kgio (~> 2.6) + raindrops (~> 0.7) + webrick (1.8.2) + +PLATFORMS + ruby + x86_64-darwin-23 + +DEPENDENCIES + json (~> 2.8) + mysql2 (~> 0.5) + passenger (~> 6.0) + pg (~> 1.5) + puma (~> 6.4) + sequel (~> 5.0) + sequel_pg (~> 1.6) + sinatra (~> 4.0) + unicorn (~> 6.1) + +BUNDLED WITH + 2.5.16 diff --git a/frameworks/Ruby/sinatra-sequel/boot.rb b/frameworks/Ruby/sinatra-sequel/boot.rb index b91ea6e1317..966bb4ed617 100644 --- a/frameworks/Ruby/sinatra-sequel/boot.rb +++ b/frameworks/Ruby/sinatra-sequel/boot.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require 'bundler/setup' require 'time' -require 'oj' MAX_PK = 10_000 ID_RANGE = (1..MAX_PK).freeze @@ -12,20 +11,17 @@ SERVER_STRING = if defined?(PhusionPassenger) - [ - PhusionPassenger::SharedConstants::SERVER_TOKEN_NAME, - PhusionPassenger::VERSION_STRING - ].join('/').freeze + 'passenger' elsif defined?(Puma) - Puma::Const::PUMA_SERVER_STRING + 'puma' elsif defined?(Unicorn) - Unicorn::HttpParser::DEFAULTS['SERVER_SOFTWARE'] + 'unicorn' + elsif defined?(Agoo) + 'agoo' end Bundler.require(:default) # Load core modules -Oj.mimic_JSON - def connect(dbtype) Bundler.require(dbtype) # Load database-specific modules diff --git a/frameworks/Ruby/sinatra-sequel/config/java_tune.sh b/frameworks/Ruby/sinatra-sequel/config/java_tune.sh deleted file mode 100644 index 412b1e74fdc..00000000000 --- a/frameworks/Ruby/sinatra-sequel/config/java_tune.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/sh -stack_size=1 -cache_size=240 -meta_size=192 -avail_mem=$(awk '/^MemAvailable/ { print int(0.6 * $2 / 1024); exit }' /proc/meminfo) -heap_size=$(( avail_mem - meta_size - cache_size - (stack_size * MAX_CONCURRENCY * THREAD_FACTOR) )) - -JRUBY_OPTS="-J-server -J-XX:+AggressiveOpts -J-Djava.net.preferIPv4Stack=true" -#JRUBY_OPTS="$JRUBY_OPTS -J-XX:+UseSerialGC" -JRUBY_OPTS="$JRUBY_OPTS -J-XX:+CMSClassUnloadingEnabled -J-XX:+UseConcMarkSweepGC" -#JRUBY_OPTS="$JRUBY_OPTS -J-XX:+UseG1GC -J-XX:+UseStringDeduplication" -JRUBY_OPTS="$JRUBY_OPTS -J-Xms${heap_size}m -J-Xmx${heap_size}m" -JRUBY_OPTS="$JRUBY_OPTS -J-Xss${stack_size}m" -JRUBY_OPTS="$JRUBY_OPTS -J-XX:MaxMetaspaceSize=${meta_size}m" -JRUBY_OPTS="$JRUBY_OPTS -J-XX:ReservedCodeCacheSize=${cache_size}m" -JRUBY_OPTS="$JRUBY_OPTS -Xcompile.invokedynamic=true -J-XX:+UseNUMA -J-XX:+AlwaysPreTouch" - -export JRUBY_OPTS diff --git a/frameworks/Ruby/sinatra-sequel/sinatra-sequel-base.dockerfile b/frameworks/Ruby/sinatra-sequel/sinatra-sequel-base.dockerfile deleted file mode 100644 index 942d363b75c..00000000000 --- a/frameworks/Ruby/sinatra-sequel/sinatra-sequel-base.dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -FROM ruby:3.4-rc - -ENV RUBY_YJIT_ENABLE=1 - -# Use Jemalloc -RUN apt-get update && \ - apt-get install -y --no-install-recommends libjemalloc2 -ENV LD_PRELOAD=libjemalloc.so.2 - -ADD ./ /sinatra-sequel -WORKDIR /sinatra-sequel - -RUN bundle install --jobs=4 --gemfile=/sinatra-sequel/Gemfile diff --git a/frameworks/Ruby/sinatra-sequel/sinatra-sequel-postgres-passenger-mri.dockerfile b/frameworks/Ruby/sinatra-sequel/sinatra-sequel-postgres-passenger-mri.dockerfile index 5651c491808..88f3d9dbfa3 100644 --- a/frameworks/Ruby/sinatra-sequel/sinatra-sequel-postgres-passenger-mri.dockerfile +++ b/frameworks/Ruby/sinatra-sequel/sinatra-sequel-postgres-passenger-mri.dockerfile @@ -1,4 +1,4 @@ -FROM ruby:3.3 +FROM ruby:3.4-rc ENV RUBY_YJIT_ENABLE=1 @@ -10,6 +10,7 @@ ENV LD_PRELOAD=libjemalloc.so.2 ADD ./ /sinatra-sequel WORKDIR /sinatra-sequel +ENV BUNDLE_WITHOUT=mysql:puma:unicorn RUN bundle install --jobs=4 --gemfile=/sinatra-sequel/Gemfile # TODO: https://github.com/phusion/passenger/issues/1916 diff --git a/frameworks/Ruby/sinatra-sequel/sinatra-sequel-postgres-unicorn-mri.dockerfile b/frameworks/Ruby/sinatra-sequel/sinatra-sequel-postgres-unicorn-mri.dockerfile index 4d03257046a..b23651b44fc 100644 --- a/frameworks/Ruby/sinatra-sequel/sinatra-sequel-postgres-unicorn-mri.dockerfile +++ b/frameworks/Ruby/sinatra-sequel/sinatra-sequel-postgres-unicorn-mri.dockerfile @@ -10,6 +10,7 @@ ENV LD_PRELOAD=libjemalloc.so.2 ADD ./ /sinatra-sequel WORKDIR /sinatra-sequel +ENV BUNDLE_WITHOUT=mysql:passenger:puma RUN bundle install --jobs=4 --gemfile=/sinatra-sequel/Gemfile ENV DBTYPE=postgresql diff --git a/frameworks/Ruby/sinatra-sequel/sinatra-sequel-postgres.dockerfile b/frameworks/Ruby/sinatra-sequel/sinatra-sequel-postgres.dockerfile index eee06752667..aff4f5d206a 100644 --- a/frameworks/Ruby/sinatra-sequel/sinatra-sequel-postgres.dockerfile +++ b/frameworks/Ruby/sinatra-sequel/sinatra-sequel-postgres.dockerfile @@ -10,6 +10,7 @@ ENV LD_PRELOAD=libjemalloc.so.2 ADD ./ /sinatra-sequel WORKDIR /sinatra-sequel +ENV BUNDLE_WITHOUT=mysql:passenger:unicorn RUN bundle install --jobs=4 --gemfile=/sinatra-sequel/Gemfile ENV DBTYPE=postgresql diff --git a/frameworks/Ruby/sinatra-sequel/sinatra-sequel.dockerfile b/frameworks/Ruby/sinatra-sequel/sinatra-sequel.dockerfile index 80198c299ab..d446161f602 100644 --- a/frameworks/Ruby/sinatra-sequel/sinatra-sequel.dockerfile +++ b/frameworks/Ruby/sinatra-sequel/sinatra-sequel.dockerfile @@ -10,6 +10,7 @@ ENV LD_PRELOAD=libjemalloc.so.2 ADD ./ /sinatra-sequel WORKDIR /sinatra-sequel +ENV BUNDLE_WITHOUT=postgresql:passenger:unicorn RUN bundle install --jobs=4 --gemfile=/sinatra-sequel/Gemfile ENV DBTYPE=mysql diff --git a/frameworks/Ruby/sinatra/Gemfile b/frameworks/Ruby/sinatra/Gemfile index 95fe5743580..88720bf547b 100644 --- a/frameworks/Ruby/sinatra/Gemfile +++ b/frameworks/Ruby/sinatra/Gemfile @@ -1,16 +1,30 @@ source 'https://rubygems.org' gem 'activerecord', '~> 7.2', require: 'active_record' -gem 'oj' -gem 'passenger', '~> 6.0', platforms: [:ruby, :mswin], require: false -gem 'puma', '~> 6.4', require: false +gem 'json', '~> 2.8' gem 'sinatra', '~> 4.0', require: 'sinatra/base' -gem 'unicorn', '~> 6.1', platforms: [:ruby, :mswin], require: false -group :mysql do +group :mysql, optional: true do gem 'mysql2', '~> 0.5', :platforms=>[:ruby, :mswin] end -group :postgresql do +group :postgresql, optional: true do gem 'pg', '~> 1.5', platforms: [:ruby, :mswin] end + +group :passenger, optional: true do + gem 'passenger', '~> 6.0', platforms: [:ruby, :mswin], require: false +end + +group :puma, optional: true do + gem 'puma', '~> 6.4', require: false +end + +group :unicorn do + gem 'unicorn', '~> 6.1', platforms: [:ruby, :mswin], require: false +end + +group :agoo, optional: true do + gem 'agoo', require: false + gem 'rackup' +end diff --git a/frameworks/Ruby/sinatra/Gemfile.lock b/frameworks/Ruby/sinatra/Gemfile.lock new file mode 100644 index 00000000000..ddfcc1fb8be --- /dev/null +++ b/frameworks/Ruby/sinatra/Gemfile.lock @@ -0,0 +1,89 @@ +GEM + remote: https://rubygems.org/ + specs: + activemodel (7.2.1.1) + activesupport (= 7.2.1.1) + activerecord (7.2.1.1) + activemodel (= 7.2.1.1) + activesupport (= 7.2.1.1) + timeout (>= 0.4.0) + activesupport (7.2.1.1) + base64 + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + logger (>= 1.4.2) + minitest (>= 5.1) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + agoo (2.15.13) + base64 (0.2.0) + bigdecimal (3.1.8) + concurrent-ruby (1.3.4) + connection_pool (2.4.1) + drb (2.2.1) + i18n (1.14.6) + concurrent-ruby (~> 1.0) + json (2.8.1) + kgio (2.11.4) + logger (1.6.1) + minitest (5.25.1) + mustermann (3.0.3) + ruby2_keywords (~> 0.0.1) + mysql2 (0.5.6) + nio4r (2.7.4) + passenger (6.0.23) + rack (>= 1.6.13) + rackup + rake (>= 12.3.3) + pg (1.5.8) + puma (6.5.0) + nio4r (~> 2.0) + rack (3.1.8) + rack-protection (4.0.0) + base64 (>= 0.1.0) + rack (>= 3.0.0, < 4) + rack-session (2.0.0) + rack (>= 3.0.0) + rackup (2.1.0) + rack (>= 3) + webrick (~> 1.8) + raindrops (0.20.1) + rake (13.2.1) + ruby2_keywords (0.0.5) + securerandom (0.3.1) + sinatra (4.0.0) + mustermann (~> 3.0) + rack (>= 3.0.0, < 4) + rack-protection (= 4.0.0) + rack-session (>= 2.0.0, < 3) + tilt (~> 2.0) + tilt (2.4.0) + timeout (0.4.1) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + unicorn (6.1.0) + kgio (~> 2.6) + raindrops (~> 0.7) + webrick (1.8.2) + +PLATFORMS + ruby + x86_64-darwin-23 + +DEPENDENCIES + activerecord (~> 7.2) + agoo + json (~> 2.8) + mysql2 (~> 0.5) + passenger (~> 6.0) + pg (~> 1.5) + puma (~> 6.4) + rackup + sinatra (~> 4.0) + unicorn (~> 6.1) + +BUNDLED WITH + 2.5.16 diff --git a/frameworks/Ruby/sinatra/benchmark_config.json b/frameworks/Ruby/sinatra/benchmark_config.json index 2c0be64ed47..3e318c9aa58 100644 --- a/frameworks/Ruby/sinatra/benchmark_config.json +++ b/frameworks/Ruby/sinatra/benchmark_config.json @@ -44,6 +44,28 @@ "versus": "rack-postgres-puma-mri", "notes": "" }, + "postgres-agoo-mri": { + "json_url": "/json", + "db_url": "/db", + "query_url": "/queries?queries=", + "fortune_url": "/fortunes", + "update_url": "/updates?queries=", + "plaintext_url": "/plaintext", + "port": 8080, + "approach": "Realistic", + "classification": "Micro", + "database": "Postgres", + "framework": "sinatra", + "language": "Ruby", + "orm": "Full", + "platform": "Rack", + "webserver": "Agoo", + "os": "Linux", + "database_os": "Linux", + "display_name": "sinatra-postgres-agoo-mri", + "versus": "rack-postgres-agoo-mri", + "notes": "" + }, "postgres-passenger-mri": { "db_url": "/db", "query_url": "/queries?queries=", diff --git a/frameworks/Ruby/sinatra/boot.rb b/frameworks/Ruby/sinatra/boot.rb index 97fa0c40bdb..261e12f1145 100644 --- a/frameworks/Ruby/sinatra/boot.rb +++ b/frameworks/Ruby/sinatra/boot.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require 'bundler/setup' require 'time' -require 'oj' MAX_PK = 10_000 ID_RANGE = (1..MAX_PK).freeze @@ -11,20 +10,17 @@ SERVER_STRING = if defined?(PhusionPassenger) - [ - PhusionPassenger::SharedConstants::SERVER_TOKEN_NAME, - PhusionPassenger::VERSION_STRING - ].join('/').freeze + 'passenger' elsif defined?(Puma) - Puma::Const::PUMA_SERVER_STRING + 'puma' elsif defined?(Unicorn) - Unicorn::HttpParser::DEFAULTS['SERVER_SOFTWARE'] + 'unicorn' + elsif defined?(Agoo) + 'agoo' end Bundler.require(:default) # Load core modules -Oj.mimic_JSON - def connect(dbtype) Bundler.require(dbtype) # Load database-specific modules diff --git a/frameworks/Ruby/sinatra/sinatra-postgres-agoo-mri.dockerfile b/frameworks/Ruby/sinatra/sinatra-postgres-agoo-mri.dockerfile new file mode 100644 index 00000000000..c6f2f33c4a1 --- /dev/null +++ b/frameworks/Ruby/sinatra/sinatra-postgres-agoo-mri.dockerfile @@ -0,0 +1,20 @@ +FROM ruby:3.4-rc + +ENV RUBY_YJIT_ENABLE=1 + +# Use Jemalloc +RUN apt-get update && \ + apt-get install -y --no-install-recommends libjemalloc2 +ENV LD_PRELOAD=libjemalloc.so.2 + +ADD ./ /sinatra +WORKDIR /sinatra + +ENV BUNDLE_WITH=postgresql:agoo +RUN bundle install --jobs=4 --gemfile=/sinatra/Gemfile + +ENV DBTYPE=postgresql + +EXPOSE 8080 + +CMD RACK_ENV=production bundle exec rackup -r agoo -s agoo -p 8080 -q -O workers=$(ruby config/auto_tune.rb | grep -Eo '[0-9]+' | head -n 1) diff --git a/frameworks/Ruby/sinatra/sinatra-postgres-passenger-mri.dockerfile b/frameworks/Ruby/sinatra/sinatra-postgres-passenger-mri.dockerfile index 2a09b22aeba..78bbf09f2a6 100644 --- a/frameworks/Ruby/sinatra/sinatra-postgres-passenger-mri.dockerfile +++ b/frameworks/Ruby/sinatra/sinatra-postgres-passenger-mri.dockerfile @@ -1,4 +1,4 @@ -FROM ruby:3.3 +FROM ruby:3.4-rc ENV RUBY_YJIT_ENABLE=1 @@ -10,6 +10,7 @@ ENV LD_PRELOAD=libjemalloc.so.2 ADD ./ /sinatra WORKDIR /sinatra +ENV BUNDLE_WITH=postgresql:passenger RUN bundle install --jobs=4 --gemfile=/sinatra/Gemfile # TODO: https://github.com/phusion/passenger/issues/1916 diff --git a/frameworks/Ruby/sinatra/sinatra-postgres-unicorn-mri.dockerfile b/frameworks/Ruby/sinatra/sinatra-postgres-unicorn-mri.dockerfile index 027a5593040..4ca88527522 100644 --- a/frameworks/Ruby/sinatra/sinatra-postgres-unicorn-mri.dockerfile +++ b/frameworks/Ruby/sinatra/sinatra-postgres-unicorn-mri.dockerfile @@ -10,6 +10,7 @@ ENV LD_PRELOAD=libjemalloc.so.2 ADD ./ /sinatra WORKDIR /sinatra +ENV BUNDLE_WITH=postgresql:unicorn RUN bundle install --jobs=4 --gemfile=/sinatra/Gemfile ENV DBTYPE=postgresql diff --git a/frameworks/Ruby/sinatra/sinatra-postgres.dockerfile b/frameworks/Ruby/sinatra/sinatra-postgres.dockerfile index 6258ac08c7d..2162bc12caa 100644 --- a/frameworks/Ruby/sinatra/sinatra-postgres.dockerfile +++ b/frameworks/Ruby/sinatra/sinatra-postgres.dockerfile @@ -10,6 +10,7 @@ ENV LD_PRELOAD=libjemalloc.so.2 ADD ./ /sinatra WORKDIR /sinatra +ENV BUNDLE_WITH=postgresql:puma RUN bundle install --jobs=4 --gemfile=/sinatra/Gemfile ENV DBTYPE=postgresql diff --git a/frameworks/Ruby/sinatra/sinatra.dockerfile b/frameworks/Ruby/sinatra/sinatra.dockerfile index 8e204def810..19a64c836a6 100644 --- a/frameworks/Ruby/sinatra/sinatra.dockerfile +++ b/frameworks/Ruby/sinatra/sinatra.dockerfile @@ -10,6 +10,7 @@ ENV LD_PRELOAD=libjemalloc.so.2 ADD ./ /sinatra WORKDIR /sinatra +ENV BUNDLE_WITH=mysql:puma RUN bundle install --jobs=4 --gemfile=/sinatra/Gemfile ENV DBTYPE=mysql diff --git a/frameworks/Rust/astra/Cargo.lock b/frameworks/Rust/astra/Cargo.lock index 3eb313f1e71..de8ed1f1b7d 100644 --- a/frameworks/Rust/astra/Cargo.lock +++ b/frameworks/Rust/astra/Cargo.lock @@ -268,11 +268,12 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libmimalloc-sys" -version = "0.1.24" +version = "0.1.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7705fc40f6ed493f73584abbb324e74f96b358ff60dfe5659a0f8fc12c590a69" +checksum = "23aa6811d3bd4deb8a84dde645f943476d13b248d818edcf8ce0b2f37f036b44" dependencies = [ "cc", + "libc", ] [[package]] @@ -292,9 +293,9 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" [[package]] name = "mimalloc" -version = "0.1.28" +version = "0.1.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0dfa131390c2f6bdb3242f65ff271fcdaca5ff7b6c08f28398be7f2280e3926" +checksum = "fa01922b5ea280a911e323e4d2fd24b7fe5cc4042e0d2cda3c40775cdc4bdc9c" dependencies = [ "libmimalloc-sys", ] diff --git a/frameworks/Rust/axum/benchmark_config.json b/frameworks/Rust/axum/benchmark_config.json index ade60875389..c33a1edad78 100755 --- a/frameworks/Rust/axum/benchmark_config.json +++ b/frameworks/Rust/axum/benchmark_config.json @@ -28,7 +28,7 @@ "docker_cmd": "/app/axum-sqlx", "db_url": "/db", "fortune_url": "/fortunes", - "cached_query_url": "/cached-queries?queries=", + "cached_query_url": "/cached-queries?q=", "port": 8000, "approach": "Realistic", "classification": "Fullstack", @@ -50,8 +50,8 @@ "docker_cmd": "/app/axum-pg", "db_url": "/db", "fortune_url": "/fortunes", - "query_url": "/queries?queries=", - "update_url": "/updates?queries=", + "query_url": "/queries?q=", + "update_url": "/updates?q=", "port": 8000, "approach": "Realistic", "classification": "Fullstack", @@ -72,8 +72,8 @@ "dockerfile": "axum.dockerfile", "docker_cmd": "/app/axum-pg-pool", "db_url": "/db", - "query_url": "/queries?queries=", - "update_url": "/updates?queries=", + "query_url": "/queries?q=", + "update_url": "/updates?q=", "fortune_url": "/fortunes", "port": 8000, "approach": "Realistic", @@ -95,9 +95,9 @@ "dockerfile": "axum.dockerfile", "docker_cmd": "/app/axum-mongo", "db_url": "/db", - "query_url": "/queries?queries=", + "query_url": "/queries?q=", "fortune_url": "/fortunes", - "update_url": "/updates?queries=", + "update_url": "/updates?q=", "port": 8000, "approach": "Realistic", "classification": "Fullstack", @@ -118,8 +118,8 @@ "dockerfile": "axum.dockerfile", "docker_cmd": "/app/axum-mongo-raw", "db_url": "/db", - "query_url": "/queries?queries=", - "update_url": "/updates?queries=", + "query_url": "/queries?q=", + "update_url": "/updates?q=", "port": 8000, "approach": "Realistic", "classification": "Fullstack", diff --git a/frameworks/Rust/axum/src/common/mod.rs b/frameworks/Rust/axum/src/common/mod.rs index d55958155f1..808b2a70eeb 100644 --- a/frameworks/Rust/axum/src/common/mod.rs +++ b/frameworks/Rust/axum/src/common/mod.rs @@ -36,14 +36,14 @@ where /// Generate a single integer in the range 1 to 10,000 (inclusive) #[allow(dead_code)] -#[inline] +#[inline(always)] pub fn random_id(rng: &mut SmallRng) -> i32 { rng.gen_range(1..10_001) } /// Generate vector of integers in the range 1 to 10,000 (inclusive) #[allow(dead_code)] -#[inline] +#[inline(always)] pub fn random_ids(rng: &mut SmallRng, count: usize) -> Vec { rng.sample_iter(Uniform::new(1, 10_001)) .take(count) diff --git a/frameworks/Rust/axum/src/common/utils.rs b/frameworks/Rust/axum/src/common/utils.rs index 063411f3da7..0da86151e62 100644 --- a/frameworks/Rust/axum/src/common/utils.rs +++ b/frameworks/Rust/axum/src/common/utils.rs @@ -7,13 +7,14 @@ use serde::Deserialize; #[derive(Debug, Deserialize)] pub struct Params { - queries: Option, + q: Option, } #[allow(dead_code)] +#[inline(always)] pub fn parse_params(params: Params) -> usize { params - .queries + .q .and_then(|q| q.parse().ok()) .unwrap_or(1) .clamp(1, 500) diff --git a/frameworks/Rust/axum/src/main.rs b/frameworks/Rust/axum/src/main.rs index 70858e27ddc..0b33fb7edcb 100644 --- a/frameworks/Rust/axum/src/main.rs +++ b/frameworks/Rust/axum/src/main.rs @@ -26,13 +26,16 @@ pub async fn json() -> impl IntoResponse { (StatusCode::OK, Json(message)) } -#[tokio::main] -async fn main() { +fn main() { dotenv().ok(); + server::start_tokio(serve_app) +} + +async fn serve_app() { let app = Router::new() .route("/plaintext", get(plaintext)) .route("/json", get(json)); server::serve_hyper(app, Some(8000)).await -} +} \ No newline at end of file diff --git a/frameworks/Rust/axum/src/main_sqlx.rs b/frameworks/Rust/axum/src/main_sqlx.rs index 163372b83d8..b3a7937841b 100644 --- a/frameworks/Rust/axum/src/main_sqlx.rs +++ b/frameworks/Rust/axum/src/main_sqlx.rs @@ -1,6 +1,8 @@ mod common; mod sqlx; +use std::sync::Arc; + use ::sqlx::PgPool; use axum::{ extract::{Query, State}, @@ -56,7 +58,7 @@ async fn queries( let ids = random_ids(&mut rng, count); let mut worlds: Vec = Vec::with_capacity(count); - for id in ids { + for id in &ids { let world: World = ::sqlx::query_as(common::SELECT_WORLD_BY_ID) .bind(id) .fetch_one(&mut *db.acquire().await.unwrap()) @@ -96,7 +98,7 @@ async fn cache( ) -> impl IntoResponse { let count = parse_params(params); let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap(); - let mut worlds: Vec> = Vec::with_capacity(count); + let mut worlds: Vec>> = Vec::with_capacity(count); for id in random_ids(&mut rng, count) { worlds.push(cache.get(&id).await); @@ -113,7 +115,7 @@ async fn preload_cache(AppState { db, cache }: &AppState) { .expect("error loading worlds"); for world in worlds { - cache.insert(world.id, world).await; + cache.insert(world.id, Arc::new(world)).await; } } @@ -121,7 +123,7 @@ async fn preload_cache(AppState { db, cache }: &AppState) { #[derive(Clone)] struct AppState { db: PgPool, - cache: Cache, + cache: Cache>, } #[tokio::main] diff --git a/frameworks/Rust/axum/src/pg/database.rs b/frameworks/Rust/axum/src/pg/database.rs index bc78b2ba5c8..b9cff086c5a 100644 --- a/frameworks/Rust/axum/src/pg/database.rs +++ b/frameworks/Rust/axum/src/pg/database.rs @@ -90,21 +90,18 @@ impl PgConnection { } pub async fn update_worlds(&self, num: usize) -> Result, PgError> { - let worlds = self.fetch_random_worlds(num).await?; + let mut worlds = self.fetch_random_worlds(num).await?; // Update the worlds with new random numbers let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap(); let mut ids = Vec::with_capacity(num); let mut nids = Vec::with_capacity(num); - let worlds: Vec = worlds - .into_iter() - .map(|mut w| { - w.randomnumber = random_id(&mut rng); - ids.push(w.id); - nids.push(w.randomnumber); - w - }) - .collect(); + + for w in &mut worlds { + w.randomnumber = random_id(&mut rng); + ids.push(w.id); + nids.push(w.randomnumber); + } // Update the random worlds in the database. self.client diff --git a/frameworks/Rust/ntex/Cargo.toml b/frameworks/Rust/ntex/Cargo.toml index ec2173e26e3..3bbf3dffa0f 100755 --- a/frameworks/Rust/ntex/Cargo.toml +++ b/frameworks/Rust/ntex/Cargo.toml @@ -34,11 +34,11 @@ default = [] tokio = ["ntex/tokio"] # compio runtime -compio = ["ntex/compio", ] +compio = ["ntex/compio"] [dependencies] -ntex = "2.4" -ntex-compio = "0.1.2" +ntex = "2.8" +ntex-compio = "0.2" ntex-bytes = { version = "0.1.21", features=["simd"] } mimalloc = { version = "0.1.25", default-features = false } snmalloc-rs = { version = "0.3.3", features = ["native-cpu"] } @@ -47,16 +47,18 @@ buf-min = { version = "0.7", features = ["ntex-bytes"] } env_logger = "0.11" nanorand = { version = "0.7", default-features = false, features = ["std", "wyrand", "tls"] } atoi = "2.0" -num_cpus = "1.16" -smallvec = "1.13" +core_affinity = "0.8" futures = "0.3" -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" +sonic-rs = "0.3.16" +serde = { version = "1", features = ["derive"] } +serde_json = "1" log = { version = "0.4", features = ["release_max_level_off"] } -compio-driver = { version = "0.4", features = ["io-uring", "io-uring-socket"]} tok_io = {version = "1", package = "tokio" } tokio-postgres = { git="https://github.com/fafhrd91/postgres.git", branch="ntex-2" } +[target.'cfg(target_os = "linux")'.dependencies] +compio-driver = { version = "*", features = ["io-uring"]} + [profile.release] opt-level = 3 codegen-units = 1 diff --git a/frameworks/Rust/ntex/src/db.rs b/frameworks/Rust/ntex/src/db.rs index c5d525eb6de..cd4443f3c00 100644 --- a/frameworks/Rust/ntex/src/db.rs +++ b/frameworks/Rust/ntex/src/db.rs @@ -2,26 +2,31 @@ use std::{borrow::Cow, cell::RefCell, fmt::Write as FmtWrite}; use nanorand::{Rng, WyRand}; -use ntex::util::{BufMut, Bytes, BytesMut}; -use smallvec::SmallVec; +use ntex::util::{Bytes, BytesMut}; use tokio_postgres::types::ToSql; use tokio_postgres::{connect, Client, Statement}; -use yarte::{ywrite_html, Serialize}; +use yarte::TemplateBytesTrait; use super::utils; -#[derive(Copy, Clone, Serialize, Debug)] +#[derive(Copy, Clone, Debug, sonic_rs::Serialize)] pub struct World { pub id: i32, pub randomnumber: i32, } -#[derive(Serialize, Debug)] +#[derive(Debug, sonic_rs::Serialize)] pub struct Fortune { pub id: i32, pub message: Cow<'static, str>, } +#[derive(yarte::TemplateBytes)] +#[template(path = "fortune.hbs")] +pub struct FortunesTemplate<'a> { + pub fortunes: &'a Vec, +} + /// Postgres interface pub struct PgConnection { cl: Client, @@ -30,6 +35,7 @@ pub struct PgConnection { rng: WyRand, updates: Vec, buf: RefCell, + fbuf: RefCell>, } impl PgConnection { @@ -69,6 +75,7 @@ impl PgConnection { updates, rng: WyRand::new(), buf: RefCell::new(BytesMut::with_capacity(10 * 1024 * 1024)), + fbuf: RefCell::new(Vec::with_capacity(64)), } } } @@ -81,23 +88,26 @@ impl PgConnection { let mut body = self.buf.borrow_mut(); utils::reserve(&mut body, 1024); - World { - id: row.get(0), - randomnumber: row.get(1), - } - .to_bytes_mut(&mut *body); + sonic_rs::to_writer( + utils::BytesWriter(&mut body), + &World { + id: row.get(0), + randomnumber: row.get(1), + }, + ) + .unwrap(); body.split().freeze() } pub async fn get_worlds(&self, num: usize) -> Bytes { let mut rng = self.rng.clone(); - let mut queries = SmallVec::<[_; 32]>::new(); + let mut queries = Vec::with_capacity(num); (0..num).for_each(|_| { let w_id = (rng.generate::() % 10_000 + 1) as i32; queries.push(self.cl.query_one(&self.world, &[&w_id])); }); - let mut worlds = SmallVec::<[_; 32]>::new(); + let mut worlds = Vec::with_capacity(num); for fut in queries { let row = fut.await.unwrap(); worlds.push(World { @@ -108,25 +118,19 @@ impl PgConnection { let mut body = self.buf.borrow_mut(); utils::reserve(&mut body, 2 * 1024); - body.put_u8(b'['); - worlds.iter().for_each(|w| { - w.to_bytes_mut(&mut *body); - body.put_u8(b','); - }); - let idx = body.len() - 1; - body[idx] = b']'; + sonic_rs::to_writer(utils::BytesWriter(&mut body), &worlds[..]).unwrap(); body.split().freeze() } pub async fn update(&self, num: usize) -> Bytes { let mut rng = nanorand::tls_rng(); - let mut queries = SmallVec::<[_; 32]>::new(); + let mut queries = Vec::with_capacity(num); (0..num).for_each(|_| { let w_id = (rng.generate::() % 10_000 + 1) as i32; queries.push(self.cl.query_one(&self.world, &[&w_id])); }); - let mut worlds = SmallVec::<[_; 32]>::new(); + let mut worlds = Vec::with_capacity(num); for fut in queries.into_iter() { let row = fut.await.unwrap(); worlds.push(World { @@ -147,23 +151,18 @@ impl PgConnection { let mut body = self.buf.borrow_mut(); utils::reserve(&mut body, 2 * 1024); - body.put_u8(b'['); - worlds.iter().for_each(|w| { - w.to_bytes_mut(&mut *body); - body.put_u8(b','); - }); - let idx = body.len() - 1; - body[idx] = b']'; + sonic_rs::to_writer(utils::BytesWriter(&mut body), &worlds[..]).unwrap(); body.split().freeze() } pub async fn tell_fortune(&self) -> Bytes { let rows = self.cl.query_raw(&self.fortune, &[]).await.unwrap(); - let mut fortunes: SmallVec<[_; 32]> = smallvec::smallvec![Fortune { + let mut fortunes = self.fbuf.borrow_mut(); + fortunes.push(Fortune { id: 0, message: Cow::Borrowed("Additional fortune added at request time."), - }]; + }); fortunes.extend(rows.iter().map(|row| Fortune { id: row.get(0), message: Cow::Owned(row.get(1)), @@ -172,7 +171,13 @@ impl PgConnection { let mut body = std::mem::replace(&mut *self.buf.borrow_mut(), BytesMut::new()); utils::reserve(&mut body, 4 * 1024); - ywrite_html!(body, "{{> fortune }}"); + + FortunesTemplate { + fortunes: &*fortunes, + } + .write_call(&mut body); + fortunes.clear(); + let result = body.split().freeze(); let _ = std::mem::replace(&mut *self.buf.borrow_mut(), body); result diff --git a/frameworks/Rust/ntex/src/main.rs b/frameworks/Rust/ntex/src/main.rs index 218ed03c727..5210d85863f 100644 --- a/frameworks/Rust/ntex/src/main.rs +++ b/frameworks/Rust/ntex/src/main.rs @@ -2,8 +2,8 @@ static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; use ntex::http::header::{CONTENT_TYPE, SERVER}; -use ntex::{http, time::Seconds, util::BytesMut, util::PoolId, web}; -use yarte::Serialize; +use ntex::{http, time::Seconds, util::BytesMut, util::PoolId, util::Ready, web}; +use sonic_rs::Serialize; mod utils; @@ -15,10 +15,13 @@ pub struct Message { #[web::get("/json")] async fn json() -> web::HttpResponse { let mut body = BytesMut::with_capacity(utils::SIZE); - Message { - message: "Hello, World!", - } - .to_bytes_mut(&mut body); + sonic_rs::to_writer( + utils::BytesWriter(&mut body), + &Message { + message: "Hello, World!", + }, + ) + .unwrap(); let mut response = web::HttpResponse::with_body(http::StatusCode::OK, body.into()); response.headers_mut().insert(SERVER, utils::HDR_SERVER); @@ -45,6 +48,10 @@ async fn plaintext() -> web::HttpResponse { async fn main() -> std::io::Result<()> { println!("Started http server: 127.0.0.1:8080"); + let cores = core_affinity::get_core_ids().unwrap(); + let total_cores = cores.len(); + let cores = std::sync::Arc::new(std::sync::Mutex::new(cores)); + // start http server ntex::server::build() .backlog(1024) @@ -60,7 +67,17 @@ async fn main() -> std::io::Result<()> { .payload_read_rate(Seconds::ZERO, Seconds::ZERO, 0) .h1(web::App::new().service(json).service(plaintext).finish()) })? - .workers(num_cpus::get()) + .configure(move |cfg| { + let cores = cores.clone(); + cfg.on_worker_start(move |_| { + if let Some(core) = cores.lock().unwrap().pop() { + core_affinity::set_for_current(core); + } + Ready::<_, &str>::Ok(()) + }); + Ok(()) + })? + .workers(total_cores) .run() .await } diff --git a/frameworks/Rust/ntex/src/main_db.rs b/frameworks/Rust/ntex/src/main_db.rs index 78c21fa7132..4197322bc13 100644 --- a/frameworks/Rust/ntex/src/main_db.rs +++ b/frameworks/Rust/ntex/src/main_db.rs @@ -1,13 +1,12 @@ #[cfg(not(target_os = "macos"))] #[global_allocator] static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; -// static GLOBAL: snmalloc_rs::SnMalloc = snmalloc_rs::SnMalloc; use ntex::http::header::{CONTENT_TYPE, SERVER}; use ntex::http::{HttpService, KeepAlive, Request, Response, StatusCode}; use ntex::service::{Service, ServiceCtx, ServiceFactory}; use ntex::web::{Error, HttpResponse}; -use ntex::{time::Seconds, util::PoolId}; +use ntex::{time::Seconds, util::PoolId, util::Ready}; mod db; mod utils; @@ -83,6 +82,10 @@ impl ServiceFactory for AppFactory { async fn main() -> std::io::Result<()> { println!("Starting http server: 127.0.0.1:8080"); + let cores = core_affinity::get_core_ids().unwrap(); + let total_cores = cores.len(); + let cores = std::sync::Arc::new(std::sync::Mutex::new(cores)); + ntex::server::build() .backlog(1024) .bind("techempower", "0.0.0.0:8080", |cfg| { @@ -97,7 +100,17 @@ async fn main() -> std::io::Result<()> { .payload_read_rate(Seconds::ZERO, Seconds::ZERO, 0) .h1(AppFactory) })? - .workers(num_cpus::get()) + .configure(move |cfg| { + let cores = cores.clone(); + cfg.on_worker_start(move |_| { + if let Some(core) = cores.lock().unwrap().pop() { + core_affinity::set_for_current(core); + } + Ready::<_, &str>::Ok(()) + }); + Ok(()) + })? + .workers(total_cores) .run() .await } diff --git a/frameworks/Rust/ntex/src/main_plt.rs b/frameworks/Rust/ntex/src/main_plt.rs index b6c435f6773..9cc80ac9952 100644 --- a/frameworks/Rust/ntex/src/main_plt.rs +++ b/frameworks/Rust/ntex/src/main_plt.rs @@ -3,8 +3,9 @@ static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; use std::{future::Future, io, pin::Pin, task::Context, task::Poll}; -use ntex::{fn_service, http::h1, io::Io, io::RecvError, util::ready, util::PoolId}; -use yarte::Serialize; +use ntex::util::{ready, PoolId, Ready}; +use ntex::{fn_service, http::h1, io::Io, io::RecvError}; +use sonic_rs::Serialize; mod utils; @@ -42,10 +43,13 @@ impl Future for App { buf.extend_from_slice(JSON); this.codec.set_date_header(buf); - Message { - message: "Hello, World!", - } - .to_bytes_mut(buf); + sonic_rs::to_writer( + utils::BytesWriter(buf), + &Message { + message: "Hello, World!", + }, + ) + .unwrap(); } "/plaintext" => { buf.extend_from_slice(PLAIN); @@ -75,6 +79,10 @@ impl Future for App { async fn main() -> io::Result<()> { println!("Started http server: 127.0.0.1:8080"); + let cores = core_affinity::get_core_ids().unwrap(); + let total_cores = cores.len(); + let cores = std::sync::Arc::new(std::sync::Mutex::new(cores)); + // start http server ntex::server::build() .backlog(1024) @@ -88,7 +96,17 @@ async fn main() -> io::Result<()> { codec: h1::Codec::default(), }) })? - .workers(num_cpus::get()) + .configure(move |cfg| { + let cores = cores.clone(); + cfg.on_worker_start(move |_| { + if let Some(core) = cores.lock().unwrap().pop() { + core_affinity::set_for_current(core); + } + Ready::<_, &str>::Ok(()) + }); + Ok(()) + })? + .workers(total_cores) .run() .await } diff --git a/frameworks/Rust/ntex/src/utils.rs b/frameworks/Rust/ntex/src/utils.rs index 719d5962dd4..7a2e6bf1df1 100644 --- a/frameworks/Rust/ntex/src/utils.rs +++ b/frameworks/Rust/ntex/src/utils.rs @@ -1,8 +1,9 @@ #![allow(dead_code)] -use std::cmp; +use std::{cmp, io, io::Write, mem::MaybeUninit, slice::from_raw_parts_mut}; use atoi::FromRadix10; use ntex::{http::header::HeaderValue, util::BufMut, util::Bytes, util::BytesMut}; +use sonic_rs::writer::WriteExt; pub const HDR_SERVER: HeaderValue = HeaderValue::from_static("N"); pub const HDR_JSON_CONTENT_TYPE: HeaderValue = HeaderValue::from_static("application/json"); @@ -30,3 +31,36 @@ pub fn reserve(buf: &mut BytesMut, lw: usize) { buf.reserve(HW); } } + +pub struct BytesWriter<'a>(pub &'a mut BytesMut); + +impl<'a> Write for BytesWriter<'a> { + fn write(&mut self, src: &[u8]) -> Result { + self.0.extend_from_slice(src); + Ok(src.len()) + } + + fn flush(&mut self) -> Result<(), io::Error> { + Ok(()) + } +} + +impl<'a> WriteExt for BytesWriter<'a> { + #[inline(always)] + fn reserve_with(&mut self, additional: usize) -> Result<&mut [MaybeUninit], io::Error> { + self.0.reserve(additional); + + unsafe { + let ptr = self.0.as_mut_ptr().add(self.0.len()) as *mut MaybeUninit; + Ok(from_raw_parts_mut(ptr, additional)) + } + } + + #[inline(always)] + unsafe fn flush_len(&mut self, additional: usize) { + unsafe { + let new_len = self.0.len() + additional; + self.0.set_len(new_len); + } + } +} diff --git a/frameworks/Rust/ntex/templates/fortune.hbs b/frameworks/Rust/ntex/templates/fortune.hbs index b9e25a52a8e..499ac5ae966 100644 --- a/frameworks/Rust/ntex/templates/fortune.hbs +++ b/frameworks/Rust/ntex/templates/fortune.hbs @@ -1,5 +1,5 @@ Fortunes - {{~# each fortunes ~}} - - {{~/each ~}} +{{~# each fortunes ~}} + +{{~/each ~}}
idmessage
{{id}}{{message}}
{{id}}{{message}}
diff --git a/frameworks/Rust/ntex/templates/fortune.html b/frameworks/Rust/ntex/templates/fortune.html deleted file mode 100644 index 7c448b9d999..00000000000 --- a/frameworks/Rust/ntex/templates/fortune.html +++ /dev/null @@ -1,12 +0,0 @@ - - - Fortunes - - - - {% for item in items %} - - {% endfor %} -
idmessage
{{item.id}}{{item.message}}
- - diff --git a/frameworks/Rust/ntex/templates/fortune.stpl b/frameworks/Rust/ntex/templates/fortune.stpl deleted file mode 100644 index 238f470a43d..00000000000 --- a/frameworks/Rust/ntex/templates/fortune.stpl +++ /dev/null @@ -1,10 +0,0 @@ - - - Fortunes - - - - <% for item in items { %><% } %> -
idmessage
<%= item.id %><%= &*item.message %>
- - diff --git a/frameworks/Rust/salvo/Cargo.toml b/frameworks/Rust/salvo/Cargo.toml index a7795fc6636..58fa4a6a315 100644 --- a/frameworks/Rust/salvo/Cargo.toml +++ b/frameworks/Rust/salvo/Cargo.toml @@ -43,13 +43,13 @@ diesel = { version = "2", features = ["postgres", "r2d2"] } deadpool = { version = "0.12", features = ["rt_tokio_1", "serde", "managed"] } deadpool-postgres = "0.14" futures-util = "0.3" -lru = "0.12.0" +lru = "0.12" markup = "0.15" # mimalloc = { version = "0.1", default-features = false } -mongodb = { version = "2.4.0", features = ["zstd-compression", "snappy-compression", "zlib-compression"] } +mongodb = { version = "2", features = ["zstd-compression", "snappy-compression", "zlib-compression"] } once_cell = "1" rand = { version = "0.8", features = ["min_const_gen", "small_rng"] } -salvo = { version = "0.68", default-features = false, features = ["anyhow", "server", "http1", "affix"] } +salvo = { version = "0.74", default-features = false, features = ["anyhow", "server", "http1", "affix-state"] } serde = { version = "1", features = ["derive"] } serde_json = "1" # smallvec = "1" @@ -60,7 +60,7 @@ tokio-pg-mapper = "0.2.0" tokio-pg-mapper-derive = "0.2.0" tokio-postgres = "0.7" v_htmlescape = "0.15" -dotenv = "0.15.0" +dotenvy = "0.15" [profile.release] lto = true diff --git a/frameworks/Rust/salvo/salvo-diesel.dockerfile b/frameworks/Rust/salvo/salvo-diesel.dockerfile index 1577e454443..0943b3af73a 100644 --- a/frameworks/Rust/salvo/salvo-diesel.dockerfile +++ b/frameworks/Rust/salvo/salvo-diesel.dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.78-buster +FROM rust:1.83 ENV TECHEMPOWER_POSTGRES_URL=postgres://benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world ENV TECHEMPOWER_MAX_POOL_SIZE=28 diff --git a/frameworks/Rust/salvo/salvo-lru.dockerfile b/frameworks/Rust/salvo/salvo-lru.dockerfile index 5f668adaf76..4dd1d4511be 100644 --- a/frameworks/Rust/salvo/salvo-lru.dockerfile +++ b/frameworks/Rust/salvo/salvo-lru.dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.78-buster +FROM rust:1.83 ENV TECHEMPOWER_POSTGRES_URL=postgres://benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world diff --git a/frameworks/Rust/salvo/salvo-mongo-raw.dockerfile b/frameworks/Rust/salvo/salvo-mongo-raw.dockerfile index 2fb904eb558..2f15faea13c 100644 --- a/frameworks/Rust/salvo/salvo-mongo-raw.dockerfile +++ b/frameworks/Rust/salvo/salvo-mongo-raw.dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.78-buster +FROM rust:1.83 ENV TECHEMPOWER_MONGODB_URL=mongodb://tfb-database:27017 ENV TECHEMPOWER_MAX_POOL_SIZE=28 diff --git a/frameworks/Rust/salvo/salvo-mongo.dockerfile b/frameworks/Rust/salvo/salvo-mongo.dockerfile index e8a454910c1..2eccf7dd4be 100644 --- a/frameworks/Rust/salvo/salvo-mongo.dockerfile +++ b/frameworks/Rust/salvo/salvo-mongo.dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.78-buster +FROM rust:1.83 ENV TECHEMPOWER_MONGODB_URL=mongodb://tfb-database:27017 ENV TECHEMPOWER_MAX_POOL_SIZE=28 diff --git a/frameworks/Rust/salvo/salvo-pg-pool.dockerfile b/frameworks/Rust/salvo/salvo-pg-pool.dockerfile index 8ef7fc1eeda..bb4f4dba212 100644 --- a/frameworks/Rust/salvo/salvo-pg-pool.dockerfile +++ b/frameworks/Rust/salvo/salvo-pg-pool.dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.78-buster +FROM rust:1.83 ENV TECHEMPOWER_POSTGRES_URL=postgres://benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world ENV TECHEMPOWER_MAX_POOL_SIZE=28 diff --git a/frameworks/Rust/salvo/salvo-pg.dockerfile b/frameworks/Rust/salvo/salvo-pg.dockerfile index 70deda13cd6..da1009087e1 100644 --- a/frameworks/Rust/salvo/salvo-pg.dockerfile +++ b/frameworks/Rust/salvo/salvo-pg.dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.78-buster +FROM rust:1.83 ENV TECHEMPOWER_POSTGRES_URL=postgres://benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world diff --git a/frameworks/Rust/salvo/salvo-sqlx.dockerfile b/frameworks/Rust/salvo/salvo-sqlx.dockerfile index 7d88a5d20d9..6da69983675 100644 --- a/frameworks/Rust/salvo/salvo-sqlx.dockerfile +++ b/frameworks/Rust/salvo/salvo-sqlx.dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.78-buster +FROM rust:1.83 ENV TECHEMPOWER_POSTGRES_URL=postgres://benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world ENV TECHEMPOWER_MAX_POOL_SIZE=56 diff --git a/frameworks/Rust/salvo/salvo.dockerfile b/frameworks/Rust/salvo/salvo.dockerfile index 8f0d750dcee..11553801801 100644 --- a/frameworks/Rust/salvo/salvo.dockerfile +++ b/frameworks/Rust/salvo/salvo.dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.78-buster +FROM rust:1.83 ADD ./ /salvo WORKDIR /salvo diff --git a/frameworks/Rust/salvo/src/main_diesel.rs b/frameworks/Rust/salvo/src/main_diesel.rs index 7e2e9f8b36a..12e8bdc283a 100644 --- a/frameworks/Rust/salvo/src/main_diesel.rs +++ b/frameworks/Rust/salvo/src/main_diesel.rs @@ -15,7 +15,7 @@ use std::thread::available_parallelism; use anyhow::Error; use diesel::prelude::*; use diesel::r2d2::{ConnectionManager, Pool, PoolError, PooledConnection}; -use dotenv::dotenv; +use dotenvy::dotenv; use once_cell::sync::OnceCell; use rand::rngs::SmallRng; use rand::{Rng, SeedableRng}; diff --git a/frameworks/Rust/salvo/src/main_lru.rs b/frameworks/Rust/salvo/src/main_lru.rs index 77186e58ba9..0f4abcceefc 100644 --- a/frameworks/Rust/salvo/src/main_lru.rs +++ b/frameworks/Rust/salvo/src/main_lru.rs @@ -10,7 +10,7 @@ use std::thread::available_parallelism; use anyhow::Error; use bytes::Bytes; -use dotenv::dotenv; +use dotenvy::dotenv; use lru::LruCache; use once_cell::sync::OnceCell; use rand::rngs::SmallRng; diff --git a/frameworks/Rust/salvo/src/main_mongo.rs b/frameworks/Rust/salvo/src/main_mongo.rs index 372fc5bd7b8..54dfca1b183 100644 --- a/frameworks/Rust/salvo/src/main_mongo.rs +++ b/frameworks/Rust/salvo/src/main_mongo.rs @@ -10,7 +10,7 @@ use std::time::Duration; use anyhow::Error; use bytes::Bytes; -use dotenv::dotenv; +use dotenvy::dotenv; use mongodb::{ options::{ClientOptions, Compressor}, Client, Database, @@ -177,7 +177,7 @@ async fn serve() { let database = client.database("hello_world"); let router = Router::new() - .hoop(salvo::affix::inject(database)) + .hoop(salvo::affix_state::inject(database)) .push(Router::with_path("db").get(world_row)) .push(Router::with_path("fortunes").get(fortunes)) .push(Router::with_path("queries").get(queries)) diff --git a/frameworks/Rust/salvo/src/main_mongo_raw.rs b/frameworks/Rust/salvo/src/main_mongo_raw.rs index 8c4e5af5efb..ba575dc33bc 100644 --- a/frameworks/Rust/salvo/src/main_mongo_raw.rs +++ b/frameworks/Rust/salvo/src/main_mongo_raw.rs @@ -9,7 +9,7 @@ use std::time::Duration; use anyhow::Error; use bytes::Bytes; -use dotenv::dotenv; +use dotenvy::dotenv; use mongodb::{ options::{ClientOptions, Compressor}, Client, Database, @@ -140,7 +140,7 @@ async fn serve() { let database = client.database("hello_world"); let router = Router::new() - .hoop(salvo::affix::inject(database)) + .hoop(salvo::affix_state::inject(database)) .push(Router::with_path("db").get(world_row)) .push(Router::with_path("queries").get(queries)) .push(Router::with_path("updates").get(updates)); diff --git a/frameworks/Rust/salvo/src/main_pg.rs b/frameworks/Rust/salvo/src/main_pg.rs index dfa6be4ef9f..af04bf4b718 100644 --- a/frameworks/Rust/salvo/src/main_pg.rs +++ b/frameworks/Rust/salvo/src/main_pg.rs @@ -9,7 +9,7 @@ use std::thread::available_parallelism; use async_trait::async_trait; use bytes::Bytes; -use dotenv::dotenv; +use dotenvy::dotenv; use salvo::conn::tcp::TcpAcceptor; use salvo::http::header::{self, HeaderValue}; use salvo::http::ResBody; diff --git a/frameworks/Rust/salvo/src/main_pg_pool.rs b/frameworks/Rust/salvo/src/main_pg_pool.rs index f72f1c64a09..1e5d6c0b6ec 100644 --- a/frameworks/Rust/salvo/src/main_pg_pool.rs +++ b/frameworks/Rust/salvo/src/main_pg_pool.rs @@ -11,7 +11,7 @@ use std::thread::available_parallelism; use anyhow::Error; use bytes::Bytes; use deadpool_postgres::Pool; -use dotenv::dotenv; +use dotenvy::dotenv; use futures_util::{stream::FuturesUnordered, TryStreamExt}; use once_cell::sync::OnceCell; use rand::rngs::SmallRng; diff --git a/frameworks/Rust/salvo/src/main_sqlx.rs b/frameworks/Rust/salvo/src/main_sqlx.rs index 6fc8f72c922..ca52a8e6b61 100644 --- a/frameworks/Rust/salvo/src/main_sqlx.rs +++ b/frameworks/Rust/salvo/src/main_sqlx.rs @@ -9,7 +9,7 @@ use std::thread::available_parallelism; use anyhow::Error; use bytes::Bytes; -use dotenv::dotenv; +use dotenvy::dotenv; use once_cell::sync::OnceCell; use rand::rngs::SmallRng; use rand::{Rng, SeedableRng}; diff --git a/frameworks/Rust/xitca-web/Cargo.lock b/frameworks/Rust/xitca-web/Cargo.lock index 070a2e67550..a3f4e7a02cd 100644 --- a/frameworks/Rust/xitca-web/Cargo.lock +++ b/frameworks/Rust/xitca-web/Cargo.lock @@ -66,9 +66,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bb8" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b10cf871f3ff2ce56432fddc2615ac7acc3aa22ca321f8fea800846fbb32f188" +checksum = "d89aabfae550a5c44b43ab941844ffcd2e993cb6900b342debf59e9ea74acdb8" dependencies = [ "async-trait", "futures-util", @@ -111,15 +111,15 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.2" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" [[package]] name = "cc" -version = "1.1.30" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" +checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" dependencies = [ "shlex", ] @@ -130,11 +130,22 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "core_affinity" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622892f5635ce1fc38c8f16dfc938553ed64af482edb5e150bf4caedbfcb2304" +dependencies = [ + "libc", + "num_cpus", + "winapi", +] + [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" dependencies = [ "libc", ] @@ -429,9 +440,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "7a73e9fe3c49d7afb2ace819fa181a287ce54a0983eda4e0eb05c22f82ffe534" [[package]] name = "itoap" @@ -450,9 +461,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.159" +version = "0.2.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" +checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" [[package]] name = "libmimalloc-sys" @@ -534,6 +545,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "object" version = "0.36.5" @@ -598,9 +619,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -657,9 +678,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.87" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] @@ -757,12 +778,11 @@ dependencies = [ [[package]] name = "scoped-futures" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1473e24c637950c9bd38763220bea91ec3e095a89f672bbd7a10d03e77ba467" +checksum = "1b24aae2d0636530f359e9d5ef0c04669d11c5e756699b27a6a6d845d8329091" dependencies = [ - "cfg-if", - "pin-utils", + "pin-project-lite", ] [[package]] @@ -773,18 +793,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.210" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", @@ -793,9 +813,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa", "memchr", @@ -907,9 +927,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.79" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -933,8 +953,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.40.0" -source = "git+https://github.com/tokio-rs/tokio.git?rev=512e9de#512e9decfb683d22f4a145459142542caa0894c9" +version = "1.41.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" dependencies = [ "backtrace", "bytes", @@ -1031,9 +1052,9 @@ checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-normalization" @@ -1248,7 +1269,7 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "xitca-codegen" version = "0.4.0" -source = "git+http://github.com/HFQR/xitca-web?rev=1de8d9c#1de8d9c079e73f7fd9ba953741302d87e50d831a" +source = "git+http://github.com/HFQR/xitca-web?rev=3b005af#3b005af839ce718f9121d57788f5185296ceacf1" dependencies = [ "quote", "syn", @@ -1257,7 +1278,7 @@ dependencies = [ [[package]] name = "xitca-http" version = "0.7.0" -source = "git+http://github.com/HFQR/xitca-web?rev=1de8d9c#1de8d9c079e73f7fd9ba953741302d87e50d831a" +source = "git+http://github.com/HFQR/xitca-web?rev=3b005af#3b005af839ce718f9121d57788f5185296ceacf1" dependencies = [ "futures-core", "http", @@ -1307,7 +1328,7 @@ dependencies = [ [[package]] name = "xitca-postgres" version = "0.3.0" -source = "git+http://github.com/HFQR/xitca-web?rev=1de8d9c#1de8d9c079e73f7fd9ba953741302d87e50d831a" +source = "git+http://github.com/HFQR/xitca-web?rev=3b005af#3b005af839ce718f9121d57788f5185296ceacf1" dependencies = [ "fallible-iterator", "futures-core", @@ -1345,7 +1366,7 @@ dependencies = [ [[package]] name = "xitca-server" version = "0.5.0" -source = "git+http://github.com/HFQR/xitca-web?rev=1de8d9c#1de8d9c079e73f7fd9ba953741302d87e50d831a" +source = "git+http://github.com/HFQR/xitca-web?rev=3b005af#3b005af839ce718f9121d57788f5185296ceacf1" dependencies = [ "socket2 0.5.7", "tokio", @@ -1359,7 +1380,7 @@ dependencies = [ [[package]] name = "xitca-service" version = "0.3.0" -source = "git+http://github.com/HFQR/xitca-web?rev=1de8d9c#1de8d9c079e73f7fd9ba953741302d87e50d831a" +source = "git+http://github.com/HFQR/xitca-web?rev=3b005af#3b005af839ce718f9121d57788f5185296ceacf1" [[package]] name = "xitca-unsafe-collection" @@ -1375,6 +1396,7 @@ name = "xitca-web" version = "0.1.0" dependencies = [ "atoi", + "core_affinity", "diesel", "diesel-async", "futures-core", @@ -1400,7 +1422,7 @@ dependencies = [ [[package]] name = "xitca-web" version = "0.7.0" -source = "git+http://github.com/HFQR/xitca-web?rev=1de8d9c#1de8d9c079e73f7fd9ba953741302d87e50d831a" +source = "git+http://github.com/HFQR/xitca-web?rev=3b005af#3b005af839ce718f9121d57788f5185296ceacf1" dependencies = [ "futures-core", "pin-project-lite", diff --git a/frameworks/Rust/xitca-web/Cargo.toml b/frameworks/Rust/xitca-web/Cargo.toml index c547c16de48..acae496a4f2 100644 --- a/frameworks/Rust/xitca-web/Cargo.toml +++ b/frameworks/Rust/xitca-web/Cargo.toml @@ -46,7 +46,7 @@ template = ["dep:sailfish"] # io-uring optional io-uring = ["dep:tokio-uring", "xitca-http/io-uring", "xitca-server/io-uring"] # unrealistic performance optimization -perf = ["dep:mimalloc", "tokio/parking_lot"] +perf = ["dep:core_affinity", "dep:mimalloc", "tokio/parking_lot"] [dependencies] xitca-http = "0.7" @@ -81,13 +81,14 @@ sailfish = { version = "0.9", default-features = false, features = ["perf-inline tokio-uring = { version = "0.5", optional = true } # perf optional +core_affinity = { version = "0.8.1", optional = true } mimalloc = { version = "0.1", default-features = false, optional = true } # stuff can not be used or not needed in wasi target [target.'cfg(not(target_family = "wasm"))'.dependencies] futures-core = { version = "0.3", default-features = false } rand = { version = "0.8", features = ["small_rng"] } -tokio = "1" +tokio = "1.41" [profile.release] lto = true @@ -100,11 +101,10 @@ xitca-postgres-diesel = { git = "https://github.com/fakeshadow/xitca-postgres-di diesel-async = { git = "https://github.com/weiznich/diesel_async", rev = "5b8262b" } mio = { git = "https://github.com/fakeshadow/mio", rev = "9bae6012b7ecfc6083350785f71a5e8265358178" } -tokio = { git = "https://github.com/tokio-rs/tokio.git", rev = "512e9de" } - -xitca-codegen = { git = "http://github.com/HFQR/xitca-web", rev = "1de8d9c" } -xitca-http = { git = "http://github.com/HFQR/xitca-web", rev = "1de8d9c" } -xitca-postgres = { git = "http://github.com/HFQR/xitca-web", rev = "1de8d9c" } -xitca-server = { git = "http://github.com/HFQR/xitca-web", rev = "1de8d9c" } -xitca-service = { git = "http://github.com/HFQR/xitca-web", rev = "1de8d9c" } -xitca-web = { git = "http://github.com/HFQR/xitca-web", rev = "1de8d9c" } + +xitca-codegen = { git = "http://github.com/HFQR/xitca-web", rev = "3b005af" } +xitca-http = { git = "http://github.com/HFQR/xitca-web", rev = "3b005af" } +xitca-postgres = { git = "http://github.com/HFQR/xitca-web", rev = "3b005af" } +xitca-server = { git = "http://github.com/HFQR/xitca-web", rev = "3b005af" } +xitca-service = { git = "http://github.com/HFQR/xitca-web", rev = "3b005af" } +xitca-web = { git = "http://github.com/HFQR/xitca-web", rev = "3b005af" } diff --git a/frameworks/Rust/xitca-web/src/main.rs b/frameworks/Rust/xitca-web/src/main.rs index b7389fbb821..abe665f3715 100755 --- a/frameworks/Rust/xitca-web/src/main.rs +++ b/frameworks/Rust/xitca-web/src/main.rs @@ -51,14 +51,12 @@ where #[cold] #[inline(never)] fn error_handler(e: RouterError) -> Response { - match e { - RouterError::Match(_) => error_response(StatusCode::NOT_FOUND), - RouterError::NotAllowed(_) => error_response(StatusCode::METHOD_NOT_ALLOWED), - RouterError::Service(e) => { - println!("{e}"); - error_response(StatusCode::INTERNAL_SERVER_ERROR) - } - } + let status = match e { + RouterError::Match(_) => StatusCode::NOT_FOUND, + RouterError::NotAllowed(_) => StatusCode::METHOD_NOT_ALLOWED, + RouterError::Service(_) => StatusCode::INTERNAL_SERVER_ERROR, + }; + error_response(status) } async fn plain_text(ctx: Ctx<'_>) -> HandleResult { diff --git a/frameworks/Rust/xitca-web/src/main_unrealistic.rs b/frameworks/Rust/xitca-web/src/main_unrealistic.rs index e291799408c..a4f9223ba44 100644 --- a/frameworks/Rust/xitca-web/src/main_unrealistic.rs +++ b/frameworks/Rust/xitca-web/src/main_unrealistic.rs @@ -10,14 +10,13 @@ mod db; mod ser; mod util; -use std::{convert::Infallible, io}; +use std::io; use xitca_http::{ bytes::BufMutWriter, h1::dispatcher_unreal::{Dispatcher, Request, Response}, http::StatusCode, }; -use xitca_io::net::TcpStream; use xitca_service::Service; use self::{ @@ -30,49 +29,57 @@ fn main() -> io::Result<()> { let cores = std::thread::available_parallelism().map(|num| num.get()).unwrap_or(56); + let mut ids = core_affinity::get_core_ids().unwrap(); + + let worker = move |id: Option| { + if let Some(id) = id { + let _ = core_affinity::set_for_current(id); + } + + tokio::runtime::Builder::new_current_thread() + .enable_all() + .build_local(&Default::default()) + .unwrap() + .block_on(async { + let socket = tokio::net::TcpSocket::new_v4()?; + socket.set_reuseaddr(true)?; + // unrealistic due to following reason: + // 1. this only works good on unix system. + // 2. no resource distribution adjustment between sockets on different threads. causing uneven workload + // where some threads are idle while others busy. resulting in overall increased latency + socket.set_reuseport(true)?; + socket.bind(addr)?; + let listener = socket.listen(1024)?; + + let client = db::create().await.unwrap(); + + // unrealistic http dispatcher. no spec check. no security feature. + let service = Dispatcher::new(handler, State::new(client)); + + loop { + match listener.accept().await { + Ok((stream, _)) => { + let service = service.clone(); + tokio::task::spawn_local(async move { + let _ = service.call(stream.into()).await; + }); + } + Err(e) => return Err(e), + }; + } + }) + }; + let handle = core::iter::repeat_with(|| { - std::thread::spawn(move || { - tokio::runtime::Builder::new_current_thread() - .enable_all() - .build_local(&Default::default()) - .unwrap() - .block_on(async { - let socket = tokio::net::TcpSocket::new_v4()?; - socket.set_reuseaddr(true)?; - // unrealistic due to following reason: - // 1. this only works good on unix system. - // 2. no resource distribution adjustment between sockets on different threads. causing uneven workload - // where some threads are idle while others busy. resulting in overall increased latency - socket.set_reuseport(true)?; - socket.bind(addr)?; - let listener = socket.listen(1024)?; - - let client = db::create().await.unwrap(); - - // unrealistic http dispatcher. no spec check. no security feature. - let service = Dispatcher::new(handler, State::new(client)); - - loop { - match listener.accept().await { - Ok((stream, _)) => { - let stream = stream.into_std()?; - let stream = TcpStream::from_std(stream)?; - let service = service.clone(); - tokio::task::spawn_local(async move { - let _ = service.call(stream).await; - }); - } - Err(e) => return Err(e), - }; - } - }) - }) + let id = ids.pop(); + std::thread::spawn(move || worker(id)) }) - .take(cores) + .take(cores - 1) .collect::>(); // unrealistic due to no signal handling, not shutdown handling. when killing this process all resources that // need clean async shutdown will be leaked. + worker(ids.pop())?; for handle in handle { handle.join().unwrap()?; } @@ -80,7 +87,7 @@ fn main() -> io::Result<()> { Ok(()) } -async fn handler<'h>(req: Request<'h>, res: Response<'h>, state: &State) -> Response<'h, 3> { +async fn handler<'h>(req: Request<'h, State>, res: Response<'h>) -> Response<'h, 3> { // unrealistic due to no http method check match req.path { // unrealistic due to no dynamic path matching @@ -91,8 +98,7 @@ async fn handler<'h>(req: Request<'h>, res: Response<'h>, state: &State(buf.extend_from_slice(b"Hello, World!"))) - .unwrap() + .body_writer(|buf| buf.extend_from_slice(b"Hello, World!")) } "/json" => res .status(StatusCode::OK) @@ -100,12 +106,12 @@ async fn handler<'h>(req: Request<'h>, res: Response<'h>, state: &State { use sailfish::TemplateOnce; - let fortunes = state.client.tell_fortune().await.unwrap().render_once().unwrap(); + let fortunes = req.ctx.client.tell_fortune().await.unwrap().render_once().unwrap(); res.status(StatusCode::OK) .header("content-type", "text/html; charset=utf-8") .header("server", "X") @@ -114,18 +120,18 @@ async fn handler<'h>(req: Request<'h>, res: Response<'h>, state: &State { // unrealistic due to no error handling. any db/serialization error will cause process crash. // the same goes for all following unwraps on database related functions. - let world = state.client.get_world().await.unwrap(); - json_response(res, state, &world) + let world = req.ctx.client.get_world().await.unwrap(); + json_response(res, req.ctx, &world) } p if p.starts_with("/q") => { let num = p["/queries?q=".len()..].parse_query(); - let worlds = state.client.get_worlds(num).await.unwrap(); - json_response(res, state, &worlds) + let worlds = req.ctx.client.get_worlds(num).await.unwrap(); + json_response(res, req.ctx, &worlds) } p if p.starts_with("/u") => { let num = p["/updates?q=".len()..].parse_query(); - let worlds = state.client.update(num).await.unwrap(); - json_response(res, state, &worlds) + let worlds = req.ctx.client.update(num).await.unwrap(); + json_response(res, req.ctx, &worlds) } _ => res.status(StatusCode::NOT_FOUND).header("server", "X").body(&[]), } diff --git a/frameworks/Scala/snunit/build.sbt b/frameworks/Scala/snunit/build.sbt index 5573cd526ec..e77aab72639 100644 --- a/frameworks/Scala/snunit/build.sbt +++ b/frameworks/Scala/snunit/build.sbt @@ -1,9 +1,9 @@ import scala.scalanative.build._ -scalaVersion := "2.13.10" +scalaVersion := "3.5.2" -val snunitVersion = "0.3.0" -val jsoniterScalaVersion = "2.20.6" +val snunitVersion = "0.10.2" +val jsoniterScalaVersion = "2.31.3" libraryDependencies ++= Seq( "com.github.lolgab" %%% "snunit" % snunitVersion, diff --git a/frameworks/Scala/snunit/project/build.properties b/frameworks/Scala/snunit/project/build.properties index 8b9a0b0ab03..db1723b0862 100644 --- a/frameworks/Scala/snunit/project/build.properties +++ b/frameworks/Scala/snunit/project/build.properties @@ -1 +1 @@ -sbt.version=1.8.0 +sbt.version=1.10.5 diff --git a/frameworks/Scala/snunit/project/plugins.sbt b/frameworks/Scala/snunit/project/plugins.sbt index d1565a95c3a..2f7a1e7541a 100644 --- a/frameworks/Scala/snunit/project/plugins.sbt +++ b/frameworks/Scala/snunit/project/plugins.sbt @@ -1 +1 @@ -addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.9") +addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.6") diff --git a/frameworks/Scala/snunit/snunit.dockerfile b/frameworks/Scala/snunit/snunit.dockerfile index bbadb49027e..f83e84354c2 100644 --- a/frameworks/Scala/snunit/snunit.dockerfile +++ b/frameworks/Scala/snunit/snunit.dockerfile @@ -1,12 +1,12 @@ -FROM debian:bullseye-slim as builder +FROM debian:bookworm-slim as builder RUN apt-get update && apt-get install -y curl gnupg && \ - echo "deb https://repo.scala-sbt.org/scalasbt/debian /" > /etc/apt/sources.list.d/sbt.list && \ + echo "deb https://repo.scala-sbt.org/scalasbt/debian all main" | tee /etc/apt/sources.list.d/sbt.list && \ curl -sL "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x2EE0EA64E40A89B84B2DF73499E82A75642AC823" | apt-key add - && \ curl -sL https://nginx.org/keys/nginx_signing.key | apt-key add - && \ - echo "deb https://packages.nginx.org/unit/debian/ bullseye unit" > /etc/apt/sources.list.d/unit.list && \ - echo "deb-src https://packages.nginx.org/unit/debian/ bullseye unit" >> /etc/apt/sources.list.d/unit.list && \ - apt-get update && apt-get install -y clang unit-dev=1.29* openjdk-11-jdk sbt && \ + echo "deb https://packages.nginx.org/unit/debian/ bookworm unit" > /etc/apt/sources.list.d/unit.list && \ + echo "deb-src https://packages.nginx.org/unit/debian/ bookworm unit" >> /etc/apt/sources.list.d/unit.list && \ + apt-get update && apt-get install -y clang unit-dev=1.33* openjdk-17-jdk sbt=1.10.5 && \ apt-get purge -y gnupg WORKDIR /workdir @@ -15,9 +15,9 @@ COPY . . RUN sbt nativeLink -FROM nginx/unit:1.29.1-minimal +FROM unit:1.33.0-minimal COPY /config.sh /docker-entrypoint.d/ -COPY --from=builder /workdir/target/scala-2.13/workdir-out /app/example +COPY --from=builder /workdir/target/scala-3.5.2/workdir /app/example EXPOSE 8080 diff --git a/frameworks/Scala/snunit/src/main/scala/Main.scala b/frameworks/Scala/snunit/src/main/scala/Main.scala index a6a6c54ae00..4fe53ab5d3c 100644 --- a/frameworks/Scala/snunit/src/main/scala/Main.scala +++ b/frameworks/Scala/snunit/src/main/scala/Main.scala @@ -1,43 +1,37 @@ -import com.github.plokhotnyuk.jsoniter_scala.core._ -import com.github.plokhotnyuk.jsoniter_scala.macros._ -import snunit._ +import com.github.plokhotnyuk.jsoniter_scala.core.* +import com.github.plokhotnyuk.jsoniter_scala.macros.* +import snunit.* -final case class Message(message: String) +final case class Message(message: String) derives ConfiguredJsonValueCodec -object Message { - implicit final val codec: JsonValueCodec[Message] = JsonCodecMaker.make -} +final val applicationJson = Headers("Content-Type" -> "application/json") +final val textPlain = Headers("Content-Type" -> "text/plain") -object Main { - val applicationJson = Seq("Content-Type" -> "application/json") - val textPlain = Seq("Content-Type" -> "text/plain") +inline def notFound(req: Request) = req.send( + statusCode = StatusCode.NotFound, + content = "Not found", + headers = textPlain +) - @inline - private def notFound(req: Request) = req.send( - statusCode = StatusCode.NotFound, - content = "Not found", - headers = textPlain - ) - - def main(args: Array[String]): Unit = { - SyncServerBuilder - .build(req => - if (req.method == Method.GET) { - if(req.target == "/plaintext") - req.send( - statusCode = StatusCode.OK, - content = "Hello, World!", - headers = textPlain - ) - else if(req.target == "/json") - req.send( - statusCode = StatusCode.OK, - content = writeToArray(Message("Hello, World!")), - headers = applicationJson - ) - else notFound(req) - } else notFound(req) - ) - .listen() - } -} +@main +def main = + SyncServerBuilder + .setRequestHandler(req => + if (req.method == Method.GET) { + if(req.target == "/plaintext") + req.send( + statusCode = StatusCode.OK, + content = "Hello, World!", + headers = textPlain + ) + else if(req.target == "/json") + req.send( + statusCode = StatusCode.OK, + content = writeToArray(Message("Hello, World!")), + headers = applicationJson + ) + else notFound(req) + } else notFound(req) + ) + .build() + .listen() diff --git a/frameworks/TypeScript/nest/nestjs-fastify-mongo.dockerfile b/frameworks/TypeScript/nest/nestjs-fastify-mongo.dockerfile index c7d40f6c6ab..d55bacee6a7 100644 --- a/frameworks/TypeScript/nest/nestjs-fastify-mongo.dockerfile +++ b/frameworks/TypeScript/nest/nestjs-fastify-mongo.dockerfile @@ -1,4 +1,4 @@ -FROM node:20.12.2-slim +FROM node:20.16-slim COPY ./ ./ diff --git a/frameworks/TypeScript/nest/nestjs-fastify-mysql.dockerfile b/frameworks/TypeScript/nest/nestjs-fastify-mysql.dockerfile index d5e7f4a0165..40b0da005c4 100644 --- a/frameworks/TypeScript/nest/nestjs-fastify-mysql.dockerfile +++ b/frameworks/TypeScript/nest/nestjs-fastify-mysql.dockerfile @@ -1,4 +1,4 @@ -FROM node:20.12.2-slim +FROM node:20.16-slim COPY ./ ./ diff --git a/frameworks/TypeScript/nest/nestjs-fastify.dockerfile b/frameworks/TypeScript/nest/nestjs-fastify.dockerfile index 1b3dc806754..485c360794e 100644 --- a/frameworks/TypeScript/nest/nestjs-fastify.dockerfile +++ b/frameworks/TypeScript/nest/nestjs-fastify.dockerfile @@ -1,4 +1,4 @@ -FROM node:20.12.2-slim +FROM node:20.16-slim COPY ./ ./ diff --git a/frameworks/TypeScript/nest/nestjs-mongo.dockerfile b/frameworks/TypeScript/nest/nestjs-mongo.dockerfile index 09fab943ed3..a59ac0c9e14 100644 --- a/frameworks/TypeScript/nest/nestjs-mongo.dockerfile +++ b/frameworks/TypeScript/nest/nestjs-mongo.dockerfile @@ -1,4 +1,4 @@ -FROM node:20.12.2-slim +FROM node:20.16-slim COPY ./ ./ diff --git a/frameworks/TypeScript/nest/nestjs-mysql.dockerfile b/frameworks/TypeScript/nest/nestjs-mysql.dockerfile index 80147df547d..9ff806b92a8 100644 --- a/frameworks/TypeScript/nest/nestjs-mysql.dockerfile +++ b/frameworks/TypeScript/nest/nestjs-mysql.dockerfile @@ -1,4 +1,4 @@ -FROM node:20.12.2-slim +FROM node:20.16-slim COPY ./ ./ diff --git a/frameworks/TypeScript/nest/nestjs.dockerfile b/frameworks/TypeScript/nest/nestjs.dockerfile index d2382bf8999..1ea6fd1581e 100644 --- a/frameworks/TypeScript/nest/nestjs.dockerfile +++ b/frameworks/TypeScript/nest/nestjs.dockerfile @@ -1,4 +1,4 @@ -FROM node:20.12.2-slim +FROM node:20.16-slim COPY ./ ./ diff --git a/frameworks/TypeScript/nest/src/main.ts b/frameworks/TypeScript/nest/src/main.ts index 82fa61ea07d..a095bfb746b 100644 --- a/frameworks/TypeScript/nest/src/main.ts +++ b/frameworks/TypeScript/nest/src/main.ts @@ -23,6 +23,7 @@ async function bootstrapExpress() { app = await NestFactory.create(SqlModule, { logger: false, }); + app.getHttpServer().keepAliveTimeout = 0; } app.setBaseViewsDir(join(__dirname, '..', 'views')); @@ -40,12 +41,14 @@ async function bootstrapFastify() { new FastifyAdapter(), { logger: false }, ); + app.getHttpServer().keepAliveTimeout = 0; } else { app = await NestFactory.create( SqlModule, new FastifyAdapter(), { logger: false }, ); + app.getHttpServer().keepAliveTimeout = 0; } app.setViewEngine({ diff --git a/frameworks/Ur/urweb/README.md b/frameworks/Ur/urweb/README.md index e529333fe96..534c52cbc18 100644 --- a/frameworks/Ur/urweb/README.md +++ b/frameworks/Ur/urweb/README.md @@ -19,5 +19,3 @@ To compile a standalone executable running on port 8080, run `urweb bench`. See `bench.ur` is the main source file. `bench.urs` is the signature file describing the module's exported functions. `bench.urp` is the project file giving compilation directives. `benchmark_config.json` includes metadata for the framework comparison. - -`__init__.py` and `setup.py` are for starting and stopping the Ur/Web server. `setup_mysql.py` is a variant using MySQL instead of PostgreSQL. diff --git a/frameworks/Ur/urweb/urweb-cache.dockerfile b/frameworks/Ur/urweb/urweb-cache.dockerfile index 69bfbafd60e..f4acd21176c 100644 --- a/frameworks/Ur/urweb/urweb-cache.dockerfile +++ b/frameworks/Ur/urweb/urweb-cache.dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:23.10 +FROM ubuntu:24.04 ADD ./ /urweb WORKDIR /urweb diff --git a/frameworks/Ur/urweb/urweb-mysql-cache.dockerfile b/frameworks/Ur/urweb/urweb-mysql-cache.dockerfile index 4bec91de9d8..2a21638a218 100644 --- a/frameworks/Ur/urweb/urweb-mysql-cache.dockerfile +++ b/frameworks/Ur/urweb/urweb-mysql-cache.dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:23.10 +FROM ubuntu:24.04 ADD ./ /urweb WORKDIR /urweb diff --git a/frameworks/Ur/urweb/urweb-mysql.dockerfile b/frameworks/Ur/urweb/urweb-mysql.dockerfile index bee66b290b0..3559a0ce988 100644 --- a/frameworks/Ur/urweb/urweb-mysql.dockerfile +++ b/frameworks/Ur/urweb/urweb-mysql.dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:23.10 +FROM ubuntu:24.04 ADD ./ /urweb WORKDIR /urweb diff --git a/frameworks/Ur/urweb/urweb.dockerfile b/frameworks/Ur/urweb/urweb.dockerfile index 6bbba474104..8976e26a1c6 100644 --- a/frameworks/Ur/urweb/urweb.dockerfile +++ b/frameworks/Ur/urweb/urweb.dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:23.10 +FROM ubuntu:24.04 ADD ./ /urweb WORKDIR /urweb diff --git a/frameworks/Zig/httpz/.gitignore b/frameworks/Zig/httpz/.gitignore index 170dc0f1403..d8c8979f82c 100644 --- a/frameworks/Zig/httpz/.gitignore +++ b/frameworks/Zig/httpz/.gitignore @@ -1,2 +1,2 @@ -zig-cache/**/*', -zig-out: 'zig-out/**/*', +.zig-cache +zig-out diff --git a/frameworks/Zig/httpz/build.zig b/frameworks/Zig/httpz/build.zig index 5978de7c6aa..510dfbecfa2 100644 --- a/frameworks/Zig/httpz/build.zig +++ b/frameworks/Zig/httpz/build.zig @@ -1,48 +1,23 @@ const std = @import("std"); -const ModuleMap = std.StringArrayHashMap(*std.Build.Module); -var gpa = std.heap.GeneralPurposeAllocator(.{}){}; -const allocator = gpa.allocator(); -// Although this function looks imperative, note that its job is to -// declaratively construct a build graph that will be executed by an external -// runner. pub fn build(b: *std.Build) !void { - // Standard target options allows the person running `zig build` to choose - // what target to build for. Here we do not override the defaults, which - // means any target is allowed, and the default is native. Other options - // for restricting supported target set are available. const target = b.standardTargetOptions(.{}); - - // Standard optimization options allow the person running `zig build` to select - // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do nots - // set a preferred release mode, allowing the user to decide how to optimize. const optimize = b.standardOptimizeOption(.{}); const dep_opts = .{ .target = target, .optimize = optimize }; const exe = b.addExecutable(.{ .name = "httpz", - // In this case the main source file is merely a path, however, in more - // complicated build scripts, this could be a generated file. .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, }); - var modules = ModuleMap.init(allocator); - defer modules.deinit(); - const httpz_module = b.dependency("httpz", dep_opts).module("httpz"); const pg_module = b.dependency("pg", dep_opts).module("pg"); const datetimez_module = b.dependency("datetimez", dep_opts).module("zig-datetime"); const mustache_module = b.dependency("mustache", dep_opts).module("mustache"); - try modules.put("httpz", httpz_module); - try modules.put("pg", pg_module); - try modules.put("datetimez", datetimez_module); - try modules.put("mustache", mustache_module); - - // // Expose this as a module that others can import exe.root_module.addImport("httpz", httpz_module); exe.root_module.addImport("pg", pg_module); exe.root_module.addImport("datetimez", datetimez_module); diff --git a/frameworks/Zig/httpz/run.sh b/frameworks/Zig/httpz/run.sh index 582c2ad0228..a849756041d 100644 --- a/frameworks/Zig/httpz/run.sh +++ b/frameworks/Zig/httpz/run.sh @@ -1,3 +1,3 @@ echo "Waiting for Httpz framework to start..." -httpz \ No newline at end of file +httpz 3000 \ No newline at end of file diff --git a/frameworks/Zig/httpz/src/endpoints.zig b/frameworks/Zig/httpz/src/endpoints.zig index 0ee22b274de..a0f4476dab6 100644 --- a/frameworks/Zig/httpz/src/endpoints.zig +++ b/frameworks/Zig/httpz/src/endpoints.zig @@ -4,22 +4,16 @@ const pg = @import("pg"); const datetimez = @import("datetimez"); const mustache = @import("mustache"); -const Allocator = std.mem.Allocator; const Thread = std.Thread; const Mutex = Thread.Mutex; const template = "Fortunes{{#fortunes}}{{/fortunes}}
idmessage
{{id}}{{message}}
"; pub const Global = struct { pool: *pg.Pool, - prng: *std.rand.DefaultPrng, - allocator: Allocator, + rand: *std.rand.Random, mutex: std.Thread.Mutex = .{}, }; -const Message = struct { - message: []const u8, -}; - const World = struct { id: i32, randomNumber: i32, @@ -30,26 +24,24 @@ const Fortune = struct { message: []const u8, }; -pub fn plaintext(global: *Global, _: *httpz.Request, res: *httpz.Response) !void { - try setHeaders(global.allocator, res); +pub fn plaintext(_: *Global, _: *httpz.Request, res: *httpz.Response) !void { + try setHeaders(res.arena, res); res.content_type = .TEXT; res.body = "Hello, World!"; } -pub fn json(global: *Global, _: *httpz.Request, res: *httpz.Response) !void { - try setHeaders(global.allocator, res); +pub fn json(_: *Global, _: *httpz.Request, res: *httpz.Response) !void { + try setHeaders(res.arena, res); - const message = Message{ .message = "Hello, World!" }; - - try res.json(message, .{}); + try res.json(.{ .message = "Hello, World!" }, .{}); } pub fn db(global: *Global, _: *httpz.Request, res: *httpz.Response) !void { - try setHeaders(global.allocator, res); + try setHeaders(res.arena, res); global.mutex.lock(); - const random_number = 1 + (global.prng.random().uintAtMost(u32, 9999)); + const random_number = 1 + (global.rand.uintAtMostBiased(u32, 9999)); global.mutex.unlock(); const world = getWorld(global.pool, random_number) catch |err| { @@ -61,15 +53,15 @@ pub fn db(global: *Global, _: *httpz.Request, res: *httpz.Response) !void { } pub fn fortune(global: *Global, _: *httpz.Request, res: *httpz.Response) !void { - try setHeaders(global.allocator, res); + try setHeaders(res.arena, res); - const fortunes_html = try getFortunesHtml(global.allocator, global.pool); + const fortunes_html = try getFortunesHtml(res.arena, global.pool); res.header("content-type", "text/html; charset=utf-8"); res.body = fortunes_html; } -fn getWorld(pool: *pg.Pool, random_number: u32) !World{ +fn getWorld(pool: *pg.Pool, random_number: u32) !World { var conn = try pool.acquire(); defer conn.release(); @@ -81,7 +73,7 @@ fn getWorld(pool: *pg.Pool, random_number: u32) !World{ return World{ .id = row.get(i32, 0), .randomNumber = row.get(i32, 1) }; } -fn setHeaders(allocator: Allocator, res: *httpz.Response) !void { +fn setHeaders(allocator: std.mem.Allocator, res: *httpz.Response) !void { res.header("Server", "Httpz"); const now = datetimez.datetime.Date.now(); @@ -97,10 +89,10 @@ fn setHeaders(allocator: Allocator, res: *httpz.Response) !void { res.header("Date", now_str); } -fn getFortunesHtml(allocator: Allocator, pool: *pg.Pool) ![]const u8 { +fn getFortunesHtml(allocator: std.mem.Allocator, pool: *pg.Pool) ![]const u8 { const fortunes = try getFortunes(allocator, pool); - const raw = try mustache.allocRenderText(allocator, template,.{ .fortunes = fortunes }); + const raw = try mustache.allocRenderText(allocator, template, .{ .fortunes = fortunes }); // std.debug.print("mustache output {s}\n", .{raw}); @@ -111,7 +103,7 @@ fn getFortunesHtml(allocator: Allocator, pool: *pg.Pool) ![]const u8 { return html; } -fn getFortunes(allocator: Allocator, pool: *pg.Pool) ![]const Fortune { +fn getFortunes(allocator: std.mem.Allocator, pool: *pg.Pool) ![]const Fortune { var conn = try pool.acquire(); defer conn.release(); @@ -139,7 +131,7 @@ fn cmpFortuneByMessage(_: void, a: Fortune, b: Fortune) bool { return std.mem.order(u8, a.message, b.message).compare(std.math.CompareOperator.lt); } -fn deescapeHtml(allocator: Allocator, input: []const u8) ![]const u8 { +fn deescapeHtml(allocator: std.mem.Allocator, input: []const u8) ![]const u8 { var output = std.ArrayList(u8).init(allocator); defer output.deinit(); @@ -189,4 +181,3 @@ fn deescapeHtml(allocator: Allocator, input: []const u8) ![]const u8 { return output.toOwnedSlice(); } - diff --git a/frameworks/Zig/httpz/src/main.zig b/frameworks/Zig/httpz/src/main.zig index ae2c1a70ac4..b7fd4850bc2 100644 --- a/frameworks/Zig/httpz/src/main.zig +++ b/frameworks/Zig/httpz/src/main.zig @@ -7,13 +7,10 @@ const pool = @import("pool.zig"); const endpoints = @import("endpoints.zig"); -const RndGen = std.rand.DefaultPrng; -const Allocator = std.mem.Allocator; -const Pool = pg.Pool; - -var server: httpz.ServerCtx(*endpoints.Global,*endpoints.Global) = undefined; +var server: httpz.ServerCtx(*endpoints.Global, *endpoints.Global) = undefined; pub fn main() !void { + const cpu_count = try std.Thread.getCpuCount(); var gpa = std.heap.GeneralPurposeAllocator(.{ .thread_safe = true, }){}; @@ -25,15 +22,107 @@ pub fn main() !void { var prng = std.rand.DefaultPrng.init(@as(u64, @bitCast(std.time.milliTimestamp()))); - var global = endpoints.Global{ .pool = pg_pool, .prng = &prng, .allocator = allocator }; + var rand = prng.random(); + + var global = endpoints.Global{ + .pool = pg_pool, + .rand = &rand, + }; + + const args = try std.process.argsAlloc(allocator); + + const port: u16 = if (args.len > 1) try std.fmt.parseInt(u16, args[1], 0) else 3000; + + const workers = @as(u16, @intCast(16 * cpu_count)); server = try httpz.ServerApp(*endpoints.Global).init(allocator, .{ - .port = 3000, .address = "0.0.0.0", }, &global); + .port = port, + .address = "0.0.0.0", + .workers = .{ + // Number of worker threads + // (blocking mode: handled differently) + .count = workers, + + // Maximum number of concurrent connection each worker can handle + // (blocking mode: currently ignored) + .max_conn = 8_192, + + // Minimum number of connection states each worker should maintain + // (blocking mode: currently ignored) + .min_conn = 64, + + // A pool of larger buffers that can be used for any data larger than configured + // static buffers. For example, if response headers don't fit in in + // $response.header_buffer_size, a buffer will be pulled from here. + // This is per-worker. + .large_buffer_count = 16, + + // The size of each large buffer. + .large_buffer_size = 65536, + + // Size of bytes retained for the connection arena between use. This will + // result in up to `count * min_conn * retain_allocated_bytes` of memory usage. + .retain_allocated_bytes = 4096, + }, + + // configures the threadpool which processes requests. The threadpool is + // where your application code runs. + .thread_pool = .{ + // Number threads. If you're handlers are doing a lot of i/o, a higher + // number might provide better throughput + // (blocking mode: handled differently) + .count = 256, + + // The maximum number of pending requests that the thread pool will accept + // This applies back pressure to the above workers and ensures that, under load + // pending requests get precedence over processing new requests. + .backlog = 2048, + + // Size of the static buffer to give each thread. Memory usage will be + // `count * buffer_size`. If you're making heavy use of either `req.arena` or + // `res.arena`, this is likely the single easiest way to gain performance. + .buffer_size = 8192, + }, + .request = .{ + // Maximum request body size that we'll process. We can allocate up + // to this much memory per request for the body. Internally, we might + // keep this memory around for a number of requests as an optimization. + .max_body_size = 1_048_576, + + // This memory is allocated upfront. The request header _must_ fit into + // this space, else the request will be rejected. + .buffer_size = 4_096, + + // Maximum number of headers to accept. + // Additional headers will be silently ignored. + .max_header_count = 32, + + // Maximum number of URL parameters to accept. + // Additional parameters will be silently ignored. + .max_param_count = 0, + + // Maximum number of query string parameters to accept. + // Additional parameters will be silently ignored. + .max_query_count = 0, + + // Maximum number of x-www-form-urlencoded fields to support. + // Additional parameters will be silently ignored. This must be + // set to a value greater than 0 (the default) if you're going + // to use the req.formData() method. + .max_form_count = 0, + + // Maximum number of multipart/form-data fields to support. + // Additional parameters will be silently ignored. This must be + // set to a value greater than 0 (the default) if you're going + // to use the req.multiFormData() method. + .max_multiform_count = 0, + }, + }, &global); defer server.deinit(); // now that our server is up, we register our intent to handle SIGINT try std.posix.sigaction(std.posix.SIG.INT, &.{ - .handler = .{.handler = shutdown}, + .handler = .{ .handler = shutdown }, .mask = std.posix.empty_sigset, .flags = 0, }, null); @@ -44,28 +133,22 @@ pub fn main() !void { router.get("/db", endpoints.db); router.get("/fortunes", endpoints.fortune); - std.debug.print("Httpz listening at 0.0.0.0:{d}\n", .{3000}); + std.debug.print("Httpz using {d} workers listening at 0.0.0.0:{d}\n", .{ workers, port }); try server.listen(); } fn shutdown(_: c_int) callconv(.C) void { - // this will unblock the server.listen() server.stop(); } fn notFound(_: *httpz.Request, res: *httpz.Response) !void { res.status = 404; - - // you can set the body directly to a []u8, but note that the memory - // must be valid beyond your handler. Use the res.arena if you need to allocate - // memory for the body. res.body = "Not Found"; } -// note that the error handler return `void` and not `!void` fn errorHandler(req: *httpz.Request, res: *httpz.Response, err: anyerror) void { res.status = 500; res.body = "Internal Server Error"; - std.log.warn("httpz: unhandled exception for request: {s}\nErr: {}", .{req.url.raw, err}); -} \ No newline at end of file + std.log.warn("httpz: unhandled exception for request: {s}\nErr: {}", .{ req.url.raw, err }); +} diff --git a/frameworks/Zig/httpz/src/pool.zig b/frameworks/Zig/httpz/src/pool.zig index c41cb329540..91867be7906 100644 --- a/frameworks/Zig/httpz/src/pool.zig +++ b/frameworks/Zig/httpz/src/pool.zig @@ -11,7 +11,7 @@ pub fn initPool(allocator: Allocator) !*pg.Pool { //std.debug.print("Connection: {s}:{s}@{s}:{d}/{s}\n", .{ info.username, info.password, info.hostname, info.port, info.database }); const pg_pool = try Pool.init(allocator, .{ - .size = 28, + .size = 56, .connect = .{ .port = info.port, .host = info.hostname, diff --git a/toolset/databases/mongodb/mongodb.dockerfile b/toolset/databases/mongodb/mongodb.dockerfile index 4fe163b8070..11480684535 100644 --- a/toolset/databases/mongodb/mongodb.dockerfile +++ b/toolset/databases/mongodb/mongodb.dockerfile @@ -1,4 +1,4 @@ -FROM mongo:7.0 +FROM mongo:8.0 ENV MONGO_INITDB_DATABASE=hello_world